Sfoglia il codice sorgente

Improve the anchors and margin workflow

Gilles Roudiere 6 anni fa
parent
commit
e875f05c3b

+ 0 - 1
editor/editor_settings.cpp

@@ -545,7 +545,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.9));
 	_initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35));
 	_initial_set("editors/2d/bone_outline_size", 2);
-	_initial_set("editors/2d/keep_margins_when_changing_anchors", false);
 	_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
 	_initial_set("editors/2d/warped_mouse_panning", true);
 	_initial_set("editors/2d/simple_spacebar_panning", false);

+ 129 - 17
editor/plugins/canvas_item_editor_plugin.cpp

@@ -1387,7 +1387,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
 
 	// Starts anchor dragging if needed
 	if (drag_type == DRAG_NONE) {
-		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT && show_helpers) {
+		if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
 			List<CanvasItem *> selection = _get_edited_canvas_items();
 			if (selection.size() == 1) {
 				Control *control = Object::cast_to<Control>(selection[0]);
@@ -2213,6 +2213,7 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
 
 void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
 	bool accepted = false;
+
 	if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
 		if ((accepted = _gui_input_rulers_and_guides(p_event))) {
 			//printf("Rulers and guides\n");
@@ -2512,20 +2513,50 @@ void CanvasItemEditor::_draw_grid() {
 	}
 }
 
-void CanvasItemEditor::_draw_control_helpers(Control *control) {
+void CanvasItemEditor::_draw_control_anchors(Control *control) {
 	Transform2D xform = transform * control->get_global_transform_with_canvas();
 	RID ci = viewport->get_canvas_item();
+	if (tool == TOOL_SELECT && !Object::cast_to<Container>(control->get_parent())) {
+
+		// Compute the anchors
+		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);
+
+		Vector2 anchors_pos[4];
+		for (int i = 0; i < 4; i++) {
+			Vector2 value = 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, value));
+		}
+
+		// Draw the anchors handles
+		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]);
+		}
+	}
+}
+
+void CanvasItemEditor::_draw_control_helpers(Control *control) {
+	Transform2D xform = transform * control->get_global_transform_with_canvas();
 	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);
 
+		// Compute the anchors
 		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++) {
@@ -2592,16 +2623,6 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) {
 			_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();
@@ -2779,6 +2800,7 @@ void CanvasItemEditor::_draw_selection() {
 			// Draw control-related helpers
 			Control *control = Object::cast_to<Control>(canvas_item);
 			if (control && _is_node_movable(control)) {
+				_draw_control_anchors(control);
 				_draw_control_helpers(control);
 			}
 
@@ -3311,24 +3333,32 @@ void CanvasItemEditor::_notification(int p_what) {
 		// Activate / Deactivate the pivot tool
 		pivot_button->set_disabled(nb_having_pivot == 0);
 
-		// Show / Hide the layout button
+		// Show / Hide the layout and anchors mode buttons
 		if (nb_control > 0 && nb_control == selection.size()) {
 			presets_menu->set_visible(true);
 			presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node."));
+			anchor_mode_button->set_visible(true);
+			anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins."));
+
+			// Set the pressed state of the node
+			anchor_mode_button->set_pressed(anchors_mode);
 
 			// Disable if the selected node is child of a container
 			presets_menu->set_disabled(false);
+			anchor_mode_button->set_disabled(false);
 			for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
 				Control *control = Object::cast_to<Control>(E->get());
 				if (!control || Object::cast_to<Container>(control->get_parent())) {
 					presets_menu->set_disabled(true);
 					presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
+					anchor_mode_button->set_disabled(true);
+					anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent."));
 					break;
 				}
 			}
-
 		} else {
 			presets_menu->set_visible(false);
+			anchor_mode_button->set_visible(false);
 		}
 
 		// Update the viewport if bones changes
@@ -3436,9 +3466,10 @@ void CanvasItemEditor::_notification(int p_what) {
 		p->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE);
 		p->add_separator();
 		p->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_AND_MARGINS_PRESET_WIDE);
+		p->add_icon_item(get_icon("Anchor", "EditorIcons"), "Keep Ratio", ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO);
 		p->add_separator();
-		p->add_submenu_item(TTR("Anchors Only"), "Anchors");
-		p->set_item_icon(20, get_icon("Anchor", "EditorIcons"));
+		p->add_submenu_item(TTR("Anchors only"), "Anchors");
+		p->set_item_icon(21, get_icon("Anchor", "EditorIcons"));
 
 		anchors_popup->clear();
 		anchors_popup->add_icon_item(get_icon("ControlAlignTopLeft", "EditorIcons"), "Top Left", ANCHORS_PRESET_TOP_LEFT);
@@ -3460,9 +3491,31 @@ void CanvasItemEditor::_notification(int p_what) {
 		anchors_popup->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_PRESET_HCENTER_WIDE);
 		anchors_popup->add_separator();
 		anchors_popup->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_PRESET_WIDE);
+
+		anchor_mode_button->set_icon(get_icon("Anchor", "EditorIcons"));
 	}
 }
 
+void CanvasItemEditor::_selection_changed() {
+	// Update the anchors_mode
+	int nbValidControls = 0;
+	int nbAnchorsMode = 0;
+	List<Node *> selection = editor_selection->get_selected_node_list();
+	for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+		Control *control = Object::cast_to<Control>(E->get());
+		if (!control)
+			continue;
+		if (Object::cast_to<Container>(control->get_parent()))
+			continue;
+
+		nbValidControls++;
+		if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) {
+			nbAnchorsMode++;
+		}
+	}
+	anchors_mode = (nbValidControls == nbAnchorsMode);
+}
+
 void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
 
 	Array selection = editor_selection->get_selected_nodes();
@@ -3680,6 +3733,36 @@ void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_p
 	}
 
 	undo_redo->commit_action();
+
+	anchors_mode = false;
+}
+
+void CanvasItemEditor::_set_anchors_and_margins_to_keep_ratio() {
+	List<Node *> selection = editor_selection->get_selected_node_list();
+
+	undo_redo->create_action(TTR("Change Anchors and Margins"));
+
+	for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+
+		Control *control = Object::cast_to<Control>(E->get());
+		if (control) {
+			Point2 top_left_anchor = _position_to_anchor(control, Point2());
+			Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size());
+			undo_redo->add_do_method(control, "set_anchor", MARGIN_LEFT, top_left_anchor.x, false, true);
+			undo_redo->add_do_method(control, "set_anchor", MARGIN_RIGHT, bottom_right_anchor.x, false, true);
+			undo_redo->add_do_method(control, "set_anchor", MARGIN_TOP, top_left_anchor.y, false, true);
+			undo_redo->add_do_method(control, "set_anchor", MARGIN_BOTTOM, bottom_right_anchor.y, false, true);
+			undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true);
+
+			bool use_anchors = control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_");
+			undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+			undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", use_anchors);
+
+			anchors_mode = true;
+		}
+	}
+
+	undo_redo->commit_action();
 }
 
 void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) {
@@ -3811,6 +3894,21 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
 	}
 }
 
+void CanvasItemEditor::_button_toggle_anchor_mode(bool p_status) {
+	List<CanvasItem *> selection = _get_edited_canvas_items(false, false);
+	for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+		Control *control = Object::cast_to<Control>(E->get());
+		if (!control || Object::cast_to<Container>(control->get_parent()))
+			continue;
+
+		control->set_meta("_edit_use_anchors_", p_status);
+	}
+
+	anchors_mode = p_status;
+
+	viewport->update();
+}
+
 void CanvasItemEditor::_popup_callback(int p_op) {
 
 	last_option = MenuOption(p_op);
@@ -4047,6 +4145,9 @@ void CanvasItemEditor::_popup_callback(int p_op) {
 		case ANCHORS_AND_MARGINS_PRESET_WIDE: {
 			_set_anchors_and_margins_preset(Control::PRESET_WIDE);
 		} break;
+		case ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO: {
+			_set_anchors_and_margins_to_keep_ratio();
+		} break;
 
 		case ANCHORS_PRESET_TOP_LEFT: {
 			_set_anchors_preset(PRESET_TOP_LEFT);
@@ -4366,6 +4467,7 @@ void CanvasItemEditor::_bind_methods() {
 	ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset);
 	ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus);
 	ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap);
+	ClassDB::bind_method("_button_toggle_anchor_mode", &CanvasItemEditor::_button_toggle_anchor_mode);
 	ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll);
 	ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars);
 	ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback);
@@ -4378,6 +4480,7 @@ void CanvasItemEditor::_bind_methods() {
 	ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
 	ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
 	ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
+	ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed);
 	ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop);
 	ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
 	ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
@@ -4636,6 +4739,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	snap_relative = false;
 	snap_pixel = false;
 
+	anchors_mode = false;
+
 	skeleton_show_bones = true;
 
 	drag_type = DRAG_NONE;
@@ -4654,6 +4759,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	editor_selection = p_editor->get_editor_selection();
 	editor_selection->add_editor_plugin(this);
 	editor_selection->connect("selection_changed", this, "update");
+	editor_selection->connect("selection_changed", this, "_selection_changed");
 
 	hb = memnew(HBoxContainer);
 	add_child(hb);
@@ -4913,6 +5019,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	anchors_popup->set_name("Anchors");
 	anchors_popup->connect("id_pressed", this, "_popup_callback");
 
+	anchor_mode_button = memnew(ToolButton);
+	hb->add_child(anchor_mode_button);
+	anchor_mode_button->set_toggle_mode(true);
+	anchor_mode_button->hide();
+	anchor_mode_button->connect("toggled", this, "_button_toggle_anchor_mode");
+
 	animation_hb = memnew(HBoxContainer);
 	hb->add_child(animation_hb);
 	animation_hb->add_child(memnew(VSeparator));

+ 13 - 0
editor/plugins/canvas_item_editor_plugin.h

@@ -129,6 +129,7 @@ private:
 		ANCHORS_AND_MARGINS_PRESET_VCENTER_WIDE,
 		ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE,
 		ANCHORS_AND_MARGINS_PRESET_WIDE,
+		ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO,
 		ANCHORS_PRESET_TOP_LEFT,
 		ANCHORS_PRESET_TOP_RIGHT,
 		ANCHORS_PRESET_BOTTOM_LEFT,
@@ -240,6 +241,8 @@ private:
 	Point2 view_offset;
 	Point2 previous_update_view_offset;
 
+	bool anchors_mode;
+
 	Point2 grid_offset;
 	Point2 grid_step;
 	int grid_step_multiplier;
@@ -347,6 +350,8 @@ private:
 	PopupMenu *anchors_and_margins_popup;
 	PopupMenu *anchors_popup;
 
+	ToolButton *anchor_mode_button;
+
 	Button *key_loc_button;
 	Button *key_rot_button;
 	Button *key_scale_button;
@@ -438,6 +443,7 @@ private:
 	void _draw_guides();
 	void _draw_focus();
 	void _draw_grid();
+	void _draw_control_anchors(Control *control);
 	void _draw_control_helpers(Control *control);
 	void _draw_selection();
 	void _draw_axis();
@@ -462,6 +468,8 @@ private:
 
 	void _gui_input_viewport(const Ref<InputEvent> &p_event);
 
+	void _selection_changed();
+
 	void _focus_selection(int p_op);
 
 	void _solve_IK(Node2D *leaf_node, Point2 target_position);
@@ -473,6 +481,9 @@ private:
 	void _set_anchors_preset(Control::LayoutPreset p_preset);
 	void _set_margins_preset(Control::LayoutPreset p_preset);
 	void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset);
+	void _set_anchors_and_margins_to_keep_ratio();
+
+	void _button_toggle_anchor_mode(bool p_status);
 
 	HBoxContainer *zoom_hb;
 	void _zoom_on_position(float p_zoom, Point2 p_position = Point2());
@@ -575,6 +586,8 @@ public:
 
 	void focus_selection();
 
+	bool is_anchors_mode_enabled() { return anchors_mode; };
+
 	CanvasItemEditor(EditorNode *p_editor);
 };
 

+ 85 - 41
scene/gui/control.cpp

@@ -43,6 +43,7 @@
 #include "scene/scene_string_names.h"
 #ifdef TOOLS_ENABLED
 #include "editor/editor_settings.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
 #endif
 #include <stdio.h>
 
@@ -66,6 +67,7 @@ Dictionary Control::_edit_get_state() const {
 	s["margins"] = margins;
 	return s;
 }
+
 void Control::_edit_set_state(const Dictionary &p_state) {
 
 	Dictionary state = p_state;
@@ -87,7 +89,12 @@ void Control::_edit_set_state(const Dictionary &p_state) {
 }
 
 void Control::_edit_set_position(const Point2 &p_position) {
+#ifdef TOOLS_ENABLED
+	set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+#else
+	// Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
 	set_position(p_position);
+#endif
 };
 
 Point2 Control::_edit_get_position() const {
@@ -103,8 +110,14 @@ Size2 Control::_edit_get_scale() const {
 }
 
 void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
+#ifdef TOOLS_ENABLED
+	set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+	set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+#else
+	// Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
 	set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
 	set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
+#endif
 }
 
 Rect2 Control::_edit_get_rect() const {
@@ -1366,7 +1379,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
 	float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
 	float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
 
-	data.anchor[p_margin] = CLAMP(p_anchor, 0.0, 1.0);
+	data.anchor[p_margin] = p_anchor;
 
 	if (((p_margin == MARGIN_LEFT || p_margin == MARGIN_TOP) && data.anchor[p_margin] > data.anchor[(p_margin + 2) % 4]) ||
 			((p_margin == MARGIN_RIGHT || p_margin == MARGIN_BOTTOM) && data.anchor[p_margin] < data.anchor[(p_margin + 2) % 4])) {
@@ -1395,15 +1408,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
 }
 
 void Control::_set_anchor(Margin p_margin, float p_anchor) {
-#ifdef TOOLS_ENABLED
-	if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
-		set_anchor(p_margin, p_anchor, EDITOR_DEF("editors/2d/keep_margins_when_changing_anchors", false));
-	} else {
-		set_anchor(p_margin, p_anchor, false);
-	}
-#else
-	set_anchor(p_margin, p_anchor, false);
-#endif
+	set_anchor(p_margin, p_anchor);
 }
 
 void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor) {
@@ -1412,7 +1417,7 @@ void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos
 	set_margin(p_margin, p_pos);
 }
 
-void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
+void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) {
 	//Left
 	switch (p_preset) {
 		case PRESET_TOP_LEFT:
@@ -1423,21 +1428,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_LEFT_WIDE:
 		case PRESET_HCENTER_WIDE:
 		case PRESET_WIDE:
-			set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margin);
+			set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margins);
 			break;
 
 		case PRESET_CENTER_TOP:
 		case PRESET_CENTER_BOTTOM:
 		case PRESET_CENTER:
 		case PRESET_VCENTER_WIDE:
-			set_anchor(MARGIN_LEFT, 0.5, p_keep_margin);
+			set_anchor(MARGIN_LEFT, 0.5, p_keep_margins);
 			break;
 
 		case PRESET_TOP_RIGHT:
 		case PRESET_BOTTOM_RIGHT:
 		case PRESET_CENTER_RIGHT:
 		case PRESET_RIGHT_WIDE:
-			set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margin);
+			set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margins);
 			break;
 	}
 
@@ -1451,21 +1456,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_TOP_WIDE:
 		case PRESET_VCENTER_WIDE:
 		case PRESET_WIDE:
-			set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margin);
+			set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margins);
 			break;
 
 		case PRESET_CENTER_LEFT:
 		case PRESET_CENTER_RIGHT:
 		case PRESET_CENTER:
 		case PRESET_HCENTER_WIDE:
-			set_anchor(MARGIN_TOP, 0.5, p_keep_margin);
+			set_anchor(MARGIN_TOP, 0.5, p_keep_margins);
 			break;
 
 		case PRESET_BOTTOM_LEFT:
 		case PRESET_BOTTOM_RIGHT:
 		case PRESET_CENTER_BOTTOM:
 		case PRESET_BOTTOM_WIDE:
-			set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margin);
+			set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margins);
 			break;
 	}
 
@@ -1475,14 +1480,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_BOTTOM_LEFT:
 		case PRESET_CENTER_LEFT:
 		case PRESET_LEFT_WIDE:
-			set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margin);
+			set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margins);
 			break;
 
 		case PRESET_CENTER_TOP:
 		case PRESET_CENTER_BOTTOM:
 		case PRESET_CENTER:
 		case PRESET_VCENTER_WIDE:
-			set_anchor(MARGIN_RIGHT, 0.5, p_keep_margin);
+			set_anchor(MARGIN_RIGHT, 0.5, p_keep_margins);
 			break;
 
 		case PRESET_TOP_RIGHT:
@@ -1493,7 +1498,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_BOTTOM_WIDE:
 		case PRESET_HCENTER_WIDE:
 		case PRESET_WIDE:
-			set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margin);
+			set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margins);
 			break;
 	}
 
@@ -1503,14 +1508,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_TOP_RIGHT:
 		case PRESET_CENTER_TOP:
 		case PRESET_TOP_WIDE:
-			set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margin);
+			set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margins);
 			break;
 
 		case PRESET_CENTER_LEFT:
 		case PRESET_CENTER_RIGHT:
 		case PRESET_CENTER:
 		case PRESET_HCENTER_WIDE:
-			set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margin);
+			set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margins);
 			break;
 
 		case PRESET_BOTTOM_LEFT:
@@ -1521,7 +1526,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
 		case PRESET_BOTTOM_WIDE:
 		case PRESET_VCENTER_WIDE:
 		case PRESET_WIDE:
-			set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margin);
+			set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margins);
 			break;
 	}
 }
@@ -1714,7 +1719,11 @@ Point2 Control::get_global_position() const {
 	return get_global_transform().get_origin();
 }
 
-void Control::set_global_position(const Point2 &p_point) {
+void Control::_set_global_position(const Point2 &p_point) {
+	set_global_position(p_point);
+}
+
+void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) {
 
 	Transform2D inv;
 
@@ -1723,7 +1732,7 @@ void Control::set_global_position(const Point2 &p_point) {
 		inv = data.parent_canvas_item->get_global_transform().affine_inverse();
 	}
 
-	set_position(inv.xform(p_point));
+	set_position(inv.xform(p_point), p_keep_margins);
 }
 
 Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
@@ -1737,6 +1746,15 @@ Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margi
 	return result;
 }
 
+void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]) {
+
+	Size2 parent_rect_size = get_parent_anchorable_rect().size;
+	r_anchors[0] = (p_rect.position.x - p_margins[0]) / parent_rect_size.x;
+	r_anchors[1] = (p_rect.position.y - p_margins[1]) / parent_rect_size.y;
+	r_anchors[2] = (p_rect.position.x + p_rect.size.x - p_margins[2]) / parent_rect_size.x;
+	r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_margins[3]) / parent_rect_size.y;
+}
+
 void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
 
 	Size2 parent_rect_size = get_parent_anchorable_rect().size;
@@ -1746,13 +1764,28 @@ void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r
 	r_margins[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
 }
 
-void Control::set_position(const Size2 &p_point) {
+void Control::_set_position(const Size2 &p_point) {
+	set_position(p_point);
+}
 
-	_compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+void Control::set_position(const Size2 &p_point, bool p_keep_margins) {
+	if (p_keep_margins) {
+		_compute_anchors(Rect2(p_point, data.size_cache), data.margin, data.anchor);
+		_change_notify("anchor_left");
+		_change_notify("anchor_right");
+		_change_notify("anchor_top");
+		_change_notify("anchor_bottom");
+	} else {
+		_compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+	}
 	_size_changed();
 }
 
-void Control::set_size(const Size2 &p_size) {
+void Control::_set_size(const Size2 &p_size) {
+	set_size(p_size);
+}
+
+void Control::set_size(const Size2 &p_size, bool p_keep_margins) {
 
 	Size2 new_size = p_size;
 	Size2 min = get_combined_minimum_size();
@@ -1761,7 +1794,15 @@ void Control::set_size(const Size2 &p_size) {
 	if (new_size.y < min.y)
 		new_size.y = min.y;
 
-	_compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+	if (p_keep_margins) {
+		_compute_anchors(Rect2(data.pos_cache, new_size), data.margin, data.anchor);
+		_change_notify("anchor_left");
+		_change_notify("anchor_right");
+		_change_notify("anchor_top");
+		_change_notify("anchor_bottom");
+	} else {
+		_compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+	}
 	_size_changed();
 }
 
@@ -2702,20 +2743,23 @@ void Control::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
 	ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size);
 	ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size);
-	ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margin"), &Control::set_anchors_preset, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margins"), &Control::set_anchors_preset, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("set_margins_preset", "preset", "resize_mode", "margin"), &Control::set_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("set_anchors_and_margins_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
-	ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("_set_anchor", "margin", "anchor"), &Control::_set_anchor);
+	ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("get_anchor", "margin"), &Control::get_anchor);
 	ClassDB::bind_method(D_METHOD("set_margin", "margin", "offset"), &Control::set_margin);
 	ClassDB::bind_method(D_METHOD("set_anchor_and_margin", "margin", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_margin, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin);
 	ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end);
-	ClassDB::bind_method(D_METHOD("set_position", "position"), &Control::set_position);
-	ClassDB::bind_method(D_METHOD("set_size", "size"), &Control::set_size);
+	ClassDB::bind_method(D_METHOD("set_position", "position", "keep_margins"), &Control::set_position, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("_set_position", "margin"), &Control::_set_position);
+	ClassDB::bind_method(D_METHOD("set_size", "size", "keep_margins"), &Control::set_size, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size);
 	ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size);
-	ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Control::set_global_position);
+	ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_margins"), &Control::set_global_position, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position);
 	ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
 	ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees);
 	ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
@@ -2835,10 +2879,10 @@ void Control::_bind_methods() {
 	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input"));
 
 	ADD_GROUP("Anchor", "anchor_");
-	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT);
-	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_TOP);
-	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
-	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
+	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT);
+	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP);
+	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
+	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
 
 	ADD_GROUP("Margin", "margin_");
 	ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT);
@@ -2851,9 +2895,9 @@ void Control::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
 
 	ADD_GROUP("Rect", "rect_");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "_set_global_position", "get_global_position");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");

+ 8 - 4
scene/gui/control.h

@@ -217,6 +217,9 @@ private:
 	Control *_get_focus_neighbour(Margin p_margin, int p_count = 0);
 
 	void _set_anchor(Margin p_margin, float p_anchor);
+	void _set_position(const Point2 &p_point);
+	void _set_global_position(const Point2 &p_point);
+	void _set_size(const Size2 &p_size);
 
 	void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
 	void _theme_changed();
@@ -229,6 +232,7 @@ private:
 
 	Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
 	void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+	void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]);
 
 	void _size_changed();
 	String _get_tooltip() const;
@@ -325,7 +329,7 @@ public:
 
 	/* POSITIONING */
 
-	void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = true);
+	void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins = true);
 	void set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
 	void set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
 
@@ -343,12 +347,12 @@ public:
 	Point2 get_begin() const;
 	Point2 get_end() const;
 
-	void set_position(const Point2 &p_point);
-	void set_global_position(const Point2 &p_point);
+	void set_position(const Point2 &p_point, bool p_keep_margins = false);
+	void set_global_position(const Point2 &p_point, bool p_keep_margins = false);
 	Point2 get_position() const;
 	Point2 get_global_position() const;
 
-	void set_size(const Size2 &p_size);
+	void set_size(const Size2 &p_size, bool p_keep_margins = false);
 	Size2 get_size() const;
 
 	Rect2 get_rect() const;