summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp382
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h155
-rw-r--r--editor/plugins/control_editor_plugin.cpp1023
-rw-r--r--editor/plugins/control_editor_plugin.h255
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp27
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rw-r--r--editor/plugins/script_editor_plugin.cpp4
-rw-r--r--editor/plugins/theme_editor_plugin.cpp86
-rw-r--r--editor/plugins/theme_editor_plugin.h10
9 files changed, 1426 insertions, 517 deletions
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 48c5c0ebc4..a3ae0d9623 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -747,11 +747,11 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
return still_selected;
}
-List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_locked, bool remove_canvas_item_if_parent_in_selection) {
+List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) {
List<CanvasItem *> selection;
for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
- if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || !_is_node_locked(canvas_item))) {
+ if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(canvas_item))) {
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (se) {
selection.push_back(canvas_item);
@@ -1831,7 +1831,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
Point2 drag_to_local = simple_xform.xform(drag_to);
Point2 offset = drag_to_local - drag_from_local;
- Size2 scale = canvas_item->call("get_scale");
+ Size2 scale = canvas_item->_edit_get_scale();
Size2 original_scale = scale;
real_t ratio = scale.y / scale.x;
if (drag_type == DRAG_SCALE_BOTH) {
@@ -1869,7 +1869,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
}
}
- canvas_item->call("set_scale", scale);
+ canvas_item->_edit_set_scale(scale);
return true;
}
@@ -3696,8 +3696,6 @@ void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels"));
- bool has_container_parents = false;
- int nb_control = 0;
int nb_having_pivot = 0;
// Update the viewport if the canvas_item changes
@@ -3738,11 +3736,6 @@ void CanvasItemEditor::_notification(int p_what) {
se->prev_anchors[SIDE_BOTTOM] = anchors[SIDE_BOTTOM];
viewport->update();
}
- nb_control++;
-
- if (Object::cast_to<Container>(control->get_parent())) {
- has_container_parents = true;
- }
}
if (canvas_item->_edit_use_pivot()) {
@@ -3753,28 +3746,6 @@ void CanvasItemEditor::_notification(int p_what) {
// Activate / Deactivate the pivot tool
pivot_button->set_disabled(nb_having_pivot == 0);
- // Show / Hide the layout and anchors mode buttons
- if (nb_control > 0 && nb_control == selection.size()) {
- presets_menu->set_visible(true);
- anchor_mode_button->set_visible(true);
-
- // Disable if the selected node is child of a container
- if (has_container_parents) {
- 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."));
- } else {
- presets_menu->set_disabled(false);
- presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node."));
- anchor_mode_button->set_disabled(false);
- anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins."));
- }
- } else {
- presets_menu->set_visible(false);
- anchor_mode_button->set_visible(false);
- }
-
// Update the viewport if bones changes
for (KeyValue<BoneKey, BoneList> &E : bone_list) {
Object *b = ObjectDB::get_instance(E.key.from);
@@ -3852,58 +3823,6 @@ void CanvasItemEditor::_notification(int p_what) {
_update_context_menu_stylebox();
- presets_menu->set_icon(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons")));
-
- PopupMenu *p = presets_menu->get_popup();
-
- p->clear();
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT);
- p->add_separator();
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftCenter"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopCenter"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignRightCenter"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomCenter"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_AND_OFFSETS_PRESET_CENTER);
- p->add_separator();
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("ControlVcenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("ControlHcenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE);
- p->add_separator();
- p->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_AND_OFFSETS_PRESET_WIDE);
- p->add_icon_item(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")), TTR("Keep Ratio"), ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO);
- p->add_separator();
- p->add_submenu_item(TTR("Anchors only"), "Anchors");
- p->set_item_icon(21, get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
-
- anchors_popup->clear();
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_PRESET_TOP_LEFT);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_PRESET_TOP_RIGHT);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_PRESET_BOTTOM_RIGHT);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_PRESET_BOTTOM_LEFT);
- anchors_popup->add_separator();
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftCenter"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_PRESET_CENTER_LEFT);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopCenter"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_PRESET_CENTER_TOP);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignRightCenter"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_PRESET_CENTER_RIGHT);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomCenter"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_PRESET_CENTER_BOTTOM);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_PRESET_CENTER);
- anchors_popup->add_separator();
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_PRESET_LEFT_WIDE);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_PRESET_TOP_WIDE);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_PRESET_RIGHT_WIDE);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_PRESET_BOTTOM_WIDE);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlVcenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_PRESET_VCENTER_WIDE);
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlHcenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_PRESET_HCENTER_WIDE);
- anchors_popup->add_separator();
- anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_WIDE);
-
- anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
-
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed"));
warped_panning = bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning"));
@@ -3920,27 +3839,6 @@ void CanvasItemEditor::_notification(int p_what) {
}
void CanvasItemEditor::_selection_changed() {
- // Update the anchors_mode
- int nbValidControls = 0;
- int nbAnchorsMode = 0;
- List<Node *> selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Control *control = Object::cast_to<Control>(E);
- 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);
- anchor_mode_button->set_pressed(anchors_mode);
-
if (!selected_from_canvas) {
drag_type = DRAG_NONE;
}
@@ -4072,90 +3970,6 @@ void CanvasItemEditor::_update_scroll(real_t) {
viewport->update();
}
-void CanvasItemEditor::_set_anchors_and_offsets_preset(Control::LayoutPreset p_preset) {
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- undo_redo->create_action(TTR("Change Anchors and Offsets"));
-
- for (Node *E : selection) {
- Control *control = Object::cast_to<Control>(E);
- if (control) {
- undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
- switch (p_preset) {
- case PRESET_TOP_LEFT:
- case PRESET_TOP_RIGHT:
- case PRESET_BOTTOM_LEFT:
- case PRESET_BOTTOM_RIGHT:
- case PRESET_CENTER_LEFT:
- case PRESET_CENTER_TOP:
- case PRESET_CENTER_RIGHT:
- case PRESET_CENTER_BOTTOM:
- case PRESET_CENTER:
- undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE);
- break;
- case PRESET_LEFT_WIDE:
- case PRESET_TOP_WIDE:
- case PRESET_RIGHT_WIDE:
- case PRESET_BOTTOM_WIDE:
- case PRESET_VCENTER_WIDE:
- case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
- undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_MINSIZE);
- break;
- }
- undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
- }
- }
-
- undo_redo->commit_action();
-
- anchors_mode = false;
- anchor_mode_button->set_pressed(anchors_mode);
-}
-
-void CanvasItemEditor::_set_anchors_and_offsets_to_keep_ratio() {
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- undo_redo->create_action(TTR("Change Anchors and Offsets"));
-
- for (Node *E : selection) {
- Control *control = Object::cast_to<Control>(E);
- 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", SIDE_LEFT, top_left_anchor.x, false, true);
- undo_redo->add_do_method(control, "set_anchor", SIDE_RIGHT, bottom_right_anchor.x, false, true);
- undo_redo->add_do_method(control, "set_anchor", SIDE_TOP, top_left_anchor.y, false, true);
- undo_redo->add_do_method(control, "set_anchor", SIDE_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;
- anchor_mode_button->set_pressed(anchors_mode);
- }
- }
-
- undo_redo->commit_action();
-}
-
-void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) {
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- undo_redo->create_action(TTR("Change Anchors"));
- for (Node *E : selection) {
- Control *control = Object::cast_to<Control>(E);
- if (control) {
- undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
- undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
- }
- }
-
- undo_redo->commit_action();
-}
-
void CanvasItemEditor::_zoom_on_position(real_t p_zoom, Point2 p_position) {
p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM);
@@ -4298,21 +4112,6 @@ 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 (CanvasItem *E : selection) {
- Control *control = Object::cast_to<Control>(E);
- 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::_update_override_camera_button(bool p_game_running) {
if (p_game_running) {
override_camera_button->set_disabled(false);
@@ -4534,106 +4333,6 @@ void CanvasItemEditor::_popup_callback(int p_op) {
undo_redo->add_undo_method(viewport, "update", Variant());
undo_redo->commit_action();
} break;
- case ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT: {
- _set_anchors_and_offsets_preset(PRESET_TOP_LEFT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT: {
- _set_anchors_and_offsets_preset(PRESET_TOP_RIGHT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT: {
- _set_anchors_and_offsets_preset(PRESET_BOTTOM_LEFT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT: {
- _set_anchors_and_offsets_preset(PRESET_BOTTOM_RIGHT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT: {
- _set_anchors_and_offsets_preset(PRESET_CENTER_LEFT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT: {
- _set_anchors_and_offsets_preset(PRESET_CENTER_RIGHT);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP: {
- _set_anchors_and_offsets_preset(PRESET_CENTER_TOP);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM: {
- _set_anchors_and_offsets_preset(PRESET_CENTER_BOTTOM);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_CENTER: {
- _set_anchors_and_offsets_preset(PRESET_CENTER);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_TOP_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_LEFT_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_VCENTER_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE: {
- _set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_WIDE: {
- _set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- } break;
- case ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO: {
- _set_anchors_and_offsets_to_keep_ratio();
- } break;
-
- case ANCHORS_PRESET_TOP_LEFT: {
- _set_anchors_preset(PRESET_TOP_LEFT);
- } break;
- case ANCHORS_PRESET_TOP_RIGHT: {
- _set_anchors_preset(PRESET_TOP_RIGHT);
- } break;
- case ANCHORS_PRESET_BOTTOM_LEFT: {
- _set_anchors_preset(PRESET_BOTTOM_LEFT);
- } break;
- case ANCHORS_PRESET_BOTTOM_RIGHT: {
- _set_anchors_preset(PRESET_BOTTOM_RIGHT);
- } break;
- case ANCHORS_PRESET_CENTER_LEFT: {
- _set_anchors_preset(PRESET_CENTER_LEFT);
- } break;
- case ANCHORS_PRESET_CENTER_RIGHT: {
- _set_anchors_preset(PRESET_CENTER_RIGHT);
- } break;
- case ANCHORS_PRESET_CENTER_TOP: {
- _set_anchors_preset(PRESET_CENTER_TOP);
- } break;
- case ANCHORS_PRESET_CENTER_BOTTOM: {
- _set_anchors_preset(PRESET_CENTER_BOTTOM);
- } break;
- case ANCHORS_PRESET_CENTER: {
- _set_anchors_preset(PRESET_CENTER);
- } break;
- case ANCHORS_PRESET_TOP_WIDE: {
- _set_anchors_preset(PRESET_TOP_WIDE);
- } break;
- case ANCHORS_PRESET_LEFT_WIDE: {
- _set_anchors_preset(PRESET_LEFT_WIDE);
- } break;
- case ANCHORS_PRESET_RIGHT_WIDE: {
- _set_anchors_preset(PRESET_RIGHT_WIDE);
- } break;
- case ANCHORS_PRESET_BOTTOM_WIDE: {
- _set_anchors_preset(PRESET_BOTTOM_WIDE);
- } break;
- case ANCHORS_PRESET_VCENTER_WIDE: {
- _set_anchors_preset(PRESET_VCENTER_WIDE);
- } break;
- case ANCHORS_PRESET_HCENTER_WIDE: {
- _set_anchors_preset(PRESET_HCENTER_WIDE);
- } break;
- case ANCHORS_PRESET_WIDE: {
- _set_anchors_preset(Control::PRESET_WIDE);
- } break;
case ANIM_INSERT_KEY:
case ANIM_INSERT_KEY_EXISTING: {
@@ -5128,63 +4827,21 @@ void CanvasItemEditor::focus_selection() {
}
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
- key_pos = true;
- key_rot = true;
- key_scale = false;
-
- show_grid = false;
- show_origin = true;
- show_viewport = true;
- show_helpers = false;
- show_rulers = true;
- show_guides = true;
- show_transformation_gizmos = true;
- show_edit_locks = true;
zoom = 1.0 / MAX(1, EDSCALE);
view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH);
previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen
+
grid_offset = Point2();
grid_step = Point2(8, 8); // A power-of-two value works better as a default
primary_grid_steps = 8; // A power-of-two value works better as a default
grid_step_multiplier = 0;
+
snap_rotation_offset = 0;
snap_rotation_step = Math::deg2rad(15.0);
snap_scale_step = 0.1f;
- smart_snap_active = false;
- grid_snap_active = false;
- snap_node_parent = true;
- snap_node_anchors = true;
- snap_node_sides = true;
- snap_node_center = true;
- snap_other_nodes = true;
- snap_guides = true;
- snap_rotation = false;
- snap_scale = false;
- snap_relative = false;
- // Enable pixel snapping even if pixel snap rendering is disabled in the Project Settings.
- // This results in crisper visuals by preventing 2D nodes from being placed at subpixel coordinates.
- snap_pixel = true;
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
- selected_from_canvas = false;
- anchors_mode = false;
-
- drag_type = DRAG_NONE;
- drag_from = Vector2();
- drag_to = Vector2();
- dragged_guide_pos = Point2();
- dragged_guide_index = -1;
- is_hovering_h_guide = false;
- is_hovering_v_guide = false;
- pan_pressed = false;
-
- ruler_tool_active = false;
- ruler_tool_origin = Point2();
-
- bone_last_frame = 0;
-
- tool = TOOL_SELECT;
undo_redo = p_editor->get_undo_redo();
editor = p_editor;
editor_selection = p_editor->get_editor_selection();
@@ -5261,8 +4918,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->add_child(controls_vb);
- updating_scroll = false;
-
// Add some margin to the left for better aesthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
@@ -5492,28 +5147,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(context_menu_container);
_update_context_menu_stylebox();
- presets_menu = memnew(MenuButton);
- presets_menu->set_shortcut_context(this);
- presets_menu->set_text(TTR("Layout"));
- hbc_context_menu->add_child(presets_menu);
- presets_menu->hide();
- presets_menu->set_switch_on_hover(true);
-
- p = presets_menu->get_popup();
- p->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
-
- anchors_popup = memnew(PopupMenu);
- p->add_child(anchors_popup);
- anchors_popup->set_name("Anchors");
- anchors_popup->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
-
- anchor_mode_button = memnew(Button);
- anchor_mode_button->set_flat(true);
- hbc_context_menu->add_child(anchor_mode_button);
- anchor_mode_button->set_toggle_mode(true);
- anchor_mode_button->hide();
- anchor_mode_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_anchor_mode));
-
+ // Animation controls.
animation_hb = memnew(HBoxContainer);
hbc_context_menu->add_child(animation_hb);
animation_hb->add_child(memnew(VSeparator));
@@ -5599,6 +5233,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), Key::KP_DIVIDE);
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
+
+ // Store the singleton instance.
singleton = this;
// To ensure that scripts can parse the list of shortcuts correctly, we have to define
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 9fa44bfb25..73829b8abe 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CONTROL_EDITOR_PLUGIN_H
-#define CONTROL_EDITOR_PLUGIN_H
+#ifndef CANVAS_ITEM_EDITOR_PLUGIN_H
+#define CANVAS_ITEM_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
@@ -39,6 +39,7 @@
#include "scene/gui/label.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/spin_box.h"
+#include "scene/gui/texture_rect.h"
#include "scene/main/canvas_item.h"
class CanvasItemEditorViewport;
@@ -128,55 +129,6 @@ private:
UNLOCK_SELECTED,
GROUP_SELECTED,
UNGROUP_SELECTED,
- ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT,
- ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT,
- ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT,
- ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT,
- ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT,
- ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT,
- ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP,
- ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM,
- ANCHORS_AND_OFFSETS_PRESET_CENTER,
- ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_WIDE,
- ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO,
- ANCHORS_PRESET_TOP_LEFT,
- ANCHORS_PRESET_TOP_RIGHT,
- ANCHORS_PRESET_BOTTOM_LEFT,
- ANCHORS_PRESET_BOTTOM_RIGHT,
- ANCHORS_PRESET_CENTER_LEFT,
- ANCHORS_PRESET_CENTER_RIGHT,
- ANCHORS_PRESET_CENTER_TOP,
- ANCHORS_PRESET_CENTER_BOTTOM,
- ANCHORS_PRESET_CENTER,
- ANCHORS_PRESET_TOP_WIDE,
- ANCHORS_PRESET_LEFT_WIDE,
- ANCHORS_PRESET_RIGHT_WIDE,
- ANCHORS_PRESET_BOTTOM_WIDE,
- ANCHORS_PRESET_VCENTER_WIDE,
- ANCHORS_PRESET_HCENTER_WIDE,
- ANCHORS_PRESET_WIDE,
- OFFSETS_PRESET_TOP_LEFT,
- OFFSETS_PRESET_TOP_RIGHT,
- OFFSETS_PRESET_BOTTOM_LEFT,
- OFFSETS_PRESET_BOTTOM_RIGHT,
- OFFSETS_PRESET_CENTER_LEFT,
- OFFSETS_PRESET_CENTER_RIGHT,
- OFFSETS_PRESET_CENTER_TOP,
- OFFSETS_PRESET_CENTER_BOTTOM,
- OFFSETS_PRESET_CENTER,
- OFFSETS_PRESET_TOP_WIDE,
- OFFSETS_PRESET_LEFT_WIDE,
- OFFSETS_PRESET_RIGHT_WIDE,
- OFFSETS_PRESET_BOTTOM_WIDE,
- OFFSETS_PRESET_VCENTER_WIDE,
- OFFSETS_PRESET_HCENTER_WIDE,
- OFFSETS_PRESET_WIDE,
ANIM_INSERT_KEY,
ANIM_INSERT_KEY_EXISTING,
ANIM_INSERT_POS,
@@ -226,7 +178,7 @@ private:
bool selection_menu_additive_selection;
- Tool tool;
+ Tool tool = TOOL_SELECT;
Control *viewport;
Control *viewport_scrollable;
@@ -239,21 +191,20 @@ private:
HBoxContainer *hbc_context_menu;
Transform2D transform;
- bool show_grid;
- bool show_rulers;
- bool show_guides;
- bool show_origin;
- bool show_viewport;
- bool show_helpers;
- bool show_edit_locks;
- bool show_transformation_gizmos;
+ bool show_grid = false;
+ bool show_rulers = true;
+ bool show_guides = true;
+ bool show_origin = true;
+ bool show_viewport = true;
+ bool show_helpers = false;
+ bool show_edit_locks = true;
+ bool show_transformation_gizmos = true;
real_t zoom;
Point2 view_offset;
Point2 previous_update_view_offset;
- bool selected_from_canvas;
- bool anchors_mode;
+ bool selected_from_canvas = false;
Point2 grid_offset;
Point2 grid_step;
@@ -263,26 +214,30 @@ private:
real_t snap_rotation_step;
real_t snap_rotation_offset;
real_t snap_scale_step;
- bool smart_snap_active;
- bool grid_snap_active;
-
- bool snap_node_parent;
- bool snap_node_anchors;
- bool snap_node_sides;
- bool snap_node_center;
- bool snap_other_nodes;
- bool snap_guides;
- bool snap_rotation;
- bool snap_scale;
- bool snap_relative;
- bool snap_pixel;
- bool key_pos;
- bool key_rot;
- bool key_scale;
- bool pan_pressed;
-
- bool ruler_tool_active;
- Point2 ruler_tool_origin;
+ bool smart_snap_active = false;
+ bool grid_snap_active = false;
+
+ bool snap_node_parent = true;
+ bool snap_node_anchors = true;
+ bool snap_node_sides = true;
+ bool snap_node_center = true;
+ bool snap_other_nodes = true;
+ bool snap_guides = true;
+ bool snap_rotation = false;
+ bool snap_scale = false;
+ bool snap_relative = false;
+ // Enable pixel snapping even if pixel snap rendering is disabled in the Project Settings.
+ // This results in crisper visuals by preventing 2D nodes from being placed at subpixel coordinates.
+ bool snap_pixel = true;
+
+ bool key_pos = true;
+ bool key_rot = true;
+ bool key_scale = false;
+
+ bool pan_pressed = false;
+
+ bool ruler_tool_active = false;
+ Point2 ruler_tool_origin = Point2();
Point2 node_create_position;
MenuOption last_option;
@@ -310,7 +265,7 @@ private:
uint64_t last_pass = 0;
};
- uint64_t bone_last_frame;
+ uint64_t bone_last_frame = 0;
struct BoneKey {
ObjectID from;
@@ -363,12 +318,6 @@ private:
HBoxContainer *animation_hb;
MenuButton *animation_menu;
- MenuButton *presets_menu;
- PopupMenu *anchors_and_margins_popup;
- PopupMenu *anchors_popup;
-
- Button *anchor_mode_button;
-
Button *key_loc_button;
Button *key_rot_button;
Button *key_scale_button;
@@ -382,15 +331,15 @@ private:
Control *left_ruler;
Point2 drag_start_origin;
- DragType drag_type;
- Point2 drag_from;
- Point2 drag_to;
+ DragType drag_type = DRAG_NONE;
+ Point2 drag_from = Vector2();
+ Point2 drag_to = Vector2();
Point2 drag_rotation_center;
List<CanvasItem *> drag_selection;
- int dragged_guide_index;
- Point2 dragged_guide_pos;
- bool is_hovering_h_guide;
- bool is_hovering_v_guide;
+ int dragged_guide_index = -1;
+ Point2 dragged_guide_pos = Point2();
+ bool is_hovering_h_guide = false;
+ bool is_hovering_v_guide = false;
bool updating_value_dialog;
@@ -432,7 +381,7 @@ private:
Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
void _popup_callback(int p_op);
- bool updating_scroll;
+ bool updating_scroll = false;
void _update_scroll(real_t);
void _update_scrollbars();
void _snap_changed();
@@ -444,7 +393,7 @@ private:
UndoRedo *undo_redo;
- List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
+ List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true);
Rect2 _get_encompassing_rect(const Node *p_node);
@@ -518,12 +467,6 @@ private:
const SnapTarget p_snap_target, List<const CanvasItem *> p_exceptions,
const Node *p_current);
- void _set_anchors_preset(Control::LayoutPreset p_preset);
- void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset);
- void _set_anchors_and_offsets_to_keep_ratio();
-
- void _button_toggle_anchor_mode(bool p_status);
-
VBoxContainer *controls_vb;
EditorZoomWidget *zoom_widget;
void _update_zoom(real_t p_zoom);
@@ -602,8 +545,6 @@ public:
void focus_selection();
- bool is_anchors_mode_enabled() { return anchors_mode; };
-
EditorSelection *editor_selection;
CanvasItemEditor(EditorNode *p_editor);
@@ -683,4 +624,4 @@ public:
~CanvasItemEditorViewport();
};
-#endif
+#endif //CANVAS_ITEM_EDITOR_PLUGIN_H
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
new file mode 100644
index 0000000000..c4ea098e92
--- /dev/null
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -0,0 +1,1023 @@
+/*************************************************************************/
+/* control_editor_plugin.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 "control_editor_plugin.h"
+
+#include "editor/plugins/canvas_item_editor_plugin.h"
+
+void ControlPositioningWarning::_update_warning() {
+ if (!control_node) {
+ title_icon->set_texture(nullptr);
+ title_label->set_text("");
+ hint_label->set_text("");
+ return;
+ }
+
+ Node *parent_node = control_node->get_parent_control();
+ if (!parent_node) {
+ title_icon->set_texture(get_theme_icon(SNAME("SubViewport"), SNAME("EditorIcons")));
+ title_label->set_text(TTR("This node doesn't have a control parent."));
+ hint_label->set_text(TTR("Use the appropriate layout properties depending on where you are going to put it."));
+ } else if (Object::cast_to<Container>(parent_node)) {
+ title_icon->set_texture(get_theme_icon(SNAME("Container"), SNAME("EditorIcons")));
+ title_label->set_text(TTR("This node is a child of a container."));
+ hint_label->set_text(TTR("Use container properties for positioning."));
+ } else {
+ title_icon->set_texture(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons")));
+ title_label->set_text(TTR("This node is a child of a regular control."));
+ hint_label->set_text(TTR("Use anchors and the rectangle for positioning."));
+ }
+
+ bg_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg_group_note"), SNAME("EditorProperty")));
+}
+
+void ControlPositioningWarning::_update_toggler() {
+ Ref<Texture2D> arrow;
+ if (hint_label->is_visible()) {
+ arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));
+ set_tooltip(TTR("Collapse positioning hint."));
+ } else {
+ if (is_layout_rtl()) {
+ arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree"));
+ } else {
+ arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree"));
+ }
+ set_tooltip(TTR("Expand positioning hint."));
+ }
+
+ hint_icon->set_texture(arrow);
+}
+
+void ControlPositioningWarning::set_control(Control *p_node) {
+ control_node = p_node;
+ _update_warning();
+}
+
+void ControlPositioningWarning::gui_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
+ bool state = !hint_label->is_visible();
+
+ hint_filler_left->set_visible(state);
+ hint_label->set_visible(state);
+ hint_filler_right->set_visible(state);
+
+ _update_toggler();
+ }
+}
+
+void ControlPositioningWarning::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ _update_warning();
+ _update_toggler();
+ break;
+ }
+}
+
+ControlPositioningWarning::ControlPositioningWarning() {
+ set_mouse_filter(MOUSE_FILTER_STOP);
+
+ bg_panel = memnew(PanelContainer);
+ bg_panel->set_mouse_filter(MOUSE_FILTER_IGNORE);
+ add_child(bg_panel);
+
+ grid = memnew(GridContainer);
+ grid->set_columns(3);
+ bg_panel->add_child(grid);
+
+ title_icon = memnew(TextureRect);
+ title_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
+ grid->add_child(title_icon);
+
+ title_label = memnew(Label);
+ title_label->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD);
+ title_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ title_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
+ grid->add_child(title_label);
+
+ hint_icon = memnew(TextureRect);
+ hint_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
+ grid->add_child(hint_icon);
+
+ // Filler.
+ hint_filler_left = memnew(Control);
+ hint_filler_left->hide();
+ grid->add_child(hint_filler_left);
+
+ hint_label = memnew(Label);
+ hint_label->set_autowrap_mode(Label::AutowrapMode::AUTOWRAP_WORD);
+ hint_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hint_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
+ hint_label->hide();
+ grid->add_child(hint_label);
+
+ // Filler.
+ hint_filler_right = memnew(Control);
+ hint_filler_right->hide();
+ grid->add_child(hint_filler_right);
+}
+
+void EditorPropertyAnchorsPreset::_set_read_only(bool p_read_only) {
+ options->set_disabled(p_read_only);
+};
+
+void EditorPropertyAnchorsPreset::_option_selected(int p_which) {
+ int64_t val = options->get_item_metadata(p_which);
+ emit_changed(get_edited_property(), val);
+}
+
+void EditorPropertyAnchorsPreset::update_property() {
+ int64_t which = get_edited_object()->get(get_edited_property());
+
+ for (int i = 0; i < options->get_item_count(); i++) {
+ Variant val = options->get_item_metadata(i);
+ if (val != Variant() && which == (int64_t)val) {
+ options->select(i);
+ return;
+ }
+ }
+}
+
+void EditorPropertyAnchorsPreset::setup(const Vector<String> &p_options) {
+ options->clear();
+
+ Vector<String> split_after;
+ split_after.append("Custom");
+ split_after.append("PresetWide");
+ split_after.append("PresetBottomLeft");
+ split_after.append("PresetCenter");
+
+ for (int i = 0, j = 0; i < p_options.size(); i++, j++) {
+ Vector<String> text_split = p_options[i].split(":");
+ int64_t current_val = text_split[1].to_int();
+
+ String humanized_name = text_split[0];
+ if (humanized_name.begins_with("Preset")) {
+ if (humanized_name == "PresetWide") {
+ humanized_name = "Full Rect";
+ } else {
+ humanized_name = humanized_name.trim_prefix("Preset");
+ humanized_name = humanized_name.capitalize();
+ }
+
+ String icon_name = text_split[0].trim_prefix("Preset");
+ icon_name = "ControlAlign" + icon_name;
+ options->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(icon_name, "EditorIcons"), humanized_name);
+ } else {
+ options->add_item(humanized_name);
+ }
+
+ options->set_item_metadata(j, current_val);
+ if (split_after.has(text_split[0])) {
+ options->add_separator();
+ j++;
+ }
+ }
+}
+
+EditorPropertyAnchorsPreset::EditorPropertyAnchorsPreset() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ options->set_flat(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", callable_mp(this, &EditorPropertyAnchorsPreset::_option_selected));
+}
+
+void EditorPropertySizeFlags::_set_read_only(bool p_read_only) {
+ for (CheckBox *check : flag_checks) {
+ check->set_disabled(p_read_only);
+ }
+ flag_presets->set_disabled(p_read_only);
+};
+
+void EditorPropertySizeFlags::_preset_selected(int p_which) {
+ int preset = flag_presets->get_item_id(p_which);
+ if (preset == SIZE_FLAGS_PRESET_CUSTOM) {
+ flag_options->set_visible(true);
+ return;
+ }
+ flag_options->set_visible(false);
+
+ uint32_t value = 0;
+ switch (preset) {
+ case SIZE_FLAGS_PRESET_FILL:
+ value = Control::SIZE_FILL;
+ break;
+ case SIZE_FLAGS_PRESET_SHRINK_BEGIN:
+ value = Control::SIZE_SHRINK_BEGIN;
+ break;
+ case SIZE_FLAGS_PRESET_SHRINK_CENTER:
+ value = Control::SIZE_SHRINK_CENTER;
+ break;
+ case SIZE_FLAGS_PRESET_SHRINK_END:
+ value = Control::SIZE_SHRINK_END;
+ break;
+ }
+
+ bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
+ if (is_expand) {
+ value |= Control::SIZE_EXPAND;
+ }
+
+ emit_changed(get_edited_property(), value);
+}
+
+void EditorPropertySizeFlags::_expand_toggled() {
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ if (flag_expand->is_visible() && flag_expand->is_pressed()) {
+ value |= Control::SIZE_EXPAND;
+ } else {
+ value ^= Control::SIZE_EXPAND;
+ }
+
+ // Keep the custom preset selected as we toggle individual flags.
+ keep_selected_preset = true;
+ emit_changed(get_edited_property(), value);
+}
+
+void EditorPropertySizeFlags::_flag_toggled() {
+ uint32_t value = 0;
+ for (int i = 0; i < flag_checks.size(); i++) {
+ if (flag_checks[i]->is_pressed()) {
+ int flag_value = flag_checks[i]->get_meta("_value");
+ value |= flag_value;
+ }
+ }
+
+ bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
+ if (is_expand) {
+ value |= Control::SIZE_EXPAND;
+ }
+
+ // Keep the custom preset selected as we toggle individual flags.
+ keep_selected_preset = true;
+ emit_changed(get_edited_property(), value);
+}
+
+void EditorPropertySizeFlags::update_property() {
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ for (int i = 0; i < flag_checks.size(); i++) {
+ int flag_value = flag_checks[i]->get_meta("_value");
+ if (value & flag_value) {
+ flag_checks[i]->set_pressed(true);
+ } else {
+ flag_checks[i]->set_pressed(false);
+ }
+ }
+
+ bool is_expand = value & Control::SIZE_EXPAND;
+ flag_expand->set_pressed(is_expand);
+
+ if (keep_selected_preset) {
+ keep_selected_preset = false;
+ return;
+ }
+
+ FlagPreset preset = SIZE_FLAGS_PRESET_CUSTOM;
+ if (value == Control::SIZE_FILL || value == (Control::SIZE_FILL | Control::SIZE_EXPAND)) {
+ preset = SIZE_FLAGS_PRESET_FILL;
+ } else if (value == Control::SIZE_SHRINK_BEGIN || value == (Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND)) {
+ preset = SIZE_FLAGS_PRESET_SHRINK_BEGIN;
+ } else if (value == Control::SIZE_SHRINK_CENTER || value == (Control::SIZE_SHRINK_CENTER | Control::SIZE_EXPAND)) {
+ preset = SIZE_FLAGS_PRESET_SHRINK_CENTER;
+ } else if (value == Control::SIZE_SHRINK_END || value == (Control::SIZE_SHRINK_END | Control::SIZE_EXPAND)) {
+ preset = SIZE_FLAGS_PRESET_SHRINK_END;
+ }
+
+ int preset_idx = flag_presets->get_item_index(preset);
+ if (preset_idx >= 0) {
+ flag_presets->select(preset_idx);
+ }
+ flag_options->set_visible(preset == SIZE_FLAGS_PRESET_CUSTOM);
+}
+
+void EditorPropertySizeFlags::setup(const Vector<String> &p_options, bool p_vertical) {
+ vertical = p_vertical;
+
+ if (p_options.size() == 0) {
+ flag_presets->clear();
+ flag_presets->add_item(TTR("Container Default"));
+ flag_presets->set_disabled(true);
+ flag_expand->set_visible(false);
+ return;
+ }
+
+ Map<int, String> flags;
+ for (int i = 0, j = 0; i < p_options.size(); i++, j++) {
+ Vector<String> text_split = p_options[i].split(":");
+ int64_t current_val = text_split[1].to_int();
+ flags[current_val] = text_split[0];
+
+ if (current_val == SIZE_EXPAND) {
+ continue;
+ }
+
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_text(text_split[0]);
+ cb->set_clip_text(true);
+ cb->set_meta("_value", current_val);
+ cb->connect("pressed", callable_mp(this, &EditorPropertySizeFlags::_flag_toggled));
+ add_focusable(cb);
+
+ flag_options->add_child(cb);
+ flag_checks.append(cb);
+ }
+
+ Control *gui_base = EditorNode::get_singleton()->get_gui_base();
+ String wide_preset_icon = SNAME("ControlAlignHCenterWide");
+ if (vertical) {
+ wide_preset_icon = SNAME("ControlAlignVCenterWide");
+ }
+
+ flag_presets->clear();
+ if (flags.has(SIZE_FILL)) {
+ flag_presets->add_icon_item(gui_base->get_theme_icon(wide_preset_icon, SNAME("EditorIcons")), TTR("Fill"), SIZE_FLAGS_PRESET_FILL);
+ }
+ // Shrink Begin is the same as no flags at all, as such it cannot be disabled.
+ flag_presets->add_icon_item(gui_base->get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Shrink Begin"), SIZE_FLAGS_PRESET_SHRINK_BEGIN);
+ if (flags.has(SIZE_SHRINK_CENTER)) {
+ flag_presets->add_icon_item(gui_base->get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Shrink Center"), SIZE_FLAGS_PRESET_SHRINK_CENTER);
+ }
+ if (flags.has(SIZE_SHRINK_END)) {
+ flag_presets->add_icon_item(gui_base->get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Shrink End"), SIZE_FLAGS_PRESET_SHRINK_END);
+ }
+ flag_presets->add_separator();
+ flag_presets->add_item(TTR("Custom"), SIZE_FLAGS_PRESET_CUSTOM);
+
+ flag_expand->set_visible(flags.has(SIZE_EXPAND));
+}
+
+EditorPropertySizeFlags::EditorPropertySizeFlags() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+
+ flag_presets = memnew(OptionButton);
+ flag_presets->set_clip_text(true);
+ flag_presets->set_flat(true);
+ vb->add_child(flag_presets);
+ add_focusable(flag_presets);
+ set_label_reference(flag_presets);
+ flag_presets->connect("item_selected", callable_mp(this, &EditorPropertySizeFlags::_preset_selected));
+
+ flag_options = memnew(VBoxContainer);
+ flag_options->hide();
+ vb->add_child(flag_options);
+
+ flag_expand = memnew(CheckBox);
+ flag_expand->set_text(TTR("Expand"));
+ vb->add_child(flag_expand);
+ add_focusable(flag_expand);
+ flag_expand->connect("pressed", callable_mp(this, &EditorPropertySizeFlags::_expand_toggled));
+}
+
+bool EditorInspectorPluginControl::can_handle(Object *p_object) {
+ return Object::cast_to<Control>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginControl::parse_group(Object *p_object, const String &p_group) {
+ Control *control = Object::cast_to<Control>(p_object);
+ if (!control || p_group != "Layout") {
+ return;
+ }
+
+ ControlPositioningWarning *pos_warning = memnew(ControlPositioningWarning);
+ pos_warning->set_control(control);
+ add_custom_control(pos_warning);
+}
+
+bool EditorInspectorPluginControl::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
+ Control *control = Object::cast_to<Control>(p_object);
+ if (!control) {
+ return false;
+ }
+
+ if (p_path == "anchors_preset") {
+ EditorPropertyAnchorsPreset *prop_editor = memnew(EditorPropertyAnchorsPreset);
+ Vector<String> options = p_hint_text.split(",");
+ prop_editor->setup(options);
+ add_property_editor(p_path, prop_editor);
+
+ return true;
+ }
+
+ if (p_path == "size_flags_horizontal" || p_path == "size_flags_vertical") {
+ EditorPropertySizeFlags *prop_editor = memnew(EditorPropertySizeFlags);
+ Vector<String> options;
+ if (!p_hint_text.is_empty()) {
+ options = p_hint_text.split(",");
+ }
+ prop_editor->setup(options, p_path == "size_flags_vertical");
+ add_property_editor(p_path, prop_editor);
+
+ return true;
+ }
+
+ return false;
+}
+
+void ControlEditorToolbar::_set_anchors_and_offsets_preset(Control::LayoutPreset p_preset) {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Anchors and Offsets"));
+
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ if (control) {
+ undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE);
+ break;
+ case PRESET_LEFT_WIDE:
+ case PRESET_TOP_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_MINSIZE);
+ break;
+ }
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+ }
+ }
+
+ undo_redo->commit_action();
+
+ anchors_mode = false;
+ anchor_mode_button->set_pressed(anchors_mode);
+}
+
+void ControlEditorToolbar::_set_anchors_and_offsets_to_keep_ratio() {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Anchors and Offsets"));
+
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ 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", SIDE_LEFT, top_left_anchor.x, false, true);
+ undo_redo->add_do_method(control, "set_anchor", SIDE_RIGHT, bottom_right_anchor.x, false, true);
+ undo_redo->add_do_method(control, "set_anchor", SIDE_TOP, top_left_anchor.y, false, true);
+ undo_redo->add_do_method(control, "set_anchor", SIDE_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;
+ anchor_mode_button->set_pressed(anchors_mode);
+ }
+ }
+
+ undo_redo->commit_action();
+}
+
+void ControlEditorToolbar::_set_anchors_preset(Control::LayoutPreset p_preset) {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Anchors"));
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ if (control) {
+ undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+ }
+ }
+
+ undo_redo->commit_action();
+}
+
+void ControlEditorToolbar::_set_container_h_preset(Control::SizeFlags p_preset) {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Horizontal Size Flags"));
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ if (control) {
+ undo_redo->add_do_method(control, "set_h_size_flags", p_preset);
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+ }
+ }
+
+ undo_redo->commit_action();
+}
+
+void ControlEditorToolbar::_set_container_v_preset(Control::SizeFlags p_preset) {
+ List<Node *> selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Horizontal Size Flags"));
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ if (control) {
+ undo_redo->add_do_method(control, "set_v_size_flags", p_preset);
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+ }
+ }
+
+ undo_redo->commit_action();
+}
+
+Vector2 ControlEditorToolbar::_anchor_to_position(const Control *p_control, Vector2 anchor) {
+ ERR_FAIL_COND_V(!p_control, Vector2());
+
+ Transform2D parent_transform = p_control->get_transform().affine_inverse();
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
+
+ if (p_control->is_layout_rtl()) {
+ return parent_transform.xform(parent_rect.position + Vector2(parent_rect.size.x - parent_rect.size.x * anchor.x, parent_rect.size.y * anchor.y));
+ } else {
+ return parent_transform.xform(parent_rect.position + Vector2(parent_rect.size.x * anchor.x, parent_rect.size.y * anchor.y));
+ }
+}
+
+Vector2 ControlEditorToolbar::_position_to_anchor(const Control *p_control, Vector2 position) {
+ ERR_FAIL_COND_V(!p_control, Vector2());
+
+ Rect2 parent_rect = p_control->get_parent_anchorable_rect();
+
+ Vector2 output = Vector2();
+ if (p_control->is_layout_rtl()) {
+ output.x = (parent_rect.size.x == 0) ? 0.0 : (parent_rect.size.x - p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x;
+ } else {
+ output.x = (parent_rect.size.x == 0) ? 0.0 : (p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x;
+ }
+ output.y = (parent_rect.size.y == 0) ? 0.0 : (p_control->get_transform().xform(position).y - parent_rect.position.y) / parent_rect.size.y;
+ return output;
+}
+
+void ControlEditorToolbar::_button_toggle_anchor_mode(bool p_status) {
+ List<Control *> selection = _get_edited_controls(false, false);
+ for (Control *E : selection) {
+ if (Object::cast_to<Container>(E->get_parent())) {
+ continue;
+ }
+
+ E->set_meta("_edit_use_anchors_", p_status);
+ }
+
+ anchors_mode = p_status;
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+bool ControlEditorToolbar::_is_node_locked(const Node *p_node) {
+ return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
+}
+
+List<Control *> ControlEditorToolbar::_get_edited_controls(bool retrieve_locked, bool remove_controls_if_parent_in_selection) {
+ List<Control *> selection;
+ for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
+ Control *control = Object::cast_to<Control>(E.key);
+ if (control && control->is_visible_in_tree() && control->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(control))) {
+ selection.push_back(control);
+ }
+ }
+
+ if (remove_controls_if_parent_in_selection) {
+ List<Control *> filtered_selection;
+ for (Control *E : selection) {
+ if (!selection.find(E->get_parent())) {
+ filtered_selection.push_back(E);
+ }
+ }
+ return filtered_selection;
+ }
+
+ return selection;
+}
+
+void ControlEditorToolbar::_popup_callback(int p_op) {
+ switch (p_op) {
+ case ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT: {
+ _set_anchors_and_offsets_preset(PRESET_TOP_LEFT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT: {
+ _set_anchors_and_offsets_preset(PRESET_TOP_RIGHT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT: {
+ _set_anchors_and_offsets_preset(PRESET_BOTTOM_LEFT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT: {
+ _set_anchors_and_offsets_preset(PRESET_BOTTOM_RIGHT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT: {
+ _set_anchors_and_offsets_preset(PRESET_CENTER_LEFT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT: {
+ _set_anchors_and_offsets_preset(PRESET_CENTER_RIGHT);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP: {
+ _set_anchors_and_offsets_preset(PRESET_CENTER_TOP);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM: {
+ _set_anchors_and_offsets_preset(PRESET_CENTER_BOTTOM);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_CENTER: {
+ _set_anchors_and_offsets_preset(PRESET_CENTER);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_TOP_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_LEFT_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_VCENTER_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE: {
+ _set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_WIDE: {
+ _set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ } break;
+ case ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO: {
+ _set_anchors_and_offsets_to_keep_ratio();
+ } break;
+
+ case ANCHORS_PRESET_TOP_LEFT: {
+ _set_anchors_preset(PRESET_TOP_LEFT);
+ } break;
+ case ANCHORS_PRESET_TOP_RIGHT: {
+ _set_anchors_preset(PRESET_TOP_RIGHT);
+ } break;
+ case ANCHORS_PRESET_BOTTOM_LEFT: {
+ _set_anchors_preset(PRESET_BOTTOM_LEFT);
+ } break;
+ case ANCHORS_PRESET_BOTTOM_RIGHT: {
+ _set_anchors_preset(PRESET_BOTTOM_RIGHT);
+ } break;
+ case ANCHORS_PRESET_CENTER_LEFT: {
+ _set_anchors_preset(PRESET_CENTER_LEFT);
+ } break;
+ case ANCHORS_PRESET_CENTER_RIGHT: {
+ _set_anchors_preset(PRESET_CENTER_RIGHT);
+ } break;
+ case ANCHORS_PRESET_CENTER_TOP: {
+ _set_anchors_preset(PRESET_CENTER_TOP);
+ } break;
+ case ANCHORS_PRESET_CENTER_BOTTOM: {
+ _set_anchors_preset(PRESET_CENTER_BOTTOM);
+ } break;
+ case ANCHORS_PRESET_CENTER: {
+ _set_anchors_preset(PRESET_CENTER);
+ } break;
+ case ANCHORS_PRESET_TOP_WIDE: {
+ _set_anchors_preset(PRESET_TOP_WIDE);
+ } break;
+ case ANCHORS_PRESET_LEFT_WIDE: {
+ _set_anchors_preset(PRESET_LEFT_WIDE);
+ } break;
+ case ANCHORS_PRESET_RIGHT_WIDE: {
+ _set_anchors_preset(PRESET_RIGHT_WIDE);
+ } break;
+ case ANCHORS_PRESET_BOTTOM_WIDE: {
+ _set_anchors_preset(PRESET_BOTTOM_WIDE);
+ } break;
+ case ANCHORS_PRESET_VCENTER_WIDE: {
+ _set_anchors_preset(PRESET_VCENTER_WIDE);
+ } break;
+ case ANCHORS_PRESET_HCENTER_WIDE: {
+ _set_anchors_preset(PRESET_HCENTER_WIDE);
+ } break;
+ case ANCHORS_PRESET_WIDE: {
+ _set_anchors_preset(Control::PRESET_WIDE);
+ } break;
+
+ case CONTAINERS_H_PRESET_FILL: {
+ _set_container_h_preset(Control::SIZE_FILL);
+ } break;
+ case CONTAINERS_H_PRESET_FILL_EXPAND: {
+ _set_container_h_preset(Control::SIZE_EXPAND_FILL);
+ } break;
+ case CONTAINERS_H_PRESET_SHRINK_BEGIN: {
+ _set_container_h_preset(Control::SIZE_SHRINK_BEGIN);
+ } break;
+ case CONTAINERS_H_PRESET_SHRINK_CENTER: {
+ _set_container_h_preset(Control::SIZE_SHRINK_CENTER);
+ } break;
+ case CONTAINERS_H_PRESET_SHRINK_END: {
+ _set_container_h_preset(Control::SIZE_SHRINK_END);
+ } break;
+
+ case CONTAINERS_V_PRESET_FILL: {
+ _set_container_v_preset(Control::SIZE_FILL);
+ } break;
+ case CONTAINERS_V_PRESET_FILL_EXPAND: {
+ _set_container_v_preset(Control::SIZE_EXPAND_FILL);
+ } break;
+ case CONTAINERS_V_PRESET_SHRINK_BEGIN: {
+ _set_container_v_preset(Control::SIZE_SHRINK_BEGIN);
+ } break;
+ case CONTAINERS_V_PRESET_SHRINK_CENTER: {
+ _set_container_v_preset(Control::SIZE_SHRINK_CENTER);
+ } break;
+ case CONTAINERS_V_PRESET_SHRINK_END: {
+ _set_container_v_preset(Control::SIZE_SHRINK_END);
+ } break;
+ }
+}
+
+void ControlEditorToolbar::_selection_changed() {
+ // Update the anchors_mode.
+ int nb_controls = 0;
+ int nb_valid_controls = 0;
+ int nb_anchors_mode = 0;
+
+ List<Node *> selection = editor_selection->get_selected_node_list();
+ for (Node *E : selection) {
+ Control *control = Object::cast_to<Control>(E);
+ if (!control) {
+ continue;
+ }
+
+ nb_controls++;
+ if (Object::cast_to<Container>(control->get_parent())) {
+ continue;
+ }
+
+ nb_valid_controls++;
+ if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) {
+ nb_anchors_mode++;
+ }
+ }
+
+ anchors_mode = (nb_valid_controls == nb_anchors_mode);
+ anchor_mode_button->set_pressed(anchors_mode);
+
+ if (nb_controls > 0) {
+ set_physics_process(true);
+ } else {
+ set_physics_process(false);
+ set_visible(false);
+ }
+}
+
+void ControlEditorToolbar::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ anchor_layouts_icon->set_texture(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons")));
+
+ PopupMenu *p = anchor_presets_menu->get_popup();
+ p->clear();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT);
+ p->add_separator();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_AND_OFFSETS_PRESET_CENTER);
+ p->add_separator();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE);
+ p->add_separator();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_AND_OFFSETS_PRESET_WIDE);
+ p->add_icon_item(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")), TTR("Keep Current Ratio"), ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO);
+ p->set_item_tooltip(19, TTR("Adjust anchors and offsets to match the current rect size."));
+
+ p->add_separator();
+ p->add_submenu_item(TTR("Anchors only"), "Anchors");
+ p->set_item_icon(21, get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
+
+ anchors_popup->clear();
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_PRESET_TOP_LEFT);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_PRESET_TOP_RIGHT);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_PRESET_BOTTOM_RIGHT);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_PRESET_BOTTOM_LEFT);
+ anchors_popup->add_separator();
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_PRESET_CENTER_LEFT);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_PRESET_CENTER_TOP);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_PRESET_CENTER_RIGHT);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_PRESET_CENTER_BOTTOM);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_PRESET_CENTER);
+ anchors_popup->add_separator();
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_PRESET_LEFT_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_PRESET_TOP_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_PRESET_RIGHT_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_PRESET_BOTTOM_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_PRESET_VCENTER_WIDE);
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_PRESET_HCENTER_WIDE);
+ anchors_popup->add_separator();
+ anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_WIDE);
+
+ anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
+
+ container_layouts_icon->set_texture(get_theme_icon(SNAME("Container"), SNAME("EditorIcons")));
+
+ p = container_h_presets_menu->get_popup();
+ p->clear();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("Fill"), CONTAINERS_H_PRESET_FILL);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("Fill & Expand"), CONTAINERS_H_PRESET_FILL_EXPAND);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Shrink Begin"), CONTAINERS_H_PRESET_SHRINK_BEGIN);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Shrink Center"), CONTAINERS_H_PRESET_SHRINK_CENTER);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Shrink End"), CONTAINERS_H_PRESET_SHRINK_END);
+
+ p = container_v_presets_menu->get_popup();
+ p->clear();
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("Fill"), CONTAINERS_V_PRESET_FILL);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("Fill & Expand"), CONTAINERS_V_PRESET_FILL_EXPAND);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Shrink Begin"), CONTAINERS_V_PRESET_SHRINK_BEGIN);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Shrink Center"), CONTAINERS_V_PRESET_SHRINK_CENTER);
+ p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Shrink End"), CONTAINERS_V_PRESET_SHRINK_END);
+ } break;
+
+ case NOTIFICATION_PHYSICS_PROCESS: {
+ bool has_control_parents = false;
+ bool has_container_parents = false;
+
+ // Update the viewport if the canvas_item changes
+ List<Control *> selection = _get_edited_controls(true);
+ for (Control *control : selection) {
+ if (Object::cast_to<Control>(control->get_parent())) {
+ has_control_parents = true;
+ }
+ if (Object::cast_to<Container>(control->get_parent())) {
+ has_container_parents = true;
+ }
+ }
+
+ // Show / Hide the control layout buttons.
+ if (selection.size() > 0) {
+ set_visible(true);
+
+ // Toggle anchor and container layout buttons depending on parents of the selected nodes.
+ // - If there are no control parents, enable everything.
+ // - If there are container parents, then enable only container buttons.
+ // - If there are NO container parents, then enable only anchor buttons.
+ bool enable_anchors = false;
+ bool enable_containers = false;
+ if (!has_control_parents) {
+ enable_anchors = true;
+ enable_containers = true;
+ } else if (has_container_parents) {
+ enable_containers = true;
+ } else {
+ enable_anchors = true;
+ }
+
+ if (enable_anchors) {
+ anchor_presets_menu->set_disabled(false);
+ anchor_presets_menu->set_tooltip(TTR("Presets for the anchor and offset values of a Control node."));
+ anchor_mode_button->set_disabled(false);
+ anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their offsets."));
+ } else {
+ anchor_presets_menu->set_disabled(true);
+ anchor_presets_menu->set_tooltip(TTR("Children of containers have their anchors and offsets values controlled by their parent."));
+ anchor_mode_button->set_disabled(true);
+ anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and offsets values controlled by their parent."));
+ }
+
+ if (enable_containers) {
+ container_h_presets_menu->set_disabled(false);
+ container_h_presets_menu->set_tooltip(TTR("Horizontal sizing setting for children of a Container node."));
+ container_v_presets_menu->set_disabled(false);
+ container_v_presets_menu->set_tooltip(TTR("Vertical sizing setting for children of a Container node."));
+ } else {
+ container_h_presets_menu->set_disabled(true);
+ container_h_presets_menu->set_tooltip(TTR("Children of regular controls are controlled by their anchors and offsets."));
+ container_v_presets_menu->set_disabled(true);
+ container_v_presets_menu->set_tooltip(TTR("Children of regular controls are controlled by their anchors and offsets."));
+ }
+ } else {
+ set_visible(false);
+ }
+ } break;
+ }
+}
+
+ControlEditorToolbar::ControlEditorToolbar(EditorNode *p_editor) {
+ anchor_layouts_icon = memnew(TextureRect);
+ anchor_layouts_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
+ add_child(anchor_layouts_icon);
+
+ Label *l = memnew(Label);
+ l->set_text(TTR("Anchors"));
+ l->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
+ add_child(l);
+
+ anchor_presets_menu = memnew(MenuButton);
+ anchor_presets_menu->set_shortcut_context(this);
+ anchor_presets_menu->set_text(TTR("Preset"));
+ add_child(anchor_presets_menu);
+ anchor_presets_menu->set_switch_on_hover(true);
+
+ PopupMenu *p = anchor_presets_menu->get_popup();
+ p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback));
+
+ anchors_popup = memnew(PopupMenu);
+ p->add_child(anchors_popup);
+ anchors_popup->set_name("Anchors");
+ anchors_popup->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback));
+
+ anchor_mode_button = memnew(Button);
+ anchor_mode_button->set_flat(true);
+ anchor_mode_button->set_toggle_mode(true);
+ add_child(anchor_mode_button);
+ anchor_mode_button->connect("toggled", callable_mp(this, &ControlEditorToolbar::_button_toggle_anchor_mode));
+
+ add_child(memnew(VSeparator));
+
+ container_layouts_icon = memnew(TextureRect);
+ container_layouts_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
+ add_child(container_layouts_icon);
+
+ l = memnew(Label);
+ l->set_text(TTR("Containers"));
+ l->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
+ add_child(l);
+
+ container_h_presets_menu = memnew(MenuButton);
+ container_h_presets_menu->set_shortcut_context(this);
+ container_h_presets_menu->set_text(TTR("Horizontal"));
+ add_child(container_h_presets_menu);
+ container_h_presets_menu->set_switch_on_hover(true);
+
+ p = container_h_presets_menu->get_popup();
+ p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback));
+
+ container_v_presets_menu = memnew(MenuButton);
+ container_v_presets_menu->set_shortcut_context(this);
+ container_v_presets_menu->set_text(TTR("Vertical"));
+ add_child(container_v_presets_menu);
+ container_v_presets_menu->set_switch_on_hover(true);
+
+ p = container_v_presets_menu->get_popup();
+ p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback));
+
+ undo_redo = p_editor->get_undo_redo();
+ editor_selection = p_editor->get_editor_selection();
+ editor_selection->add_editor_plugin(this);
+ editor_selection->connect("selection_changed", callable_mp(this, &ControlEditorToolbar::_selection_changed));
+
+ singleton = this;
+}
+
+ControlEditorToolbar *ControlEditorToolbar::singleton = nullptr;
+
+ControlEditorPlugin::ControlEditorPlugin(EditorNode *p_editor) {
+ editor = p_editor;
+
+ toolbar = memnew(ControlEditorToolbar(editor));
+ toolbar->hide();
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
+
+ Ref<EditorInspectorPluginControl> plugin;
+ plugin.instantiate();
+ add_inspector_plugin(plugin);
+}
diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h
new file mode 100644
index 0000000000..610846a97b
--- /dev/null
+++ b/editor/plugins/control_editor_plugin.h
@@ -0,0 +1,255 @@
+/*************************************************************************/
+/* control_editor_plugin.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 CONTROL_EDITOR_PLUGIN_H
+#define CONTROL_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/control.h"
+#include "scene/gui/label.h"
+#include "scene/gui/margin_container.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/texture_rect.h"
+
+class ControlPositioningWarning : public MarginContainer {
+ GDCLASS(ControlPositioningWarning, MarginContainer);
+
+ Control *control_node = nullptr;
+
+ PanelContainer *bg_panel = nullptr;
+ GridContainer *grid = nullptr;
+ TextureRect *title_icon = nullptr;
+ TextureRect *hint_icon = nullptr;
+ Label *title_label = nullptr;
+ Label *hint_label = nullptr;
+ Control *hint_filler_left = nullptr;
+ Control *hint_filler_right = nullptr;
+
+ void _update_warning();
+ void _update_toggler();
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
+
+protected:
+ void _notification(int p_notification);
+
+public:
+ void set_control(Control *p_node);
+
+ ControlPositioningWarning();
+};
+
+class EditorPropertyAnchorsPreset : public EditorProperty {
+ GDCLASS(EditorPropertyAnchorsPreset, EditorProperty);
+ OptionButton *options;
+
+ void _option_selected(int p_which);
+
+protected:
+ virtual void _set_read_only(bool p_read_only) override;
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property() override;
+ EditorPropertyAnchorsPreset();
+};
+
+class EditorPropertySizeFlags : public EditorProperty {
+ GDCLASS(EditorPropertySizeFlags, EditorProperty);
+
+ enum FlagPreset {
+ SIZE_FLAGS_PRESET_FILL,
+ SIZE_FLAGS_PRESET_SHRINK_BEGIN,
+ SIZE_FLAGS_PRESET_SHRINK_CENTER,
+ SIZE_FLAGS_PRESET_SHRINK_END,
+ SIZE_FLAGS_PRESET_CUSTOM,
+ };
+
+ OptionButton *flag_presets;
+ CheckBox *flag_expand;
+ VBoxContainer *flag_options;
+ Vector<CheckBox *> flag_checks;
+
+ bool vertical = false;
+
+ bool keep_selected_preset = false;
+
+ void _preset_selected(int p_which);
+ void _expand_toggled();
+ void _flag_toggled();
+
+protected:
+ virtual void _set_read_only(bool p_read_only) override;
+
+public:
+ void setup(const Vector<String> &p_options, bool p_vertical);
+ virtual void update_property() override;
+ EditorPropertySizeFlags();
+};
+
+class EditorInspectorPluginControl : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginControl, EditorInspectorPlugin);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual void parse_group(Object *p_object, const String &p_group) override;
+ virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false) override;
+};
+
+class ControlEditorToolbar : public HBoxContainer {
+ GDCLASS(ControlEditorToolbar, HBoxContainer);
+
+ UndoRedo *undo_redo;
+ EditorSelection *editor_selection;
+
+ enum MenuOption {
+ ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT,
+ ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT,
+ ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT,
+ ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT,
+ ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT,
+ ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT,
+ ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP,
+ ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM,
+ ANCHORS_AND_OFFSETS_PRESET_CENTER,
+ ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE,
+ ANCHORS_AND_OFFSETS_PRESET_WIDE,
+
+ ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO,
+
+ ANCHORS_PRESET_TOP_LEFT,
+ ANCHORS_PRESET_TOP_RIGHT,
+ ANCHORS_PRESET_BOTTOM_LEFT,
+ ANCHORS_PRESET_BOTTOM_RIGHT,
+ ANCHORS_PRESET_CENTER_LEFT,
+ ANCHORS_PRESET_CENTER_RIGHT,
+ ANCHORS_PRESET_CENTER_TOP,
+ ANCHORS_PRESET_CENTER_BOTTOM,
+ ANCHORS_PRESET_CENTER,
+ ANCHORS_PRESET_TOP_WIDE,
+ ANCHORS_PRESET_LEFT_WIDE,
+ ANCHORS_PRESET_RIGHT_WIDE,
+ ANCHORS_PRESET_BOTTOM_WIDE,
+ ANCHORS_PRESET_VCENTER_WIDE,
+ ANCHORS_PRESET_HCENTER_WIDE,
+ ANCHORS_PRESET_WIDE,
+
+ // Offsets Presets are not currently in use.
+ OFFSETS_PRESET_TOP_LEFT,
+ OFFSETS_PRESET_TOP_RIGHT,
+ OFFSETS_PRESET_BOTTOM_LEFT,
+ OFFSETS_PRESET_BOTTOM_RIGHT,
+ OFFSETS_PRESET_CENTER_LEFT,
+ OFFSETS_PRESET_CENTER_RIGHT,
+ OFFSETS_PRESET_CENTER_TOP,
+ OFFSETS_PRESET_CENTER_BOTTOM,
+ OFFSETS_PRESET_CENTER,
+ OFFSETS_PRESET_TOP_WIDE,
+ OFFSETS_PRESET_LEFT_WIDE,
+ OFFSETS_PRESET_RIGHT_WIDE,
+ OFFSETS_PRESET_BOTTOM_WIDE,
+ OFFSETS_PRESET_VCENTER_WIDE,
+ OFFSETS_PRESET_HCENTER_WIDE,
+ OFFSETS_PRESET_WIDE,
+
+ CONTAINERS_H_PRESET_FILL,
+ CONTAINERS_H_PRESET_FILL_EXPAND,
+ CONTAINERS_H_PRESET_SHRINK_BEGIN,
+ CONTAINERS_H_PRESET_SHRINK_CENTER,
+ CONTAINERS_H_PRESET_SHRINK_END,
+ CONTAINERS_V_PRESET_FILL,
+ CONTAINERS_V_PRESET_FILL_EXPAND,
+ CONTAINERS_V_PRESET_SHRINK_BEGIN,
+ CONTAINERS_V_PRESET_SHRINK_CENTER,
+ CONTAINERS_V_PRESET_SHRINK_END,
+ };
+
+ TextureRect *anchor_layouts_icon;
+ MenuButton *anchor_presets_menu;
+ PopupMenu *anchors_popup;
+ TextureRect *container_layouts_icon;
+ MenuButton *container_h_presets_menu;
+ MenuButton *container_v_presets_menu;
+
+ Button *anchor_mode_button;
+
+ bool anchors_mode = false;
+
+ void _set_anchors_preset(Control::LayoutPreset p_preset);
+ void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset);
+ void _set_anchors_and_offsets_to_keep_ratio();
+ void _set_container_h_preset(Control::SizeFlags p_preset);
+ void _set_container_v_preset(Control::SizeFlags p_preset);
+
+ Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor);
+ Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
+
+ void _button_toggle_anchor_mode(bool p_status);
+
+ bool _is_node_locked(const Node *p_node);
+ List<Control *> _get_edited_controls(bool retrieve_locked = false, bool remove_controls_if_parent_in_selection = true);
+ void _popup_callback(int p_op);
+ void _selection_changed();
+
+protected:
+ void _notification(int p_notification);
+
+ static ControlEditorToolbar *singleton;
+
+public:
+ bool is_anchors_mode_enabled() { return anchors_mode; };
+
+ static ControlEditorToolbar *get_singleton() { return singleton; }
+
+ ControlEditorToolbar(EditorNode *p_editor);
+};
+
+class ControlEditorPlugin : public EditorPlugin {
+ GDCLASS(ControlEditorPlugin, EditorPlugin);
+
+ EditorNode *editor;
+ ControlEditorToolbar *toolbar;
+
+public:
+ virtual String get_name() const override { return "Control"; }
+
+ ControlEditorPlugin(EditorNode *p_editor);
+};
+
+#endif //CONTROL_EDITOR_PLUGIN_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 9884c45916..5f58724594 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1594,9 +1594,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
- clicked = _select_ray(b->get_position());
-
//clicking is always deferred to either move or release
+ clicked = _select_ray(b->get_position());
+ selection_in_progress = true;
if (clicked.is_null()) {
//default to regionselect
@@ -1615,6 +1615,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ selection_in_progress = false;
+
if (clicked.is_valid()) {
_select_clicked(false);
}
@@ -1719,17 +1721,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
nav_mode = NAVIGATION_ORBIT;
} else {
const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
- if (clicked.is_valid() && movement_threshold_passed) {
- _compute_edit(_edit.original_mouse_pos);
- clicked = ObjectID();
-
- _edit.mode = TRANSFORM_TRANSLATE;
- }
// enable region-select if nothing has been selected yet or multi-select (shift key) is active
- if (movement_threshold_passed && (get_selected_count() == 0 || clicked_wants_append)) {
- cursor.region_select = true;
- cursor.region_begin = _edit.original_mouse_pos;
+ if (selection_in_progress && movement_threshold_passed) {
+ if (get_selected_count() == 0 || clicked_wants_append) {
+ cursor.region_select = true;
+ cursor.region_begin = _edit.original_mouse_pos;
+ clicked = ObjectID();
+ }
}
if (cursor.region_select) {
@@ -1738,6 +1737,12 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
+ if (clicked.is_valid() && movement_threshold_passed) {
+ _compute_edit(_edit.original_mouse_pos);
+ clicked = ObjectID();
+ _edit.mode = TRANSFORM_TRANSLATE;
+ }
+
if (_edit.mode == TRANSFORM_NONE) {
return;
}
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index cb7c5a714b..333702fd94 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -269,6 +269,7 @@ private:
ObjectID clicked;
Vector<_RayResult> selection_results;
bool clicked_wants_append;
+ bool selection_in_progress = false;
PopupMenu *selection_menu;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 17de3ba026..97d12af544 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2265,7 +2265,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
if (use_external_editor &&
(EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) &&
p_resource->get_path().is_resource_file() &&
- !Ref<VisualScript>(p_resource).is_valid()) {
+ !p_resource->is_class("VisualScript")) {
String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
@@ -2364,7 +2364,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
se->set_edited_resource(p_resource);
- if (!Ref<VisualScript>(p_resource).is_valid()) {
+ if (!p_resource->is_class("VisualScript")) {
bool highlighter_set = false;
for (int i = 0; i < syntax_highlighters.size(); i++) {
Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index aaa09237cf..50ca20b2a5 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -1211,7 +1211,8 @@ void ThemeItemEditorDialog::_update_edit_types() {
bool item_reselected = false;
edit_type_list->clear();
- int e_idx = 0;
+ TreeItem *list_root = edit_type_list->create_item();
+
for (const StringName &E : theme_types) {
Ref<Texture2D> item_icon;
if (E == "") {
@@ -1219,19 +1220,21 @@ void ThemeItemEditorDialog::_update_edit_types() {
} else {
item_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
}
- edit_type_list->add_item(E, item_icon);
+ TreeItem *list_item = edit_type_list->create_item(list_root);
+ list_item->set_text(0, E);
+ list_item->set_icon(0, item_icon);
+ list_item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TYPES_TREE_REMOVE_ITEM, false, TTR("Remove Type"));
if (E == edited_item_type) {
- edit_type_list->select(e_idx);
+ list_item->select(0);
item_reselected = true;
}
- e_idx++;
}
if (!item_reselected) {
edited_item_type = "";
- if (edit_type_list->get_item_count() > 0) {
- edit_type_list->select(0);
+ if (list_root->get_child_count() > 0) {
+ list_root->get_child(0)->select(0);
}
}
@@ -1240,9 +1243,9 @@ void ThemeItemEditorDialog::_update_edit_types() {
default_types.sort_custom<StringName::AlphCompare>();
String selected_type = "";
- Vector<int> selected_ids = edit_type_list->get_selected_items();
- if (selected_ids.size() > 0) {
- selected_type = edit_type_list->get_item_text(selected_ids[0]);
+ TreeItem *selected_item = edit_type_list->get_selected();
+ if (selected_item) {
+ selected_type = selected_item->get_text(0);
edit_items_add_color->set_disabled(false);
edit_items_add_constant->set_disabled(false);
@@ -1276,11 +1279,26 @@ void ThemeItemEditorDialog::_update_edit_types() {
_update_edit_item_tree(selected_type);
}
-void ThemeItemEditorDialog::_edited_type_selected(int p_item_idx) {
- String selected_type = edit_type_list->get_item_text(p_item_idx);
+void ThemeItemEditorDialog::_edited_type_selected() {
+ TreeItem *selected_item = edit_type_list->get_selected();
+ String selected_type = selected_item->get_text(0);
_update_edit_item_tree(selected_type);
}
+void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id) {
+ TreeItem *item = Object::cast_to<TreeItem>(p_item);
+ if (!item) {
+ return;
+ }
+
+ switch (p_id) {
+ case TYPES_TREE_REMOVE_ITEM: {
+ String type_name = item->get_text(0);
+ _remove_theme_type(type_name);
+ } break;
+ }
+}
+
void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
edited_item_type = p_item_type;
@@ -1429,8 +1447,8 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
// If some type is selected, but it doesn't seem to have any items, show a guiding message.
- Vector<int> selected_ids = edit_type_list->get_selected_items();
- if (selected_ids.size() > 0) {
+ TreeItem *selected_item = edit_type_list->get_selected();
+ if (selected_item) {
if (!has_any_items) {
edit_items_message->set_text(TTR("This theme type is empty.\nAdd more items to it manually or by importing from another theme."));
edit_items_message->show();
@@ -1477,16 +1495,15 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
const String new_type = edit_add_type_value->get_text().strip_edges();
edit_add_type_value->clear();
- edited_theme->add_icon_type(new_type);
- edited_theme->add_stylebox_type(new_type);
- edited_theme->add_font_type(new_type);
- edited_theme->add_font_size_type(new_type);
- edited_theme->add_color_type(new_type);
- edited_theme->add_constant_type(new_type);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Add Theme Type"));
- _update_edit_types();
+ ur->add_do_method(*edited_theme, "add_type", new_type);
+ ur->add_undo_method(*edited_theme, "remove_type", new_type);
+ ur->add_do_method(this, "_update_edit_types");
+ ur->add_undo_method(this, "_update_edit_types");
- edited_theme->emit_changed();
+ ur->commit_action();
}
void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
@@ -1531,6 +1548,27 @@ void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String
ur->commit_action();
}
+void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) {
+ Ref<Theme> old_snapshot = edited_theme->duplicate();
+ Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Theme Type"));
+
+ new_snapshot->remove_type(p_theme_type);
+
+ ur->add_do_method(*edited_theme, "clear");
+ ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+ // If the type was empty, it cannot be restored with merge, but thankfully we can fake it.
+ ur->add_undo_method(*edited_theme, "add_type", p_theme_type);
+ ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+ ur->add_do_method(this, "_update_edit_types");
+ ur->add_undo_method(this, "_update_edit_types");
+
+ ur->commit_action();
+}
+
void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) {
List<StringName> names;
@@ -1863,10 +1901,14 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
edit_type_label->set_text(TTR("Types:"));
edit_dialog_side_vb->add_child(edit_type_label);
- edit_type_list = memnew(ItemList);
+ edit_type_list = memnew(Tree);
+ edit_type_list->set_hide_root(true);
+ edit_type_list->set_hide_folding(true);
+ edit_type_list->set_columns(1);
edit_type_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
edit_dialog_side_vb->add_child(edit_type_list);
edit_type_list->connect("item_selected", callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected));
+ edit_type_list->connect("button_pressed", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed));
Label *edit_add_type_label = memnew(Label);
edit_add_type_label->set_text(TTR("Add Type:"));
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index c00ce3ae65..af54c3ce83 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -188,7 +188,11 @@ class ThemeItemEditorDialog : public AcceptDialog {
TabContainer *tc;
- ItemList *edit_type_list;
+ enum TypesTreeAction {
+ TYPES_TREE_REMOVE_ITEM,
+ };
+
+ Tree *edit_type_list;
LineEdit *edit_add_type_value;
String edited_item_type;
@@ -240,13 +244,15 @@ class ThemeItemEditorDialog : public AcceptDialog {
void _dialog_about_to_show();
void _update_edit_types();
- void _edited_type_selected(int p_item_idx);
+ void _edited_type_selected();
+ void _edited_type_button_pressed(Object *p_item, int p_column, int p_id);
void _update_edit_item_tree(String p_item_type);
void _item_tree_button_pressed(Object *p_item, int p_column, int p_id);
void _add_theme_type(const String &p_new_text);
void _add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type);
+ void _remove_theme_type(const String &p_theme_type);
void _remove_data_type_items(Theme::DataType p_data_type, String p_item_type);
void _remove_class_items();
void _remove_custom_items();