Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/gui/control_editor_plugin.cpp
9902 views
1
/**************************************************************************/
2
/* control_editor_plugin.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "control_editor_plugin.h"
32
33
#include "editor/editor_node.h"
34
#include "editor/editor_undo_redo_manager.h"
35
#include "editor/scene/canvas_item_editor_plugin.h"
36
#include "editor/themes/editor_scale.h"
37
#include "scene/gui/button.h"
38
#include "scene/gui/check_box.h"
39
#include "scene/gui/check_button.h"
40
#include "scene/gui/grid_container.h"
41
#include "scene/gui/label.h"
42
#include "scene/gui/option_button.h"
43
#include "scene/gui/panel_container.h"
44
#include "scene/gui/separator.h"
45
#include "scene/gui/texture_rect.h"
46
47
// Inspector controls.
48
49
void ControlPositioningWarning::_update_warning() {
50
if (!control_node) {
51
title_icon->set_texture(nullptr);
52
title_label->set_text("");
53
hint_label->set_text("");
54
return;
55
}
56
57
Node *parent_node = control_node->get_parent_control();
58
if (!parent_node) {
59
title_icon->set_texture(get_editor_theme_icon(SNAME("SubViewport")));
60
title_label->set_text(TTR("This node doesn't have a control parent."));
61
hint_label->set_text(TTR("Use the appropriate layout properties depending on where you are going to put it."));
62
} else if (Object::cast_to<Container>(parent_node)) {
63
title_icon->set_texture(get_editor_theme_icon(SNAME("ContainerLayout")));
64
title_label->set_text(TTR("This node is a child of a container."));
65
hint_label->set_text(TTR("Use container properties for positioning."));
66
} else {
67
title_icon->set_texture(get_editor_theme_icon(SNAME("ControlLayout")));
68
title_label->set_text(TTR("This node is a child of a regular control."));
69
hint_label->set_text(TTR("Use anchors and the rectangle for positioning."));
70
}
71
72
bg_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("bg_group_note"), SNAME("EditorProperty")));
73
}
74
75
void ControlPositioningWarning::_update_toggler() {
76
Ref<Texture2D> arrow;
77
if (hint_label->is_visible()) {
78
arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));
79
set_tooltip_text(TTR("Collapse positioning hint."));
80
} else {
81
if (is_layout_rtl()) {
82
arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree"));
83
} else {
84
arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree"));
85
}
86
set_tooltip_text(TTR("Expand positioning hint."));
87
}
88
89
hint_icon->set_texture(arrow);
90
}
91
92
void ControlPositioningWarning::set_control(Control *p_node) {
93
control_node = p_node;
94
_update_warning();
95
}
96
97
void ControlPositioningWarning::gui_input(const Ref<InputEvent> &p_event) {
98
Ref<InputEventMouseButton> mb = p_event;
99
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
100
bool state = !hint_label->is_visible();
101
102
hint_filler_left->set_visible(state);
103
hint_label->set_visible(state);
104
hint_filler_right->set_visible(state);
105
106
_update_toggler();
107
}
108
}
109
110
void ControlPositioningWarning::_notification(int p_notification) {
111
switch (p_notification) {
112
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
113
case NOTIFICATION_TRANSLATION_CHANGED:
114
case NOTIFICATION_THEME_CHANGED:
115
_update_warning();
116
_update_toggler();
117
break;
118
}
119
}
120
121
ControlPositioningWarning::ControlPositioningWarning() {
122
set_mouse_filter(MOUSE_FILTER_STOP);
123
124
bg_panel = memnew(PanelContainer);
125
bg_panel->set_mouse_filter(MOUSE_FILTER_IGNORE);
126
add_child(bg_panel);
127
128
grid = memnew(GridContainer);
129
grid->set_columns(3);
130
bg_panel->add_child(grid);
131
132
title_icon = memnew(TextureRect);
133
title_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
134
grid->add_child(title_icon);
135
136
title_label = memnew(Label);
137
title_label->set_autowrap_mode(TextServer::AutowrapMode::AUTOWRAP_WORD);
138
title_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
139
title_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
140
grid->add_child(title_label);
141
142
hint_icon = memnew(TextureRect);
143
hint_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
144
grid->add_child(hint_icon);
145
146
// Filler.
147
hint_filler_left = memnew(Control);
148
hint_filler_left->hide();
149
grid->add_child(hint_filler_left);
150
151
hint_label = memnew(Label);
152
hint_label->set_autowrap_mode(TextServer::AutowrapMode::AUTOWRAP_WORD);
153
hint_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
154
hint_label->set_vertical_alignment(VerticalAlignment::VERTICAL_ALIGNMENT_CENTER);
155
hint_label->hide();
156
grid->add_child(hint_label);
157
158
// Filler.
159
hint_filler_right = memnew(Control);
160
hint_filler_right->hide();
161
grid->add_child(hint_filler_right);
162
}
163
164
void EditorPropertyAnchorsPreset::_set_read_only(bool p_read_only) {
165
options->set_disabled(p_read_only);
166
}
167
168
void EditorPropertyAnchorsPreset::_notification(int p_what) {
169
switch (p_what) {
170
case NOTIFICATION_THEME_CHANGED: {
171
for (int i = 0; i < options->get_item_count(); i++) {
172
if (options->is_item_separator(i)) {
173
continue;
174
}
175
int64_t preset = options->get_item_metadata(i);
176
if (preset < 0 || PRESET_FULL_RECT < preset) {
177
continue;
178
}
179
static const StringName icon_names[] = {
180
StringName("ControlAlignTopLeft", true),
181
StringName("ControlAlignTopRight", true),
182
StringName("ControlAlignBottomLeft", true),
183
StringName("ControlAlignBottomRight", true),
184
StringName("ControlAlignCenterLeft", true),
185
StringName("ControlAlignCenterTop", true),
186
StringName("ControlAlignCenterRight", true),
187
StringName("ControlAlignCenterBottom", true),
188
StringName("ControlAlignCenter", true),
189
StringName("ControlAlignLeftWide", true),
190
StringName("ControlAlignTopWide", true),
191
StringName("ControlAlignRightWide", true),
192
StringName("ControlAlignBottomWide", true),
193
StringName("ControlAlignVCenterWide", true),
194
StringName("ControlAlignHCenterWide", true),
195
StringName("ControlAlignFullRect", true),
196
};
197
options->set_item_icon(i, get_editor_theme_icon(icon_names[preset]));
198
}
199
} break;
200
}
201
}
202
203
void EditorPropertyAnchorsPreset::_option_selected(int p_which) {
204
int64_t val = options->get_item_metadata(p_which);
205
emit_changed(get_edited_property(), val);
206
}
207
208
void EditorPropertyAnchorsPreset::update_property() {
209
int64_t which = get_edited_property_value();
210
211
for (int i = 0; i < options->get_item_count(); i++) {
212
Variant val = options->get_item_metadata(i);
213
if (val != Variant() && which == (int64_t)val) {
214
options->select(i);
215
return;
216
}
217
}
218
}
219
220
void EditorPropertyAnchorsPreset::setup(const Vector<String> &p_options) {
221
options->clear();
222
223
const Vector<int> split_after = {
224
-1,
225
PRESET_FULL_RECT,
226
PRESET_BOTTOM_LEFT,
227
PRESET_CENTER,
228
};
229
230
for (int i = 0; i < p_options.size(); i++) {
231
Vector<String> text_split = p_options[i].split(":");
232
int64_t current_val = text_split[1].to_int();
233
234
const String &option_name = text_split[0];
235
options->add_item(option_name);
236
options->set_item_metadata(-1, current_val);
237
if (split_after.has(current_val)) {
238
options->add_separator();
239
}
240
}
241
}
242
243
EditorPropertyAnchorsPreset::EditorPropertyAnchorsPreset() {
244
options = memnew(OptionButton);
245
options->set_clip_text(true);
246
options->set_flat(true);
247
add_child(options);
248
add_focusable(options);
249
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyAnchorsPreset::_option_selected));
250
}
251
252
void EditorPropertySizeFlags::_set_read_only(bool p_read_only) {
253
for (CheckBox *check : flag_checks) {
254
check->set_disabled(p_read_only);
255
}
256
flag_presets->set_disabled(p_read_only);
257
}
258
259
void EditorPropertySizeFlags::_preset_selected(int p_which) {
260
int preset = flag_presets->get_item_id(p_which);
261
if (preset == SIZE_FLAGS_PRESET_CUSTOM) {
262
flag_options->set_visible(true);
263
return;
264
}
265
flag_options->set_visible(false);
266
267
uint32_t value = 0;
268
switch (preset) {
269
case SIZE_FLAGS_PRESET_FILL:
270
value = Control::SIZE_FILL;
271
break;
272
case SIZE_FLAGS_PRESET_SHRINK_BEGIN:
273
value = Control::SIZE_SHRINK_BEGIN;
274
break;
275
case SIZE_FLAGS_PRESET_SHRINK_CENTER:
276
value = Control::SIZE_SHRINK_CENTER;
277
break;
278
case SIZE_FLAGS_PRESET_SHRINK_END:
279
value = Control::SIZE_SHRINK_END;
280
break;
281
}
282
283
bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
284
if (is_expand) {
285
value |= Control::SIZE_EXPAND;
286
}
287
288
emit_changed(get_edited_property(), value);
289
}
290
291
void EditorPropertySizeFlags::_expand_toggled() {
292
uint32_t value = get_edited_property_value();
293
294
if (flag_expand->is_visible() && flag_expand->is_pressed()) {
295
value |= Control::SIZE_EXPAND;
296
} else {
297
value ^= Control::SIZE_EXPAND;
298
}
299
300
// Keep the custom preset selected as we toggle individual flags.
301
keep_selected_preset = true;
302
emit_changed(get_edited_property(), value);
303
}
304
305
void EditorPropertySizeFlags::_flag_toggled() {
306
uint32_t value = 0;
307
for (int i = 0; i < flag_checks.size(); i++) {
308
if (flag_checks[i]->is_pressed()) {
309
int flag_value = flag_checks[i]->get_meta("_value");
310
value |= flag_value;
311
}
312
}
313
314
bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
315
if (is_expand) {
316
value |= Control::SIZE_EXPAND;
317
}
318
319
// Keep the custom preset selected as we toggle individual flags.
320
keep_selected_preset = true;
321
emit_changed(get_edited_property(), value);
322
}
323
324
void EditorPropertySizeFlags::update_property() {
325
uint32_t value = get_edited_property_value();
326
327
for (int i = 0; i < flag_checks.size(); i++) {
328
int flag_value = flag_checks[i]->get_meta("_value");
329
if (value & flag_value) {
330
flag_checks[i]->set_pressed(true);
331
} else {
332
flag_checks[i]->set_pressed(false);
333
}
334
}
335
336
bool is_expand = value & Control::SIZE_EXPAND;
337
flag_expand->set_pressed(is_expand);
338
339
if (keep_selected_preset) {
340
keep_selected_preset = false;
341
return;
342
}
343
344
FlagPreset preset = SIZE_FLAGS_PRESET_CUSTOM;
345
if (value == Control::SIZE_FILL || value == (Control::SIZE_FILL | Control::SIZE_EXPAND)) {
346
preset = SIZE_FLAGS_PRESET_FILL;
347
} else if (value == Control::SIZE_SHRINK_BEGIN || value == (Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND)) {
348
preset = SIZE_FLAGS_PRESET_SHRINK_BEGIN;
349
} else if (value == Control::SIZE_SHRINK_CENTER || value == (Control::SIZE_SHRINK_CENTER | Control::SIZE_EXPAND)) {
350
preset = SIZE_FLAGS_PRESET_SHRINK_CENTER;
351
} else if (value == Control::SIZE_SHRINK_END || value == (Control::SIZE_SHRINK_END | Control::SIZE_EXPAND)) {
352
preset = SIZE_FLAGS_PRESET_SHRINK_END;
353
}
354
355
int preset_idx = flag_presets->get_item_index(preset);
356
if (preset_idx >= 0) {
357
flag_presets->select(preset_idx);
358
}
359
flag_options->set_visible(preset == SIZE_FLAGS_PRESET_CUSTOM);
360
}
361
362
void EditorPropertySizeFlags::setup(const Vector<String> &p_options, bool p_vertical) {
363
vertical = p_vertical;
364
365
if (p_options.is_empty()) {
366
flag_presets->clear();
367
flag_presets->add_item(TTR("Container Default"));
368
flag_presets->set_disabled(true);
369
flag_expand->set_visible(false);
370
return;
371
}
372
373
HashMap<int, String> flags;
374
for (int i = 0, j = 0; i < p_options.size(); i++, j++) {
375
Vector<String> text_split = p_options[i].split(":");
376
int64_t current_val = text_split[1].to_int();
377
flags[current_val] = text_split[0];
378
379
if (current_val == SIZE_EXPAND) {
380
continue;
381
}
382
383
CheckBox *cb = memnew(CheckBox);
384
cb->set_text(text_split[0]);
385
cb->set_clip_text(true);
386
cb->set_meta("_value", current_val);
387
cb->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySizeFlags::_flag_toggled));
388
add_focusable(cb);
389
390
flag_options->add_child(cb);
391
flag_checks.append(cb);
392
}
393
394
Control *gui_base = EditorNode::get_singleton()->get_gui_base();
395
StringName wide_preset_icon = SNAME("ControlAlignHCenterWide");
396
StringName begin_preset_icon = SNAME("ControlAlignCenterLeft");
397
StringName end_preset_icon = SNAME("ControlAlignCenterRight");
398
if (vertical) {
399
wide_preset_icon = SNAME("ControlAlignVCenterWide");
400
begin_preset_icon = SNAME("ControlAlignCenterTop");
401
end_preset_icon = SNAME("ControlAlignCenterBottom");
402
}
403
404
flag_presets->clear();
405
if (flags.has(SIZE_FILL)) {
406
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(wide_preset_icon), TTR("Fill"), SIZE_FLAGS_PRESET_FILL);
407
}
408
// Shrink Begin is the same as no flags at all, as such it cannot be disabled.
409
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(begin_preset_icon), TTR("Shrink Begin"), SIZE_FLAGS_PRESET_SHRINK_BEGIN);
410
if (flags.has(SIZE_SHRINK_CENTER)) {
411
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(SNAME("ControlAlignCenter")), TTR("Shrink Center"), SIZE_FLAGS_PRESET_SHRINK_CENTER);
412
}
413
if (flags.has(SIZE_SHRINK_END)) {
414
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(end_preset_icon), TTR("Shrink End"), SIZE_FLAGS_PRESET_SHRINK_END);
415
}
416
flag_presets->add_separator();
417
flag_presets->add_item(TTR("Custom"), SIZE_FLAGS_PRESET_CUSTOM);
418
419
flag_expand->set_visible(flags.has(SIZE_EXPAND));
420
}
421
422
EditorPropertySizeFlags::EditorPropertySizeFlags() {
423
VBoxContainer *vb = memnew(VBoxContainer);
424
add_child(vb);
425
426
flag_presets = memnew(OptionButton);
427
flag_presets->set_clip_text(true);
428
flag_presets->set_flat(true);
429
vb->add_child(flag_presets);
430
add_focusable(flag_presets);
431
set_label_reference(flag_presets);
432
flag_presets->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertySizeFlags::_preset_selected));
433
434
flag_options = memnew(VBoxContainer);
435
flag_options->hide();
436
vb->add_child(flag_options);
437
438
flag_expand = memnew(CheckBox);
439
flag_expand->set_text(TTR("Expand"));
440
vb->add_child(flag_expand);
441
add_focusable(flag_expand);
442
flag_expand->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySizeFlags::_expand_toggled));
443
}
444
445
bool EditorInspectorPluginControl::can_handle(Object *p_object) {
446
return Object::cast_to<Control>(p_object) != nullptr;
447
}
448
449
void EditorInspectorPluginControl::parse_category(Object *p_object, const String &p_category) {
450
inside_control_category = p_category == "Control";
451
}
452
453
void EditorInspectorPluginControl::parse_group(Object *p_object, const String &p_group) {
454
if (!inside_control_category) {
455
return;
456
}
457
458
Control *control = Object::cast_to<Control>(p_object);
459
if (!control || p_group != "Layout") {
460
return;
461
}
462
463
ControlPositioningWarning *pos_warning = memnew(ControlPositioningWarning);
464
pos_warning->set_control(control);
465
add_custom_control(pos_warning);
466
}
467
468
bool EditorInspectorPluginControl::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
469
Control *control = Object::cast_to<Control>(p_object);
470
if (!control) {
471
return false;
472
}
473
474
if (p_path == "anchors_preset") {
475
EditorPropertyAnchorsPreset *prop_editor = memnew(EditorPropertyAnchorsPreset);
476
Vector<String> options = p_hint_text.split(",");
477
prop_editor->setup(options);
478
add_property_editor(p_path, prop_editor);
479
480
return true;
481
}
482
483
if (p_path == "size_flags_horizontal" || p_path == "size_flags_vertical") {
484
EditorPropertySizeFlags *prop_editor = memnew(EditorPropertySizeFlags);
485
Vector<String> options;
486
if (!p_hint_text.is_empty()) {
487
options = p_hint_text.split(",");
488
}
489
prop_editor->setup(options, p_path == "size_flags_vertical");
490
add_property_editor(p_path, prop_editor);
491
492
return true;
493
}
494
495
return false;
496
}
497
498
// Toolbars controls.
499
500
Size2 ControlEditorPopupButton::get_minimum_size() const {
501
Vector2 base_size = Vector2(26, 26) * EDSCALE;
502
503
if (arrow_icon.is_null()) {
504
return base_size;
505
}
506
507
Vector2 final_size;
508
final_size.x = base_size.x + arrow_icon->get_width();
509
final_size.y = MAX(base_size.y, arrow_icon->get_height());
510
511
return final_size;
512
}
513
514
void ControlEditorPopupButton::toggled(bool p_pressed) {
515
if (!p_pressed) {
516
return;
517
}
518
519
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
520
521
popup_panel->set_size(Size2(size.width, 0));
522
Point2 gp = get_screen_position();
523
gp.y += size.y;
524
if (is_layout_rtl()) {
525
gp.x += size.width - popup_panel->get_size().width;
526
}
527
popup_panel->set_position(gp);
528
529
popup_panel->popup();
530
}
531
532
void ControlEditorPopupButton::_popup_visibility_changed(bool p_visible) {
533
set_pressed(p_visible);
534
}
535
536
void ControlEditorPopupButton::_notification(int p_what) {
537
switch (p_what) {
538
case NOTIFICATION_THEME_CHANGED: {
539
arrow_icon = get_theme_icon("select_arrow", "Tree");
540
} break;
541
542
case NOTIFICATION_DRAW: {
543
if (arrow_icon.is_valid()) {
544
Vector2 arrow_pos = Point2(26, 0) * EDSCALE;
545
if (is_layout_rtl()) {
546
arrow_pos.x = get_size().x - arrow_pos.x - arrow_icon->get_width();
547
}
548
arrow_pos.y = get_size().y / 2 - arrow_icon->get_height() / 2;
549
draw_texture(arrow_icon, arrow_pos);
550
}
551
} break;
552
553
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
554
popup_panel->set_layout_direction((Window::LayoutDirection)get_layout_direction());
555
} break;
556
557
case NOTIFICATION_VISIBILITY_CHANGED: {
558
if (!is_visible_in_tree()) {
559
popup_panel->hide();
560
}
561
} break;
562
}
563
}
564
565
ControlEditorPopupButton::ControlEditorPopupButton() {
566
set_theme_type_variation(SceneStringName(FlatButton));
567
set_toggle_mode(true);
568
set_focus_mode(FOCUS_NONE);
569
570
popup_panel = memnew(PopupPanel);
571
add_child(popup_panel);
572
popup_panel->connect("about_to_popup", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(true));
573
popup_panel->connect("popup_hide", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(false));
574
575
popup_vbox = memnew(VBoxContainer);
576
popup_panel->add_child(popup_vbox);
577
}
578
579
void ControlEditorPresetPicker::_add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name) {
580
ERR_FAIL_COND(preset_buttons.has(p_preset));
581
582
Button *b = memnew(Button);
583
b->set_custom_minimum_size(Size2i(36, 36) * EDSCALE);
584
b->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
585
b->set_tooltip_text(p_name);
586
b->set_flat(true);
587
p_row->add_child(b);
588
b->connect(SceneStringName(pressed), callable_mp(this, &ControlEditorPresetPicker::_preset_button_pressed).bind(p_preset));
589
590
preset_buttons[p_preset] = b;
591
}
592
593
void ControlEditorPresetPicker::_add_separator(BoxContainer *p_box, Separator *p_separator) {
594
p_separator->add_theme_constant_override("separation", grid_separation);
595
p_separator->set_custom_minimum_size(Size2i(1, 1));
596
p_box->add_child(p_separator);
597
}
598
599
void AnchorPresetPicker::_preset_button_pressed(const int p_preset) {
600
emit_signal("anchors_preset_selected", p_preset);
601
}
602
603
void AnchorPresetPicker::_notification(int p_notification) {
604
switch (p_notification) {
605
case NOTIFICATION_THEME_CHANGED: {
606
preset_buttons[PRESET_TOP_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopLeft")));
607
preset_buttons[PRESET_CENTER_TOP]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop")));
608
preset_buttons[PRESET_TOP_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopRight")));
609
610
preset_buttons[PRESET_CENTER_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft")));
611
preset_buttons[PRESET_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
612
preset_buttons[PRESET_CENTER_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight")));
613
614
preset_buttons[PRESET_BOTTOM_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomLeft")));
615
preset_buttons[PRESET_CENTER_BOTTOM]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom")));
616
preset_buttons[PRESET_BOTTOM_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomRight")));
617
618
preset_buttons[PRESET_TOP_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopWide")));
619
preset_buttons[PRESET_HCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide")));
620
preset_buttons[PRESET_BOTTOM_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide")));
621
622
preset_buttons[PRESET_LEFT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide")));
623
preset_buttons[PRESET_VCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide")));
624
preset_buttons[PRESET_RIGHT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide")));
625
626
preset_buttons[PRESET_FULL_RECT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignFullRect")));
627
} break;
628
}
629
}
630
631
void AnchorPresetPicker::_bind_methods() {
632
ADD_SIGNAL(MethodInfo("anchors_preset_selected", PropertyInfo(Variant::INT, "preset")));
633
}
634
635
AnchorPresetPicker::AnchorPresetPicker() {
636
VBoxContainer *main_vb = memnew(VBoxContainer);
637
main_vb->add_theme_constant_override("separation", grid_separation);
638
add_child(main_vb);
639
640
HBoxContainer *top_row = memnew(HBoxContainer);
641
top_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
642
top_row->add_theme_constant_override("separation", grid_separation);
643
main_vb->add_child(top_row);
644
645
_add_row_button(top_row, PRESET_TOP_LEFT, TTRC("Top Left"));
646
_add_row_button(top_row, PRESET_CENTER_TOP, TTRC("Center Top"));
647
_add_row_button(top_row, PRESET_TOP_RIGHT, TTRC("Top Right"));
648
_add_separator(top_row, memnew(VSeparator));
649
_add_row_button(top_row, PRESET_TOP_WIDE, TTRC("Top Wide"));
650
651
HBoxContainer *mid_row = memnew(HBoxContainer);
652
mid_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
653
mid_row->add_theme_constant_override("separation", grid_separation);
654
main_vb->add_child(mid_row);
655
656
_add_row_button(mid_row, PRESET_CENTER_LEFT, TTRC("Center Left"));
657
_add_row_button(mid_row, PRESET_CENTER, TTRC("Center"));
658
_add_row_button(mid_row, PRESET_CENTER_RIGHT, TTRC("Center Right"));
659
_add_separator(mid_row, memnew(VSeparator));
660
_add_row_button(mid_row, PRESET_HCENTER_WIDE, TTRC("HCenter Wide"));
661
662
HBoxContainer *bot_row = memnew(HBoxContainer);
663
bot_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
664
bot_row->add_theme_constant_override("separation", grid_separation);
665
main_vb->add_child(bot_row);
666
667
_add_row_button(bot_row, PRESET_BOTTOM_LEFT, TTRC("Bottom Left"));
668
_add_row_button(bot_row, PRESET_CENTER_BOTTOM, TTRC("Center Bottom"));
669
_add_row_button(bot_row, PRESET_BOTTOM_RIGHT, TTRC("Bottom Right"));
670
_add_separator(bot_row, memnew(VSeparator));
671
_add_row_button(bot_row, PRESET_BOTTOM_WIDE, TTRC("Bottom Wide"));
672
673
_add_separator(main_vb, memnew(HSeparator));
674
675
HBoxContainer *extra_row = memnew(HBoxContainer);
676
extra_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
677
extra_row->add_theme_constant_override("separation", grid_separation);
678
main_vb->add_child(extra_row);
679
680
_add_row_button(extra_row, PRESET_LEFT_WIDE, TTRC("Left Wide"));
681
_add_row_button(extra_row, PRESET_VCENTER_WIDE, TTRC("VCenter Wide"));
682
_add_row_button(extra_row, PRESET_RIGHT_WIDE, TTRC("Right Wide"));
683
_add_separator(extra_row, memnew(VSeparator));
684
_add_row_button(extra_row, PRESET_FULL_RECT, TTRC("Full Rect"));
685
}
686
687
void SizeFlagPresetPicker::_preset_button_pressed(const int p_preset) {
688
int flags = (SizeFlags)p_preset;
689
if (expand_button->is_pressed()) {
690
flags |= SIZE_EXPAND;
691
}
692
693
emit_signal("size_flags_selected", flags);
694
}
695
696
void SizeFlagPresetPicker::_expand_button_pressed() {
697
emit_signal("expand_flag_toggled", expand_button->is_pressed());
698
}
699
700
void SizeFlagPresetPicker::set_allowed_flags(Vector<SizeFlags> &p_flags) {
701
preset_buttons[SIZE_SHRINK_BEGIN]->set_disabled(!p_flags.has(SIZE_SHRINK_BEGIN));
702
preset_buttons[SIZE_SHRINK_CENTER]->set_disabled(!p_flags.has(SIZE_SHRINK_CENTER));
703
preset_buttons[SIZE_SHRINK_END]->set_disabled(!p_flags.has(SIZE_SHRINK_END));
704
preset_buttons[SIZE_FILL]->set_disabled(!p_flags.has(SIZE_FILL));
705
706
expand_button->set_disabled(!p_flags.has(SIZE_EXPAND));
707
if (p_flags.has(SIZE_EXPAND)) {
708
expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags."));
709
} else {
710
expand_button->set_pressed(false);
711
expand_button->set_tooltip_text(TTR("Some parents of the selected nodes do not support the Expand flag."));
712
}
713
}
714
715
void SizeFlagPresetPicker::set_expand_flag(bool p_expand) {
716
expand_button->set_pressed(p_expand);
717
}
718
719
void SizeFlagPresetPicker::_notification(int p_notification) {
720
switch (p_notification) {
721
case NOTIFICATION_THEME_CHANGED: {
722
if (vertical) {
723
preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop")));
724
preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
725
preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom")));
726
727
preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide")));
728
} else {
729
preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft")));
730
preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
731
preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight")));
732
733
preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide")));
734
}
735
} break;
736
}
737
}
738
739
void SizeFlagPresetPicker::_bind_methods() {
740
ADD_SIGNAL(MethodInfo("size_flags_selected", PropertyInfo(Variant::INT, "size_flags")));
741
ADD_SIGNAL(MethodInfo("expand_flag_toggled", PropertyInfo(Variant::BOOL, "expand_flag")));
742
}
743
744
SizeFlagPresetPicker::SizeFlagPresetPicker(bool p_vertical) {
745
vertical = p_vertical;
746
747
VBoxContainer *main_vb = memnew(VBoxContainer);
748
add_child(main_vb);
749
750
HBoxContainer *main_row = memnew(HBoxContainer);
751
main_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
752
main_row->add_theme_constant_override("separation", grid_separation);
753
main_vb->add_child(main_row);
754
755
_add_row_button(main_row, SIZE_SHRINK_BEGIN, TTR("Shrink Begin"));
756
_add_row_button(main_row, SIZE_SHRINK_CENTER, TTR("Shrink Center"));
757
_add_row_button(main_row, SIZE_SHRINK_END, TTR("Shrink End"));
758
_add_separator(main_row, memnew(VSeparator));
759
_add_row_button(main_row, SIZE_FILL, TTR("Fill"));
760
761
expand_button = memnew(CheckButton);
762
expand_button->set_flat(true);
763
expand_button->set_text(TTR("Expand"));
764
expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags."));
765
expand_button->connect(SceneStringName(pressed), callable_mp(this, &SizeFlagPresetPicker::_expand_button_pressed));
766
main_vb->add_child(expand_button);
767
}
768
769
// Toolbar.
770
771
void ControlEditorToolbar::_anchors_preset_selected(int p_preset) {
772
LayoutPreset preset = (LayoutPreset)p_preset;
773
List<Node *> selection = editor_selection->get_top_selected_node_list();
774
775
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
776
undo_redo->create_action(TTR("Change Anchors, Offsets, Grow Direction"));
777
778
for (Node *E : selection) {
779
Control *control = Object::cast_to<Control>(E);
780
if (control) {
781
undo_redo->add_do_property(control, "layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS);
782
undo_redo->add_do_property(control, "anchors_preset", preset);
783
undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
784
}
785
}
786
787
undo_redo->commit_action();
788
789
anchors_mode = false;
790
anchor_mode_button->set_pressed(anchors_mode);
791
}
792
793
void ControlEditorToolbar::_anchors_to_current_ratio() {
794
List<Node *> selection = editor_selection->get_top_selected_node_list();
795
796
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
797
undo_redo->create_action(TTR("Change Anchors, Offsets (Keep Ratio)"));
798
799
for (Node *E : selection) {
800
Control *control = Object::cast_to<Control>(E);
801
if (control) {
802
Point2 top_left_anchor = _position_to_anchor(control, Point2());
803
Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size());
804
undo_redo->add_do_method(control, "set_anchor", SIDE_LEFT, top_left_anchor.x, false, true);
805
undo_redo->add_do_method(control, "set_anchor", SIDE_RIGHT, bottom_right_anchor.x, false, true);
806
undo_redo->add_do_method(control, "set_anchor", SIDE_TOP, top_left_anchor.y, false, true);
807
undo_redo->add_do_method(control, "set_anchor", SIDE_BOTTOM, bottom_right_anchor.y, false, true);
808
undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true);
809
810
const bool use_anchors = control->get_meta("_edit_use_anchors_", false);
811
undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
812
if (use_anchors) {
813
undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", true);
814
} else {
815
undo_redo->add_undo_method(control, "remove_meta", "_edit_use_anchors_");
816
}
817
818
anchors_mode = true;
819
anchor_mode_button->set_pressed(anchors_mode);
820
}
821
}
822
823
undo_redo->commit_action();
824
}
825
826
void ControlEditorToolbar::_anchor_mode_toggled(bool p_status) {
827
List<Control *> selection = _get_edited_controls();
828
for (Control *E : selection) {
829
if (Object::cast_to<Container>(E->get_parent())) {
830
continue;
831
}
832
833
if (p_status) {
834
E->set_meta("_edit_use_anchors_", true);
835
} else {
836
E->remove_meta("_edit_use_anchors_");
837
}
838
}
839
840
anchors_mode = p_status;
841
CanvasItemEditor::get_singleton()->update_viewport();
842
}
843
844
void ControlEditorToolbar::_container_flags_selected(int p_flags, bool p_vertical) {
845
List<Node *> selection = editor_selection->get_top_selected_node_list();
846
847
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
848
if (p_vertical) {
849
undo_redo->create_action(TTR("Change Vertical Size Flags"));
850
} else {
851
undo_redo->create_action(TTR("Change Horizontal Size Flags"));
852
}
853
854
for (Node *E : selection) {
855
Control *control = Object::cast_to<Control>(E);
856
if (control) {
857
int old_flags = p_vertical ? control->get_v_size_flags() : control->get_h_size_flags();
858
if (p_vertical) {
859
undo_redo->add_do_method(control, "set_v_size_flags", p_flags);
860
undo_redo->add_undo_method(control, "set_v_size_flags", old_flags);
861
} else {
862
undo_redo->add_do_method(control, "set_h_size_flags", p_flags);
863
undo_redo->add_undo_method(control, "set_h_size_flags", old_flags);
864
}
865
}
866
}
867
868
undo_redo->commit_action();
869
}
870
871
void ControlEditorToolbar::_expand_flag_toggled(bool p_expand, bool p_vertical) {
872
List<Node *> selection = editor_selection->get_top_selected_node_list();
873
874
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
875
if (p_vertical) {
876
undo_redo->create_action(TTR("Change Vertical Expand Flag"));
877
} else {
878
undo_redo->create_action(TTR("Change Horizontal Expand Flag"));
879
}
880
881
for (Node *E : selection) {
882
Control *control = Object::cast_to<Control>(E);
883
if (control) {
884
int old_flags = p_vertical ? control->get_v_size_flags() : control->get_h_size_flags();
885
int new_flags = old_flags;
886
887
if (p_expand) {
888
new_flags |= Control::SIZE_EXPAND;
889
} else {
890
new_flags &= ~Control::SIZE_EXPAND;
891
}
892
893
if (p_vertical) {
894
undo_redo->add_do_method(control, "set_v_size_flags", new_flags);
895
undo_redo->add_undo_method(control, "set_v_size_flags", old_flags);
896
} else {
897
undo_redo->add_do_method(control, "set_h_size_flags", new_flags);
898
undo_redo->add_undo_method(control, "set_h_size_flags", old_flags);
899
}
900
}
901
}
902
903
undo_redo->commit_action();
904
}
905
906
Vector2 ControlEditorToolbar::_position_to_anchor(const Control *p_control, Vector2 position) {
907
ERR_FAIL_NULL_V(p_control, Vector2());
908
909
Rect2 parent_rect = p_control->get_parent_anchorable_rect();
910
911
Vector2 output;
912
if (p_control->is_layout_rtl()) {
913
output.x = (parent_rect.size.x == 0) ? 0.0 : (parent_rect.size.x - p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x;
914
} else {
915
output.x = (parent_rect.size.x == 0) ? 0.0 : (p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x;
916
}
917
output.y = (parent_rect.size.y == 0) ? 0.0 : (p_control->get_transform().xform(position).y - parent_rect.position.y) / parent_rect.size.y;
918
return output;
919
}
920
921
bool ControlEditorToolbar::_is_node_locked(const Node *p_node) {
922
return p_node->get_meta("_edit_lock_", false);
923
}
924
925
List<Control *> ControlEditorToolbar::_get_edited_controls() {
926
List<Control *> selection;
927
for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
928
Control *control = Object::cast_to<Control>(E.key);
929
if (control && control->is_visible_in_tree() && control->get_viewport() == EditorNode::get_singleton()->get_scene_root() && !_is_node_locked(control)) {
930
selection.push_back(control);
931
}
932
}
933
934
return selection;
935
}
936
937
void ControlEditorToolbar::_selection_changed() {
938
// Update toolbar visibility.
939
bool has_controls = false;
940
bool has_control_parents = false;
941
bool has_container_parents = false;
942
943
// Also update which size flags can be configured for the selected nodes.
944
Vector<SizeFlags> allowed_h_flags = {
945
SIZE_SHRINK_BEGIN,
946
SIZE_SHRINK_CENTER,
947
SIZE_SHRINK_END,
948
SIZE_FILL,
949
SIZE_EXPAND,
950
};
951
Vector<SizeFlags> allowed_v_flags = {
952
SIZE_SHRINK_BEGIN,
953
SIZE_SHRINK_CENTER,
954
SIZE_SHRINK_END,
955
SIZE_FILL,
956
SIZE_EXPAND,
957
};
958
959
for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
960
Control *control = Object::cast_to<Control>(E.key);
961
if (!control) {
962
continue;
963
}
964
has_controls = true;
965
966
if (Object::cast_to<Control>(control->get_parent())) {
967
has_control_parents = true;
968
}
969
if (Object::cast_to<Container>(control->get_parent())) {
970
has_container_parents = true;
971
972
Container *parent_container = Object::cast_to<Container>(control->get_parent());
973
974
Vector<int> container_h_flags = parent_container->get_allowed_size_flags_horizontal();
975
Vector<SizeFlags> tmp_flags = allowed_h_flags.duplicate();
976
for (int i = 0; i < allowed_h_flags.size(); i++) {
977
if (!container_h_flags.has((int)allowed_h_flags[i])) {
978
tmp_flags.erase(allowed_h_flags[i]);
979
}
980
}
981
allowed_h_flags = tmp_flags;
982
983
Vector<int> container_v_flags = parent_container->get_allowed_size_flags_vertical();
984
tmp_flags = allowed_v_flags.duplicate();
985
for (int i = 0; i < allowed_v_flags.size(); i++) {
986
if (!container_v_flags.has((int)allowed_v_flags[i])) {
987
tmp_flags.erase(allowed_v_flags[i]);
988
}
989
}
990
allowed_v_flags = tmp_flags;
991
}
992
}
993
994
// Set general toolbar visibility.
995
set_visible(has_controls);
996
997
// Set anchor tools visibility.
998
if (has_controls && (!has_control_parents || !has_container_parents)) {
999
anchors_button->set_visible(true);
1000
anchor_mode_button->set_visible(true);
1001
1002
// Update anchor mode.
1003
int nb_valid_controls = 0;
1004
int nb_anchors_mode = 0;
1005
1006
List<Node *> selection = editor_selection->get_top_selected_node_list();
1007
for (Node *E : selection) {
1008
Control *control = Object::cast_to<Control>(E);
1009
if (!control) {
1010
continue;
1011
}
1012
if (Object::cast_to<Container>(control->get_parent())) {
1013
continue;
1014
}
1015
1016
nb_valid_controls++;
1017
if (control->get_meta("_edit_use_anchors_", false)) {
1018
nb_anchors_mode++;
1019
}
1020
}
1021
1022
anchors_mode = (nb_valid_controls == nb_anchors_mode);
1023
anchor_mode_button->set_pressed(anchors_mode);
1024
} else {
1025
anchors_button->set_visible(false);
1026
anchor_mode_button->set_visible(false);
1027
anchor_mode_button->set_pressed(false);
1028
}
1029
1030
// Set container tools visibility.
1031
if (has_controls && (!has_control_parents || has_container_parents)) {
1032
containers_button->set_visible(true);
1033
1034
// Update allowed size flags.
1035
if (has_container_parents) {
1036
container_h_picker->set_allowed_flags(allowed_h_flags);
1037
container_v_picker->set_allowed_flags(allowed_v_flags);
1038
} else {
1039
Vector<SizeFlags> allowed_all_flags = {
1040
SIZE_SHRINK_BEGIN,
1041
SIZE_SHRINK_CENTER,
1042
SIZE_SHRINK_END,
1043
SIZE_FILL,
1044
SIZE_EXPAND,
1045
};
1046
1047
container_h_picker->set_allowed_flags(allowed_all_flags);
1048
container_v_picker->set_allowed_flags(allowed_all_flags);
1049
}
1050
1051
// Update expand toggles.
1052
int nb_valid_controls = 0;
1053
int nb_h_expand = 0;
1054
int nb_v_expand = 0;
1055
1056
List<Node *> selection = editor_selection->get_top_selected_node_list();
1057
for (Node *E : selection) {
1058
Control *control = Object::cast_to<Control>(E);
1059
if (!control) {
1060
continue;
1061
}
1062
1063
nb_valid_controls++;
1064
if (control->get_h_size_flags() & Control::SIZE_EXPAND) {
1065
nb_h_expand++;
1066
}
1067
if (control->get_v_size_flags() & Control::SIZE_EXPAND) {
1068
nb_v_expand++;
1069
}
1070
}
1071
1072
container_h_picker->set_expand_flag(nb_valid_controls == nb_h_expand);
1073
container_v_picker->set_expand_flag(nb_valid_controls == nb_v_expand);
1074
} else {
1075
containers_button->set_visible(false);
1076
}
1077
}
1078
1079
void ControlEditorToolbar::_notification(int p_what) {
1080
switch (p_what) {
1081
case NOTIFICATION_THEME_CHANGED: {
1082
anchors_button->set_button_icon(get_editor_theme_icon(SNAME("ControlLayout")));
1083
anchor_mode_button->set_button_icon(get_editor_theme_icon(SNAME("Anchor")));
1084
containers_button->set_button_icon(get_editor_theme_icon(SNAME("ContainerLayout")));
1085
} break;
1086
}
1087
}
1088
1089
ControlEditorToolbar::ControlEditorToolbar() {
1090
// Anchor and offset tools.
1091
anchors_button = memnew(ControlEditorPopupButton);
1092
anchors_button->set_tooltip_text(TTR("Presets for the anchor and offset values of a Control node."));
1093
add_child(anchors_button);
1094
1095
Label *anchors_label = memnew(Label);
1096
anchors_label->set_text(TTR("Anchor preset"));
1097
anchors_button->get_popup_hbox()->add_child(anchors_label);
1098
AnchorPresetPicker *anchors_picker = memnew(AnchorPresetPicker);
1099
anchors_picker->set_h_size_flags(SIZE_SHRINK_CENTER);
1100
anchors_button->get_popup_hbox()->add_child(anchors_picker);
1101
anchors_picker->connect("anchors_preset_selected", callable_mp(this, &ControlEditorToolbar::_anchors_preset_selected));
1102
1103
anchors_button->get_popup_hbox()->add_child(memnew(HSeparator));
1104
1105
Button *keep_ratio_button = memnew(Button);
1106
keep_ratio_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
1107
keep_ratio_button->set_text(TTR("Set to Current Ratio"));
1108
keep_ratio_button->set_tooltip_text(TTR("Adjust anchors and offsets to match the current rect size."));
1109
anchors_button->get_popup_hbox()->add_child(keep_ratio_button);
1110
keep_ratio_button->connect(SceneStringName(pressed), callable_mp(this, &ControlEditorToolbar::_anchors_to_current_ratio));
1111
1112
anchor_mode_button = memnew(Button);
1113
anchor_mode_button->set_theme_type_variation(SceneStringName(FlatButton));
1114
anchor_mode_button->set_toggle_mode(true);
1115
anchor_mode_button->set_tooltip_text(TTR("When active, moving Control nodes changes their anchors instead of their offsets."));
1116
anchor_mode_button->set_accessibility_name(TTRC("Change Anchors"));
1117
add_child(anchor_mode_button);
1118
anchor_mode_button->connect(SceneStringName(toggled), callable_mp(this, &ControlEditorToolbar::_anchor_mode_toggled));
1119
1120
// Container tools.
1121
containers_button = memnew(ControlEditorPopupButton);
1122
containers_button->set_tooltip_text(TTR("Sizing settings for children of a Container node."));
1123
add_child(containers_button);
1124
1125
Label *container_h_label = memnew(Label);
1126
container_h_label->set_text(TTR("Horizontal alignment"));
1127
containers_button->get_popup_hbox()->add_child(container_h_label);
1128
container_h_picker = memnew(SizeFlagPresetPicker(false));
1129
containers_button->get_popup_hbox()->add_child(container_h_picker);
1130
container_h_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(false));
1131
container_h_picker->connect("expand_flag_toggled", callable_mp(this, &ControlEditorToolbar::_expand_flag_toggled).bind(false));
1132
1133
containers_button->get_popup_hbox()->add_child(memnew(HSeparator));
1134
1135
Label *container_v_label = memnew(Label);
1136
container_v_label->set_text(TTR("Vertical alignment"));
1137
containers_button->get_popup_hbox()->add_child(container_v_label);
1138
container_v_picker = memnew(SizeFlagPresetPicker(true));
1139
containers_button->get_popup_hbox()->add_child(container_v_picker);
1140
container_v_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(true));
1141
container_v_picker->connect("expand_flag_toggled", callable_mp(this, &ControlEditorToolbar::_expand_flag_toggled).bind(true));
1142
1143
// Editor connections.
1144
editor_selection = EditorNode::get_singleton()->get_editor_selection();
1145
editor_selection->add_editor_plugin(this);
1146
editor_selection->connect("selection_changed", callable_mp(this, &ControlEditorToolbar::_selection_changed));
1147
1148
singleton = this;
1149
}
1150
1151
ControlEditorToolbar *ControlEditorToolbar::singleton = nullptr;
1152
1153
// Editor plugin.
1154
1155
ControlEditorPlugin::ControlEditorPlugin() {
1156
toolbar = memnew(ControlEditorToolbar);
1157
toolbar->hide();
1158
add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
1159
1160
Ref<EditorInspectorPluginControl> plugin;
1161
plugin.instantiate();
1162
add_inspector_plugin(plugin);
1163
}
1164
1165