Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/gui/control_editor_plugin.cpp
20974 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
options->set_theme_type_variation(SNAME("EditorInspectorButton"));
248
add_child(options);
249
add_focusable(options);
250
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyAnchorsPreset::_option_selected));
251
}
252
253
void EditorPropertySizeFlags::_set_read_only(bool p_read_only) {
254
for (CheckBox *check : flag_checks) {
255
check->set_disabled(p_read_only);
256
}
257
flag_presets->set_disabled(p_read_only);
258
}
259
260
void EditorPropertySizeFlags::_preset_selected(int p_which) {
261
int preset = flag_presets->get_item_id(p_which);
262
if (preset == SIZE_FLAGS_PRESET_CUSTOM) {
263
flag_options->set_visible(true);
264
return;
265
}
266
flag_options->set_visible(false);
267
268
uint32_t value = 0;
269
switch (preset) {
270
case SIZE_FLAGS_PRESET_FILL:
271
value = Control::SIZE_FILL;
272
break;
273
case SIZE_FLAGS_PRESET_SHRINK_BEGIN:
274
value = Control::SIZE_SHRINK_BEGIN;
275
break;
276
case SIZE_FLAGS_PRESET_SHRINK_CENTER:
277
value = Control::SIZE_SHRINK_CENTER;
278
break;
279
case SIZE_FLAGS_PRESET_SHRINK_END:
280
value = Control::SIZE_SHRINK_END;
281
break;
282
}
283
284
bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
285
if (is_expand) {
286
value |= Control::SIZE_EXPAND;
287
}
288
289
emit_changed(get_edited_property(), value);
290
}
291
292
void EditorPropertySizeFlags::_expand_toggled() {
293
uint32_t value = get_edited_property_value();
294
295
if (flag_expand->is_visible() && flag_expand->is_pressed()) {
296
value |= Control::SIZE_EXPAND;
297
} else {
298
value ^= Control::SIZE_EXPAND;
299
}
300
301
// Keep the custom preset selected as we toggle individual flags.
302
keep_selected_preset = true;
303
emit_changed(get_edited_property(), value);
304
}
305
306
void EditorPropertySizeFlags::_flag_toggled() {
307
uint32_t value = 0;
308
for (int i = 0; i < flag_checks.size(); i++) {
309
if (flag_checks[i]->is_pressed()) {
310
int flag_value = flag_checks[i]->get_meta("_value");
311
value |= flag_value;
312
}
313
}
314
315
bool is_expand = flag_expand->is_visible() && flag_expand->is_pressed();
316
if (is_expand) {
317
value |= Control::SIZE_EXPAND;
318
}
319
320
// Keep the custom preset selected as we toggle individual flags.
321
keep_selected_preset = true;
322
emit_changed(get_edited_property(), value);
323
}
324
325
void EditorPropertySizeFlags::update_property() {
326
uint32_t value = get_edited_property_value();
327
328
for (int i = 0; i < flag_checks.size(); i++) {
329
int flag_value = flag_checks[i]->get_meta("_value");
330
if (value & flag_value) {
331
flag_checks[i]->set_pressed(true);
332
} else {
333
flag_checks[i]->set_pressed(false);
334
}
335
}
336
337
bool is_expand = value & Control::SIZE_EXPAND;
338
flag_expand->set_pressed(is_expand);
339
340
if (keep_selected_preset) {
341
keep_selected_preset = false;
342
return;
343
}
344
345
FlagPreset preset = SIZE_FLAGS_PRESET_CUSTOM;
346
if (value == Control::SIZE_FILL || value == (Control::SIZE_FILL | Control::SIZE_EXPAND)) {
347
preset = SIZE_FLAGS_PRESET_FILL;
348
} else if (value == Control::SIZE_SHRINK_BEGIN || value == (Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND)) {
349
preset = SIZE_FLAGS_PRESET_SHRINK_BEGIN;
350
} else if (value == Control::SIZE_SHRINK_CENTER || value == (Control::SIZE_SHRINK_CENTER | Control::SIZE_EXPAND)) {
351
preset = SIZE_FLAGS_PRESET_SHRINK_CENTER;
352
} else if (value == Control::SIZE_SHRINK_END || value == (Control::SIZE_SHRINK_END | Control::SIZE_EXPAND)) {
353
preset = SIZE_FLAGS_PRESET_SHRINK_END;
354
}
355
356
int preset_idx = flag_presets->get_item_index(preset);
357
if (preset_idx >= 0) {
358
flag_presets->select(preset_idx);
359
}
360
flag_options->set_visible(preset == SIZE_FLAGS_PRESET_CUSTOM);
361
}
362
363
void EditorPropertySizeFlags::setup(const Vector<String> &p_options, bool p_vertical) {
364
vertical = p_vertical;
365
366
if (p_options.is_empty()) {
367
flag_presets->clear();
368
flag_presets->add_item(TTR("Container Default"));
369
flag_presets->set_disabled(true);
370
flag_expand->set_visible(false);
371
return;
372
}
373
374
HashMap<int, String> flags;
375
for (int i = 0, j = 0; i < p_options.size(); i++, j++) {
376
Vector<String> text_split = p_options[i].split(":");
377
int64_t current_val = text_split[1].to_int();
378
flags[current_val] = text_split[0];
379
380
if (current_val == SIZE_EXPAND) {
381
continue;
382
}
383
384
CheckBox *cb = memnew(CheckBox);
385
cb->set_text(text_split[0]);
386
cb->set_clip_text(true);
387
cb->set_meta("_value", current_val);
388
cb->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySizeFlags::_flag_toggled));
389
add_focusable(cb);
390
391
flag_options->add_child(cb);
392
flag_checks.append(cb);
393
}
394
395
Control *gui_base = EditorNode::get_singleton()->get_gui_base();
396
StringName wide_preset_icon = SNAME("ControlAlignHCenterWide");
397
StringName begin_preset_icon = SNAME("ControlAlignCenterLeft");
398
StringName end_preset_icon = SNAME("ControlAlignCenterRight");
399
if (vertical) {
400
wide_preset_icon = SNAME("ControlAlignVCenterWide");
401
begin_preset_icon = SNAME("ControlAlignCenterTop");
402
end_preset_icon = SNAME("ControlAlignCenterBottom");
403
}
404
405
flag_presets->clear();
406
if (flags.has(SIZE_FILL)) {
407
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(wide_preset_icon), TTR("Fill"), SIZE_FLAGS_PRESET_FILL);
408
}
409
// Shrink Begin is the same as no flags at all, as such it cannot be disabled.
410
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(begin_preset_icon), TTR("Shrink Begin"), SIZE_FLAGS_PRESET_SHRINK_BEGIN);
411
if (flags.has(SIZE_SHRINK_CENTER)) {
412
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(SNAME("ControlAlignCenter")), TTR("Shrink Center"), SIZE_FLAGS_PRESET_SHRINK_CENTER);
413
}
414
if (flags.has(SIZE_SHRINK_END)) {
415
flag_presets->add_icon_item(gui_base->get_editor_theme_icon(end_preset_icon), TTR("Shrink End"), SIZE_FLAGS_PRESET_SHRINK_END);
416
}
417
flag_presets->add_separator();
418
flag_presets->add_item(TTR("Custom"), SIZE_FLAGS_PRESET_CUSTOM);
419
420
flag_expand->set_visible(flags.has(SIZE_EXPAND));
421
}
422
423
EditorPropertySizeFlags::EditorPropertySizeFlags() {
424
VBoxContainer *vb = memnew(VBoxContainer);
425
add_child(vb);
426
427
flag_presets = memnew(OptionButton);
428
flag_presets->set_clip_text(true);
429
flag_presets->set_flat(true);
430
flag_presets->set_theme_type_variation(SNAME("EditorInspectorButton"));
431
vb->add_child(flag_presets);
432
add_focusable(flag_presets);
433
set_label_reference(flag_presets);
434
flag_presets->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertySizeFlags::_preset_selected));
435
436
flag_options = memnew(VBoxContainer);
437
flag_options->hide();
438
vb->add_child(flag_options);
439
440
flag_expand = memnew(CheckBox);
441
flag_expand->set_text(TTR("Expand"));
442
flag_expand->set_clip_text(true);
443
vb->add_child(flag_expand);
444
add_focusable(flag_expand);
445
flag_expand->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySizeFlags::_expand_toggled));
446
}
447
448
bool EditorInspectorPluginControl::can_handle(Object *p_object) {
449
return Object::cast_to<Control>(p_object) != nullptr;
450
}
451
452
void EditorInspectorPluginControl::parse_category(Object *p_object, const String &p_category) {
453
inside_control_category = p_category == "Control";
454
}
455
456
void EditorInspectorPluginControl::parse_group(Object *p_object, const String &p_group) {
457
if (!inside_control_category) {
458
return;
459
}
460
461
Control *control = Object::cast_to<Control>(p_object);
462
if (!control || p_group != "Layout") {
463
return;
464
}
465
466
ControlPositioningWarning *pos_warning = memnew(ControlPositioningWarning);
467
pos_warning->set_control(control);
468
add_custom_control(pos_warning);
469
}
470
471
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) {
472
Control *control = Object::cast_to<Control>(p_object);
473
if (!control) {
474
return false;
475
}
476
477
if (p_path == "anchors_preset") {
478
EditorPropertyAnchorsPreset *prop_editor = memnew(EditorPropertyAnchorsPreset);
479
Vector<String> options = p_hint_text.split(",");
480
prop_editor->setup(options);
481
add_property_editor(p_path, prop_editor);
482
483
return true;
484
}
485
486
if (p_path == "size_flags_horizontal" || p_path == "size_flags_vertical") {
487
EditorPropertySizeFlags *prop_editor = memnew(EditorPropertySizeFlags);
488
Vector<String> options;
489
if (!p_hint_text.is_empty()) {
490
options = p_hint_text.split(",");
491
}
492
prop_editor->setup(options, p_path == "size_flags_vertical");
493
add_property_editor(p_path, prop_editor);
494
495
return true;
496
}
497
498
return false;
499
}
500
501
// Toolbars controls.
502
503
Size2 ControlEditorPopupButton::get_minimum_size() const {
504
Vector2 base_size = Vector2(26, 26) * EDSCALE;
505
506
if (arrow_icon.is_null()) {
507
return base_size;
508
}
509
510
Vector2 final_size;
511
final_size.x = base_size.x + arrow_icon->get_width();
512
final_size.y = MAX(base_size.y, arrow_icon->get_height());
513
514
return final_size;
515
}
516
517
void ControlEditorPopupButton::toggled(bool p_pressed) {
518
if (!p_pressed) {
519
return;
520
}
521
522
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
523
524
popup_panel->set_size(Size2(size.width, 0));
525
Point2 gp = get_screen_position();
526
gp.y += size.y;
527
if (is_layout_rtl()) {
528
gp.x += size.width - popup_panel->get_size().width;
529
}
530
popup_panel->set_position(gp);
531
532
popup_panel->popup();
533
}
534
535
void ControlEditorPopupButton::_popup_visibility_changed(bool p_visible) {
536
set_pressed(p_visible);
537
}
538
539
void ControlEditorPopupButton::_notification(int p_what) {
540
switch (p_what) {
541
case NOTIFICATION_THEME_CHANGED: {
542
arrow_icon = get_theme_icon("select_arrow", "Tree");
543
} break;
544
545
case NOTIFICATION_DRAW: {
546
if (arrow_icon.is_valid()) {
547
Vector2 arrow_pos = Point2(26, 0) * EDSCALE;
548
if (is_layout_rtl()) {
549
arrow_pos.x = get_size().x - arrow_pos.x - arrow_icon->get_width();
550
}
551
arrow_pos.y = get_size().y / 2 - arrow_icon->get_height() / 2;
552
draw_texture(arrow_icon, arrow_pos);
553
}
554
} break;
555
556
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
557
popup_panel->set_layout_direction((Window::LayoutDirection)get_layout_direction());
558
} break;
559
560
case NOTIFICATION_VISIBILITY_CHANGED: {
561
if (!is_visible_in_tree()) {
562
popup_panel->hide();
563
}
564
} break;
565
}
566
}
567
568
ControlEditorPopupButton::ControlEditorPopupButton() {
569
set_theme_type_variation(SceneStringName(FlatButton));
570
set_toggle_mode(true);
571
set_focus_mode(FOCUS_NONE);
572
573
popup_panel = memnew(PopupPanel);
574
add_child(popup_panel);
575
popup_panel->connect("about_to_popup", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(true));
576
popup_panel->connect("popup_hide", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(false));
577
578
popup_vbox = memnew(VBoxContainer);
579
popup_panel->add_child(popup_vbox);
580
}
581
582
void ControlEditorPresetPicker::_add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name) {
583
ERR_FAIL_COND(preset_buttons.has(p_preset));
584
585
Button *b = memnew(Button);
586
b->set_custom_minimum_size(Size2i(36, 36) * EDSCALE);
587
b->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
588
b->set_tooltip_text(p_name);
589
b->set_flat(true);
590
p_row->add_child(b);
591
b->connect(SceneStringName(pressed), callable_mp(this, &ControlEditorPresetPicker::_preset_button_pressed).bind(p_preset));
592
593
preset_buttons[p_preset] = b;
594
}
595
596
void ControlEditorPresetPicker::_add_separator(BoxContainer *p_box, Separator *p_separator) {
597
p_separator->add_theme_constant_override("separation", grid_separation);
598
p_separator->set_custom_minimum_size(Size2i(1, 1));
599
p_box->add_child(p_separator);
600
}
601
602
void AnchorPresetPicker::_preset_button_pressed(const int p_preset) {
603
emit_signal("anchors_preset_selected", p_preset);
604
}
605
606
void AnchorPresetPicker::_notification(int p_notification) {
607
switch (p_notification) {
608
case NOTIFICATION_THEME_CHANGED: {
609
preset_buttons[PRESET_TOP_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopLeft")));
610
preset_buttons[PRESET_CENTER_TOP]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop")));
611
preset_buttons[PRESET_TOP_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopRight")));
612
613
preset_buttons[PRESET_CENTER_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft")));
614
preset_buttons[PRESET_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
615
preset_buttons[PRESET_CENTER_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight")));
616
617
preset_buttons[PRESET_BOTTOM_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomLeft")));
618
preset_buttons[PRESET_CENTER_BOTTOM]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom")));
619
preset_buttons[PRESET_BOTTOM_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomRight")));
620
621
preset_buttons[PRESET_TOP_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopWide")));
622
preset_buttons[PRESET_HCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide")));
623
preset_buttons[PRESET_BOTTOM_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide")));
624
625
preset_buttons[PRESET_LEFT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide")));
626
preset_buttons[PRESET_VCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide")));
627
preset_buttons[PRESET_RIGHT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide")));
628
629
preset_buttons[PRESET_FULL_RECT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignFullRect")));
630
} break;
631
}
632
}
633
634
void AnchorPresetPicker::_bind_methods() {
635
ADD_SIGNAL(MethodInfo("anchors_preset_selected", PropertyInfo(Variant::INT, "preset")));
636
}
637
638
AnchorPresetPicker::AnchorPresetPicker() {
639
VBoxContainer *main_vb = memnew(VBoxContainer);
640
main_vb->add_theme_constant_override("separation", grid_separation);
641
add_child(main_vb);
642
643
HBoxContainer *top_row = memnew(HBoxContainer);
644
top_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
645
top_row->add_theme_constant_override("separation", grid_separation);
646
main_vb->add_child(top_row);
647
648
_add_row_button(top_row, PRESET_TOP_LEFT, TTRC("Top Left"));
649
_add_row_button(top_row, PRESET_CENTER_TOP, TTRC("Center Top"));
650
_add_row_button(top_row, PRESET_TOP_RIGHT, TTRC("Top Right"));
651
_add_separator(top_row, memnew(VSeparator));
652
_add_row_button(top_row, PRESET_TOP_WIDE, TTRC("Top Wide"));
653
654
HBoxContainer *mid_row = memnew(HBoxContainer);
655
mid_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
656
mid_row->add_theme_constant_override("separation", grid_separation);
657
main_vb->add_child(mid_row);
658
659
_add_row_button(mid_row, PRESET_CENTER_LEFT, TTRC("Center Left"));
660
_add_row_button(mid_row, PRESET_CENTER, TTRC("Center"));
661
_add_row_button(mid_row, PRESET_CENTER_RIGHT, TTRC("Center Right"));
662
_add_separator(mid_row, memnew(VSeparator));
663
_add_row_button(mid_row, PRESET_HCENTER_WIDE, TTRC("HCenter Wide"));
664
665
HBoxContainer *bot_row = memnew(HBoxContainer);
666
bot_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
667
bot_row->add_theme_constant_override("separation", grid_separation);
668
main_vb->add_child(bot_row);
669
670
_add_row_button(bot_row, PRESET_BOTTOM_LEFT, TTRC("Bottom Left"));
671
_add_row_button(bot_row, PRESET_CENTER_BOTTOM, TTRC("Center Bottom"));
672
_add_row_button(bot_row, PRESET_BOTTOM_RIGHT, TTRC("Bottom Right"));
673
_add_separator(bot_row, memnew(VSeparator));
674
_add_row_button(bot_row, PRESET_BOTTOM_WIDE, TTRC("Bottom Wide"));
675
676
_add_separator(main_vb, memnew(HSeparator));
677
678
HBoxContainer *extra_row = memnew(HBoxContainer);
679
extra_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
680
extra_row->add_theme_constant_override("separation", grid_separation);
681
main_vb->add_child(extra_row);
682
683
_add_row_button(extra_row, PRESET_LEFT_WIDE, TTRC("Left Wide"));
684
_add_row_button(extra_row, PRESET_VCENTER_WIDE, TTRC("VCenter Wide"));
685
_add_row_button(extra_row, PRESET_RIGHT_WIDE, TTRC("Right Wide"));
686
_add_separator(extra_row, memnew(VSeparator));
687
_add_row_button(extra_row, PRESET_FULL_RECT, TTRC("Full Rect"));
688
}
689
690
void SizeFlagPresetPicker::_preset_button_pressed(const int p_preset) {
691
int flags = (SizeFlags)p_preset;
692
if (expand_button->is_pressed()) {
693
flags |= SIZE_EXPAND;
694
}
695
696
emit_signal("size_flags_selected", flags);
697
}
698
699
void SizeFlagPresetPicker::_expand_button_pressed() {
700
emit_signal("expand_flag_toggled", expand_button->is_pressed());
701
}
702
703
void SizeFlagPresetPicker::set_allowed_flags(Vector<SizeFlags> &p_flags) {
704
preset_buttons[SIZE_SHRINK_BEGIN]->set_disabled(!p_flags.has(SIZE_SHRINK_BEGIN));
705
preset_buttons[SIZE_SHRINK_CENTER]->set_disabled(!p_flags.has(SIZE_SHRINK_CENTER));
706
preset_buttons[SIZE_SHRINK_END]->set_disabled(!p_flags.has(SIZE_SHRINK_END));
707
preset_buttons[SIZE_FILL]->set_disabled(!p_flags.has(SIZE_FILL));
708
709
expand_button->set_disabled(!p_flags.has(SIZE_EXPAND));
710
if (p_flags.has(SIZE_EXPAND)) {
711
expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags."));
712
} else {
713
expand_button->set_pressed(false);
714
expand_button->set_tooltip_text(TTR("Some parents of the selected nodes do not support the Expand flag."));
715
}
716
}
717
718
void SizeFlagPresetPicker::set_expand_flag(bool p_expand) {
719
expand_button->set_pressed(p_expand);
720
}
721
722
void SizeFlagPresetPicker::_notification(int p_notification) {
723
switch (p_notification) {
724
case NOTIFICATION_THEME_CHANGED: {
725
if (vertical) {
726
preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop")));
727
preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
728
preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom")));
729
730
preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide")));
731
} else {
732
preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft")));
733
preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter")));
734
preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight")));
735
736
preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide")));
737
}
738
} break;
739
}
740
}
741
742
void SizeFlagPresetPicker::_bind_methods() {
743
ADD_SIGNAL(MethodInfo("size_flags_selected", PropertyInfo(Variant::INT, "size_flags")));
744
ADD_SIGNAL(MethodInfo("expand_flag_toggled", PropertyInfo(Variant::BOOL, "expand_flag")));
745
}
746
747
SizeFlagPresetPicker::SizeFlagPresetPicker(bool p_vertical) {
748
vertical = p_vertical;
749
750
VBoxContainer *main_vb = memnew(VBoxContainer);
751
add_child(main_vb);
752
753
HBoxContainer *main_row = memnew(HBoxContainer);
754
main_row->set_alignment(BoxContainer::ALIGNMENT_CENTER);
755
main_row->add_theme_constant_override("separation", grid_separation);
756
main_vb->add_child(main_row);
757
758
_add_row_button(main_row, SIZE_SHRINK_BEGIN, TTR("Shrink Begin"));
759
_add_row_button(main_row, SIZE_SHRINK_CENTER, TTR("Shrink Center"));
760
_add_row_button(main_row, SIZE_SHRINK_END, TTR("Shrink End"));
761
_add_separator(main_row, memnew(VSeparator));
762
_add_row_button(main_row, SIZE_FILL, TTR("Fill"));
763
764
expand_button = memnew(CheckButton);
765
expand_button->set_flat(true);
766
expand_button->set_text(TTR("Expand"));
767
expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags."));
768
expand_button->connect(SceneStringName(pressed), callable_mp(this, &SizeFlagPresetPicker::_expand_button_pressed));
769
main_vb->add_child(expand_button);
770
}
771
772
// Toolbar.
773
774
void ControlEditorToolbar::_anchors_preset_selected(int p_preset) {
775
LayoutPreset preset = (LayoutPreset)p_preset;
776
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
777
778
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
779
undo_redo->create_action(TTR("Change Anchors, Offsets, Grow Direction"));
780
781
for (Node *E : selection) {
782
Control *control = Object::cast_to<Control>(E);
783
if (control) {
784
undo_redo->add_do_property(control, "layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS);
785
undo_redo->add_do_property(control, "anchors_preset", preset);
786
undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
787
}
788
}
789
790
undo_redo->commit_action();
791
792
anchors_mode = false;
793
anchor_mode_button->set_pressed(anchors_mode);
794
}
795
796
void ControlEditorToolbar::_anchors_to_current_ratio() {
797
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
798
799
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
800
undo_redo->create_action(TTR("Change Anchors, Offsets (Keep Ratio)"));
801
802
for (Node *E : selection) {
803
Control *control = Object::cast_to<Control>(E);
804
if (control) {
805
Point2 top_left_anchor = _position_to_anchor(control, Point2());
806
Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size());
807
undo_redo->add_do_method(control, "set_anchor", SIDE_LEFT, top_left_anchor.x, false, true);
808
undo_redo->add_do_method(control, "set_anchor", SIDE_RIGHT, bottom_right_anchor.x, false, true);
809
undo_redo->add_do_method(control, "set_anchor", SIDE_TOP, top_left_anchor.y, false, true);
810
undo_redo->add_do_method(control, "set_anchor", SIDE_BOTTOM, bottom_right_anchor.y, false, true);
811
undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true);
812
813
const bool use_anchors = control->get_meta("_edit_use_anchors_", false);
814
undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
815
if (use_anchors) {
816
undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", true);
817
} else {
818
undo_redo->add_undo_method(control, "remove_meta", "_edit_use_anchors_");
819
}
820
821
anchors_mode = true;
822
anchor_mode_button->set_pressed(anchors_mode);
823
}
824
}
825
826
undo_redo->commit_action();
827
}
828
829
void ControlEditorToolbar::_anchor_mode_toggled(bool p_status) {
830
List<Control *> selection = _get_edited_controls();
831
for (Control *E : selection) {
832
if (Object::cast_to<Container>(E->get_parent())) {
833
continue;
834
}
835
836
if (p_status) {
837
E->set_meta("_edit_use_anchors_", true);
838
} else {
839
E->remove_meta("_edit_use_anchors_");
840
}
841
}
842
843
anchors_mode = p_status;
844
CanvasItemEditor::get_singleton()->update_viewport();
845
}
846
847
void ControlEditorToolbar::_container_flags_selected(int p_flags, bool p_vertical) {
848
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
849
850
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
851
if (p_vertical) {
852
undo_redo->create_action(TTR("Change Vertical Size Flags"));
853
} else {
854
undo_redo->create_action(TTR("Change Horizontal Size Flags"));
855
}
856
857
for (Node *E : selection) {
858
Control *control = Object::cast_to<Control>(E);
859
if (control) {
860
int old_flags = p_vertical ? control->get_v_size_flags() : control->get_h_size_flags();
861
if (p_vertical) {
862
undo_redo->add_do_method(control, "set_v_size_flags", p_flags);
863
undo_redo->add_undo_method(control, "set_v_size_flags", old_flags);
864
} else {
865
undo_redo->add_do_method(control, "set_h_size_flags", p_flags);
866
undo_redo->add_undo_method(control, "set_h_size_flags", old_flags);
867
}
868
}
869
}
870
871
undo_redo->commit_action();
872
}
873
874
void ControlEditorToolbar::_expand_flag_toggled(bool p_expand, bool p_vertical) {
875
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
876
877
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
878
if (p_vertical) {
879
undo_redo->create_action(TTR("Change Vertical Expand Flag"));
880
} else {
881
undo_redo->create_action(TTR("Change Horizontal Expand Flag"));
882
}
883
884
for (Node *E : selection) {
885
Control *control = Object::cast_to<Control>(E);
886
if (control) {
887
int old_flags = p_vertical ? control->get_v_size_flags() : control->get_h_size_flags();
888
int new_flags = old_flags;
889
890
if (p_expand) {
891
new_flags |= Control::SIZE_EXPAND;
892
} else {
893
new_flags &= ~Control::SIZE_EXPAND;
894
}
895
896
if (p_vertical) {
897
undo_redo->add_do_method(control, "set_v_size_flags", new_flags);
898
undo_redo->add_undo_method(control, "set_v_size_flags", old_flags);
899
} else {
900
undo_redo->add_do_method(control, "set_h_size_flags", new_flags);
901
undo_redo->add_undo_method(control, "set_h_size_flags", old_flags);
902
}
903
}
904
}
905
906
undo_redo->commit_action();
907
}
908
909
Vector2 ControlEditorToolbar::_position_to_anchor(const Control *p_control, Vector2 position) {
910
ERR_FAIL_NULL_V(p_control, Vector2());
911
912
Rect2 parent_rect = p_control->get_parent_anchorable_rect();
913
914
Vector2 output;
915
if (p_control->is_layout_rtl()) {
916
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;
917
} else {
918
output.x = (parent_rect.size.x == 0) ? 0.0 : (p_control->get_transform().xform(position).x - parent_rect.position.x) / parent_rect.size.x;
919
}
920
output.y = (parent_rect.size.y == 0) ? 0.0 : (p_control->get_transform().xform(position).y - parent_rect.position.y) / parent_rect.size.y;
921
return output;
922
}
923
924
bool ControlEditorToolbar::_is_node_locked(const Node *p_node) {
925
return p_node->get_meta("_edit_lock_", false);
926
}
927
928
List<Control *> ControlEditorToolbar::_get_edited_controls() {
929
List<Control *> selection;
930
for (const KeyValue<ObjectID, Object *> &E : editor_selection->get_selection()) {
931
Control *control = ObjectDB::get_instance<Control>(E.key);
932
if (control && control->is_visible_in_tree() && control->get_viewport() == EditorNode::get_singleton()->get_scene_root() && !_is_node_locked(control)) {
933
selection.push_back(control);
934
}
935
}
936
937
return selection;
938
}
939
940
void ControlEditorToolbar::_selection_changed() {
941
// Update toolbar visibility.
942
bool has_controls = false;
943
bool has_control_parents = false;
944
bool has_container_parents = false;
945
946
// Also update which size flags can be configured for the selected nodes.
947
Vector<SizeFlags> allowed_h_flags = {
948
SIZE_SHRINK_BEGIN,
949
SIZE_SHRINK_CENTER,
950
SIZE_SHRINK_END,
951
SIZE_FILL,
952
SIZE_EXPAND,
953
};
954
Vector<SizeFlags> allowed_v_flags = {
955
SIZE_SHRINK_BEGIN,
956
SIZE_SHRINK_CENTER,
957
SIZE_SHRINK_END,
958
SIZE_FILL,
959
SIZE_EXPAND,
960
};
961
962
for (const KeyValue<ObjectID, Object *> &E : editor_selection->get_selection()) {
963
Control *control = ObjectDB::get_instance<Control>(E.key);
964
if (!control) {
965
continue;
966
}
967
has_controls = true;
968
969
if (Object::cast_to<Control>(control->get_parent())) {
970
has_control_parents = true;
971
}
972
if (Object::cast_to<Container>(control->get_parent())) {
973
has_container_parents = true;
974
975
Container *parent_container = Object::cast_to<Container>(control->get_parent());
976
977
Vector<int> container_h_flags = parent_container->get_allowed_size_flags_horizontal();
978
Vector<SizeFlags> tmp_flags = allowed_h_flags.duplicate();
979
for (int i = 0; i < allowed_h_flags.size(); i++) {
980
if (!container_h_flags.has((int)allowed_h_flags[i])) {
981
tmp_flags.erase(allowed_h_flags[i]);
982
}
983
}
984
allowed_h_flags = tmp_flags;
985
986
Vector<int> container_v_flags = parent_container->get_allowed_size_flags_vertical();
987
tmp_flags = allowed_v_flags.duplicate();
988
for (int i = 0; i < allowed_v_flags.size(); i++) {
989
if (!container_v_flags.has((int)allowed_v_flags[i])) {
990
tmp_flags.erase(allowed_v_flags[i]);
991
}
992
}
993
allowed_v_flags = tmp_flags;
994
}
995
}
996
997
// Set general toolbar visibility.
998
set_visible(has_controls);
999
1000
// Set anchor tools visibility.
1001
if (has_controls && (!has_control_parents || !has_container_parents)) {
1002
anchors_button->set_visible(true);
1003
anchor_mode_button->set_visible(true);
1004
1005
// Update anchor mode.
1006
int nb_valid_controls = 0;
1007
int nb_anchors_mode = 0;
1008
1009
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
1010
for (Node *E : selection) {
1011
Control *control = Object::cast_to<Control>(E);
1012
if (!control) {
1013
continue;
1014
}
1015
if (Object::cast_to<Container>(control->get_parent())) {
1016
continue;
1017
}
1018
1019
nb_valid_controls++;
1020
if (control->get_meta("_edit_use_anchors_", false)) {
1021
nb_anchors_mode++;
1022
}
1023
}
1024
1025
anchors_mode = (nb_valid_controls == nb_anchors_mode);
1026
anchor_mode_button->set_pressed(anchors_mode);
1027
} else {
1028
anchors_button->set_visible(false);
1029
anchor_mode_button->set_visible(false);
1030
anchor_mode_button->set_pressed(false);
1031
}
1032
1033
// Set container tools visibility.
1034
if (has_controls && (!has_control_parents || has_container_parents)) {
1035
containers_button->set_visible(true);
1036
1037
// Update allowed size flags.
1038
if (has_container_parents) {
1039
container_h_picker->set_allowed_flags(allowed_h_flags);
1040
container_v_picker->set_allowed_flags(allowed_v_flags);
1041
} else {
1042
Vector<SizeFlags> allowed_all_flags = {
1043
SIZE_SHRINK_BEGIN,
1044
SIZE_SHRINK_CENTER,
1045
SIZE_SHRINK_END,
1046
SIZE_FILL,
1047
SIZE_EXPAND,
1048
};
1049
1050
container_h_picker->set_allowed_flags(allowed_all_flags);
1051
container_v_picker->set_allowed_flags(allowed_all_flags);
1052
}
1053
1054
// Update expand toggles.
1055
int nb_valid_controls = 0;
1056
int nb_h_expand = 0;
1057
int nb_v_expand = 0;
1058
1059
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
1060
for (Node *E : selection) {
1061
Control *control = Object::cast_to<Control>(E);
1062
if (!control) {
1063
continue;
1064
}
1065
1066
nb_valid_controls++;
1067
if (control->get_h_size_flags() & Control::SIZE_EXPAND) {
1068
nb_h_expand++;
1069
}
1070
if (control->get_v_size_flags() & Control::SIZE_EXPAND) {
1071
nb_v_expand++;
1072
}
1073
}
1074
1075
container_h_picker->set_expand_flag(nb_valid_controls == nb_h_expand);
1076
container_v_picker->set_expand_flag(nb_valid_controls == nb_v_expand);
1077
} else {
1078
containers_button->set_visible(false);
1079
}
1080
}
1081
1082
void ControlEditorToolbar::_notification(int p_what) {
1083
switch (p_what) {
1084
case NOTIFICATION_THEME_CHANGED: {
1085
anchors_button->set_button_icon(get_editor_theme_icon(SNAME("ControlLayout")));
1086
anchor_mode_button->set_button_icon(get_editor_theme_icon(SNAME("Anchor")));
1087
containers_button->set_button_icon(get_editor_theme_icon(SNAME("ContainerLayout")));
1088
} break;
1089
}
1090
}
1091
1092
ControlEditorToolbar::ControlEditorToolbar() {
1093
// Anchor and offset tools.
1094
anchors_button = memnew(ControlEditorPopupButton);
1095
anchors_button->set_tooltip_text(TTR("Presets for the anchor and offset values of a Control node."));
1096
add_child(anchors_button);
1097
1098
Label *anchors_label = memnew(Label);
1099
anchors_label->set_text(TTR("Anchor preset"));
1100
anchors_button->get_popup_hbox()->add_child(anchors_label);
1101
AnchorPresetPicker *anchors_picker = memnew(AnchorPresetPicker);
1102
anchors_picker->set_h_size_flags(SIZE_SHRINK_CENTER);
1103
anchors_button->get_popup_hbox()->add_child(anchors_picker);
1104
anchors_picker->connect("anchors_preset_selected", callable_mp(this, &ControlEditorToolbar::_anchors_preset_selected));
1105
1106
anchors_button->get_popup_hbox()->add_child(memnew(HSeparator));
1107
1108
Button *keep_ratio_button = memnew(Button);
1109
keep_ratio_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
1110
keep_ratio_button->set_text(TTR("Set to Current Ratio"));
1111
keep_ratio_button->set_tooltip_text(TTR("Adjust anchors and offsets to match the current rect size."));
1112
anchors_button->get_popup_hbox()->add_child(keep_ratio_button);
1113
keep_ratio_button->connect(SceneStringName(pressed), callable_mp(this, &ControlEditorToolbar::_anchors_to_current_ratio));
1114
1115
anchor_mode_button = memnew(Button);
1116
anchor_mode_button->set_theme_type_variation(SceneStringName(FlatButton));
1117
anchor_mode_button->set_toggle_mode(true);
1118
anchor_mode_button->set_tooltip_text(TTR("When active, moving Control nodes changes their anchors instead of their offsets."));
1119
anchor_mode_button->set_accessibility_name(TTRC("Change Anchors"));
1120
add_child(anchor_mode_button);
1121
anchor_mode_button->connect(SceneStringName(toggled), callable_mp(this, &ControlEditorToolbar::_anchor_mode_toggled));
1122
1123
// Container tools.
1124
containers_button = memnew(ControlEditorPopupButton);
1125
containers_button->set_tooltip_text(TTR("Sizing settings for children of a Container node."));
1126
add_child(containers_button);
1127
1128
Label *container_h_label = memnew(Label);
1129
container_h_label->set_text(TTR("Horizontal alignment"));
1130
containers_button->get_popup_hbox()->add_child(container_h_label);
1131
container_h_picker = memnew(SizeFlagPresetPicker(false));
1132
containers_button->get_popup_hbox()->add_child(container_h_picker);
1133
container_h_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(false));
1134
container_h_picker->connect("expand_flag_toggled", callable_mp(this, &ControlEditorToolbar::_expand_flag_toggled).bind(false));
1135
1136
containers_button->get_popup_hbox()->add_child(memnew(HSeparator));
1137
1138
Label *container_v_label = memnew(Label);
1139
container_v_label->set_text(TTR("Vertical alignment"));
1140
containers_button->get_popup_hbox()->add_child(container_v_label);
1141
container_v_picker = memnew(SizeFlagPresetPicker(true));
1142
containers_button->get_popup_hbox()->add_child(container_v_picker);
1143
container_v_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(true));
1144
container_v_picker->connect("expand_flag_toggled", callable_mp(this, &ControlEditorToolbar::_expand_flag_toggled).bind(true));
1145
1146
// Editor connections.
1147
editor_selection = EditorNode::get_singleton()->get_editor_selection();
1148
editor_selection->add_editor_plugin(this);
1149
editor_selection->connect("selection_changed", callable_mp(this, &ControlEditorToolbar::_selection_changed));
1150
1151
singleton = this;
1152
}
1153
1154
ControlEditorToolbar *ControlEditorToolbar::singleton = nullptr;
1155
1156
// Editor plugin.
1157
1158
ControlEditorPlugin::ControlEditorPlugin() {
1159
toolbar = memnew(ControlEditorToolbar);
1160
toolbar->hide();
1161
add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
1162
1163
Ref<EditorInspectorPluginControl> plugin;
1164
plugin.instantiate();
1165
add_inspector_plugin(plugin);
1166
}
1167
1168