浏览代码

Merge pull request #21934 from groud/add_scale_mode

Add a scale mode to the 2D editor
Rémi Verschelde 7 年之前
父节点
当前提交
f98d946cd9
共有 2 个文件被更改,包括 349 次插入196 次删除
  1. 338 192
      editor/plugins/canvas_item_editor_plugin.cpp
  2. 11 4
      editor/plugins/canvas_item_editor_plugin.h

+ 338 - 192
editor/plugins/canvas_item_editor_plugin.cpp

@@ -55,6 +55,7 @@
 #define MAX_ZOOM 100
 
 #define RULER_WIDTH 15 * EDSCALE
+#define SCALE_HANDLE_DISTANCE 25
 
 class SnapDialog : public ConfirmationDialog {
 
@@ -335,10 +336,11 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
 	if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack())
 		return;
 
-	if (k->get_control())
-		return;
+	if (k->get_scancode() == KEY_CONTROL || k->get_scancode() == KEY_ALT || k->get_scancode() == KEY_SHIFT) {
+		viewport->update();
+	}
 
-	if (k->is_pressed() && !k->is_echo()) {
+	if (k->is_pressed() && !k->get_control() && !k->is_echo()) {
 		if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) {
 			// Multiply the grid size
 			grid_step_multiplier = MIN(grid_step_multiplier + 1, 12);
@@ -1267,7 +1269,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
 	if (drag_type == DRAG_NONE) {
 		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
 			drag_selection = _get_edited_canvas_items();
-			if (drag_selection.size() > 0 && ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) {
+			if (drag_selection.size() > 0 && ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) {
 				drag_type = DRAG_ROTATE;
 				drag_from = transform.affine_inverse().xform(b->get_position());
 				CanvasItem *canvas_item = drag_selection[0];
@@ -1615,6 +1617,89 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
 	return false;
 }
 
+bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
+
+	Ref<InputEventMouseButton> b = p_event;
+	Ref<InputEventMouseMotion> m = p_event;
+
+	// Drag resize handles
+	if (drag_type == DRAG_NONE) {
+		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && ((b->get_alt() && b->get_control()) || tool == TOOL_SCALE)) {
+			List<CanvasItem *> selection = _get_edited_canvas_items();
+			if (selection.size() == 1) {
+				CanvasItem *canvas_item = selection[0];
+
+				Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+				Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+				Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+
+				Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
+				Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+				if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
+					drag_type = DRAG_SCALE_X;
+				}
+				Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+				if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
+					drag_type = DRAG_SCALE_Y;
+				}
+				if (drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
+					drag_from = transform.affine_inverse().xform(b->get_position());
+					drag_selection = List<CanvasItem *>();
+					drag_selection.push_back(canvas_item);
+					_save_canvas_item_state(drag_selection);
+					return true;
+				}
+			}
+		}
+	}
+
+	if (drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
+		// Resize the node
+		if (m.is_valid()) {
+			_restore_canvas_item_state(drag_selection, true);
+			CanvasItem *canvas_item = drag_selection[0];
+
+			drag_to = transform.affine_inverse().xform(m->get_position());
+
+			bool uniform = m->get_shift();
+			Point2 offset = drag_to - drag_from;
+			Size2 scale = canvas_item->call("get_scale");
+			float ratio = scale.y / scale.x;
+			if (drag_type == DRAG_SCALE_X) {
+				scale.x += offset.x / SCALE_HANDLE_DISTANCE;
+				if (uniform) {
+					scale.y = scale.x * ratio;
+				}
+				canvas_item->call("set_scale", scale);
+
+			} else if (drag_type == DRAG_SCALE_Y) {
+				scale.y -= offset.y / SCALE_HANDLE_DISTANCE;
+				if (uniform) {
+					scale.x = scale.y / ratio;
+				}
+				canvas_item->call("set_scale", scale);
+			}
+		}
+
+		// Confirm resize
+		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
+			_commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem"));
+			drag_type = DRAG_NONE;
+			viewport->update();
+			return true;
+		}
+
+		// Cancel a drag
+		if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+			_restore_canvas_item_state(drag_selection);
+			drag_type = DRAG_NONE;
+			viewport->update();
+			return true;
+		}
+	}
+	return false;
+}
+
 bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
 	Ref<InputEventMouseButton> b = p_event;
 	Ref<InputEventMouseMotion> m = p_event;
@@ -1624,7 +1709,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
 		//Start moving the nodes
 		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
 			List<CanvasItem *> selection = _get_edited_canvas_items();
-			if ((b->get_alt() || tool == TOOL_MOVE) && selection.size() > 0) {
+			if (((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) && selection.size() > 0) {
 				drag_type = DRAG_MOVE;
 				drag_from = transform.affine_inverse().xform(b->get_position());
 				drag_selection = selection;
@@ -2006,6 +2091,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
 		//printf("Open scene on double click\n");
 	} else if ((accepted = _gui_input_anchors(p_event))) {
 		//printf("Anchors\n");
+	} else if ((accepted = _gui_input_scale(p_event))) {
+		//printf("Set scale\n");
 	} else if ((accepted = _gui_input_pivot(p_event))) {
 		//printf("Set pivot\n");
 	} else if ((accepted = _gui_input_resize(p_event))) {
@@ -2288,6 +2375,188 @@ void CanvasItemEditor::_draw_grid() {
 	}
 }
 
+void CanvasItemEditor::_draw_control_helpers(Control *control) {
+	Transform2D xform = transform * control->get_global_transform_with_canvas();
+	RID ci = viewport->get_canvas_item();
+	if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) {
+		// Draw the helpers
+		Color color_base = Color(0.8, 0.8, 0.8, 0.5);
+
+		float anchors_values[4];
+		anchors_values[0] = control->get_anchor(MARGIN_LEFT);
+		anchors_values[1] = control->get_anchor(MARGIN_TOP);
+		anchors_values[2] = control->get_anchor(MARGIN_RIGHT);
+		anchors_values[3] = control->get_anchor(MARGIN_BOTTOM);
+
+		// Draw the anchors
+		Vector2 anchors[4];
+		Vector2 anchors_pos[4];
+		for (int i = 0; i < 4; i++) {
+			anchors[i] = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]);
+			anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i]));
+		}
+
+		// Get which anchor is dragged
+		int dragged_anchor = -1;
+		switch (drag_type) {
+			case DRAG_ANCHOR_ALL:
+			case DRAG_ANCHOR_TOP_LEFT:
+				dragged_anchor = 0;
+				break;
+			case DRAG_ANCHOR_TOP_RIGHT:
+				dragged_anchor = 1;
+				break;
+			case DRAG_ANCHOR_BOTTOM_RIGHT:
+				dragged_anchor = 2;
+				break;
+			case DRAG_ANCHOR_BOTTOM_LEFT:
+				dragged_anchor = 3;
+				break;
+			default:
+				break;
+		}
+
+		if (dragged_anchor >= 0) {
+			// Draw the 4 lines when dragged
+			bool snapped;
+			Color color_snapped = Color(0.64, 0.93, 0.67, 0.5);
+
+			Vector2 corners_pos[4];
+			for (int i = 0; i < 4; i++) {
+				corners_pos[i] = xform.xform(_anchor_to_position(control, Vector2((i == 0 || i == 3) ? ANCHOR_BEGIN : ANCHOR_END, (i <= 1) ? ANCHOR_BEGIN : ANCHOR_END)));
+			}
+
+			Vector2 line_starts[4];
+			Vector2 line_ends[4];
+			for (int i = 0; i < 4; i++) {
+				float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i];
+				line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val);
+				line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val);
+				snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0;
+				viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1);
+			}
+
+			// Display the percentages next to the lines
+			float percent_val;
+			percent_val = anchors_values[(dragged_anchor + 2) % 4] - anchors_values[dragged_anchor];
+			percent_val = (dragged_anchor >= 2) ? -percent_val : percent_val;
+			_draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 1) % 4]) / 2, (Margin)((dragged_anchor + 1) % 4));
+
+			percent_val = anchors_values[(dragged_anchor + 3) % 4] - anchors_values[(dragged_anchor + 1) % 4];
+			percent_val = ((dragged_anchor + 1) % 4 >= 2) ? -percent_val : percent_val;
+			_draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 3) % 4]) / 2, (Margin)(dragged_anchor));
+
+			percent_val = anchors_values[(dragged_anchor + 1) % 4];
+			percent_val = ((dragged_anchor + 1) % 4 >= 2) ? ANCHOR_END - percent_val : percent_val;
+			_draw_percentage_at_position(percent_val, (line_starts[dragged_anchor] + anchors_pos[dragged_anchor]) / 2, (Margin)(dragged_anchor));
+
+			percent_val = anchors_values[dragged_anchor];
+			percent_val = (dragged_anchor >= 2) ? ANCHOR_END - percent_val : percent_val;
+			_draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4));
+		}
+
+		Rect2 anchor_rects[4];
+		anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size());
+		anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y));
+		anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size());
+		anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y));
+
+		for (int i = 0; i < 4; i++) {
+			anchor_handle->draw_rect(ci, anchor_rects[i]);
+		}
+
+		// Draw the margin values and the node width/height when dragging control side
+		float ratio = 0.33;
+		Transform2D parent_transform = xform * control->get_transform().affine_inverse();
+		float node_pos_in_parent[4];
+
+		Rect2 parent_rect = control->get_parent_anchorable_rect();
+
+		node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x;
+		node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y;
+		node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x;
+		node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y;
+
+		Point2 start, end;
+		switch (drag_type) {
+			case DRAG_LEFT:
+			case DRAG_TOP_LEFT:
+			case DRAG_BOTTOM_LEFT:
+				_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
+			case DRAG_MOVE:
+				start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio));
+				end = start - Vector2(control->get_margin(MARGIN_LEFT), 0);
+				_draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP);
+				viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
+				break;
+			default:
+				break;
+		}
+		switch (drag_type) {
+			case DRAG_RIGHT:
+			case DRAG_TOP_RIGHT:
+			case DRAG_BOTTOM_RIGHT:
+				_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
+			case DRAG_MOVE:
+				start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio));
+				end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0);
+				_draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM);
+				viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
+				break;
+			default:
+				break;
+		}
+		switch (drag_type) {
+			case DRAG_TOP:
+			case DRAG_TOP_LEFT:
+			case DRAG_TOP_RIGHT:
+				_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT);
+			case DRAG_MOVE:
+				start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]);
+				end = start - Vector2(0, control->get_margin(MARGIN_TOP));
+				_draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT);
+				viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
+				break;
+			default:
+				break;
+		}
+		switch (drag_type) {
+			case DRAG_BOTTOM:
+			case DRAG_BOTTOM_LEFT:
+			case DRAG_BOTTOM_RIGHT:
+				_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT);
+			case DRAG_MOVE:
+				start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]);
+				end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM));
+				_draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT);
+				viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
+				break;
+			default:
+				break;
+		}
+
+		switch (drag_type) {
+			//Draw the ghost rect if the node if rotated/scaled
+			case DRAG_LEFT:
+			case DRAG_TOP_LEFT:
+			case DRAG_TOP:
+			case DRAG_TOP_RIGHT:
+			case DRAG_RIGHT:
+			case DRAG_BOTTOM_RIGHT:
+			case DRAG_BOTTOM:
+			case DRAG_BOTTOM_LEFT:
+			case DRAG_MOVE:
+				if (control->get_rotation() != 0.0 || control->get_scale() != Vector2(1, 1)) {
+					Rect2 rect = Rect2(Vector2(node_pos_in_parent[0], node_pos_in_parent[1]), control->get_size());
+					viewport->draw_rect(parent_transform.xform(rect), color_base, false);
+				}
+				break;
+			default:
+				break;
+		}
+	}
+}
+
 void CanvasItemEditor::_draw_selection() {
 	Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons");
 	Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons");
@@ -2346,199 +2615,26 @@ void CanvasItemEditor::_draw_selection() {
 			}
 		} else {
 
-			Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
-			viewport->draw_set_transform_matrix(transform);
+			Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+			Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+			viewport->draw_set_transform_matrix(simple_xform);
 			viewport->draw_texture(position_icon, -(position_icon->get_size() / 2));
-			viewport->draw_set_transform_matrix(Transform2D());
+			viewport->draw_set_transform_matrix(viewport->get_transform());
 		}
 
-		if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
+		if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
 			// Draw the pivot
 			if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/
 				viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor());
 			}
 
+			// Draw control-related helpers
 			Control *control = Object::cast_to<Control>(canvas_item);
 			if (control) {
-				if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) {
-					// Draw the helpers
-					Color color_base = Color(0.8, 0.8, 0.8, 0.5);
-
-					float anchors_values[4];
-					anchors_values[0] = control->get_anchor(MARGIN_LEFT);
-					anchors_values[1] = control->get_anchor(MARGIN_TOP);
-					anchors_values[2] = control->get_anchor(MARGIN_RIGHT);
-					anchors_values[3] = control->get_anchor(MARGIN_BOTTOM);
-
-					// Draw the anchors
-					Vector2 anchors[4];
-					Vector2 anchors_pos[4];
-					for (int i = 0; i < 4; i++) {
-						anchors[i] = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]);
-						anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i]));
-					}
-
-					// Get which anchor is dragged
-					int dragged_anchor = -1;
-					switch (drag_type) {
-						case DRAG_ANCHOR_ALL:
-						case DRAG_ANCHOR_TOP_LEFT:
-							dragged_anchor = 0;
-							break;
-						case DRAG_ANCHOR_TOP_RIGHT:
-							dragged_anchor = 1;
-							break;
-						case DRAG_ANCHOR_BOTTOM_RIGHT:
-							dragged_anchor = 2;
-							break;
-						case DRAG_ANCHOR_BOTTOM_LEFT:
-							dragged_anchor = 3;
-							break;
-						default:
-							break;
-					}
-
-					if (dragged_anchor >= 0) {
-						// Draw the 4 lines when dragged
-						bool snapped;
-						Color color_snapped = Color(0.64, 0.93, 0.67, 0.5);
-
-						Vector2 corners_pos[4];
-						for (int i = 0; i < 4; i++) {
-							corners_pos[i] = xform.xform(_anchor_to_position(control, Vector2((i == 0 || i == 3) ? ANCHOR_BEGIN : ANCHOR_END, (i <= 1) ? ANCHOR_BEGIN : ANCHOR_END)));
-						}
-
-						Vector2 line_starts[4];
-						Vector2 line_ends[4];
-						for (int i = 0; i < 4; i++) {
-							float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i];
-							line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val);
-							line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val);
-							snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0;
-							viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1);
-						}
-
-						// Display the percentages next to the lines
-						float percent_val;
-						percent_val = anchors_values[(dragged_anchor + 2) % 4] - anchors_values[dragged_anchor];
-						percent_val = (dragged_anchor >= 2) ? -percent_val : percent_val;
-						_draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 1) % 4]) / 2, (Margin)((dragged_anchor + 1) % 4));
-
-						percent_val = anchors_values[(dragged_anchor + 3) % 4] - anchors_values[(dragged_anchor + 1) % 4];
-						percent_val = ((dragged_anchor + 1) % 4 >= 2) ? -percent_val : percent_val;
-						_draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 3) % 4]) / 2, (Margin)(dragged_anchor));
-
-						percent_val = anchors_values[(dragged_anchor + 1) % 4];
-						percent_val = ((dragged_anchor + 1) % 4 >= 2) ? ANCHOR_END - percent_val : percent_val;
-						_draw_percentage_at_position(percent_val, (line_starts[dragged_anchor] + anchors_pos[dragged_anchor]) / 2, (Margin)(dragged_anchor));
-
-						percent_val = anchors_values[dragged_anchor];
-						percent_val = (dragged_anchor >= 2) ? ANCHOR_END - percent_val : percent_val;
-						_draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4));
-					}
-
-					Rect2 anchor_rects[4];
-					anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size());
-					anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y));
-					anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size());
-					anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y));
-
-					for (int i = 0; i < 4; i++) {
-						anchor_handle->draw_rect(ci, anchor_rects[i]);
-					}
-
-					// Draw the margin values and the node width/height when dragging control side
-					float ratio = 0.33;
-					Transform2D parent_transform = xform * control->get_transform().affine_inverse();
-					float node_pos_in_parent[4];
-
-					Rect2 parent_rect = control->get_parent_anchorable_rect();
-
-					node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x;
-					node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y;
-					node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x;
-					node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y;
-
-					Point2 start, end;
-					switch (drag_type) {
-						case DRAG_LEFT:
-						case DRAG_TOP_LEFT:
-						case DRAG_BOTTOM_LEFT:
-							_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
-						case DRAG_MOVE:
-							start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio));
-							end = start - Vector2(control->get_margin(MARGIN_LEFT), 0);
-							_draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP);
-							viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
-							break;
-						default:
-							break;
-					}
-					switch (drag_type) {
-						case DRAG_RIGHT:
-						case DRAG_TOP_RIGHT:
-						case DRAG_BOTTOM_RIGHT:
-							_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
-						case DRAG_MOVE:
-							start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio));
-							end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0);
-							_draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM);
-							viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
-							break;
-						default:
-							break;
-					}
-					switch (drag_type) {
-						case DRAG_TOP:
-						case DRAG_TOP_LEFT:
-						case DRAG_TOP_RIGHT:
-							_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT);
-						case DRAG_MOVE:
-							start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]);
-							end = start - Vector2(0, control->get_margin(MARGIN_TOP));
-							_draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT);
-							viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
-							break;
-						default:
-							break;
-					}
-					switch (drag_type) {
-						case DRAG_BOTTOM:
-						case DRAG_BOTTOM_LEFT:
-						case DRAG_BOTTOM_RIGHT:
-							_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT);
-						case DRAG_MOVE:
-							start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]);
-							end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM));
-							_draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT);
-							viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
-							break;
-						default:
-							break;
-					}
-
-					switch (drag_type) {
-						//Draw the ghost rect if the node if rotated/scaled
-						case DRAG_LEFT:
-						case DRAG_TOP_LEFT:
-						case DRAG_TOP:
-						case DRAG_TOP_RIGHT:
-						case DRAG_RIGHT:
-						case DRAG_BOTTOM_RIGHT:
-						case DRAG_BOTTOM:
-						case DRAG_BOTTOM_LEFT:
-						case DRAG_MOVE:
-							if (control->get_rotation() != 0.0 || control->get_scale() != Vector2(1, 1)) {
-								Rect2 rect = Rect2(Vector2(node_pos_in_parent[0], node_pos_in_parent[1]), control->get_size());
-								viewport->draw_rect(parent_transform.xform(rect), color_base, false);
-							}
-							break;
-						default:
-							break;
-					}
-				}
+				_draw_control_helpers(control);
 			}
 
+			// Draw the resize handles
 			if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) {
 				Rect2 rect = canvas_item->_edit_get_rect();
 				Vector2 endpoints[4] = {
@@ -2548,7 +2644,6 @@ void CanvasItemEditor::_draw_selection() {
 					xform.xform(rect.position + Vector2(0, rect.size.y))
 				};
 				for (int i = 0; i < 4; i++) {
-					// Draw the resize handles
 					int prev = (i + 3) % 4;
 					int next = (i + 1) % 4;
 
@@ -2563,6 +2658,46 @@ void CanvasItemEditor::_draw_selection() {
 					select_handle->draw(ci, (ofs - (select_handle->get_size() / 2)).floor());
 				}
 			}
+
+			// Draw the rescale handles
+			bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+			bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT);
+			if ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
+
+				Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+				Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+
+				Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
+				bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+				Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom;
+
+				if (drag_type == DRAG_SCALE_X) {
+					scale_factor.x += offset.x;
+					if (uniform) {
+						scale_factor.y += offset.x;
+					}
+				} else if (drag_type == DRAG_SCALE_Y) {
+					scale_factor.y -= offset.y;
+					if (uniform) {
+						scale_factor.x -= offset.y;
+					}
+				}
+
+				//scale_factor *= zoom;
+
+				viewport->draw_set_transform_matrix(simple_xform);
+				Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+				Color x_axis_color(1.0, 0.4, 0.4, 0.6);
+				viewport->draw_rect(x_handle_rect, x_axis_color);
+				viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color);
+
+				Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
+				Color y_axis_color(0.4, 1.0, 0.4, 0.6);
+				viewport->draw_rect(y_handle_rect, y_axis_color);
+				viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color);
+
+				viewport->draw_set_transform_matrix(viewport->get_transform());
+			}
 		}
 	}
 
@@ -2737,15 +2872,16 @@ void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Trans
 		_draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform);
 	}
 
-	if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || canvas_item->get_meta("_edit_lock_"))) {
+	if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_")))) {
 		Transform2D xform = transform * canvas_xform * parent_xform;
 
 		// Draw the node's position
 		Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons");
-		Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
-		viewport->draw_set_transform_matrix(transform);
+		Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized();
+		Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
+		viewport->draw_set_transform_matrix(simple_xform);
 		viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5));
-		viewport->draw_set_transform_matrix(Transform2D());
+		viewport->draw_set_transform_matrix(viewport->get_transform());
 	}
 }
 
@@ -3073,6 +3209,7 @@ void CanvasItemEditor::_notification(int p_what) {
 		select_button->set_icon(get_icon("ToolSelect", "EditorIcons"));
 		list_select_button->set_icon(get_icon("ListSelect", "EditorIcons"));
 		move_button->set_icon(get_icon("ToolMove", "EditorIcons"));
+		scale_button->set_icon(get_icon("ToolScale", "EditorIcons"));
 		rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons"));
 		snap_button->set_icon(get_icon("Snap", "EditorIcons"));
 		snap_config_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons"));
@@ -3381,7 +3518,7 @@ void CanvasItemEditor::_button_toggle_snap(bool p_status) {
 
 void CanvasItemEditor::_button_tool_select(int p_index) {
 
-	ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button };
+	ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button };
 	for (int i = 0; i < TOOL_MAX; i++) {
 		tb[i]->set_pressed(i == p_index);
 	}
@@ -4273,6 +4410,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q));
 	select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate") + "\n" + TTR("Alt+Drag: Move") + "\n" + TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).") + "\n" + TTR("Alt+RMB: Depth list selection"));
 
+	hb->add_child(memnew(VSeparator));
+
 	move_button = memnew(ToolButton);
 	hb->add_child(move_button);
 	move_button->set_toggle_mode(true);
@@ -4280,6 +4419,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W));
 	move_button->set_tooltip(TTR("Move Mode"));
 
+	scale_button = memnew(ToolButton);
+	hb->add_child(scale_button);
+	scale_button->set_toggle_mode(true);
+	scale_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SCALE));
+	scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S));
+	scale_button->set_tooltip(TTR("Scale Mode"));
+
 	rotate_button = memnew(ToolButton);
 	hb->add_child(rotate_button);
 	rotate_button->set_toggle_mode(true);

+ 11 - 4
editor/plugins/canvas_item_editor_plugin.h

@@ -77,6 +77,7 @@ class CanvasItemEditor : public VBoxContainer {
 		TOOL_SELECT,
 		TOOL_LIST_SELECT,
 		TOOL_MOVE,
+		TOOL_SCALE,
 		TOOL_ROTATE,
 		TOOL_EDIT_PIVOT,
 		TOOL_PAN,
@@ -189,6 +190,8 @@ class CanvasItemEditor : public VBoxContainer {
 		DRAG_ANCHOR_BOTTOM_LEFT,
 		DRAG_ANCHOR_ALL,
 		DRAG_MOVE,
+		DRAG_SCALE_X,
+		DRAG_SCALE_Y,
 		DRAG_ROTATE,
 		DRAG_PIVOT,
 		DRAG_V_GUIDE,
@@ -298,17 +301,19 @@ class CanvasItemEditor : public VBoxContainer {
 	List<PoseClipboard> pose_clipboard;
 
 	ToolButton *select_button;
-	ToolButton *list_select_button;
+
 	ToolButton *move_button;
+	ToolButton *scale_button;
 	ToolButton *rotate_button;
 
+	ToolButton *list_select_button;
+	ToolButton *pivot_button;
+	ToolButton *pan_button;
+
 	ToolButton *snap_button;
 	MenuButton *snap_config_menu;
 	PopupMenu *smartsnap_config_popup;
 
-	ToolButton *pivot_button;
-	ToolButton *pan_button;
-
 	ToolButton *lock_button;
 	ToolButton *unlock_button;
 
@@ -408,6 +413,7 @@ class CanvasItemEditor : public VBoxContainer {
 	void _draw_guides();
 	void _draw_focus();
 	void _draw_grid();
+	void _draw_control_helpers(Control *control);
 	void _draw_selection();
 	void _draw_axis();
 	void _draw_bones();
@@ -420,6 +426,7 @@ class CanvasItemEditor : public VBoxContainer {
 	bool _gui_input_anchors(const Ref<InputEvent> &p_event);
 	bool _gui_input_move(const Ref<InputEvent> &p_event);
 	bool _gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event);
+	bool _gui_input_scale(const Ref<InputEvent> &p_event);
 	bool _gui_input_pivot(const Ref<InputEvent> &p_event);
 	bool _gui_input_resize(const Ref<InputEvent> &p_event);
 	bool _gui_input_rotate(const Ref<InputEvent> &p_event);