Răsfoiți Sursa

Add drag zoom feature with CTRL+MiddleMouseButton

This change introduces a DragType enum to scene/gui/view_panner.cpp of
dragging, which includes:

- DRAG_TYPE_NONE: Not dragging
- DRAG_TYPE_PAN: Panning (dragging using MMB)
- DRAG_TYPE_ZOOM: Zooming (dragging using CTRL+MMB)

The goal of this change is the third option, which was already available
in 3D viewport but not in 2D. This feature should work in other editors
as well such as Animation Track Editor and Visual Shader Editor and so
on.
hamid 3 luni în urmă
părinte
comite
82e23da12e

+ 3 - 0
doc/classes/EditorSettings.xml

@@ -582,6 +582,9 @@
 		<member name="editors/panning/warped_mouse_panning" type="bool" setter="" getter="">
 			If [code]true[/code], warps the mouse around the 2D viewport while panning in the 2D editor. This makes it possible to pan over a large area without having to exit panning and adjust the mouse cursor.
 		</member>
+		<member name="editors/panning/zoom_style" type="int" setter="" getter="">
+			The mouse cursor movement direction to use when drag-zooming in any editor (except 3D scene editor) by moving the mouse. This does not affect zooming with the mouse wheel.
+		</member>
 		<member name="editors/polygon_editor/auto_bake_delay" type="float" setter="" getter="">
 			The delay in seconds until more complex and performance costly polygon editors commit their outlines, e.g. the 2D navigation polygon editor rebakes the navigation mesh polygons. A negative value stops the auto bake.
 		</member>

+ 1 - 0
editor/editor_settings.cpp

@@ -920,6 +920,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("editors/panning/simple_panning", false);
 	_initial_set("editors/panning/warped_mouse_panning", true);
 	_initial_set("editors/panning/2d_editor_pan_speed", 20, true);
+	EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "editors/panning/zoom_style", 0, "Vertical,Horizontal");
 
 	// Tiles editor
 	_initial_set("editors/tiles_editor/display_grid", true);

+ 1 - 0
editor/plugins/canvas_item_editor_plugin.cpp

@@ -4129,6 +4129,7 @@ void CanvasItemEditor::_update_editor_settings() {
 	panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
 	panner->set_scroll_speed(EDITOR_GET("editors/panning/2d_editor_pan_speed"));
 	panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
+	panner->set_zoom_style((ViewPanner::ZoomStyle)EDITOR_GET("editors/panning/zoom_style").operator int());
 }
 
 void CanvasItemEditor::_project_settings_changed() {

+ 3 - 4
editor/plugins/tiles/tile_atlas_view.cpp

@@ -52,7 +52,7 @@ void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event)
 
 void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
 	zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
-	_update_zoom_and_panning(true);
+	_update_zoom_and_panning(true, p_origin);
 	emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
 }
 
@@ -92,7 +92,7 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
 	return size;
 }
 
-void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
+void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos, const Vector2 &p_mouse_pos) {
 	if (tile_set_atlas_source.is_null()) {
 		return;
 	}
@@ -132,8 +132,7 @@ void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
 
 	// Zoom on the position.
 	if (p_zoom_on_mouse_pos) {
-		// Offset the panning relative to the center of panel.
-		Vector2 relative_mpos = get_local_mouse_position() - get_size() / 2;
+		Vector2 relative_mpos = p_mouse_pos - get_size() / 2;
 		panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;
 	} else {
 		// Center of panel.

+ 1 - 1
editor/plugins/tiles/tile_atlas_view.h

@@ -58,7 +58,7 @@ private:
 	Button *button_center_view = nullptr;
 	CenterContainer *center_container = nullptr;
 	Vector2 panning;
-	void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false);
+	void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false, const Vector2 &p_mouse_pos = Vector2());
 	void _zoom_widget_changed();
 	void _center_view();
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;

+ 39 - 9
scene/gui/view_panner.cpp

@@ -92,16 +92,26 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
 			return false;
 		}
 
-		bool is_drag_event = mb->get_button_index() == MouseButton::MIDDLE ||
+		drag_type = DragType::DRAG_TYPE_NONE;
+
+		bool is_drag_zoom_event = mb->get_button_index() == MouseButton::MIDDLE && mb->is_ctrl_pressed();
+
+		if (is_drag_zoom_event) {
+			if (mb->is_pressed()) {
+				drag_type = DragType::DRAG_TYPE_ZOOM;
+				drag_zoom_position = mb->get_position();
+			}
+			return true;
+		}
+
+		bool is_drag_pan_event = mb->get_button_index() == MouseButton::MIDDLE ||
 				(enable_rmb && mb->get_button_index() == MouseButton::RIGHT) ||
 				(!simple_panning_enabled && mb->get_button_index() == MouseButton::LEFT && is_panning()) ||
 				(force_drag && mb->get_button_index() == MouseButton::LEFT);
 
-		if (is_drag_event) {
+		if (is_drag_pan_event) {
 			if (mb->is_pressed()) {
-				is_dragging = true;
-			} else {
-				is_dragging = false;
+				drag_type = DragType::DRAG_TYPE_PAN;
 			}
 			return mb->get_button_index() != MouseButton::LEFT || mb->is_pressed(); // Don't consume LMB release events (it fixes some selection problems).
 		}
@@ -109,13 +119,23 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
 
 	Ref<InputEventMouseMotion> mm = p_event;
 	if (mm.is_valid()) {
-		if (is_dragging) {
+		if (drag_type == DragType::DRAG_TYPE_PAN) {
 			if (warped_panning_viewport && p_canvas_rect.has_area()) {
 				pan_callback.call(warped_panning_viewport->wrap_mouse_in_rect(mm->get_relative(), p_canvas_rect), p_event);
 			} else {
 				pan_callback.call(mm->get_relative(), p_event);
 			}
 			return true;
+		} else if (drag_type == DragType::DRAG_TYPE_ZOOM) {
+			float drag_zoom_distance = 0.0;
+			if (zoom_style == ZoomStyle::ZOOM_VERTICAL) {
+				drag_zoom_distance = mm->get_relative().y;
+			} else if (zoom_style == ZoomStyle::ZOOM_HORIZONTAL) {
+				drag_zoom_distance = mm->get_relative().x * -1.0; // Needs to be flipped to match the 3D horizontal zoom style.
+			}
+			float drag_zoom_factor = 1.0 + (drag_zoom_distance * scroll_zoom_factor * drag_zoom_sensitivity_factor);
+			zoom_callback.call(drag_zoom_factor, drag_zoom_position, p_event);
+			return true;
 		}
 	}
 
@@ -157,7 +177,11 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
 		if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) {
 			pan_key_pressed = k->is_pressed();
 			if (simple_panning_enabled || Input::get_singleton()->get_mouse_button_mask().has_flag(MouseButtonMask::LEFT)) {
-				is_dragging = pan_key_pressed;
+				if (pan_key_pressed) {
+					drag_type = DragType::DRAG_TYPE_PAN;
+				} else if (drag_type == DragType::DRAG_TYPE_PAN) {
+					drag_type = DragType::DRAG_TYPE_NONE;
+				}
 			}
 			return true;
 		}
@@ -168,7 +192,9 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
 
 void ViewPanner::release_pan_key() {
 	pan_key_pressed = false;
-	is_dragging = false;
+	if (drag_type == DragType::DRAG_TYPE_PAN) {
+		drag_type = DragType::DRAG_TYPE_NONE;
+	}
 }
 
 void ViewPanner::set_callbacks(Callable p_pan_callback, Callable p_zoom_callback) {
@@ -207,6 +233,10 @@ void ViewPanner::set_pan_axis(PanAxis p_pan_axis) {
 	pan_axis = p_pan_axis;
 }
 
+void ViewPanner::set_zoom_style(ZoomStyle p_zoom_style) {
+	zoom_style = p_zoom_style;
+}
+
 void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) {
 	set_control_scheme(p_scheme);
 	set_pan_shortcut(p_shortcut);
@@ -218,7 +248,7 @@ void ViewPanner::setup_warped_panning(Viewport *p_viewport, bool p_allowed) {
 }
 
 bool ViewPanner::is_panning() const {
-	return is_dragging || pan_key_pressed;
+	return (drag_type == DragType::DRAG_TYPE_PAN) || pan_key_pressed;
 }
 
 void ViewPanner::set_force_drag(bool p_force) {

+ 19 - 1
scene/gui/view_panner.h

@@ -51,15 +51,32 @@ public:
 		PAN_AXIS_VERTICAL,
 	};
 
+	enum DragType {
+		DRAG_TYPE_NONE,
+		DRAG_TYPE_PAN,
+		DRAG_TYPE_ZOOM,
+	};
+
+	enum ZoomStyle {
+		ZOOM_VERTICAL,
+		ZOOM_HORIZONTAL,
+	};
+
 private:
 	int scroll_speed = 32;
 	float scroll_zoom_factor = 1.1;
 	PanAxis pan_axis = PAN_AXIS_BOTH;
 
-	bool is_dragging = false;
 	bool pan_key_pressed = false;
 	bool force_drag = false;
 
+	DragType drag_type = DragType::DRAG_TYPE_NONE;
+
+	ZoomStyle zoom_style = ZoomStyle::ZOOM_VERTICAL;
+
+	Vector2 drag_zoom_position;
+	float drag_zoom_sensitivity_factor = -0.01f;
+
 	bool enable_rmb = false;
 	bool simple_panning_enabled = false;
 
@@ -80,6 +97,7 @@ public:
 	void set_scroll_speed(int p_scroll_speed);
 	void set_scroll_zoom_factor(float p_scroll_zoom_factor);
 	void set_pan_axis(PanAxis p_pan_axis);
+	void set_zoom_style(ZoomStyle p_zoom_style);
 
 	void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning);
 	void setup_warped_panning(Viewport *p_viewport, bool p_allowed);