Reorganize inspector layout workflow for Control nodes

This commit is contained in:
Yuri Sizov 2021-11-08 23:53:41 +03:00
parent 242c636a63
commit 107b6f299c
43 changed files with 2118 additions and 550 deletions

View file

@ -1007,20 +1007,30 @@ bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal,
return false;
}
void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix) {
void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_COND(!type);
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP));
String prefix = p_prefix;
if (p_indent_depth > 0) {
prefix = vformat("%s,%d", p_prefix, p_indent_depth);
}
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, prefix, PROPERTY_USAGE_GROUP));
}
void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix) {
void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) {
OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_COND(!type);
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_SUBGROUP));
String prefix = p_prefix;
if (p_indent_depth > 0) {
prefix = vformat("%s,%d", p_prefix, p_indent_depth);
}
type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, prefix, PROPERTY_USAGE_SUBGROUP));
}
void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) {

View file

@ -328,8 +328,8 @@ public:
static bool get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal);
static void get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false);
static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = "");
static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "");
static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0);
static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0);
static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_DEFAULT);
static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix);
static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);

View file

@ -142,7 +142,9 @@ enum PropertyUsageFlags {
#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ::ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index)
#define ADD_PROPERTY_DEFAULT(m_property, m_default) ::ClassDB::set_property_default_value(get_class_static(), m_property, m_default)
#define ADD_GROUP(m_name, m_prefix) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
#define ADD_GROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_group(get_class_static(), m_name, m_prefix, m_depth)
#define ADD_SUBGROUP(m_name, m_prefix) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
#define ADD_SUBGROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix, m_depth)
#define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property)
#define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, _scs_create(m_count_property_setter), _scs_create(m_count_property_getter), m_prefix)

View file

@ -10,6 +10,20 @@
<tutorials>
</tutorials>
<methods>
<method name="_get_allowed_size_flags_horizontal" qualifiers="virtual const">
<return type="PackedInt32Array" />
<description>
Implement to return a list of allowed horizontal [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the inspector dock.
[b]Note:[/b] Having no size flags is equal to having [constant Control.SIZE_SHRINK_BEGIN]. As such, this value is always implicitly allowed.
</description>
</method>
<method name="_get_allowed_size_flags_vertical" qualifiers="virtual const">
<return type="PackedInt32Array" />
<description>
Implement to return a list of allowed vertical [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the inspector dock.
[b]Note:[/b] Having no size flags is equal to having [constant Control.SIZE_SHRINK_BEGIN]. As such, this value is always implicitly allowed.
</description>
</method>
<method name="fit_child_in_rect">
<return type="void" />
<argument index="0" name="child" type="Control" />

View file

@ -1278,20 +1278,24 @@
<constant name="PRESET_MODE_KEEP_SIZE" value="3" enum="LayoutPresetMode">
The control's size will not change.
</constant>
<constant name="SIZE_SHRINK_BEGIN" value="0" enum="SizeFlags">
Tells the parent [Container] to align the node with its start, either the top or the left edge. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
[b]Note:[/b] Setting this flag is equal to not having any size flags.
</constant>
<constant name="SIZE_FILL" value="1" enum="SizeFlags">
Tells the parent [Container] to expand the bounds of this node to fill all the available space without pushing any other node. Use with [member size_flags_horizontal] and [member size_flags_vertical].
Tells the parent [Container] to expand the bounds of this node to fill all the available space without pushing any other node. It is mutually exclusive with shrink size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="SIZE_EXPAND" value="2" enum="SizeFlags">
Tells the parent [Container] to let this node take all the available space on the axis you flag. If multiple neighboring nodes are set to expand, they'll share the space based on their stretch ratio. See [member size_flags_stretch_ratio]. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="SIZE_EXPAND_FILL" value="3" enum="SizeFlags">
Sets the node's size flags to both fill and expand. See the 2 constants above for more information.
Sets the node's size flags to both fill and expand. See [constant SIZE_FILL] and [constant SIZE_EXPAND] for more information.
</constant>
<constant name="SIZE_SHRINK_CENTER" value="4" enum="SizeFlags">
Tells the parent [Container] to center the node in itself. It centers the control based on its bounding box, so it doesn't work with the fill or expand size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical].
Tells the parent [Container] to center the node in the available space. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="SIZE_SHRINK_END" value="8" enum="SizeFlags">
Tells the parent [Container] to align the node with its end, either the bottom or the right edge. It doesn't work with the fill or expand size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical].
Tells the parent [Container] to align the node with its end, either the bottom or the right edge. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="MOUSE_FILTER_STOP" value="0" enum="MouseFilter">
The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. These events are automatically marked as handled, and they will not propagate further to other controls. This also results in blocking signals in other controls.

View file

@ -1181,6 +1181,15 @@ void EditorInspectorSection::_notification(int p_what) {
header_height += get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
int inspector_margin = get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_size > 0) {
inspector_margin += indent_depth * section_indent_size;
}
Ref<StyleBoxFlat> section_indent_style = get_theme_stylebox(SNAME("indent_box"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_style.is_valid()) {
inspector_margin += section_indent_style->get_margin(SIDE_LEFT) + section_indent_style->get_margin(SIDE_RIGHT);
}
Size2 size = get_size() - Vector2(inspector_margin, 0);
Vector2 offset = Vector2(is_layout_rtl() ? 0 : inspector_margin, header_height);
for (int i = 0; i < get_child_count(); i++) {
@ -1216,14 +1225,31 @@ void EditorInspectorSection::_notification(int p_what) {
bool rtl = is_layout_rtl();
// Compute the height of the section header.
// Compute the height and width of the section header.
int header_height = font->get_height(font_size);
if (arrow.is_valid()) {
header_height = MAX(header_height, arrow->get_height());
}
header_height += get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
Rect2 header_rect = Rect2(Vector2(), Vector2(get_size().width, header_height));
int section_indent = 0;
int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_size > 0) {
section_indent = indent_depth * section_indent_size;
}
Ref<StyleBoxFlat> section_indent_style = get_theme_stylebox(SNAME("indent_box"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_style.is_valid()) {
section_indent += section_indent_style->get_margin(SIDE_LEFT) + section_indent_style->get_margin(SIDE_RIGHT);
}
int header_width = get_size().width - section_indent;
int header_offset_x = 0.0;
if (!rtl) {
header_offset_x += section_indent;
}
// Draw header area.
Rect2 header_rect = Rect2(Vector2(header_offset_x, 0.0), Vector2(header_width, header_height));
Color c = bg_color;
c.a *= 0.4;
if (foldable && header_rect.has_point(get_local_mouse_position())) {
@ -1231,24 +1257,46 @@ void EditorInspectorSection::_notification(int p_what) {
}
draw_rect(header_rect, c);
// Draw header title and folding arrow.
const int arrow_margin = 2;
const int arrow_width = arrow.is_valid() ? arrow->get_width() : 0;
Color color = get_theme_color(SNAME("font_color"));
float text_width = get_size().width - Math::round(arrow_width + arrow_margin * EDSCALE);
draw_string(font, Point2(rtl ? 0 : Math::round(arrow_width + arrow_margin * EDSCALE), font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2).floor(), label, rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT, text_width, font_size, color);
float text_width = get_size().width - Math::round(arrow_width + arrow_margin * EDSCALE) - section_indent;
Point2 text_offset = Point2(0, font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2);
HorizontalAlignment text_align = HORIZONTAL_ALIGNMENT_LEFT;
if (rtl) {
text_align = HORIZONTAL_ALIGNMENT_RIGHT;
} else {
text_offset.x = section_indent + Math::round(arrow_width + arrow_margin * EDSCALE);
}
draw_string(font, text_offset.floor(), label, text_align, text_width, font_size, color);
if (arrow.is_valid()) {
Point2 arrow_position = Point2(0, (header_height - arrow->get_height()) / 2);
if (rtl) {
draw_texture(arrow, Point2(get_size().width - arrow->get_width() - Math::round(arrow_margin * EDSCALE), (header_height - arrow->get_height()) / 2).floor());
arrow_position.x = get_size().width - section_indent - arrow->get_width() - Math::round(arrow_margin * EDSCALE);
} else {
draw_texture(arrow, Point2(Math::round(arrow_margin * EDSCALE), (header_height - arrow->get_height()) / 2).floor());
arrow_position.x = section_indent + Math::round(arrow_margin * EDSCALE);
}
draw_texture(arrow, arrow_position.floor());
}
// Draw dropping highlight.
if (dropping && !vbox->is_visible_in_tree()) {
Color accent_color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
draw_rect(Rect2(Point2(), get_size()), accent_color, false);
}
// Draw section indentation.
if (section_indent_style.is_valid() && section_indent > 0) {
Rect2 indent_rect = Rect2(Vector2(), Vector2(indent_depth * section_indent_size, get_size().height));
if (rtl) {
indent_rect.position.x = get_size().width - section_indent + section_indent_style->get_margin(SIDE_RIGHT);
} else {
indent_rect.position.x = section_indent_style->get_margin(SIDE_LEFT);
}
draw_style_box(section_indent_style, indent_rect);
}
} break;
case NOTIFICATION_DRAG_BEGIN: {
Dictionary dd = get_viewport()->gui_get_drag_data();
@ -1311,15 +1359,25 @@ Size2 EditorInspectorSection::get_minimum_size() const {
ms.height += font->get_height(font_size) + get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
ms.width += get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
int section_indent_size = get_theme_constant(SNAME("indent_size"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_size > 0) {
ms.width += indent_depth * section_indent_size;
}
Ref<StyleBoxFlat> section_indent_style = get_theme_stylebox(SNAME("indent_box"), SNAME("EditorInspectorSection"));
if (indent_depth > 0 && section_indent_style.is_valid()) {
ms.width += section_indent_style->get_margin(SIDE_LEFT) + section_indent_style->get_margin(SIDE_RIGHT);
}
return ms;
}
void EditorInspectorSection::setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable) {
void EditorInspectorSection::setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth) {
section = p_section;
label = p_label;
object = p_object;
bg_color = p_bg_color;
foldable = p_foldable;
indent_depth = p_indent_depth;
if (!foldable && !vbox_added) {
add_child(vbox);
@ -1401,12 +1459,8 @@ void EditorInspectorSection::_bind_methods() {
}
EditorInspectorSection::EditorInspectorSection() {
object = nullptr;
foldable = false;
vbox = memnew(VBoxContainer);
vbox_added = false;
dropping = false;
dropping_unfold_timer = memnew(Timer);
dropping_unfold_timer->set_wait_time(0.6);
dropping_unfold_timer->set_one_shot(true);
@ -2004,7 +2058,7 @@ void EditorInspectorArray::setup_with_move_element_function(Object *p_object, St
array_element_prefix = p_array_element_prefix;
page = p_page;
EditorInspectorSection::setup(String(p_array_element_prefix) + "_array", p_label, p_object, p_bg_color, p_foldable);
EditorInspectorSection::setup(String(p_array_element_prefix) + "_array", p_label, p_object, p_bg_color, p_foldable, 0);
_setup();
}
@ -2015,7 +2069,7 @@ void EditorInspectorArray::setup_with_count_property(Object *p_object, String p_
array_element_prefix = p_array_element_prefix;
page = p_page;
EditorInspectorSection::setup(String(count_property) + "_array", p_label, p_object, p_bg_color, p_foldable);
EditorInspectorSection::setup(String(count_property) + "_array", p_label, p_object, p_bg_color, p_foldable, 0);
_setup();
}
@ -2376,6 +2430,7 @@ void EditorInspector::update_tree() {
String group_base;
String subgroup;
String subgroup_base;
int section_depth = 0;
VBoxContainer *category_vbox = nullptr;
List<PropertyInfo> plist;
@ -2400,14 +2455,29 @@ void EditorInspector::update_tree() {
if (p.usage & PROPERTY_USAGE_SUBGROUP) {
// Setup a property sub-group.
subgroup = p.name;
subgroup_base = p.hint_string;
Vector<String> hint_parts = p.hint_string.split(",");
subgroup_base = hint_parts[0];
if (hint_parts.size() > 1) {
section_depth = hint_parts[1].to_int();
} else {
section_depth = 0;
}
continue;
} else if (p.usage & PROPERTY_USAGE_GROUP) {
// Setup a property group.
group = p.name;
group_base = p.hint_string;
Vector<String> hint_parts = p.hint_string.split(",");
group_base = hint_parts[0];
if (hint_parts.size() > 1) {
section_depth = hint_parts[1].to_int();
} else {
section_depth = 0;
}
subgroup = "";
subgroup_base = "";
@ -2419,6 +2489,7 @@ void EditorInspector::update_tree() {
group_base = "";
subgroup = "";
subgroup_base = "";
section_depth = 0;
if (!show_categories) {
continue;
@ -2660,7 +2731,7 @@ void EditorInspector::update_tree() {
Color c = sscolor;
c.a /= level;
section->setup(acc_path, component, object, c, use_folding);
section->setup(acc_path, component, object, c, use_folding, section_depth);
// Add editors at the start of a group.
for (Ref<EditorInspectorPlugin> &ped : valid_plugins) {

View file

@ -261,17 +261,18 @@ class EditorInspectorSection : public Container {
String label;
String section;
bool vbox_added; // Optimization.
bool vbox_added = false; // Optimization.
Color bg_color;
bool foldable;
bool foldable = false;
int indent_depth = 0;
Timer *dropping_unfold_timer;
bool dropping;
bool dropping = false;
void _test_unfold();
protected:
Object *object;
Object *object = nullptr;
VBoxContainer *vbox;
void _notification(int p_what);
@ -281,7 +282,7 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable);
void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0);
VBoxContainer *get_vbox();
void unfold();
void fold();

View file

@ -132,6 +132,7 @@
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
#include "editor/plugins/collision_shape_2d_editor_plugin.h"
#include "editor/plugins/control_editor_plugin.h"
#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
#include "editor/plugins/curve_editor_plugin.h"
@ -7024,6 +7025,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(InputEventEditorPlugin(this)));
add_editor_plugin(memnew(SubViewportPreviewEditorPlugin(this)));
add_editor_plugin(memnew(TextControlEditorPlugin(this)));
add_editor_plugin(memnew(ControlEditorPlugin(this)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i, this));

View file

@ -466,6 +466,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "Editor", font_color);
theme->set_color("highlighted_font_color", "Editor", font_hover_color);
theme->set_color("disabled_font_color", "Editor", font_disabled_color);
theme->set_color("readonly_font_color", "Editor", font_readonly_color);
theme->set_color("mono_color", "Editor", mono_color);
@ -875,9 +876,21 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("readonly_color", "EditorProperty", readonly_color);
theme->set_color("readonly_warning_color", "EditorProperty", readonly_warning_color);
Ref<StyleBoxFlat> style_property_group_note = style_default->duplicate();
Color property_group_note_color = accent_color;
property_group_note_color.a = 0.1;
style_property_group_note->set_bg_color(property_group_note_color);
theme->set_stylebox("bg_group_note", "EditorProperty", style_property_group_note);
Color inspector_section_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.35);
theme->set_color("font_color", "EditorInspectorSection", inspector_section_color);
Color inspector_indent_color = accent_color;
inspector_indent_color.a = 0.2;
Ref<StyleBoxFlat> inspector_indent_style = make_flat_stylebox(inspector_indent_color, 2.0 * EDSCALE, 0, 2.0 * EDSCALE, 0);
theme->set_stylebox("indent_box", "EditorInspectorSection", inspector_indent_style);
theme->set_constant("indent_size", "EditorInspectorSection", 6.0 * EDSCALE);
theme->set_constant("inspector_margin", "Editor", 12 * EDSCALE);
// Tree & ItemList background

View file

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 214 B

View file

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

View file

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 214 B

View file

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View file

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

View file

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 294 B

View file

@ -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);
@ -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

View file

@ -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 smart_snap_active = false;
bool grid_snap_active = false;
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 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 ruler_tool_active;
Point2 ruler_tool_origin;
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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -70,6 +70,24 @@ void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vert
queue_sort();
}
Vector<int> AspectRatioContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> AspectRatioContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void AspectRatioContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {

View file

@ -72,6 +72,9 @@ public:
void set_alignment_vertical(AlignmentMode p_alignment_vertical);
AlignmentMode get_alignment_vertical() const { return alignment_vertical; }
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
};
VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);

View file

@ -331,6 +331,30 @@ Control *BoxContainer::add_spacer(bool p_begin) {
return c;
}
Vector<int> BoxContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (!vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> BoxContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
BoxContainer::BoxContainer(bool p_vertical) {
vertical = p_vertical;
}

View file

@ -62,6 +62,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
BoxContainer(bool p_vertical = false);
};

View file

@ -69,6 +69,14 @@ bool CenterContainer::is_using_top_left() const {
return use_top_left;
}
Vector<int> CenterContainer::get_allowed_size_flags_horizontal() const {
return Vector<int>();
}
Vector<int> CenterContainer::get_allowed_size_flags_vertical() const {
return Vector<int>();
}
void CenterContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_SORT_CHILDREN) {
Size2 size = get_size();

View file

@ -48,6 +48,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
CenterContainer();
};

View file

@ -143,6 +143,34 @@ void Container::queue_sort() {
pending_sort = true;
}
Vector<int> Container::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
if (GDVIRTUAL_CALL(_get_allowed_size_flags_horizontal, flags)) {
return flags;
}
flags.append(SIZE_FILL);
flags.append(SIZE_EXPAND);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> Container::get_allowed_size_flags_vertical() const {
Vector<int> flags;
if (GDVIRTUAL_CALL(_get_allowed_size_flags_vertical, flags)) {
return flags;
}
flags.append(SIZE_FILL);
flags.append(SIZE_EXPAND);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void Container::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@ -177,6 +205,9 @@ void Container::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort);
ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect);
GDVIRTUAL_BIND(_get_allowed_size_flags_horizontal);
GDVIRTUAL_BIND(_get_allowed_size_flags_vertical);
BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN);
BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN);

View file

@ -46,6 +46,9 @@ protected:
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_horizontal)
GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_vertical)
void _notification(int p_what);
static void _bind_methods();
@ -57,6 +60,9 @@ public:
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
virtual Vector<int> get_allowed_size_flags_horizontal() const;
virtual Vector<int> get_allowed_size_flags_vertical() const;
TypedArray<String> get_configuration_warnings() const override;
Container();

View file

@ -47,7 +47,7 @@
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/control_editor_plugin.h"
#endif
#ifdef TOOLS_ENABLED
@ -56,51 +56,71 @@ Dictionary Control::_edit_get_state() const {
s["rotation"] = get_rotation();
s["scale"] = get_scale();
s["pivot"] = get_pivot_offset();
Array anchors;
anchors.push_back(get_anchor(SIDE_LEFT));
anchors.push_back(get_anchor(SIDE_TOP));
anchors.push_back(get_anchor(SIDE_RIGHT));
anchors.push_back(get_anchor(SIDE_BOTTOM));
s["anchors"] = anchors;
Array offsets;
offsets.push_back(get_offset(SIDE_LEFT));
offsets.push_back(get_offset(SIDE_TOP));
offsets.push_back(get_offset(SIDE_RIGHT));
offsets.push_back(get_offset(SIDE_BOTTOM));
s["offsets"] = offsets;
s["layout_mode"] = _get_layout_mode();
s["anchors_layout_preset"] = _get_anchors_layout_preset();
return s;
}
void Control::_edit_set_state(const Dictionary &p_state) {
ERR_FAIL_COND((p_state.size() <= 0) ||
!p_state.has("rotation") || !p_state.has("scale") ||
!p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
!p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets") ||
!p_state.has("layout_mode") || !p_state.has("anchors_layout_preset"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
set_scale(state["scale"]);
set_pivot_offset(state["pivot"]);
Array anchors = state["anchors"];
// If anchors are not in their default position, force the anchor layout mode in place of position.
LayoutMode _layout = (LayoutMode)(int)state["layout_mode"];
if (_layout == LayoutMode::LAYOUT_MODE_POSITION) {
bool anchors_mode = ((real_t)anchors[0] != 0.0 || (real_t)anchors[1] != 0.0 || (real_t)anchors[2] != 0.0 || (real_t)anchors[3] != 0.0);
if (anchors_mode) {
_layout = LayoutMode::LAYOUT_MODE_ANCHORS;
}
}
_set_layout_mode(_layout);
if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS) {
_set_anchors_layout_preset((int)state["anchors_layout_preset"]);
}
data.anchor[SIDE_LEFT] = anchors[0];
data.anchor[SIDE_TOP] = anchors[1];
data.anchor[SIDE_RIGHT] = anchors[2];
data.anchor[SIDE_BOTTOM] = anchors[3];
Array offsets = state["offsets"];
data.offset[SIDE_LEFT] = offsets[0];
data.offset[SIDE_TOP] = offsets[1];
data.offset[SIDE_RIGHT] = offsets[2];
data.offset[SIDE_BOTTOM] = offsets[3];
_size_changed();
}
void Control::_edit_set_position(const Point2 &p_position) {
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
#else
// Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position(p_position);
#endif
set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
};
Point2 Control::_edit_get_position() const {
@ -116,15 +136,9 @@ Size2 Control::_edit_get_scale() const {
}
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
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
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
set_size(p_edit_rect.size.snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
}
Rect2 Control::_edit_get_rect() const {
@ -177,6 +191,7 @@ String Control::properties_managed_by_container[] = {
"anchor_right",
"anchor_bottom",
"rect_position",
"rect_rotation",
"rect_scale",
"rect_size"
};
@ -430,6 +445,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const {
}
void Control::_validate_property(PropertyInfo &property) const {
// Update theme type variation options.
if (property.name == "theme_type_variation") {
List<StringName> names;
@ -455,10 +471,99 @@ void Control::_validate_property(PropertyInfo &property) const {
property.hint_string = hint_string;
}
if (!Object::cast_to<Container>(get_parent())) {
// Validate which positioning properties should be displayed depending on the parent and the layout mode.
Node *parent_node = get_parent_control();
if (!parent_node) {
// If there is no parent, display both anchor and container options.
// Set the layout mode to be disabled with the proper value.
if (property.name == "layout_mode") {
property.hint_string = "Position,Anchors,Container,Uncontrolled";
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
// Use the layout mode to display or hide advanced anchoring properties.
bool use_custom_anchors = _get_anchors_layout_preset() == -1; // Custom "preset".
if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
property.usage ^= PROPERTY_USAGE_EDITOR;
}
} else if (Object::cast_to<Container>(parent_node)) {
// If the parent is a container, display only container-related properties.
if (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_") || property.name == "anchors_preset" ||
(property.name.begins_with("rect_") && property.name != "rect_min_size" && property.name != "rect_clip_content" && property.name != "rect_global_position")) {
property.usage ^= PROPERTY_USAGE_EDITOR;
} else if (property.name == "layout_mode") {
// Set the layout mode to be disabled with the proper value.
property.hint_string = "Position,Anchors,Container,Uncontrolled";
property.usage |= PROPERTY_USAGE_READ_ONLY;
} else if (property.name == "size_flags_horizontal" || property.name == "size_flags_vertical") {
// Filter allowed size flags based on the parent container configuration.
Container *parent_container = Object::cast_to<Container>(parent_node);
Vector<int> size_flags;
if (property.name == "size_flags_horizontal") {
size_flags = parent_container->get_allowed_size_flags_horizontal();
} else if (property.name == "size_flags_vertical") {
size_flags = parent_container->get_allowed_size_flags_vertical();
}
// Enforce the order of the options, regardless of what the container provided.
String hint_string;
if (size_flags.has(SIZE_FILL)) {
hint_string += "Fill:1";
}
if (size_flags.has(SIZE_EXPAND)) {
if (!hint_string.is_empty()) {
hint_string += ",";
}
hint_string += "Expand:2";
}
if (size_flags.has(SIZE_SHRINK_CENTER)) {
if (!hint_string.is_empty()) {
hint_string += ",";
}
hint_string += "Shrink Center:4";
}
if (size_flags.has(SIZE_SHRINK_END)) {
if (!hint_string.is_empty()) {
hint_string += ",";
}
hint_string += "Shrink End:8";
}
if (hint_string.is_empty()) {
property.hint_string = "";
property.usage |= PROPERTY_USAGE_READ_ONLY;
} else {
property.hint_string = hint_string;
}
}
} else {
// If the parent is NOT a container or not a control at all, display only anchoring-related properties.
if (property.name.begins_with("size_flags_")) {
property.usage ^= PROPERTY_USAGE_EDITOR;
} else if (property.name == "layout_mode") {
// Set the layout mode to be enabled with proper options.
property.hint_string = "Position,Anchors";
}
// Use the layout mode to display or hide advanced anchoring properties.
bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS;
if (!use_anchors && property.name == "anchors_preset") {
property.usage ^= PROPERTY_USAGE_EDITOR;
}
bool use_custom_anchors = use_anchors && _get_anchors_layout_preset() == -1; // Custom "preset".
if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
property.usage ^= PROPERTY_USAGE_EDITOR;
}
}
// Disable the property if it's managed by the parent container.
if (!Object::cast_to<Container>(parent_node)) {
return;
}
// Disable the property if it's managed by the parent container.
bool property_is_managed_by_container = false;
for (unsigned i = 0; i < properties_managed_by_container_count; i++) {
property_is_managed_by_container = properties_managed_by_container[i] == property.name;
@ -1390,6 +1495,54 @@ void Control::_size_changed() {
}
}
void Control::_set_layout_mode(LayoutMode p_mode) {
bool list_changed = false;
if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) {
if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)p_mode) {
list_changed = true;
}
set_meta("_edit_layout_mode", (int)p_mode);
if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) {
set_meta("_edit_use_custom_anchors", false);
set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE);
set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT);
}
} else {
if (has_meta("_edit_layout_mode")) {
remove_meta("_edit_layout_mode");
list_changed = true;
}
}
if (list_changed) {
notify_property_list_changed();
}
}
Control::LayoutMode Control::_get_layout_mode() const {
Node *parent_node = get_parent_control();
// In these modes the property is read-only.
if (!parent_node) {
return LayoutMode::LAYOUT_MODE_UNCONTROLLED;
} else if (Object::cast_to<Container>(parent_node)) {
return LayoutMode::LAYOUT_MODE_CONTAINER;
}
// If anchors are not in the top-left position, this is definitely in anchors mode.
if (_get_anchors_layout_preset() != (int)LayoutPreset::PRESET_TOP_LEFT) {
return LayoutMode::LAYOUT_MODE_ANCHORS;
}
// Otherwise check what was saved.
if (has_meta("_edit_layout_mode")) {
return (LayoutMode)(int)get_meta("_edit_layout_mode");
}
// Or fallback on default.
return LayoutMode::LAYOUT_MODE_POSITION;
}
void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
ERR_FAIL_INDEX((int)p_side, 4);
@ -1431,6 +1584,120 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos,
set_offset(p_side, p_pos);
}
void Control::_set_anchors_layout_preset(int p_preset) {
set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS);
if (p_preset == -1) {
set_meta("_edit_use_custom_anchors", true);
notify_property_list_changed();
return; // Keep settings as is.
}
set_meta("_edit_use_custom_anchors", false);
LayoutPreset preset = (LayoutPreset)p_preset;
// Set correct anchors.
set_anchors_preset(preset);
// Select correct preset mode.
switch (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:
set_offsets_preset(preset, LayoutPresetMode::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:
set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE);
break;
}
// Select correct grow directions.
set_grow_direction_preset(preset);
notify_property_list_changed();
}
int Control::_get_anchors_layout_preset() const {
// If the custom preset was selected by user, use it.
if (has_meta("_edit_use_custom_anchors") && (bool)get_meta("_edit_use_custom_anchors")) {
return -1;
}
// Check anchors to determine if the current state matches a preset, or not.
float left = get_anchor(SIDE_LEFT);
float right = get_anchor(SIDE_RIGHT);
float top = get_anchor(SIDE_TOP);
float bottom = get_anchor(SIDE_BOTTOM);
if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
return (int)LayoutPreset::PRESET_TOP_LEFT;
}
if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
return (int)LayoutPreset::PRESET_TOP_RIGHT;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_END && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_BOTTOM_LEFT;
}
if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_BOTTOM_RIGHT;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == 0.5 && bottom == 0.5) {
return (int)LayoutPreset::PRESET_CENTER_LEFT;
}
if (left == ANCHOR_END && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
return (int)LayoutPreset::PRESET_CENTER_RIGHT;
}
if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
return (int)LayoutPreset::PRESET_CENTER_TOP;
}
if (left == 0.5 && right == 0.5 && top == ANCHOR_END && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_CENTER_BOTTOM;
}
if (left == 0.5 && right == 0.5 && top == 0.5 && bottom == 0.5) {
return (int)LayoutPreset::PRESET_CENTER;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_LEFT_WIDE;
}
if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_RIGHT_WIDE;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
return (int)LayoutPreset::PRESET_TOP_WIDE;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_BOTTOM_WIDE;
}
if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_VCENTER_WIDE;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
return (int)LayoutPreset::PRESET_HCENTER_WIDE;
}
if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
return (int)LayoutPreset::PRESET_WIDE;
}
// Does not match any preset, return "Custom".
return -1;
}
void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
ERR_FAIL_INDEX((int)p_preset, 16);
@ -1687,6 +1954,62 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset
set_offsets_preset(p_preset, p_resize_mode, p_margin);
}
void Control::set_grow_direction_preset(LayoutPreset p_preset) {
// Select correct horizontal grow direction.
switch (p_preset) {
case PRESET_TOP_LEFT:
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
set_h_grow_direction(GrowDirection::GROW_DIRECTION_END);
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
set_h_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_TOP_WIDE:
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
break;
}
// Select correct vertical grow direction.
switch (p_preset) {
case PRESET_TOP_LEFT:
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
set_v_grow_direction(GrowDirection::GROW_DIRECTION_END);
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
set_v_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_LEFT_WIDE:
case PRESET_RIGHT_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
break;
}
}
real_t Control::get_anchor(Side p_side) const {
ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
@ -2847,14 +3170,22 @@ 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_layout_mode", "mode"), &Control::_set_layout_mode);
ClassDB::bind_method(D_METHOD("_get_layout_mode"), &Control::_get_layout_mode);
ClassDB::bind_method(D_METHOD("_set_anchors_layout_preset", "preset"), &Control::_set_anchors_layout_preset);
ClassDB::bind_method(D_METHOD("_get_anchors_layout_preset"), &Control::_get_anchors_layout_preset);
ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_offsets"), &Control::set_anchors_preset, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_anchors_and_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("_set_anchor", "side", "anchor"), &Control::_set_anchor);
ClassDB::bind_method(D_METHOD("set_anchor", "side", "anchor", "keep_offset", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_anchor", "side"), &Control::get_anchor);
ClassDB::bind_method(D_METHOD("set_offset", "side", "offset"), &Control::set_offset);
ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("set_anchor_and_offset", "side", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_offset, 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", "keep_offsets"), &Control::set_position, DEFVAL(false));
@ -2868,7 +3199,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset);
ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin);
ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end);
ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position);
@ -2996,37 +3326,54 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
ADD_GROUP("Anchor", "anchor_");
ADD_GROUP("Layout", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
const String anchors_presets_options = "Custom:-1,PresetWide:15,"
"PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2,"
"PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8,"
"PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14";
ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset");
ADD_PROPERTY_DEFAULT("anchors_preset", -1);
ADD_SUBGROUP_INDENT("Anchor Points", "anchor_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM);
ADD_GROUP("Offset", "offset_");
ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_BOTTOM);
ADD_GROUP("Grow Direction", "grow_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_SUBGROUP_INDENT("Grow Direction", "grow_", 1);
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Left,Right,Both"), "set_h_grow_direction", "get_h_grow_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Top,Bottom,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Layout Direction", "layout_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
ADD_GROUP("Auto Translate", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
ADD_GROUP("Rect", "rect_");
ADD_SUBGROUP("Rectangle", "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, "", PROPERTY_USAGE_NONE), "_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_SUBGROUP("Transform", "rect_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents");
ADD_SUBGROUP("Container Sizing", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Auto Translate", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
ADD_GROUP("Hint", "hint_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
@ -3044,11 +3391,6 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
ADD_GROUP("Size Flags", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation");
@ -3107,6 +3449,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT);
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE);
BIND_ENUM_CONSTANT(SIZE_SHRINK_BEGIN);
BIND_ENUM_CONSTANT(SIZE_FILL);
BIND_ENUM_CONSTANT(SIZE_EXPAND);
BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL);

View file

@ -67,12 +67,13 @@ public:
};
enum SizeFlags {
SIZE_SHRINK_BEGIN = 0,
SIZE_FILL = 1,
SIZE_EXPAND = 2,
SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
SIZE_SHRINK_CENTER = 4, //ignored by expand or fill
SIZE_SHRINK_END = 8, //ignored by expand or fill
SIZE_SHRINK_CENTER = 4,
SIZE_SHRINK_END = 8,
SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
};
enum MouseFilter {
@ -128,6 +129,13 @@ public:
PRESET_MODE_KEEP_SIZE
};
enum LayoutMode {
LAYOUT_MODE_POSITION,
LAYOUT_MODE_ANCHORS,
LAYOUT_MODE_CONTAINER,
LAYOUT_MODE_UNCONTROLLED,
};
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
@ -230,7 +238,7 @@ private:
} data;
static constexpr unsigned properties_managed_by_container_count = 11;
static constexpr unsigned properties_managed_by_container_count = 12;
static String properties_managed_by_container[properties_managed_by_container_count];
void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
@ -241,6 +249,12 @@ private:
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
void _set_layout_mode(LayoutMode p_mode);
LayoutMode _get_layout_mode() const;
void _set_anchors_layout_preset(int p_preset);
int _get_anchors_layout_preset() const;
void _theme_changed();
void _notify_theme_changed();
@ -285,9 +299,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
virtual void _validate_property(PropertyInfo &property) const override;
void _notification(int p_notification);
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
//bind helpers
@ -378,6 +393,7 @@ public:
void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_grow_direction_preset(LayoutPreset p_preset);
void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
real_t get_anchor(Side p_side) const;
@ -563,6 +579,7 @@ VARIANT_ENUM_CAST(Control::LayoutPresetMode);
VARIANT_ENUM_CAST(Control::MouseFilter);
VARIANT_ENUM_CAST(Control::GrowDirection);
VARIANT_ENUM_CAST(Control::Anchor);
VARIANT_ENUM_CAST(Control::LayoutMode);
VARIANT_ENUM_CAST(Control::LayoutDirection);
VARIANT_ENUM_CAST(Control::TextDirection);
VARIANT_ENUM_CAST(Control::StructuredTextParser);

View file

@ -223,6 +223,30 @@ Size2 FlowContainer::get_minimum_size() const {
return minimum;
}
Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (!vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> FlowContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void FlowContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {

View file

@ -54,6 +54,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
FlowContainer(bool p_vertical = false);
};

View file

@ -959,6 +959,25 @@ bool GraphNode::is_resizable() const {
return resizable;
}
Vector<int> GraphNode::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> GraphNode::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_EXPAND);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphNode::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title);

View file

@ -182,6 +182,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
bool is_resizing() const { return resizing; }
GraphNode();

View file

@ -65,6 +65,24 @@ Size2 MarginContainer::get_minimum_size() const {
return max;
}
Vector<int> MarginContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> MarginContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void MarginContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {

View file

@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
MarginContainer();
};

View file

@ -60,6 +60,24 @@ Size2 PanelContainer::get_minimum_size() const {
return ms;
}
Vector<int> PanelContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> PanelContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void PanelContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();

View file

@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
PanelContainer();
};

View file

@ -336,6 +336,30 @@ bool SplitContainer::is_collapsed() const {
return collapsed;
}
Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (!vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
Vector<int> SplitContainer::get_allowed_size_flags_vertical() const {
Vector<int> flags;
flags.append(SIZE_FILL);
if (vertical) {
flags.append(SIZE_EXPAND);
}
flags.append(SIZE_SHRINK_BEGIN);
flags.append(SIZE_SHRINK_CENTER);
flags.append(SIZE_SHRINK_END);
return flags;
}
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);

View file

@ -79,6 +79,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
SplitContainer(bool p_vertical = false);
};

View file

@ -91,6 +91,14 @@ int SubViewportContainer::get_stretch_shrink() const {
return shrink;
}
Vector<int> SubViewportContainer::get_allowed_size_flags_horizontal() const {
return Vector<int>();
}
Vector<int> SubViewportContainer::get_allowed_size_flags_vertical() const {
return Vector<int>();
}
void SubViewportContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_RESIZED) {
if (!stretch) {

View file

@ -54,6 +54,9 @@ public:
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
SubViewportContainer();
};

View file

@ -1177,6 +1177,14 @@ bool TabContainer::get_use_hidden_tabs_for_min_size() const {
return use_hidden_tabs_for_min_size;
}
Vector<int> TabContainer::get_allowed_size_flags_horizontal() const {
return Vector<int>();
}
Vector<int> TabContainer::get_allowed_size_flags_vertical() const {
return Vector<int>();
}
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabContainer::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabContainer::set_current_tab);

View file

@ -133,6 +133,9 @@ public:
void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs);
bool get_use_hidden_tabs_for_min_size() const;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
TabContainer();
};