summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/GraphEdit.xml11
-rw-r--r--editor/editor_node.cpp3
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp68
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h7
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp4
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp5
-rw-r--r--scene/gui/graph_edit.cpp60
-rw-r--r--scene/gui/graph_edit.h18
-rw-r--r--scene/gui/view_panner.cpp136
-rw-r--r--scene/gui/view_panner.h63
10 files changed, 311 insertions, 64 deletions
diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml
index 46960d7cc4..fd34e340e7 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -214,6 +214,9 @@
<member name="minimap_size" type="Vector2" setter="set_minimap_size" getter="get_minimap_size" default="Vector2(240, 160)">
The size of the minimap rectangle. The map itself is based on the size of the grid area and is scaled to fit this rectangle.
</member>
+ <member name="panning_scheme" type="int" setter="set_panning_scheme" getter="get_panning_scheme" enum="GraphEdit.PanningScheme" default="0">
+ Defines the control scheme for panning with mouse wheel.
+ </member>
<member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" />
<member name="right_disconnects" type="bool" setter="set_right_disconnects" getter="is_right_disconnects_enabled" default="false">
If [code]true[/code], enables disconnection of existing connections in the GraphEdit by dragging the right end.
@@ -345,6 +348,14 @@
</description>
</signal>
</signals>
+ <constants>
+ <constant name="SCROLL_ZOOMS" value="0" enum="PanningScheme">
+ [kbd]Mouse Wheel[/kbd] will zoom, [kbd]Ctrl + Mouse Wheel[/kbd] will move the view.
+ </constant>
+ <constant name="SCROLL_PANS" value="1" enum="PanningScheme">
+ [kbd]Mouse Wheel[/kbd] will move the view, [kbd]Ctrl + Mouse Wheel[/kbd] will zoom.
+ </constant>
+ </constants>
<theme_items>
<theme_item name="activity" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
</theme_item>
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index afd5407f37..26ce78c0c6 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6038,6 +6038,9 @@ EditorNode::EditorNode() {
EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_VHS_CIRCLE);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle", PROPERTY_USAGE_DEFAULT));
EDITOR_DEF("run/auto_save/save_before_running", true);
+ EDITOR_DEF("interface/editors/sub_editor_panning_scheme", 0);
+ // Should be in sync with ControlScheme in ViewPanner.
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/editors/sub_editor_panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans", PROPERTY_USAGE_DEFAULT));
const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
for (const String &E : textfile_ext) {
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index c85956991a..24ede3b85e 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -37,57 +37,31 @@
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/gui/texture_rect.h"
+#include "scene/gui/view_panner.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
- Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid()) {
- drag_type = DRAG_TYPE_NONE;
-
- Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_LEFT) - (mb->get_button_index() == MouseButton::WHEEL_RIGHT), (mb->get_button_index() == MouseButton::WHEEL_UP) - (mb->get_button_index() == MouseButton::WHEEL_DOWN));
- if (scroll_vec != Vector2()) {
- if (mb->is_ctrl_pressed()) {
- if (mb->is_shift_pressed()) {
- panning.x += 32 * mb->get_factor() * scroll_vec.y;
- panning.y += 32 * mb->get_factor() * scroll_vec.x;
- } else {
- panning.y += 32 * mb->get_factor() * scroll_vec.y;
- panning.x += 32 * mb->get_factor() * scroll_vec.x;
- }
-
- emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
- _update_zoom_and_panning(true);
- accept_event();
+ if (panner->gui_input(p_event)) {
+ accept_event();
+ }
+}
- } else if (!mb->is_shift_pressed()) {
- zoom_widget->set_zoom_by_increments(scroll_vec.y * 2);
- emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
- _update_zoom_and_panning(true);
- accept_event();
- }
- }
+void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec) {
+ _pan_callback(-p_scroll_vec * 32);
+}
- if (mb->get_button_index() == MouseButton::MIDDLE || mb->get_button_index() == MouseButton::RIGHT) {
- if (mb->is_pressed()) {
- drag_type = DRAG_TYPE_PAN;
- } else {
- drag_type = DRAG_TYPE_NONE;
- }
- accept_event();
- }
- }
+void TileAtlasView::_pan_callback(Vector2 p_scroll_vec) {
+ panning += p_scroll_vec;
+ emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
+ _update_zoom_and_panning(true);
+}
- Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid()) {
- if (drag_type == DRAG_TYPE_PAN) {
- panning += mm->get_relative();
- _update_zoom_and_panning();
- emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
- accept_event();
- }
- }
+void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) {
+ zoom_widget->set_zoom_by_increments(-p_scroll_vec.y * 2);
+ emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
+ _update_zoom_and_panning(true);
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
@@ -548,6 +522,11 @@ void TileAtlasView::update() {
void TileAtlasView::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED:
+ panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ break;
+
case NOTIFICATION_READY:
button_center_view->set_icon(get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
break;
@@ -561,6 +540,9 @@ void TileAtlasView::_bind_methods() {
TileAtlasView::TileAtlasView() {
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
+
Panel *panel = memnew(Panel);
panel->set_clip_contents(true);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index ca7f083132..6a0e0ae820 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -41,6 +41,8 @@
#include "scene/gui/texture_rect.h"
#include "scene/resources/tile_set.h"
+class ViewPanner;
+
class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
@@ -64,6 +66,11 @@ private:
void _center_view();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin);
+
Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 03797b1797..cca97d7105 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -3209,6 +3209,10 @@ void VisualShaderEditor::_notification(int p_what) {
}
}
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ }
+
if (p_what == NOTIFICATION_DRAG_BEGIN) {
Dictionary dd = get_viewport()->gui_get_drag_data();
if (members->is_visible_in_tree() && dd.has("id")) {
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index bc40f4f9b4..80e7862d9d 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -3753,6 +3753,11 @@ void VisualScriptEditor::_toggle_scripts_pressed() {
void VisualScriptEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ } break;
+
case NOTIFICATION_READY: {
variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph), varray(-1), CONNECT_DEFERRED);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 378bfb69dc..79b73f7cc3 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
+#include "scene/gui/view_panner.h"
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
@@ -1069,13 +1070,9 @@ void GraphEdit::set_selected(Node *p_child) {
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
+ panner->gui_input(p_ev);
Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && Input::get_singleton()->is_key_pressed(Key::SPACE)))) {
- Vector2i relative = Input::get_singleton()->warp_mouse_motion(mm, get_global_rect());
- h_scroll->set_value(h_scroll->get_value() - relative.x);
- v_scroll->set_value(v_scroll->get_value() - relative.y);
- }
if (mm.is_valid() && dragging) {
if (!moving_selection) {
@@ -1327,22 +1324,6 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
top_layer->update();
minimap->update();
}
-
- int scroll_direction = (b->get_button_index() == MouseButton::WHEEL_DOWN) - (b->get_button_index() == MouseButton::WHEEL_UP);
- if (scroll_direction != 0) {
- if (b->is_ctrl_pressed()) {
- if (b->is_shift_pressed()) {
- // Horizontal scrolling.
- h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- } else {
- // Vertical scrolling.
- v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- }
- } else {
- // Zooming.
- set_zoom_custom(scroll_direction < 0 ? zoom * zoom_step : zoom / zoom_step, b->get_position());
- }
- }
}
if (p_ev->is_pressed()) {
@@ -1373,6 +1354,23 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
+void GraphEdit::_scroll_callback(Vector2 p_scroll_vec) {
+ if (p_scroll_vec.x != 0) {
+ h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x));
+ } else {
+ v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y));
+ }
+}
+
+void GraphEdit::_pan_callback(Vector2 p_scroll_vec) {
+ h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x);
+ v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y);
+}
+
+void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) {
+ set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin);
+}
+
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
for (Connection &E : connections) {
if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) {
@@ -1406,6 +1404,15 @@ void GraphEdit::force_connection_drag_end() {
emit_signal(SNAME("connection_drag_ended"));
}
+void GraphEdit::set_panning_scheme(PanningScheme p_scheme) {
+ panning_scheme = p_scheme;
+ panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme);
+}
+
+GraphEdit::PanningScheme GraphEdit::get_panning_scheme() const {
+ return panning_scheme;
+}
+
void GraphEdit::set_zoom(float p_zoom) {
set_zoom_custom(p_zoom, get_size() / 2);
}
@@ -2190,6 +2197,9 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
ClassDB::bind_method(D_METHOD("get_connection_line", "from", "to"), &GraphEdit::get_connection_line);
+ ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
+ ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
+
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom);
@@ -2244,6 +2254,7 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset"), "set_scroll_ofs", "get_scroll_ofs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
ADD_GROUP("Connection Lines", "connection_lines");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness");
@@ -2277,6 +2288,9 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs")));
ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::STRING, "slot"), PropertyInfo(Variant::BOOL, "is_output")));
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
+
+ BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
+ BIND_ENUM_CONSTANT(SCROLL_PANS);
}
GraphEdit::GraphEdit() {
@@ -2289,6 +2303,10 @@ GraphEdit::GraphEdit() {
// Allow zooming 4 times from the default zoom level.
zoom_max = (1 * Math::pow(zoom_step, 4));
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback));
+ panner->set_disable_rmb(true);
+
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 145e0dcc59..4e998d30a7 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -41,6 +41,7 @@
#include "scene/gui/texture_rect.h"
class GraphEdit;
+class ViewPanner;
class GraphEditFilter : public Control {
GDCLASS(GraphEditFilter, Control);
@@ -103,6 +104,12 @@ public:
float activity = 0.0;
};
+ // Should be in sync with ControlScheme in ViewPanner.
+ enum PanningScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
private:
Label *zoom_label;
Button *zoom_minus;
@@ -122,6 +129,11 @@ private:
float port_grab_distance_horizontal = 0.0;
float port_grab_distance_vertical;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin);
+
bool connecting = false;
String connecting_from;
bool connecting_out = false;
@@ -136,6 +148,7 @@ private:
bool connecting_valid = false;
Vector2 click_pos;
+ PanningScheme panning_scheme = SCROLL_ZOOMS;
bool dragging = false;
bool just_selected = false;
bool moving_selection = false;
@@ -277,6 +290,9 @@ public:
void remove_valid_connection_type(int p_type, int p_with_type);
bool is_valid_connection_type(int p_type, int p_with_type) const;
+ void set_panning_scheme(PanningScheme p_scheme);
+ PanningScheme get_panning_scheme() const;
+
void set_zoom(float p_zoom);
void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const;
@@ -338,4 +354,6 @@ public:
GraphEdit();
};
+VARIANT_ENUM_CAST(GraphEdit::PanningScheme);
+
#endif // GRAPHEdit_H
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
new file mode 100644
index 0000000000..375b3732a4
--- /dev/null
+++ b/scene/gui/view_panner.cpp
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* view_panner.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "view_panner.h"
+
+#include "core/input/input.h"
+#include "core/os/keyboard.h"
+
+bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP));
+ if (scroll_vec != Vector2()) {
+ if (control_scheme == SCROLL_PANS) {
+ if (mb->is_ctrl_pressed()) {
+ callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ } else {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, panning);
+
+ return true;
+ }
+ } else {
+ if (mb->is_ctrl_pressed()) {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, panning);
+
+ return true;
+ } else if (!mb->is_shift_pressed()) {
+ callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ return true;
+ }
+ }
+ }
+
+ if (mb->get_button_index() == MouseButton::MIDDLE || (mb->get_button_index() == MouseButton::RIGHT && !disable_rmb) || (mb->get_button_index() == MouseButton::LEFT && (Input::get_singleton()->is_key_pressed(Key::SPACE) || !mb->is_pressed()))) {
+ if (mb->is_pressed()) {
+ is_dragging = true;
+ } else {
+ is_dragging = false;
+ }
+ return true;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ if (is_dragging) {
+ if (p_canvas_rect != Rect2()) {
+ callback_helper(pan_callback, Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect));
+ } else {
+ callback_helper(pan_callback, mm->get_relative());
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ViewPanner::callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2) {
+ if (p_callback == zoom_callback) {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * 2);
+ Variant var1 = p_arg1;
+ argptr[0] = &var1;
+ Variant var2 = p_arg2;
+ argptr[1] = &var2;
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, 2, result, ce);
+ } else {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
+ Variant var = p_arg1;
+ argptr[0] = &var;
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, 1, result, ce);
+ }
+}
+
+void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) {
+ scroll_callback = p_scroll_callback;
+ pan_callback = p_pan_callback;
+ zoom_callback = p_zoom_callback;
+}
+
+void ViewPanner::set_control_scheme(ControlScheme p_scheme) {
+ control_scheme = p_scheme;
+}
+
+void ViewPanner::set_disable_rmb(bool p_disable) {
+ disable_rmb = p_disable;
+}
diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h
new file mode 100644
index 0000000000..e083d83de4
--- /dev/null
+++ b/scene/gui/view_panner.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* view_panner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VIEW_PANNER_H
+#define VIEW_PANNER_H
+
+#include "core/object/ref_counted.h"
+
+class InputEvent;
+
+class ViewPanner : public RefCounted {
+ GDCLASS(ViewPanner, RefCounted);
+
+ bool is_dragging = false;
+ bool disable_rmb = false;
+
+ Callable scroll_callback;
+ Callable pan_callback;
+ Callable zoom_callback;
+
+ void callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2 = Vector2());
+
+public:
+ enum ControlScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+ ControlScheme control_scheme = SCROLL_ZOOMS;
+
+ void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback);
+ void set_control_scheme(ControlScheme p_scheme);
+ void set_disable_rmb(bool p_disable);
+ bool gui_input(const Ref<InputEvent> &p_ev, Rect2 p_canvas_rect = Rect2());
+};
+
+#endif // VIEW_PANNER_H