Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/inspector/editor_properties.cpp
9903 views
1
/**************************************************************************/
2
/* editor_properties.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 "editor_properties.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/input/input_map.h"
35
#include "editor/docks/inspector_dock.h"
36
#include "editor/docks/scene_tree_dock.h"
37
#include "editor/editor_node.h"
38
#include "editor/editor_string_names.h"
39
#include "editor/gui/create_dialog.h"
40
#include "editor/gui/editor_file_dialog.h"
41
#include "editor/gui/editor_spin_slider.h"
42
#include "editor/gui/editor_variant_type_selectors.h"
43
#include "editor/inspector/editor_properties_array_dict.h"
44
#include "editor/inspector/editor_properties_vector.h"
45
#include "editor/inspector/editor_resource_picker.h"
46
#include "editor/inspector/property_selector.h"
47
#include "editor/scene/scene_tree_editor.h"
48
#include "editor/script/script_editor_plugin.h"
49
#include "editor/settings/editor_settings.h"
50
#include "editor/settings/project_settings_editor.h"
51
#include "editor/themes/editor_scale.h"
52
#include "scene/2d/gpu_particles_2d.h"
53
#include "scene/3d/fog_volume.h"
54
#include "scene/3d/gpu_particles_3d.h"
55
#include "scene/gui/color_picker.h"
56
#include "scene/gui/grid_container.h"
57
#include "scene/main/window.h"
58
#include "scene/resources/font.h"
59
#include "scene/resources/mesh.h"
60
#include "scene/resources/visual_shader_nodes.h"
61
62
///////////////////// NIL /////////////////////////
63
64
void EditorPropertyNil::update_property() {
65
}
66
67
EditorPropertyNil::EditorPropertyNil() {
68
Label *prop_label = memnew(Label);
69
prop_label->set_text("<null>");
70
add_child(prop_label);
71
}
72
73
//////////////////// VARIANT ///////////////////////
74
75
void EditorPropertyVariant::_change_type(int p_to_type) {
76
new_type = Variant::Type(p_to_type);
77
78
Variant zero;
79
Callable::CallError ce;
80
Variant::construct(new_type, zero, nullptr, 0, ce);
81
emit_changed(get_edited_property(), zero);
82
}
83
84
void EditorPropertyVariant::_popup_edit_menu() {
85
if (change_type == nullptr) {
86
change_type = memnew(EditorVariantTypePopupMenu(false));
87
change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type));
88
content->add_child(change_type);
89
}
90
91
Rect2 rect = edit_button->get_screen_rect();
92
change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));
93
change_type->popup();
94
}
95
96
void EditorPropertyVariant::_set_read_only(bool p_read_only) {
97
edit_button->set_disabled(p_read_only);
98
if (sub_property) {
99
sub_property->set_read_only(p_read_only);
100
}
101
}
102
103
void EditorPropertyVariant::_notification(int p_what) {
104
if (p_what == NOTIFICATION_THEME_CHANGED) {
105
edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
106
}
107
}
108
109
void EditorPropertyVariant::update_property() {
110
const Variant &value = get_edited_property_value();
111
if (new_type == Variant::VARIANT_MAX) {
112
new_type = value.get_type();
113
}
114
115
if (new_type != current_type) {
116
current_type = new_type;
117
118
if (sub_property) {
119
memdelete(sub_property);
120
sub_property = nullptr;
121
}
122
123
if (current_type == Variant::OBJECT) {
124
sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource", PROPERTY_USAGE_NONE);
125
} else {
126
sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE);
127
}
128
ERR_FAIL_NULL(sub_property);
129
130
sub_property->set_object_and_property(get_edited_object(), get_edited_property());
131
sub_property->set_name_split_ratio(0);
132
sub_property->set_selectable(false);
133
sub_property->set_use_folding(is_using_folding());
134
sub_property->set_read_only(is_read_only());
135
sub_property->set_h_size_flags(SIZE_EXPAND_FILL);
136
sub_property->connect(SNAME("property_changed"), callable_mp((EditorProperty *)this, &EditorProperty::emit_changed));
137
content->add_child(sub_property);
138
content->move_child(sub_property, 0);
139
sub_property->update_property();
140
} else if (sub_property) {
141
sub_property->update_property();
142
}
143
new_type = Variant::VARIANT_MAX;
144
}
145
146
EditorPropertyVariant::EditorPropertyVariant() {
147
content = memnew(HBoxContainer);
148
add_child(content);
149
150
edit_button = memnew(Button);
151
edit_button->set_flat(true);
152
edit_button->set_accessibility_name(TTRC("Edit"));
153
edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVariant::_popup_edit_menu));
154
content->add_child(edit_button);
155
}
156
157
///////////////////// TEXT /////////////////////////
158
159
void EditorPropertyText::_set_read_only(bool p_read_only) {
160
text->set_editable(!p_read_only);
161
}
162
163
void EditorPropertyText::_text_submitted(const String &p_string) {
164
if (updating) {
165
return;
166
}
167
168
if (text->has_focus()) {
169
_text_changed(p_string);
170
}
171
}
172
173
void EditorPropertyText::_text_changed(const String &p_string) {
174
if (updating) {
175
return;
176
}
177
178
// Set tooltip so that the full text is displayed in a tooltip if hovered.
179
// This is useful when using a narrow inspector, as the text can be trimmed otherwise.
180
text->set_tooltip_text(get_tooltip_string(text->get_text()));
181
182
if (string_name) {
183
emit_changed(get_edited_property(), StringName(p_string));
184
} else {
185
emit_changed(get_edited_property(), p_string);
186
}
187
}
188
189
void EditorPropertyText::update_property() {
190
String s = get_edited_property_value();
191
updating = true;
192
if (text->get_text() != s) {
193
int caret = text->get_caret_column();
194
text->set_text(s);
195
text->set_tooltip_text(get_tooltip_string(s));
196
text->set_caret_column(caret);
197
}
198
text->set_editable(!is_read_only());
199
updating = false;
200
}
201
202
void EditorPropertyText::set_string_name(bool p_enabled) {
203
string_name = p_enabled;
204
if (p_enabled) {
205
Label *prefix = memnew(Label("&"));
206
prefix->set_tooltip_text("StringName");
207
prefix->set_mouse_filter(MOUSE_FILTER_STOP);
208
text->get_parent()->add_child(prefix);
209
text->get_parent()->move_child(prefix, 0);
210
}
211
}
212
213
void EditorPropertyText::set_secret(bool p_enabled) {
214
text->set_secret(p_enabled);
215
}
216
217
void EditorPropertyText::set_placeholder(const String &p_string) {
218
text->set_placeholder(p_string);
219
}
220
221
EditorPropertyText::EditorPropertyText() {
222
HBoxContainer *hb = memnew(HBoxContainer);
223
add_child(hb);
224
225
text = memnew(LineEdit);
226
text->set_h_size_flags(SIZE_EXPAND_FILL);
227
text->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Prevents translating placeholder.
228
hb->add_child(text);
229
add_focusable(text);
230
text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyText::_text_changed));
231
text->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyText::_text_submitted));
232
}
233
234
///////////////////// MULTILINE TEXT /////////////////////////
235
236
void EditorPropertyMultilineText::_set_read_only(bool p_read_only) {
237
text->set_editable(!p_read_only);
238
open_big_text->set_disabled(p_read_only);
239
}
240
241
void EditorPropertyMultilineText::_big_text_changed() {
242
text->set_text(big_text->get_text());
243
// Set tooltip so that the full text is displayed in a tooltip if hovered.
244
// This is useful when using a narrow inspector, as the text can be trimmed otherwise.
245
text->set_tooltip_text(get_tooltip_string(big_text->get_text()));
246
emit_changed(get_edited_property(), big_text->get_text(), "", true);
247
}
248
249
void EditorPropertyMultilineText::_text_changed() {
250
text->set_tooltip_text(get_tooltip_string(text->get_text()));
251
emit_changed(get_edited_property(), text->get_text(), "", true);
252
}
253
254
void EditorPropertyMultilineText::_open_big_text() {
255
if (!big_text_dialog) {
256
big_text = memnew(TextEdit);
257
if (expression) {
258
big_text->set_syntax_highlighter(text->get_syntax_highlighter());
259
big_text->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("expression"), EditorStringName(EditorFonts)));
260
big_text->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("expression_size"), EditorStringName(EditorFonts)));
261
}
262
big_text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_big_text_changed));
263
big_text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
264
big_text_dialog = memnew(AcceptDialog);
265
big_text_dialog->add_child(big_text);
266
big_text_dialog->set_title(TTR("Edit Text:"));
267
add_child(big_text_dialog);
268
}
269
270
big_text_dialog->popup_centered_clamped(Size2(1000, 900) * EDSCALE, 0.8);
271
big_text->set_text(text->get_text());
272
big_text->grab_focus();
273
}
274
275
void EditorPropertyMultilineText::update_property() {
276
String t = get_edited_property_value();
277
if (text->get_text() != t) {
278
text->set_text(t);
279
text->set_tooltip_text(get_tooltip_string(t));
280
if (big_text && big_text->is_visible_in_tree()) {
281
big_text->set_text(t);
282
}
283
}
284
}
285
286
void EditorPropertyMultilineText::_notification(int p_what) {
287
switch (p_what) {
288
case NOTIFICATION_THEME_CHANGED: {
289
Ref<Texture2D> df = get_editor_theme_icon(SNAME("DistractionFree"));
290
open_big_text->set_button_icon(df);
291
292
Ref<Font> font;
293
int font_size;
294
if (expression) {
295
font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));
296
font_size = get_theme_font_size(SNAME("expression_size"), EditorStringName(EditorFonts));
297
298
text->add_theme_font_override(SceneStringName(font), font);
299
text->add_theme_font_size_override(SceneStringName(font_size), font_size);
300
if (big_text) {
301
big_text->add_theme_font_override(SceneStringName(font), font);
302
big_text->add_theme_font_size_override(SceneStringName(font_size), font_size);
303
}
304
} else {
305
font = get_theme_font(SceneStringName(font), SNAME("TextEdit"));
306
font_size = get_theme_font_size(SceneStringName(font_size), SNAME("TextEdit"));
307
}
308
text->set_custom_minimum_size(Vector2(0, font->get_height(font_size) * 6));
309
} break;
310
}
311
}
312
313
EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
314
HBoxContainer *hb = memnew(HBoxContainer);
315
hb->add_theme_constant_override("separation", 0);
316
add_child(hb);
317
set_bottom_editor(hb);
318
text = memnew(TextEdit);
319
text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_text_changed));
320
text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
321
add_focusable(text);
322
hb->add_child(text);
323
text->set_h_size_flags(SIZE_EXPAND_FILL);
324
open_big_text = memnew(Button);
325
open_big_text->set_accessibility_name(TTRC("Open Text Edit Dialog"));
326
open_big_text->set_flat(true);
327
open_big_text->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyMultilineText::_open_big_text));
328
hb->add_child(open_big_text);
329
big_text_dialog = nullptr;
330
big_text = nullptr;
331
if (p_expression) {
332
expression = true;
333
Ref<EditorStandardSyntaxHighlighter> highlighter;
334
highlighter.instantiate();
335
text->set_syntax_highlighter(highlighter);
336
}
337
}
338
339
///////////////////// TEXT ENUM /////////////////////////
340
341
void EditorPropertyTextEnum::_set_read_only(bool p_read_only) {
342
option_button->set_disabled(p_read_only);
343
edit_button->set_disabled(p_read_only);
344
}
345
346
void EditorPropertyTextEnum::_emit_changed_value(const String &p_string) {
347
if (string_name) {
348
emit_changed(get_edited_property(), StringName(p_string));
349
} else {
350
emit_changed(get_edited_property(), p_string);
351
}
352
}
353
354
void EditorPropertyTextEnum::_option_selected(int p_which) {
355
_emit_changed_value(option_button->get_item_text(p_which));
356
}
357
358
void EditorPropertyTextEnum::_edit_custom_value() {
359
default_layout->hide();
360
edit_custom_layout->show();
361
custom_value_edit->grab_focus();
362
}
363
364
void EditorPropertyTextEnum::_custom_value_submitted(const String &p_value) {
365
edit_custom_layout->hide();
366
default_layout->show();
367
368
_emit_changed_value(p_value.strip_edges());
369
}
370
371
void EditorPropertyTextEnum::_custom_value_accepted() {
372
String new_value = custom_value_edit->get_text().strip_edges();
373
_custom_value_submitted(new_value);
374
}
375
376
void EditorPropertyTextEnum::_custom_value_canceled() {
377
custom_value_edit->set_text(get_edited_property_value());
378
379
edit_custom_layout->hide();
380
default_layout->show();
381
}
382
383
void EditorPropertyTextEnum::update_property() {
384
String current_value = get_edited_property_value();
385
int default_option = options.find(current_value);
386
387
// The list can change in the loose mode.
388
if (loose_mode) {
389
custom_value_edit->set_text(current_value);
390
option_button->clear();
391
392
// Manually entered value.
393
if (default_option < 0 && !current_value.is_empty()) {
394
option_button->add_item(current_value, options.size() + 1001);
395
option_button->select(0);
396
397
option_button->add_separator();
398
}
399
400
// Add an explicit empty value for clearing the property.
401
option_button->add_item("", options.size() + 1000);
402
403
for (int i = 0; i < options.size(); i++) {
404
option_button->add_item(options[i], i);
405
if (options[i] == current_value) {
406
option_button->select(option_button->get_item_count() - 1);
407
}
408
}
409
} else {
410
option_button->select(default_option);
411
if (default_option < 0) {
412
option_button->set_text(current_value);
413
}
414
}
415
}
416
417
void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_string_name, bool p_loose_mode) {
418
string_name = p_string_name;
419
loose_mode = p_loose_mode;
420
421
options.clear();
422
423
if (loose_mode) {
424
// Add an explicit empty value for clearing the property in the loose mode.
425
option_button->add_item("", options.size() + 1000);
426
}
427
428
for (int i = 0; i < p_options.size(); i++) {
429
options.append(p_options[i]);
430
option_button->add_item(p_options[i], i);
431
}
432
433
if (loose_mode) {
434
edit_button->show();
435
}
436
}
437
438
void EditorPropertyTextEnum::_notification(int p_what) {
439
switch (p_what) {
440
case NOTIFICATION_THEME_CHANGED: {
441
edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
442
accept_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck")));
443
cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail")));
444
} break;
445
}
446
}
447
448
EditorPropertyTextEnum::EditorPropertyTextEnum() {
449
HBoxContainer *hb = memnew(HBoxContainer);
450
add_child(hb);
451
452
default_layout = memnew(HBoxContainer);
453
default_layout->set_h_size_flags(SIZE_EXPAND_FILL);
454
hb->add_child(default_layout);
455
456
edit_custom_layout = memnew(HBoxContainer);
457
edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);
458
edit_custom_layout->hide();
459
hb->add_child(edit_custom_layout);
460
461
option_button = memnew(OptionButton);
462
option_button->set_accessibility_name(TTRC("Enum Options"));
463
option_button->set_h_size_flags(SIZE_EXPAND_FILL);
464
option_button->set_clip_text(true);
465
option_button->set_flat(true);
466
option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
467
default_layout->add_child(option_button);
468
option_button->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyTextEnum::_option_selected));
469
470
edit_button = memnew(Button);
471
edit_button->set_accessibility_name(TTRC("Edit"));
472
edit_button->set_flat(true);
473
edit_button->hide();
474
default_layout->add_child(edit_button);
475
edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_edit_custom_value));
476
477
custom_value_edit = memnew(LineEdit);
478
custom_value_edit->set_accessibility_name(TTRC("Custom Value"));
479
custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL);
480
edit_custom_layout->add_child(custom_value_edit);
481
custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));
482
483
accept_button = memnew(Button);
484
accept_button->set_accessibility_name(TTRC("Accept Custom Value Edit"));
485
accept_button->set_flat(true);
486
edit_custom_layout->add_child(accept_button);
487
accept_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_accepted));
488
489
cancel_button = memnew(Button);
490
cancel_button->set_accessibility_name(TTRC("Cancel Custom Value Edit"));
491
cancel_button->set_flat(true);
492
edit_custom_layout->add_child(cancel_button);
493
cancel_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled));
494
495
add_focusable(option_button);
496
add_focusable(edit_button);
497
add_focusable(custom_value_edit);
498
add_focusable(accept_button);
499
add_focusable(cancel_button);
500
}
501
502
//////////////////// LOCALE ////////////////////////
503
504
void EditorPropertyLocale::_locale_selected(const String &p_locale) {
505
emit_changed(get_edited_property(), p_locale);
506
update_property();
507
}
508
509
void EditorPropertyLocale::_locale_pressed() {
510
if (!dialog) {
511
dialog = memnew(EditorLocaleDialog);
512
dialog->connect("locale_selected", callable_mp(this, &EditorPropertyLocale::_locale_selected));
513
add_child(dialog);
514
}
515
516
String locale_code = get_edited_property_value();
517
dialog->set_locale(locale_code);
518
dialog->popup_locale_dialog();
519
}
520
521
void EditorPropertyLocale::update_property() {
522
String locale_code = get_edited_property_value();
523
locale->set_text(locale_code);
524
locale->set_tooltip_text(locale_code);
525
}
526
527
void EditorPropertyLocale::setup(const String &p_hint_text) {
528
}
529
530
void EditorPropertyLocale::_notification(int p_what) {
531
switch (p_what) {
532
case NOTIFICATION_THEME_CHANGED: {
533
locale_edit->set_button_icon(get_editor_theme_icon(SNAME("Translation")));
534
} break;
535
}
536
}
537
538
void EditorPropertyLocale::_locale_focus_exited() {
539
_locale_selected(locale->get_text());
540
}
541
542
EditorPropertyLocale::EditorPropertyLocale() {
543
HBoxContainer *locale_hb = memnew(HBoxContainer);
544
add_child(locale_hb);
545
locale = memnew(LineEdit);
546
locale->set_accessibility_name(TTRC("Locale"));
547
locale_hb->add_child(locale);
548
locale->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyLocale::_locale_selected));
549
locale->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyLocale::_locale_focus_exited));
550
locale->set_h_size_flags(SIZE_EXPAND_FILL);
551
552
locale_edit = memnew(Button);
553
locale_edit->set_accessibility_name(TTRC("Edit"));
554
locale_edit->set_clip_text(true);
555
locale_hb->add_child(locale_edit);
556
add_focusable(locale);
557
dialog = nullptr;
558
locale_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocale::_locale_pressed));
559
}
560
561
///////////////////// PATH /////////////////////////
562
563
void EditorPropertyPath::_set_read_only(bool p_read_only) {
564
path->set_editable(!p_read_only);
565
path_edit->set_disabled(p_read_only);
566
}
567
568
void EditorPropertyPath::_path_selected(const String &p_path) {
569
String full_path = p_path;
570
571
if (enable_uid) {
572
const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
573
if (id != ResourceUID::INVALID_ID) {
574
full_path = ResourceUID::get_singleton()->id_to_text(id);
575
}
576
}
577
578
emit_changed(get_edited_property(), full_path);
579
update_property();
580
}
581
582
String EditorPropertyPath::_get_path_text(bool p_allow_uid) {
583
String full_path = get_edited_property_value();
584
if (!p_allow_uid && full_path.begins_with("uid://")) {
585
full_path = ResourceUID::uid_to_path(full_path);
586
}
587
588
return full_path;
589
}
590
591
void EditorPropertyPath::_path_pressed() {
592
if (!dialog) {
593
dialog = memnew(EditorFileDialog);
594
dialog->connect("file_selected", callable_mp(this, &EditorPropertyPath::_path_selected));
595
dialog->connect("dir_selected", callable_mp(this, &EditorPropertyPath::_path_selected));
596
add_child(dialog);
597
}
598
599
String full_path = _get_path_text();
600
601
dialog->clear_filters();
602
603
if (global) {
604
dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
605
} else {
606
dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
607
}
608
609
if (folder) {
610
dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
611
dialog->set_current_dir(full_path);
612
} else {
613
dialog->set_file_mode(save_mode ? EditorFileDialog::FILE_MODE_SAVE_FILE : EditorFileDialog::FILE_MODE_OPEN_FILE);
614
for (int i = 0; i < extensions.size(); i++) {
615
String e = extensions[i].strip_edges();
616
if (!e.is_empty()) {
617
dialog->add_filter(extensions[i].strip_edges());
618
}
619
}
620
dialog->set_current_path(full_path);
621
}
622
623
dialog->popup_file_dialog();
624
}
625
626
void EditorPropertyPath::update_property() {
627
String full_path = _get_path_text(display_uid);
628
path->set_text(full_path);
629
path->set_tooltip_text(full_path);
630
631
toggle_uid->set_visible(get_edited_property_value().operator String().begins_with("uid://"));
632
}
633
634
void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global, bool p_enable_uid) {
635
extensions = p_extensions;
636
folder = p_folder;
637
global = p_global;
638
enable_uid = p_enable_uid;
639
}
640
641
void EditorPropertyPath::set_save_mode() {
642
save_mode = true;
643
}
644
645
void EditorPropertyPath::_notification(int p_what) {
646
switch (p_what) {
647
case NOTIFICATION_THEME_CHANGED: {
648
if (folder) {
649
path_edit->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
650
} else {
651
path_edit->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse")));
652
}
653
_update_uid_icon();
654
} break;
655
}
656
}
657
658
void EditorPropertyPath::_path_focus_exited() {
659
_path_selected(path->get_text());
660
}
661
662
void EditorPropertyPath::_toggle_uid_display() {
663
display_uid = !display_uid;
664
_update_uid_icon();
665
update_property();
666
}
667
668
void EditorPropertyPath::_update_uid_icon() {
669
toggle_uid->set_button_icon(get_editor_theme_icon(display_uid ? SNAME("UID") : SNAME("NodePath")));
670
}
671
672
void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
673
const Dictionary drag_data = p_data;
674
if (!drag_data.has("type")) {
675
return;
676
}
677
if (String(drag_data["type"]) != "files") {
678
return;
679
}
680
const Vector<String> filesPaths = drag_data["files"];
681
if (filesPaths.is_empty()) {
682
return;
683
}
684
685
_path_selected(filesPaths[0]);
686
}
687
688
bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
689
const Dictionary drag_data = p_data;
690
if (!drag_data.has("type")) {
691
return false;
692
}
693
if (String(drag_data["type"]) != "files") {
694
return false;
695
}
696
const Vector<String> filesPaths = drag_data["files"];
697
if (filesPaths.is_empty()) {
698
return false;
699
}
700
701
for (const String &extension : extensions) {
702
if (filesPaths[0].ends_with(extension.substr(1))) {
703
return true;
704
}
705
}
706
707
return false;
708
}
709
710
EditorPropertyPath::EditorPropertyPath() {
711
HBoxContainer *path_hb = memnew(HBoxContainer);
712
add_child(path_hb);
713
path = memnew(LineEdit);
714
path->set_accessibility_name(TTRC("Path"));
715
SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath);
716
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
717
path_hb->add_child(path);
718
path->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyPath::_path_selected));
719
path->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyPath::_path_focus_exited));
720
path->set_h_size_flags(SIZE_EXPAND_FILL);
721
722
toggle_uid = memnew(Button);
723
toggle_uid->set_accessibility_name(TTRC("Toggle Display UID"));
724
toggle_uid->set_tooltip_text(TTRC("Toggles displaying between path and UID.\nThe UID is the actual value of this property."));
725
toggle_uid->set_pressed(false);
726
path_hb->add_child(toggle_uid);
727
add_focusable(toggle_uid);
728
toggle_uid->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_toggle_uid_display));
729
730
path_edit = memnew(Button);
731
path_edit->set_accessibility_name(TTRC("Edit"));
732
path_hb->add_child(path_edit);
733
add_focusable(path);
734
path_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_path_pressed));
735
}
736
737
///////////////////// CLASS NAME /////////////////////////
738
739
void EditorPropertyClassName::_set_read_only(bool p_read_only) {
740
property->set_disabled(p_read_only);
741
}
742
743
void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) {
744
base_type = p_base_type;
745
dialog->set_base_type(base_type);
746
selected_type = p_selected_type;
747
property->set_text(selected_type);
748
}
749
750
void EditorPropertyClassName::update_property() {
751
String s = get_edited_property_value();
752
property->set_text(s);
753
selected_type = s;
754
}
755
756
void EditorPropertyClassName::_property_selected() {
757
dialog->popup_create(true, true, get_edited_property_value(), get_edited_property());
758
}
759
760
void EditorPropertyClassName::_dialog_created() {
761
selected_type = dialog->get_selected_type();
762
emit_changed(get_edited_property(), selected_type);
763
update_property();
764
}
765
766
EditorPropertyClassName::EditorPropertyClassName() {
767
property = memnew(Button);
768
property->set_clip_text(true);
769
add_child(property);
770
add_focusable(property);
771
property->set_text(selected_type);
772
property->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyClassName::_property_selected));
773
dialog = memnew(CreateDialog);
774
dialog->set_base_type(base_type);
775
dialog->connect("create", callable_mp(this, &EditorPropertyClassName::_dialog_created));
776
add_child(dialog);
777
}
778
779
///////////////////// CHECK /////////////////////////
780
781
void EditorPropertyCheck::_set_read_only(bool p_read_only) {
782
checkbox->set_disabled(p_read_only);
783
}
784
785
void EditorPropertyCheck::_checkbox_pressed() {
786
emit_changed(get_edited_property(), checkbox->is_pressed());
787
}
788
789
void EditorPropertyCheck::update_property() {
790
bool c = get_edited_property_value();
791
checkbox->set_pressed(c);
792
checkbox->set_disabled(is_read_only());
793
}
794
795
EditorPropertyCheck::EditorPropertyCheck() {
796
checkbox = memnew(CheckBox);
797
checkbox->set_text(TTR("On"));
798
add_child(checkbox);
799
add_focusable(checkbox);
800
checkbox->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyCheck::_checkbox_pressed));
801
}
802
803
///////////////////// ENUM /////////////////////////
804
805
void EditorPropertyEnum::_set_read_only(bool p_read_only) {
806
options->set_disabled(p_read_only);
807
}
808
809
void EditorPropertyEnum::_option_selected(int p_which) {
810
int64_t val = options->get_item_metadata(p_which);
811
emit_changed(get_edited_property(), val);
812
}
813
814
void EditorPropertyEnum::update_property() {
815
Variant current = get_edited_property_value();
816
if (current.get_type() == Variant::NIL) {
817
options->select(-1);
818
options->set_text("<null>");
819
return;
820
}
821
822
int64_t which = current;
823
for (int i = 0; i < options->get_item_count(); i++) {
824
if (which == (int64_t)options->get_item_metadata(i)) {
825
options->select(i);
826
return;
827
}
828
}
829
options->select(-1);
830
options->set_text(itos(which));
831
}
832
833
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
834
options->clear();
835
HashMap<int64_t, Vector<String>> items;
836
int64_t current_val = 0;
837
for (const String &option : p_options) {
838
Vector<String> text_split = option.split(":");
839
if (text_split.size() != 1) {
840
current_val = text_split[1].to_int();
841
}
842
items[current_val].push_back(text_split[0]);
843
current_val += 1;
844
}
845
846
for (const KeyValue<int64_t, Vector<String>> &K : items) {
847
options->add_item(String(", ").join(K.value));
848
options->set_item_metadata(-1, K.key);
849
}
850
}
851
852
void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
853
options->set_clip_text(p_enable);
854
}
855
856
EditorPropertyEnum::EditorPropertyEnum() {
857
options = memnew(OptionButton);
858
options->set_clip_text(true);
859
options->set_flat(true);
860
options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
861
add_child(options);
862
add_focusable(options);
863
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyEnum::_option_selected));
864
}
865
866
///////////////////// FLAGS /////////////////////////
867
868
void EditorPropertyFlags::_set_read_only(bool p_read_only) {
869
for (CheckBox *check : flags) {
870
check->set_disabled(p_read_only);
871
}
872
}
873
874
void EditorPropertyFlags::_flag_toggled(int p_index) {
875
uint32_t value = get_edited_property_value();
876
if (flags[p_index]->is_pressed()) {
877
value |= flag_values[p_index];
878
} else {
879
value &= ~flag_values[p_index];
880
}
881
882
emit_changed(get_edited_property(), value);
883
}
884
885
void EditorPropertyFlags::update_property() {
886
uint32_t value = get_edited_property_value();
887
888
for (int i = 0; i < flags.size(); i++) {
889
flags[i]->set_pressed((value & flag_values[i]) == flag_values[i]);
890
}
891
}
892
893
void EditorPropertyFlags::setup(const Vector<String> &p_options) {
894
ERR_FAIL_COND(flags.size());
895
896
bool first = true;
897
uint32_t current_val;
898
for (int i = 0; i < p_options.size(); i++) {
899
// An empty option is not considered a "flag".
900
String option = p_options[i].strip_edges();
901
if (option.is_empty()) {
902
continue;
903
}
904
const int flag_index = flags.size(); // Index of the next element (added by the code below).
905
906
// Value for a flag can be explicitly overridden.
907
Vector<String> text_split = option.split(":");
908
if (text_split.size() != 1) {
909
current_val = text_split[1].to_int();
910
} else {
911
current_val = 1 << i;
912
}
913
flag_values.push_back(current_val);
914
915
// Create a CheckBox for the current flag.
916
CheckBox *cb = memnew(CheckBox);
917
cb->set_text(text_split[0]);
918
cb->set_clip_text(true);
919
cb->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(flag_index));
920
add_focusable(cb);
921
vbox->add_child(cb);
922
flags.push_back(cb);
923
924
// Can't use `i == 0` because we want to find the first none-empty option.
925
if (first) {
926
set_label_reference(cb);
927
first = false;
928
}
929
}
930
}
931
932
EditorPropertyFlags::EditorPropertyFlags() {
933
vbox = memnew(VBoxContainer);
934
vbox->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
935
add_child(vbox);
936
}
937
938
///////////////////// LAYERS /////////////////////////
939
940
void EditorPropertyLayersGrid::_rename_pressed(int p_menu) {
941
// Show rename popup for active layer.
942
if (renamed_layer_index == INT32_MAX) {
943
return;
944
}
945
String name = names[renamed_layer_index];
946
rename_dialog->set_title(vformat(TTR("Renaming layer %d:"), renamed_layer_index + 1));
947
rename_dialog_text->set_text(name);
948
rename_dialog_text->select(0, name.length());
949
rename_dialog->popup_centered(Size2(300, 80) * EDSCALE);
950
rename_dialog_text->grab_focus();
951
}
952
953
void EditorPropertyLayersGrid::_rename_operation_confirm() {
954
String new_name = rename_dialog_text->get_text().strip_edges();
955
if (new_name.length() == 0) {
956
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
957
return;
958
} else if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {
959
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
960
return;
961
}
962
names.set(renamed_layer_index, new_name);
963
tooltips.set(renamed_layer_index, new_name + "\n" + vformat(TTR("Bit %d, value %d"), renamed_layer_index, 1 << renamed_layer_index));
964
emit_signal(SNAME("rename_confirmed"), renamed_layer_index, new_name);
965
}
966
967
EditorPropertyLayersGrid::EditorPropertyLayersGrid() {
968
rename_dialog = memnew(ConfirmationDialog);
969
VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
970
rename_dialog->add_child(rename_dialog_vb);
971
rename_dialog_text = memnew(LineEdit);
972
rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);
973
rename_dialog->set_ok_button_text(TTR("Rename"));
974
add_child(rename_dialog);
975
rename_dialog->register_text_enter(rename_dialog_text);
976
rename_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorPropertyLayersGrid::_rename_operation_confirm));
977
layer_rename = memnew(PopupMenu);
978
layer_rename->add_item(TTR("Rename layer"), 0);
979
add_child(layer_rename);
980
layer_rename->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayersGrid::_rename_pressed));
981
}
982
983
Size2 EditorPropertyLayersGrid::get_grid_size() const {
984
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
985
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
986
return Vector2(0, font->get_height(font_size) * 3);
987
}
988
989
void EditorPropertyLayersGrid::set_read_only(bool p_read_only) {
990
read_only = p_read_only;
991
}
992
993
Size2 EditorPropertyLayersGrid::get_minimum_size() const {
994
Size2 min_size = get_grid_size();
995
996
// Add extra rows when expanded.
997
if (expanded) {
998
const int bsize = (min_size.height * 80 / 100) / 2;
999
for (int i = 0; i < expansion_rows; ++i) {
1000
min_size.y += 2 * (bsize + 1) + 3;
1001
}
1002
}
1003
1004
return min_size;
1005
}
1006
1007
String EditorPropertyLayersGrid::get_tooltip(const Point2 &p_pos) const {
1008
for (int i = 0; i < flag_rects.size(); i++) {
1009
if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) {
1010
return tooltips[i];
1011
}
1012
}
1013
return String();
1014
}
1015
1016
void EditorPropertyLayersGrid::_update_hovered(const Vector2 &p_position) {
1017
bool expand_was_hovered = expand_hovered;
1018
expand_hovered = expand_rect.has_point(p_position);
1019
if (expand_hovered != expand_was_hovered) {
1020
queue_redraw();
1021
}
1022
1023
if (!expand_hovered) {
1024
for (int i = 0; i < flag_rects.size(); i++) {
1025
if (flag_rects[i].has_point(p_position)) {
1026
// Used to highlight the hovered flag in the layers grid.
1027
hovered_index = i;
1028
queue_redraw();
1029
return;
1030
}
1031
}
1032
}
1033
1034
// Remove highlight when no square is hovered.
1035
if (hovered_index != INT32_MAX) {
1036
hovered_index = INT32_MAX;
1037
queue_redraw();
1038
}
1039
}
1040
1041
void EditorPropertyLayersGrid::_on_hover_exit() {
1042
if (expand_hovered) {
1043
expand_hovered = false;
1044
queue_redraw();
1045
}
1046
if (hovered_index != INT32_MAX) {
1047
hovered_index = INT32_MAX;
1048
queue_redraw();
1049
}
1050
}
1051
1052
void EditorPropertyLayersGrid::_update_flag(bool p_replace) {
1053
if (hovered_index != INT32_MAX) {
1054
// Toggle the flag.
1055
// We base our choice on the hovered flag, so that it always matches the hovered flag.
1056
if (p_replace) {
1057
// Replace all flags with the hovered flag ("solo mode"),
1058
// instead of toggling the hovered flags while preserving other flags' state.
1059
if (value == uint32_t(1 << hovered_index)) {
1060
// If the flag is already enabled, enable all other items and disable the current flag.
1061
// This allows for quicker toggling.
1062
value = INT32_MAX - (1 << hovered_index);
1063
} else {
1064
value = 1 << hovered_index;
1065
}
1066
} else {
1067
if (value & (1 << hovered_index)) {
1068
value &= ~(1 << hovered_index);
1069
} else {
1070
value |= (1 << hovered_index);
1071
}
1072
}
1073
1074
emit_signal(SNAME("flag_changed"), value);
1075
queue_redraw();
1076
} else if (expand_hovered) {
1077
expanded = !expanded;
1078
update_minimum_size();
1079
queue_redraw();
1080
}
1081
}
1082
1083
void EditorPropertyLayersGrid::gui_input(const Ref<InputEvent> &p_ev) {
1084
if (read_only) {
1085
return;
1086
}
1087
const Ref<InputEventMouseMotion> mm = p_ev;
1088
if (mm.is_valid()) {
1089
_update_hovered(mm->get_position());
1090
return;
1091
}
1092
1093
const Ref<InputEventMouseButton> mb = p_ev;
1094
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
1095
_update_hovered(mb->get_position());
1096
_update_flag(mb->is_command_or_control_pressed());
1097
}
1098
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
1099
if (hovered_index != INT32_MAX) {
1100
renamed_layer_index = hovered_index;
1101
layer_rename->set_position(get_screen_position() + mb->get_position());
1102
layer_rename->reset_size();
1103
layer_rename->popup();
1104
}
1105
}
1106
}
1107
1108
void EditorPropertyLayersGrid::_notification(int p_what) {
1109
switch (p_what) {
1110
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
1111
RID ae = get_accessibility_element();
1112
ERR_FAIL_COND(ae.is_null());
1113
1114
//TODO
1115
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
1116
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Layers grid property editor")));
1117
} break;
1118
1119
case NOTIFICATION_DRAW: {
1120
Size2 grid_size = get_grid_size();
1121
grid_size.x = get_size().x;
1122
1123
flag_rects.clear();
1124
1125
int prev_expansion_rows = expansion_rows;
1126
expansion_rows = 0;
1127
1128
const int bsize = (grid_size.height * 80 / 100) / 2;
1129
const int h = bsize * 2 + 1;
1130
1131
Color color = get_theme_color(read_only ? SNAME("highlight_disabled_color") : SNAME("highlight_color"), EditorStringName(Editor));
1132
1133
Color text_color = get_theme_color(read_only ? SNAME("font_disabled_color") : SceneStringName(font_color), EditorStringName(Editor));
1134
text_color.a *= 0.5;
1135
1136
Color text_color_on = get_theme_color(read_only ? SNAME("font_disabled_color") : SNAME("font_hover_color"), EditorStringName(Editor));
1137
text_color_on.a *= 0.7;
1138
1139
const int vofs = (grid_size.height - h) / 2;
1140
1141
uint32_t layer_index = 0;
1142
1143
Point2 arrow_pos;
1144
1145
Point2 block_ofs(4, vofs);
1146
1147
while (true) {
1148
Point2 ofs = block_ofs;
1149
1150
for (int i = 0; i < 2; i++) {
1151
for (int j = 0; j < layer_group_size; j++) {
1152
const bool on = value & (1 << layer_index);
1153
Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize));
1154
1155
color.a = on ? 0.6 : 0.2;
1156
if (layer_index == hovered_index) {
1157
// Add visual feedback when hovering a flag.
1158
color.a += 0.15;
1159
}
1160
1161
draw_rect(rect2, color);
1162
flag_rects.push_back(rect2);
1163
1164
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
1165
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1166
Vector2 offset;
1167
offset.y = rect2.size.y * 0.75;
1168
1169
draw_string(font, rect2.position + offset, itos(layer_index + 1), HORIZONTAL_ALIGNMENT_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);
1170
1171
ofs.x += bsize + 1;
1172
1173
++layer_index;
1174
}
1175
1176
ofs.x = block_ofs.x;
1177
ofs.y += bsize + 1;
1178
}
1179
1180
if (layer_index >= layer_count) {
1181
if (!flag_rects.is_empty() && (expansion_rows == 0)) {
1182
const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
1183
arrow_pos = last_rect.get_end();
1184
}
1185
break;
1186
}
1187
1188
int block_size_x = layer_group_size * (bsize + 1);
1189
block_ofs.x += block_size_x + 3;
1190
1191
if (block_ofs.x + block_size_x + 12 > grid_size.width) {
1192
// Keep last valid cell position for the expansion icon.
1193
if (!flag_rects.is_empty() && (expansion_rows == 0)) {
1194
const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
1195
arrow_pos = last_rect.get_end();
1196
}
1197
++expansion_rows;
1198
1199
if (expanded) {
1200
// Expand grid to next line.
1201
block_ofs.x = 4;
1202
block_ofs.y += 2 * (bsize + 1) + 3;
1203
} else {
1204
// Skip remaining blocks.
1205
break;
1206
}
1207
}
1208
}
1209
1210
if ((expansion_rows != prev_expansion_rows) && expanded) {
1211
update_minimum_size();
1212
}
1213
1214
if ((expansion_rows == 0) && (layer_index == layer_count)) {
1215
// Whole grid was drawn, no need for expansion icon.
1216
break;
1217
}
1218
1219
Ref<Texture2D> arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));
1220
ERR_FAIL_COND(arrow.is_null());
1221
1222
Color arrow_color = get_theme_color(SNAME("highlight_color"), EditorStringName(Editor));
1223
arrow_color.a = expand_hovered ? 1.0 : 0.6;
1224
1225
arrow_pos.x += 2.0;
1226
arrow_pos.y -= arrow->get_height();
1227
1228
Rect2 arrow_draw_rect(arrow_pos, arrow->get_size());
1229
expand_rect = arrow_draw_rect;
1230
if (expanded) {
1231
arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded.
1232
}
1233
1234
RID ci = get_canvas_item();
1235
arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color);
1236
1237
} break;
1238
1239
case NOTIFICATION_MOUSE_EXIT: {
1240
_on_hover_exit();
1241
} break;
1242
}
1243
}
1244
1245
void EditorPropertyLayersGrid::set_flag(uint32_t p_flag) {
1246
value = p_flag;
1247
queue_redraw();
1248
}
1249
1250
void EditorPropertyLayersGrid::_bind_methods() {
1251
ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));
1252
ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name")));
1253
}
1254
1255
void EditorPropertyLayers::_notification(int p_what) {
1256
switch (p_what) {
1257
case NOTIFICATION_THEME_CHANGED: {
1258
button->set_texture_normal(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
1259
button->set_texture_pressed(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
1260
button->set_texture_disabled(get_editor_theme_icon(SNAME("GuiTabMenu")));
1261
} break;
1262
}
1263
}
1264
1265
void EditorPropertyLayers::_set_read_only(bool p_read_only) {
1266
button->set_disabled(p_read_only);
1267
grid->set_read_only(p_read_only);
1268
}
1269
1270
void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {
1271
emit_changed(get_edited_property(), p_grid);
1272
}
1273
1274
void EditorPropertyLayers::update_property() {
1275
uint32_t value = get_edited_property_value();
1276
1277
grid->set_flag(value);
1278
}
1279
1280
void EditorPropertyLayers::setup(LayerType p_layer_type) {
1281
layer_type = p_layer_type;
1282
int layer_group_size = 0;
1283
int layer_count = 0;
1284
switch (p_layer_type) {
1285
case LAYER_RENDER_2D: {
1286
basename = "layer_names/2d_render";
1287
layer_group_size = 5;
1288
layer_count = 20;
1289
} break;
1290
1291
case LAYER_PHYSICS_2D: {
1292
basename = "layer_names/2d_physics";
1293
layer_group_size = 4;
1294
layer_count = 32;
1295
} break;
1296
1297
case LAYER_NAVIGATION_2D: {
1298
basename = "layer_names/2d_navigation";
1299
layer_group_size = 4;
1300
layer_count = 32;
1301
} break;
1302
1303
case LAYER_RENDER_3D: {
1304
basename = "layer_names/3d_render";
1305
layer_group_size = 5;
1306
layer_count = 20;
1307
} break;
1308
1309
case LAYER_PHYSICS_3D: {
1310
basename = "layer_names/3d_physics";
1311
layer_group_size = 4;
1312
layer_count = 32;
1313
} break;
1314
1315
case LAYER_NAVIGATION_3D: {
1316
basename = "layer_names/3d_navigation";
1317
layer_group_size = 4;
1318
layer_count = 32;
1319
} break;
1320
1321
case LAYER_AVOIDANCE: {
1322
basename = "layer_names/avoidance";
1323
layer_group_size = 4;
1324
layer_count = 32;
1325
} break;
1326
}
1327
1328
Vector<String> names;
1329
Vector<String> tooltips;
1330
for (int i = 0; i < layer_count; i++) {
1331
String name;
1332
1333
if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i + 1))) {
1334
name = GLOBAL_GET(basename + vformat("/layer_%d", i + 1));
1335
}
1336
1337
if (name.is_empty()) {
1338
name = vformat(TTR("Layer %d"), i + 1);
1339
}
1340
1341
names.push_back(name);
1342
tooltips.push_back(name + "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i));
1343
}
1344
1345
grid->names = names;
1346
grid->tooltips = tooltips;
1347
grid->layer_group_size = layer_group_size;
1348
grid->layer_count = layer_count;
1349
}
1350
1351
void EditorPropertyLayers::set_layer_name(int p_index, const String &p_name) {
1352
const String property_name = basename + vformat("/layer_%d", p_index + 1);
1353
if (ProjectSettings::get_singleton()->has_setting(property_name)) {
1354
ProjectSettings::get_singleton()->set(property_name, p_name);
1355
ProjectSettings::get_singleton()->save();
1356
}
1357
}
1358
1359
String EditorPropertyLayers::get_layer_name(int p_index) const {
1360
const String property_name = basename + vformat("/layer_%d", p_index + 1);
1361
if (ProjectSettings::get_singleton()->has_setting(property_name)) {
1362
return GLOBAL_GET(property_name);
1363
}
1364
return String();
1365
}
1366
1367
void EditorPropertyLayers::_button_pressed() {
1368
int layer_count = grid->layer_count;
1369
layers->clear();
1370
for (int i = 0; i < layer_count; i++) {
1371
const String name = get_layer_name(i);
1372
if (name.is_empty()) {
1373
continue;
1374
}
1375
layers->add_check_item(name, i);
1376
int idx = layers->get_item_index(i);
1377
layers->set_item_checked(idx, grid->value & (1 << i));
1378
}
1379
1380
if (layers->get_item_count() == 0) {
1381
layers->add_item(TTR("No Named Layers"));
1382
layers->set_item_disabled(0, true);
1383
}
1384
layers->add_separator();
1385
layers->add_icon_item(get_editor_theme_icon("Edit"), TTR("Edit Layer Names"), grid->layer_count);
1386
1387
Rect2 gp = button->get_screen_rect();
1388
layers->reset_size();
1389
Vector2 popup_pos = gp.position - Vector2(layers->get_contents_minimum_size().x, 0);
1390
layers->set_position(popup_pos);
1391
layers->popup();
1392
}
1393
1394
void EditorPropertyLayers::_menu_pressed(int p_menu) {
1395
if (uint32_t(p_menu) == grid->layer_count) {
1396
ProjectSettingsEditor::get_singleton()->popup_project_settings(true);
1397
ProjectSettingsEditor::get_singleton()->set_general_page(basename);
1398
} else {
1399
if (grid->value & (1 << p_menu)) {
1400
grid->value &= ~(1 << p_menu);
1401
} else {
1402
grid->value |= (1 << p_menu);
1403
}
1404
grid->queue_redraw();
1405
layers->set_item_checked(layers->get_item_index(p_menu), grid->value & (1 << p_menu));
1406
_grid_changed(grid->value);
1407
}
1408
}
1409
1410
void EditorPropertyLayers::_refresh_names() {
1411
setup(layer_type);
1412
}
1413
1414
EditorPropertyLayers::EditorPropertyLayers() {
1415
HBoxContainer *hb = memnew(HBoxContainer);
1416
hb->set_clip_contents(true);
1417
add_child(hb);
1418
grid = memnew(EditorPropertyLayersGrid);
1419
grid->connect("flag_changed", callable_mp(this, &EditorPropertyLayers::_grid_changed));
1420
grid->connect("rename_confirmed", callable_mp(this, &EditorPropertyLayers::set_layer_name));
1421
grid->set_h_size_flags(SIZE_EXPAND_FILL);
1422
hb->add_child(grid);
1423
1424
button = memnew(TextureButton);
1425
button->set_accessibility_name(TTRC("Layers"));
1426
button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
1427
button->set_toggle_mode(true);
1428
button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLayers::_button_pressed));
1429
hb->add_child(button);
1430
1431
set_bottom_editor(hb);
1432
1433
layers = memnew(PopupMenu);
1434
add_child(layers);
1435
layers->set_hide_on_checkable_item_selection(false);
1436
layers->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayers::_menu_pressed));
1437
layers->connect("popup_hide", callable_mp((BaseButton *)button, &BaseButton::set_pressed).bind(false));
1438
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names));
1439
}
1440
1441
///////////////////// INT /////////////////////////
1442
1443
void EditorPropertyInteger::_set_read_only(bool p_read_only) {
1444
spin->set_read_only(p_read_only);
1445
}
1446
1447
void EditorPropertyInteger::_value_changed(int64_t val) {
1448
emit_changed(get_edited_property(), val);
1449
}
1450
1451
void EditorPropertyInteger::update_property() {
1452
int64_t val = get_edited_property_display_value();
1453
spin->set_value_no_signal(val);
1454
#ifdef DEBUG_ENABLED
1455
// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.
1456
if (val != (int64_t)(double)(val)) {
1457
WARN_PRINT("Cannot reliably represent '" + itos(val) + "' in the inspector, value is too large.");
1458
}
1459
#endif
1460
}
1461
1462
void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
1463
spin->set_min(p_min);
1464
spin->set_max(p_max);
1465
spin->set_step(p_step);
1466
spin->set_hide_slider(p_hide_slider);
1467
spin->set_allow_greater(p_allow_greater);
1468
spin->set_allow_lesser(p_allow_lesser);
1469
spin->set_suffix(p_suffix);
1470
}
1471
1472
EditorPropertyInteger::EditorPropertyInteger() {
1473
spin = memnew(EditorSpinSlider);
1474
spin->set_flat(true);
1475
spin->set_editing_integer(true);
1476
add_child(spin);
1477
add_focusable(spin);
1478
spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyInteger::_value_changed));
1479
}
1480
1481
///////////////////// OBJECT ID /////////////////////////
1482
1483
void EditorPropertyObjectID::_set_read_only(bool p_read_only) {
1484
edit->set_disabled(p_read_only);
1485
}
1486
1487
void EditorPropertyObjectID::_edit_pressed() {
1488
emit_signal(SNAME("object_id_selected"), get_edited_property(), get_edited_property_value());
1489
}
1490
1491
void EditorPropertyObjectID::update_property() {
1492
String type = base_type;
1493
if (type.is_empty()) {
1494
type = "Object";
1495
}
1496
1497
ObjectID id = get_edited_property_value();
1498
if (id.is_valid()) {
1499
edit->set_text(type + " ID: " + uitos(id));
1500
edit->set_tooltip_text(type + " ID: " + uitos(id));
1501
edit->set_disabled(false);
1502
edit->set_button_icon(EditorNode::get_singleton()->get_class_icon(type));
1503
} else {
1504
edit->set_text(TTR("<empty>"));
1505
edit->set_tooltip_text("");
1506
edit->set_disabled(true);
1507
edit->set_button_icon(Ref<Texture2D>());
1508
}
1509
}
1510
1511
void EditorPropertyObjectID::setup(const String &p_base_type) {
1512
base_type = p_base_type;
1513
}
1514
1515
EditorPropertyObjectID::EditorPropertyObjectID() {
1516
edit = memnew(Button);
1517
edit->set_accessibility_name(TTRC("Edit"));
1518
add_child(edit);
1519
add_focusable(edit);
1520
edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
1521
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyObjectID::_edit_pressed));
1522
}
1523
1524
///////////////////// SIGNAL /////////////////////////
1525
1526
void EditorPropertySignal::_edit_pressed() {
1527
Signal signal = get_edited_property_value();
1528
emit_signal(SNAME("object_id_selected"), get_edited_property(), signal.get_object_id());
1529
}
1530
1531
void EditorPropertySignal::update_property() {
1532
String type = base_type;
1533
1534
Signal signal = get_edited_property_value();
1535
1536
edit->set_text("Signal: " + signal.get_name());
1537
edit->set_disabled(false);
1538
edit->set_button_icon(get_editor_theme_icon(SNAME("Signals")));
1539
}
1540
1541
EditorPropertySignal::EditorPropertySignal() {
1542
edit = memnew(Button);
1543
edit->set_accessibility_name(TTRC("Edit"));
1544
add_child(edit);
1545
add_focusable(edit);
1546
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySignal::_edit_pressed));
1547
}
1548
1549
///////////////////// CALLABLE /////////////////////////
1550
1551
void EditorPropertyCallable::update_property() {
1552
String type = base_type;
1553
1554
Callable callable = get_edited_property_value();
1555
1556
edit->set_text("Callable");
1557
edit->set_disabled(true);
1558
edit->set_button_icon(get_editor_theme_icon(SNAME("Callable")));
1559
}
1560
1561
EditorPropertyCallable::EditorPropertyCallable() {
1562
edit = memnew(Button);
1563
edit->set_accessibility_name(TTRC("Edit"));
1564
add_child(edit);
1565
add_focusable(edit);
1566
}
1567
1568
///////////////////// FLOAT /////////////////////////
1569
1570
void EditorPropertyFloat::_set_read_only(bool p_read_only) {
1571
spin->set_read_only(p_read_only);
1572
}
1573
1574
void EditorPropertyFloat::_value_changed(double val) {
1575
if (radians_as_degrees) {
1576
val = Math::deg_to_rad(val);
1577
}
1578
emit_changed(get_edited_property(), val);
1579
}
1580
1581
void EditorPropertyFloat::update_property() {
1582
double val = get_edited_property_value();
1583
if (radians_as_degrees) {
1584
val = Math::rad_to_deg(val);
1585
}
1586
spin->set_value_no_signal(val);
1587
}
1588
1589
void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) {
1590
radians_as_degrees = p_radians_as_degrees;
1591
spin->set_min(p_min);
1592
spin->set_max(p_max);
1593
spin->set_step(p_step);
1594
spin->set_hide_slider(p_hide_slider);
1595
spin->set_exp_ratio(p_exp_range);
1596
spin->set_allow_greater(p_greater);
1597
spin->set_allow_lesser(p_lesser);
1598
spin->set_suffix(p_suffix);
1599
}
1600
1601
EditorPropertyFloat::EditorPropertyFloat() {
1602
spin = memnew(EditorSpinSlider);
1603
spin->set_flat(true);
1604
add_child(spin);
1605
add_focusable(spin);
1606
spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyFloat::_value_changed));
1607
}
1608
1609
///////////////////// EASING /////////////////////////
1610
1611
void EditorPropertyEasing::_set_read_only(bool p_read_only) {
1612
spin->set_read_only(p_read_only);
1613
}
1614
1615
void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
1616
if (is_read_only()) {
1617
return;
1618
}
1619
const Ref<InputEventMouseButton> mb = p_ev;
1620
if (mb.is_valid()) {
1621
if (mb->is_double_click() && mb->get_button_index() == MouseButton::LEFT) {
1622
_setup_spin();
1623
}
1624
1625
if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
1626
preset->set_position(easing_draw->get_screen_position() + mb->get_position());
1627
preset->reset_size();
1628
preset->popup();
1629
1630
// Ensure the easing doesn't appear as being dragged
1631
dragging = false;
1632
easing_draw->queue_redraw();
1633
}
1634
1635
if (mb->get_button_index() == MouseButton::LEFT) {
1636
dragging = mb->is_pressed();
1637
// Update to display the correct dragging color
1638
easing_draw->queue_redraw();
1639
}
1640
}
1641
1642
const Ref<InputEventMouseMotion> mm = p_ev;
1643
1644
if (dragging && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
1645
float rel = mm->get_relative().x;
1646
if (rel == 0) {
1647
return;
1648
}
1649
1650
if (flip) {
1651
rel = -rel;
1652
}
1653
1654
float val = get_edited_property_value();
1655
bool sg = val < 0;
1656
val = Math::abs(val);
1657
1658
val = Math::log(val) / Math::log((float)2.0);
1659
// Logarithmic space.
1660
val += rel * 0.05;
1661
1662
val = Math::pow(2.0f, val);
1663
if (sg) {
1664
val = -val;
1665
}
1666
1667
// 0 is a singularity, but both positive and negative values
1668
// are otherwise allowed. Enforce 0+ as workaround.
1669
if (Math::is_zero_approx(val)) {
1670
val = 0.00001;
1671
}
1672
1673
// Limit to a reasonable value to prevent the curve going into infinity,
1674
// which can cause crashes and other issues.
1675
val = CLAMP(val, -1'000'000, 1'000'000);
1676
1677
emit_changed(get_edited_property(), val);
1678
easing_draw->queue_redraw();
1679
}
1680
}
1681
1682
void EditorPropertyEasing::_draw_easing() {
1683
RID ci = easing_draw->get_canvas_item();
1684
1685
Size2 s = easing_draw->get_size();
1686
1687
const int point_count = 48;
1688
1689
const float exp = get_edited_property_value();
1690
1691
const Ref<Font> f = get_theme_font(SceneStringName(font), SNAME("Label"));
1692
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1693
const Color font_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit"));
1694
Color line_color;
1695
if (dragging) {
1696
line_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
1697
} else {
1698
line_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit")) * Color(1, 1, 1, 0.9);
1699
}
1700
1701
Vector<Point2> points;
1702
for (int i = 0; i <= point_count; i++) {
1703
float ifl = i / float(point_count);
1704
1705
const float h = 1.0 - Math::ease(ifl, exp);
1706
1707
if (flip) {
1708
ifl = 1.0 - ifl;
1709
}
1710
1711
points.push_back(Point2(ifl * s.width, h * s.height));
1712
}
1713
1714
easing_draw->draw_polyline(points, line_color, 1.0, true);
1715
// Draw more decimals for small numbers since higher precision is usually required for fine adjustments.
1716
int decimals;
1717
if (Math::abs(exp) < 0.1 - CMP_EPSILON) {
1718
decimals = 4;
1719
} else if (Math::abs(exp) < 1 - CMP_EPSILON) {
1720
decimals = 3;
1721
} else if (Math::abs(exp) < 10 - CMP_EPSILON) {
1722
decimals = 2;
1723
} else {
1724
decimals = 1;
1725
}
1726
f->draw_string(ci, Point2(10, 10 + f->get_ascent(font_size)), TS->format_number(rtos(exp).pad_decimals(decimals)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
1727
}
1728
1729
void EditorPropertyEasing::update_property() {
1730
easing_draw->queue_redraw();
1731
}
1732
1733
void EditorPropertyEasing::_set_preset(int p_preset) {
1734
static const float preset_value[EASING_MAX] = { 0.0, 1.0, 2.0, 0.5, -2.0, -0.5 };
1735
1736
emit_changed(get_edited_property(), preset_value[p_preset]);
1737
easing_draw->queue_redraw();
1738
}
1739
1740
void EditorPropertyEasing::_setup_spin() {
1741
spin->setup_and_show();
1742
spin->get_line_edit()->set_text(TS->format_number(rtos(get_edited_property_value())));
1743
spin->show();
1744
}
1745
1746
void EditorPropertyEasing::_spin_value_changed(double p_value) {
1747
// 0 is a singularity, but both positive and negative values
1748
// are otherwise allowed. Enforce 0+ as workaround.
1749
if (Math::is_zero_approx(p_value)) {
1750
p_value = 0.00001;
1751
}
1752
1753
// Limit to a reasonable value to prevent the curve going into infinity,
1754
// which can cause crashes and other issues.
1755
p_value = CLAMP(p_value, -1'000'000, 1'000'000);
1756
1757
if (positive_only) {
1758
// Force a positive or zero value if a negative value was manually entered by double-clicking.
1759
p_value = MAX(0.0, p_value);
1760
}
1761
1762
emit_changed(get_edited_property(), p_value);
1763
_spin_focus_exited();
1764
}
1765
1766
void EditorPropertyEasing::_spin_focus_exited() {
1767
spin->hide();
1768
// Ensure the easing doesn't appear as being dragged
1769
dragging = false;
1770
easing_draw->queue_redraw();
1771
}
1772
1773
void EditorPropertyEasing::setup(bool p_positive_only, bool p_flip) {
1774
flip = p_flip;
1775
positive_only = p_positive_only;
1776
}
1777
1778
void EditorPropertyEasing::_notification(int p_what) {
1779
switch (p_what) {
1780
case NOTIFICATION_THEME_CHANGED: {
1781
preset->clear();
1782
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveLinear")), "Linear", EASING_LINEAR);
1783
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveIn")), "Ease In", EASING_IN);
1784
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveOut")), "Ease Out", EASING_OUT);
1785
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveConstant")), "Zero", EASING_ZERO);
1786
if (!positive_only) {
1787
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveInOut")), "Ease In-Out", EASING_IN_OUT);
1788
preset->add_icon_item(get_editor_theme_icon(SNAME("CurveOutIn")), "Ease Out-In", EASING_OUT_IN);
1789
}
1790
easing_draw->set_custom_minimum_size(Size2(0, get_theme_font(SceneStringName(font), SNAME("Label"))->get_height(get_theme_font_size(SceneStringName(font_size), SNAME("Label"))) * 2));
1791
} break;
1792
}
1793
}
1794
1795
EditorPropertyEasing::EditorPropertyEasing() {
1796
easing_draw = memnew(Control);
1797
easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));
1798
easing_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPropertyEasing::_drag_easing));
1799
easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);
1800
add_child(easing_draw);
1801
1802
preset = memnew(PopupMenu);
1803
add_child(preset);
1804
preset->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyEasing::_set_preset));
1805
1806
spin = memnew(EditorSpinSlider);
1807
spin->set_flat(true);
1808
spin->set_min(-100);
1809
spin->set_max(100);
1810
spin->set_step(0);
1811
spin->set_hide_slider(true);
1812
spin->set_allow_lesser(true);
1813
spin->set_allow_greater(true);
1814
spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyEasing::_spin_value_changed));
1815
spin->get_line_edit()->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyEasing::_spin_focus_exited));
1816
spin->hide();
1817
add_child(spin);
1818
}
1819
1820
///////////////////// RECT2 /////////////////////////
1821
1822
void EditorPropertyRect2::_set_read_only(bool p_read_only) {
1823
for (int i = 0; i < 4; i++) {
1824
spin[i]->set_read_only(p_read_only);
1825
}
1826
}
1827
1828
void EditorPropertyRect2::_value_changed(double val, const String &p_name) {
1829
Rect2 r2;
1830
r2.position.x = spin[0]->get_value();
1831
r2.position.y = spin[1]->get_value();
1832
r2.size.x = spin[2]->get_value();
1833
r2.size.y = spin[3]->get_value();
1834
emit_changed(get_edited_property(), r2, p_name);
1835
}
1836
1837
void EditorPropertyRect2::update_property() {
1838
Rect2 val = get_edited_property_value();
1839
spin[0]->set_value_no_signal(val.position.x);
1840
spin[1]->set_value_no_signal(val.position.y);
1841
spin[2]->set_value_no_signal(val.size.x);
1842
spin[3]->set_value_no_signal(val.size.y);
1843
}
1844
1845
void EditorPropertyRect2::_notification(int p_what) {
1846
switch (p_what) {
1847
case NOTIFICATION_THEME_CHANGED: {
1848
const Color *colors = _get_property_colors();
1849
for (int i = 0; i < 4; i++) {
1850
spin[i]->add_theme_color_override("label_color", colors[i % 2]);
1851
}
1852
} break;
1853
}
1854
}
1855
1856
void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
1857
for (int i = 0; i < 4; i++) {
1858
spin[i]->set_min(p_min);
1859
spin[i]->set_max(p_max);
1860
spin[i]->set_step(p_step);
1861
spin[i]->set_hide_slider(p_hide_slider);
1862
spin[i]->set_allow_greater(true);
1863
spin[i]->set_allow_lesser(true);
1864
spin[i]->set_suffix(p_suffix);
1865
}
1866
}
1867
1868
EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) {
1869
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
1870
bool grid = false;
1871
BoxContainer *bc;
1872
1873
if (p_force_wide) {
1874
bc = memnew(HBoxContainer);
1875
add_child(bc);
1876
} else if (horizontal) {
1877
bc = memnew(VBoxContainer);
1878
add_child(bc);
1879
set_bottom_editor(bc);
1880
1881
bc->add_child(memnew(HBoxContainer));
1882
bc->add_child(memnew(HBoxContainer));
1883
grid = true;
1884
} else {
1885
bc = memnew(VBoxContainer);
1886
add_child(bc);
1887
}
1888
1889
static const char *desc[4] = { "x", "y", "w", "h" };
1890
for (int i = 0; i < 4; i++) {
1891
spin[i] = memnew(EditorSpinSlider);
1892
spin[i]->set_label(desc[i]);
1893
spin[i]->set_accessibility_name(desc[i]);
1894
spin[i]->set_flat(true);
1895
1896
if (grid) {
1897
bc->get_child(i / 2)->add_child(spin[i]);
1898
} else {
1899
bc->add_child(spin[i]);
1900
}
1901
1902
add_focusable(spin[i]);
1903
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2::_value_changed).bind(desc[i]));
1904
if (horizontal) {
1905
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
1906
}
1907
}
1908
1909
if (!horizontal) {
1910
set_label_reference(spin[0]); //show text and buttons around this
1911
}
1912
}
1913
1914
///////////////////// RECT2i /////////////////////////
1915
1916
void EditorPropertyRect2i::_set_read_only(bool p_read_only) {
1917
for (int i = 0; i < 4; i++) {
1918
spin[i]->set_read_only(p_read_only);
1919
}
1920
}
1921
1922
void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
1923
Rect2i r2;
1924
r2.position.x = spin[0]->get_value();
1925
r2.position.y = spin[1]->get_value();
1926
r2.size.x = spin[2]->get_value();
1927
r2.size.y = spin[3]->get_value();
1928
emit_changed(get_edited_property(), r2, p_name);
1929
}
1930
1931
void EditorPropertyRect2i::update_property() {
1932
Rect2i val = get_edited_property_value();
1933
spin[0]->set_value_no_signal(val.position.x);
1934
spin[1]->set_value_no_signal(val.position.y);
1935
spin[2]->set_value_no_signal(val.size.x);
1936
spin[3]->set_value_no_signal(val.size.y);
1937
}
1938
1939
void EditorPropertyRect2i::_notification(int p_what) {
1940
switch (p_what) {
1941
case NOTIFICATION_THEME_CHANGED: {
1942
const Color *colors = _get_property_colors();
1943
for (int i = 0; i < 4; i++) {
1944
spin[i]->add_theme_color_override("label_color", colors[i % 2]);
1945
}
1946
} break;
1947
}
1948
}
1949
1950
void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
1951
for (int i = 0; i < 4; i++) {
1952
spin[i]->set_min(p_min);
1953
spin[i]->set_max(p_max);
1954
spin[i]->set_step(1);
1955
spin[i]->set_allow_greater(true);
1956
spin[i]->set_allow_lesser(true);
1957
spin[i]->set_suffix(p_suffix);
1958
spin[i]->set_editing_integer(true);
1959
}
1960
}
1961
1962
EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {
1963
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
1964
bool grid = false;
1965
BoxContainer *bc;
1966
1967
if (p_force_wide) {
1968
bc = memnew(HBoxContainer);
1969
add_child(bc);
1970
} else if (horizontal) {
1971
bc = memnew(VBoxContainer);
1972
add_child(bc);
1973
set_bottom_editor(bc);
1974
1975
bc->add_child(memnew(HBoxContainer));
1976
bc->add_child(memnew(HBoxContainer));
1977
grid = true;
1978
} else {
1979
bc = memnew(VBoxContainer);
1980
add_child(bc);
1981
}
1982
1983
static const char *desc[4] = { "x", "y", "w", "h" };
1984
for (int i = 0; i < 4; i++) {
1985
spin[i] = memnew(EditorSpinSlider);
1986
spin[i]->set_label(desc[i]);
1987
spin[i]->set_accessibility_name(desc[i]);
1988
spin[i]->set_flat(true);
1989
1990
if (grid) {
1991
bc->get_child(i / 2)->add_child(spin[i]);
1992
} else {
1993
bc->add_child(spin[i]);
1994
}
1995
1996
add_focusable(spin[i]);
1997
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2i::_value_changed).bind(desc[i]));
1998
if (horizontal) {
1999
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2000
}
2001
}
2002
2003
if (!horizontal) {
2004
set_label_reference(spin[0]); //show text and buttons around this
2005
}
2006
}
2007
2008
///////////////////// PLANE /////////////////////////
2009
2010
void EditorPropertyPlane::_set_read_only(bool p_read_only) {
2011
for (int i = 0; i < 4; i++) {
2012
spin[i]->set_read_only(p_read_only);
2013
}
2014
}
2015
2016
void EditorPropertyPlane::_value_changed(double val, const String &p_name) {
2017
Plane p;
2018
p.normal.x = spin[0]->get_value();
2019
p.normal.y = spin[1]->get_value();
2020
p.normal.z = spin[2]->get_value();
2021
p.d = spin[3]->get_value();
2022
emit_changed(get_edited_property(), p, p_name);
2023
}
2024
2025
void EditorPropertyPlane::update_property() {
2026
Plane val = get_edited_property_value();
2027
spin[0]->set_value_no_signal(val.normal.x);
2028
spin[1]->set_value_no_signal(val.normal.y);
2029
spin[2]->set_value_no_signal(val.normal.z);
2030
spin[3]->set_value_no_signal(val.d);
2031
}
2032
2033
void EditorPropertyPlane::_notification(int p_what) {
2034
switch (p_what) {
2035
case NOTIFICATION_THEME_CHANGED: {
2036
const Color *colors = _get_property_colors();
2037
for (int i = 0; i < 4; i++) {
2038
spin[i]->add_theme_color_override("label_color", colors[i]);
2039
}
2040
} break;
2041
}
2042
}
2043
2044
void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2045
for (int i = 0; i < 4; i++) {
2046
spin[i]->set_min(p_min);
2047
spin[i]->set_max(p_max);
2048
spin[i]->set_step(p_step);
2049
spin[i]->set_hide_slider(p_hide_slider);
2050
spin[i]->set_allow_greater(true);
2051
spin[i]->set_allow_lesser(true);
2052
}
2053
spin[3]->set_suffix(p_suffix);
2054
}
2055
2056
EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) {
2057
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
2058
2059
BoxContainer *bc;
2060
2061
if (p_force_wide) {
2062
bc = memnew(HBoxContainer);
2063
add_child(bc);
2064
} else if (horizontal) {
2065
bc = memnew(HBoxContainer);
2066
add_child(bc);
2067
set_bottom_editor(bc);
2068
} else {
2069
bc = memnew(VBoxContainer);
2070
add_child(bc);
2071
}
2072
2073
static const char *desc[4] = { "x", "y", "z", "d" };
2074
for (int i = 0; i < 4; i++) {
2075
spin[i] = memnew(EditorSpinSlider);
2076
spin[i]->set_flat(true);
2077
spin[i]->set_label(desc[i]);
2078
spin[i]->set_accessibility_name(desc[i]);
2079
bc->add_child(spin[i]);
2080
add_focusable(spin[i]);
2081
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyPlane::_value_changed).bind(desc[i]));
2082
if (horizontal) {
2083
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2084
}
2085
}
2086
2087
if (!horizontal) {
2088
set_label_reference(spin[0]); //show text and buttons around this
2089
}
2090
}
2091
2092
///////////////////// QUATERNION /////////////////////////
2093
2094
void EditorPropertyQuaternion::_set_read_only(bool p_read_only) {
2095
for (int i = 0; i < 4; i++) {
2096
spin[i]->set_read_only(p_read_only);
2097
}
2098
for (int i = 0; i < 3; i++) {
2099
euler[i]->set_read_only(p_read_only);
2100
}
2101
}
2102
2103
void EditorPropertyQuaternion::_edit_custom_value() {
2104
if (edit_button->is_pressed()) {
2105
edit_custom_bc->show();
2106
for (int i = 0; i < 3; i++) {
2107
euler[i]->grab_focus();
2108
}
2109
} else {
2110
edit_custom_bc->hide();
2111
for (int i = 0; i < 4; i++) {
2112
spin[i]->grab_focus();
2113
}
2114
}
2115
update_property();
2116
}
2117
2118
void EditorPropertyQuaternion::_custom_value_changed(double val) {
2119
edit_euler.x = euler[0]->get_value();
2120
edit_euler.y = euler[1]->get_value();
2121
edit_euler.z = euler[2]->get_value();
2122
2123
Vector3 v;
2124
v.x = Math::deg_to_rad(edit_euler.x);
2125
v.y = Math::deg_to_rad(edit_euler.y);
2126
v.z = Math::deg_to_rad(edit_euler.z);
2127
2128
Quaternion temp_q = Quaternion::from_euler(v);
2129
spin[0]->set_value_no_signal(temp_q.x);
2130
spin[1]->set_value_no_signal(temp_q.y);
2131
spin[2]->set_value_no_signal(temp_q.z);
2132
spin[3]->set_value_no_signal(temp_q.w);
2133
_value_changed(-1, "");
2134
}
2135
2136
void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) {
2137
Quaternion p;
2138
p.x = spin[0]->get_value();
2139
p.y = spin[1]->get_value();
2140
p.z = spin[2]->get_value();
2141
p.w = spin[3]->get_value();
2142
2143
emit_changed(get_edited_property(), p, p_name);
2144
}
2145
2146
bool EditorPropertyQuaternion::is_grabbing_euler() {
2147
bool is_grabbing = false;
2148
for (int i = 0; i < 3; i++) {
2149
is_grabbing |= euler[i]->is_grabbing();
2150
}
2151
return is_grabbing;
2152
}
2153
2154
void EditorPropertyQuaternion::update_property() {
2155
Quaternion val = get_edited_property_value();
2156
spin[0]->set_value_no_signal(val.x);
2157
spin[1]->set_value_no_signal(val.y);
2158
spin[2]->set_value_no_signal(val.z);
2159
spin[3]->set_value_no_signal(val.w);
2160
if (!is_grabbing_euler()) {
2161
Vector3 v = val.normalized().get_euler();
2162
edit_euler.x = Math::rad_to_deg(v.x);
2163
edit_euler.y = Math::rad_to_deg(v.y);
2164
edit_euler.z = Math::rad_to_deg(v.z);
2165
euler[0]->set_value_no_signal(edit_euler.x);
2166
euler[1]->set_value_no_signal(edit_euler.y);
2167
euler[2]->set_value_no_signal(edit_euler.z);
2168
}
2169
}
2170
2171
void EditorPropertyQuaternion::_warning_pressed() {
2172
warning_dialog->popup_centered();
2173
}
2174
2175
void EditorPropertyQuaternion::_notification(int p_what) {
2176
switch (p_what) {
2177
case NOTIFICATION_THEME_CHANGED: {
2178
const Color *colors = _get_property_colors();
2179
for (int i = 0; i < 4; i++) {
2180
spin[i]->add_theme_color_override("label_color", colors[i]);
2181
}
2182
for (int i = 0; i < 3; i++) {
2183
euler[i]->add_theme_color_override("label_color", colors[i]);
2184
}
2185
edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
2186
euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));
2187
warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
2188
warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
2189
} break;
2190
}
2191
}
2192
2193
void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) {
2194
for (int i = 0; i < 4; i++) {
2195
spin[i]->set_min(p_min);
2196
spin[i]->set_max(p_max);
2197
spin[i]->set_step(p_step);
2198
spin[i]->set_hide_slider(p_hide_slider);
2199
spin[i]->set_allow_greater(true);
2200
spin[i]->set_allow_lesser(true);
2201
// Quaternion is inherently unitless, however someone may want to use it as
2202
// a generic way to store 4 values, so we'll still respect the suffix.
2203
spin[i]->set_suffix(p_suffix);
2204
}
2205
2206
for (int i = 0; i < 3; i++) {
2207
euler[i]->set_min(-360);
2208
euler[i]->set_max(360);
2209
euler[i]->set_step(0.1);
2210
euler[i]->set_hide_slider(false);
2211
euler[i]->set_allow_greater(true);
2212
euler[i]->set_allow_lesser(true);
2213
euler[i]->set_suffix(U"\u00B0");
2214
}
2215
2216
if (p_hide_editor) {
2217
edit_button->hide();
2218
}
2219
}
2220
2221
EditorPropertyQuaternion::EditorPropertyQuaternion() {
2222
bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");
2223
2224
VBoxContainer *bc = memnew(VBoxContainer);
2225
edit_custom_bc = memnew(VBoxContainer);
2226
BoxContainer *edit_custom_layout;
2227
if (horizontal) {
2228
default_layout = memnew(HBoxContainer);
2229
edit_custom_layout = memnew(HBoxContainer);
2230
set_bottom_editor(bc);
2231
} else {
2232
default_layout = memnew(VBoxContainer);
2233
edit_custom_layout = memnew(VBoxContainer);
2234
}
2235
edit_custom_bc->hide();
2236
add_child(bc);
2237
edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL);
2238
default_layout->set_h_size_flags(SIZE_EXPAND_FILL);
2239
edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);
2240
bc->add_child(default_layout);
2241
bc->add_child(edit_custom_bc);
2242
2243
static const char *desc[4] = { "x", "y", "z", "w" };
2244
for (int i = 0; i < 4; i++) {
2245
spin[i] = memnew(EditorSpinSlider);
2246
spin[i]->set_flat(true);
2247
spin[i]->set_label(desc[i]);
2248
spin[i]->set_accessibility_name(desc[i]);
2249
default_layout->add_child(spin[i]);
2250
add_focusable(spin[i]);
2251
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i]));
2252
if (horizontal) {
2253
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2254
}
2255
}
2256
2257
warning = memnew(Button);
2258
warning->set_text(TTR("Temporary Euler may be changed implicitly!"));
2259
warning->set_clip_text(true);
2260
warning->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_warning_pressed));
2261
warning_dialog = memnew(AcceptDialog);
2262
add_child(warning_dialog);
2263
warning_dialog->set_text(TTR("Temporary Euler will not be stored in the object with the original value. Instead, it will be stored as Quaternion with irreversible conversion.\nThis is due to the fact that the result of Euler->Quaternion can be determined uniquely, but the result of Quaternion->Euler can be multi-existent."));
2264
2265
euler_label = memnew(Label);
2266
euler_label->set_text(TTR("Temporary Euler"));
2267
2268
edit_custom_bc->add_child(warning);
2269
edit_custom_bc->add_child(edit_custom_layout);
2270
edit_custom_layout->add_child(euler_label);
2271
2272
for (int i = 0; i < 3; i++) {
2273
euler[i] = memnew(EditorSpinSlider);
2274
euler[i]->set_flat(true);
2275
euler[i]->set_label(desc[i]);
2276
euler[i]->set_accessibility_name(vformat(TTR("Temporary Euler %s"), desc[i]));
2277
edit_custom_layout->add_child(euler[i]);
2278
add_focusable(euler[i]);
2279
euler[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed));
2280
if (horizontal) {
2281
euler[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2282
}
2283
}
2284
2285
edit_button = memnew(Button);
2286
edit_button->set_accessibility_name(TTRC("Edit"));
2287
edit_button->set_flat(true);
2288
edit_button->set_toggle_mode(true);
2289
default_layout->add_child(edit_button);
2290
edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_edit_custom_value));
2291
2292
add_focusable(edit_button);
2293
2294
if (!horizontal) {
2295
set_label_reference(spin[0]); //show text and buttons around this
2296
}
2297
}
2298
2299
///////////////////// AABB /////////////////////////
2300
2301
void EditorPropertyAABB::_set_read_only(bool p_read_only) {
2302
for (int i = 0; i < 6; i++) {
2303
spin[i]->set_read_only(p_read_only);
2304
}
2305
}
2306
2307
void EditorPropertyAABB::_value_changed(double val, const String &p_name) {
2308
AABB p;
2309
p.position.x = spin[0]->get_value();
2310
p.position.y = spin[1]->get_value();
2311
p.position.z = spin[2]->get_value();
2312
p.size.x = spin[3]->get_value();
2313
p.size.y = spin[4]->get_value();
2314
p.size.z = spin[5]->get_value();
2315
emit_changed(get_edited_property(), p, p_name);
2316
}
2317
2318
void EditorPropertyAABB::update_property() {
2319
AABB val = get_edited_property_value();
2320
spin[0]->set_value_no_signal(val.position.x);
2321
spin[1]->set_value_no_signal(val.position.y);
2322
spin[2]->set_value_no_signal(val.position.z);
2323
spin[3]->set_value_no_signal(val.size.x);
2324
spin[4]->set_value_no_signal(val.size.y);
2325
spin[5]->set_value_no_signal(val.size.z);
2326
}
2327
2328
void EditorPropertyAABB::_notification(int p_what) {
2329
switch (p_what) {
2330
case NOTIFICATION_THEME_CHANGED: {
2331
const Color *colors = _get_property_colors();
2332
for (int i = 0; i < 6; i++) {
2333
spin[i]->add_theme_color_override("label_color", colors[i % 3]);
2334
}
2335
} break;
2336
}
2337
}
2338
2339
void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2340
for (int i = 0; i < 6; i++) {
2341
spin[i]->set_min(p_min);
2342
spin[i]->set_max(p_max);
2343
spin[i]->set_step(p_step);
2344
spin[i]->set_hide_slider(p_hide_slider);
2345
spin[i]->set_allow_greater(true);
2346
spin[i]->set_allow_lesser(true);
2347
spin[i]->set_suffix(p_suffix);
2348
}
2349
}
2350
2351
EditorPropertyAABB::EditorPropertyAABB() {
2352
GridContainer *g = memnew(GridContainer);
2353
g->set_columns(3);
2354
add_child(g);
2355
2356
static const char *desc[6] = { "x", "y", "z", "w", "h", "d" };
2357
for (int i = 0; i < 6; i++) {
2358
spin[i] = memnew(EditorSpinSlider);
2359
spin[i]->set_label(desc[i]);
2360
spin[i]->set_accessibility_name(desc[i]);
2361
spin[i]->set_flat(true);
2362
2363
g->add_child(spin[i]);
2364
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2365
add_focusable(spin[i]);
2366
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyAABB::_value_changed).bind(desc[i]));
2367
}
2368
set_bottom_editor(g);
2369
}
2370
2371
///////////////////// TRANSFORM2D /////////////////////////
2372
2373
void EditorPropertyTransform2D::_set_read_only(bool p_read_only) {
2374
for (int i = 0; i < 6; i++) {
2375
spin[i]->set_read_only(p_read_only);
2376
}
2377
}
2378
2379
void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) {
2380
Transform2D p;
2381
p[0][0] = spin[0]->get_value();
2382
p[1][0] = spin[1]->get_value();
2383
p[2][0] = spin[2]->get_value();
2384
p[0][1] = spin[3]->get_value();
2385
p[1][1] = spin[4]->get_value();
2386
p[2][1] = spin[5]->get_value();
2387
2388
emit_changed(get_edited_property(), p, p_name);
2389
}
2390
2391
void EditorPropertyTransform2D::update_property() {
2392
Transform2D val = get_edited_property_value();
2393
spin[0]->set_value_no_signal(val[0][0]);
2394
spin[1]->set_value_no_signal(val[1][0]);
2395
spin[2]->set_value_no_signal(val[2][0]);
2396
spin[3]->set_value_no_signal(val[0][1]);
2397
spin[4]->set_value_no_signal(val[1][1]);
2398
spin[5]->set_value_no_signal(val[2][1]);
2399
}
2400
2401
void EditorPropertyTransform2D::_notification(int p_what) {
2402
switch (p_what) {
2403
case NOTIFICATION_THEME_CHANGED: {
2404
const Color *colors = _get_property_colors();
2405
for (int i = 0; i < 6; i++) {
2406
// For Transform2D, use the 4th color (cyan) for the origin vector.
2407
if (i % 3 == 2) {
2408
spin[i]->add_theme_color_override("label_color", colors[3]);
2409
} else {
2410
spin[i]->add_theme_color_override("label_color", colors[i % 3]);
2411
}
2412
}
2413
} break;
2414
}
2415
}
2416
2417
void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2418
for (int i = 0; i < 6; i++) {
2419
spin[i]->set_min(p_min);
2420
spin[i]->set_max(p_max);
2421
spin[i]->set_step(p_step);
2422
spin[i]->set_hide_slider(p_hide_slider);
2423
spin[i]->set_allow_greater(true);
2424
spin[i]->set_allow_lesser(true);
2425
if (i % 3 == 2) {
2426
spin[i]->set_suffix(p_suffix);
2427
}
2428
}
2429
}
2430
2431
EditorPropertyTransform2D::EditorPropertyTransform2D(bool p_include_origin) {
2432
GridContainer *g = memnew(GridContainer);
2433
g->set_columns(p_include_origin ? 3 : 2);
2434
add_child(g);
2435
2436
static const char *desc[6] = { "xx", "xy", "xo", "yx", "yy", "yo" };
2437
for (int i = 0; i < 6; i++) {
2438
spin[i] = memnew(EditorSpinSlider);
2439
spin[i]->set_label(desc[i]);
2440
spin[i]->set_accessibility_name(desc[i]);
2441
spin[i]->set_flat(true);
2442
if (p_include_origin || i % 3 != 2) {
2443
g->add_child(spin[i]);
2444
}
2445
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2446
add_focusable(spin[i]);
2447
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform2D::_value_changed).bind(desc[i]));
2448
}
2449
set_bottom_editor(g);
2450
}
2451
2452
///////////////////// BASIS /////////////////////////
2453
2454
void EditorPropertyBasis::_set_read_only(bool p_read_only) {
2455
for (int i = 0; i < 9; i++) {
2456
spin[i]->set_read_only(p_read_only);
2457
}
2458
}
2459
2460
void EditorPropertyBasis::_value_changed(double val, const String &p_name) {
2461
Basis p;
2462
p[0][0] = spin[0]->get_value();
2463
p[0][1] = spin[1]->get_value();
2464
p[0][2] = spin[2]->get_value();
2465
p[1][0] = spin[3]->get_value();
2466
p[1][1] = spin[4]->get_value();
2467
p[1][2] = spin[5]->get_value();
2468
p[2][0] = spin[6]->get_value();
2469
p[2][1] = spin[7]->get_value();
2470
p[2][2] = spin[8]->get_value();
2471
2472
emit_changed(get_edited_property(), p, p_name);
2473
}
2474
2475
void EditorPropertyBasis::update_property() {
2476
Basis val = get_edited_property_value();
2477
spin[0]->set_value_no_signal(val[0][0]);
2478
spin[1]->set_value_no_signal(val[0][1]);
2479
spin[2]->set_value_no_signal(val[0][2]);
2480
spin[3]->set_value_no_signal(val[1][0]);
2481
spin[4]->set_value_no_signal(val[1][1]);
2482
spin[5]->set_value_no_signal(val[1][2]);
2483
spin[6]->set_value_no_signal(val[2][0]);
2484
spin[7]->set_value_no_signal(val[2][1]);
2485
spin[8]->set_value_no_signal(val[2][2]);
2486
}
2487
2488
void EditorPropertyBasis::_notification(int p_what) {
2489
switch (p_what) {
2490
case NOTIFICATION_THEME_CHANGED: {
2491
const Color *colors = _get_property_colors();
2492
for (int i = 0; i < 9; i++) {
2493
spin[i]->add_theme_color_override("label_color", colors[i % 3]);
2494
}
2495
} break;
2496
}
2497
}
2498
2499
void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2500
for (int i = 0; i < 9; i++) {
2501
spin[i]->set_min(p_min);
2502
spin[i]->set_max(p_max);
2503
spin[i]->set_step(p_step);
2504
spin[i]->set_hide_slider(p_hide_slider);
2505
spin[i]->set_allow_greater(true);
2506
spin[i]->set_allow_lesser(true);
2507
// Basis is inherently unitless, however someone may want to use it as
2508
// a generic way to store 9 values, so we'll still respect the suffix.
2509
spin[i]->set_suffix(p_suffix);
2510
}
2511
}
2512
2513
EditorPropertyBasis::EditorPropertyBasis() {
2514
GridContainer *g = memnew(GridContainer);
2515
g->set_columns(3);
2516
add_child(g);
2517
2518
static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };
2519
for (int i = 0; i < 9; i++) {
2520
spin[i] = memnew(EditorSpinSlider);
2521
spin[i]->set_label(desc[i]);
2522
spin[i]->set_accessibility_name(desc[i]);
2523
spin[i]->set_flat(true);
2524
g->add_child(spin[i]);
2525
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2526
add_focusable(spin[i]);
2527
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyBasis::_value_changed).bind(desc[i]));
2528
}
2529
set_bottom_editor(g);
2530
}
2531
2532
///////////////////// TRANSFORM3D /////////////////////////
2533
2534
void EditorPropertyTransform3D::_set_read_only(bool p_read_only) {
2535
for (int i = 0; i < 12; i++) {
2536
spin[i]->set_read_only(p_read_only);
2537
}
2538
}
2539
2540
void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) {
2541
Transform3D p;
2542
p.basis[0][0] = spin[0]->get_value();
2543
p.basis[0][1] = spin[1]->get_value();
2544
p.basis[0][2] = spin[2]->get_value();
2545
p.origin[0] = spin[3]->get_value();
2546
p.basis[1][0] = spin[4]->get_value();
2547
p.basis[1][1] = spin[5]->get_value();
2548
p.basis[1][2] = spin[6]->get_value();
2549
p.origin[1] = spin[7]->get_value();
2550
p.basis[2][0] = spin[8]->get_value();
2551
p.basis[2][1] = spin[9]->get_value();
2552
p.basis[2][2] = spin[10]->get_value();
2553
p.origin[2] = spin[11]->get_value();
2554
2555
emit_changed(get_edited_property(), p, p_name);
2556
}
2557
2558
void EditorPropertyTransform3D::update_property() {
2559
update_using_transform(get_edited_property_value());
2560
}
2561
2562
void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) {
2563
spin[0]->set_value_no_signal(p_transform.basis[0][0]);
2564
spin[1]->set_value_no_signal(p_transform.basis[0][1]);
2565
spin[2]->set_value_no_signal(p_transform.basis[0][2]);
2566
spin[3]->set_value_no_signal(p_transform.origin[0]);
2567
spin[4]->set_value_no_signal(p_transform.basis[1][0]);
2568
spin[5]->set_value_no_signal(p_transform.basis[1][1]);
2569
spin[6]->set_value_no_signal(p_transform.basis[1][2]);
2570
spin[7]->set_value_no_signal(p_transform.origin[1]);
2571
spin[8]->set_value_no_signal(p_transform.basis[2][0]);
2572
spin[9]->set_value_no_signal(p_transform.basis[2][1]);
2573
spin[10]->set_value_no_signal(p_transform.basis[2][2]);
2574
spin[11]->set_value_no_signal(p_transform.origin[2]);
2575
}
2576
2577
void EditorPropertyTransform3D::_notification(int p_what) {
2578
switch (p_what) {
2579
case NOTIFICATION_THEME_CHANGED: {
2580
const Color *colors = _get_property_colors();
2581
for (int i = 0; i < 12; i++) {
2582
spin[i]->add_theme_color_override("label_color", colors[i % 4]);
2583
}
2584
} break;
2585
}
2586
}
2587
2588
void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2589
for (int i = 0; i < 12; i++) {
2590
spin[i]->set_min(p_min);
2591
spin[i]->set_max(p_max);
2592
spin[i]->set_step(p_step);
2593
spin[i]->set_hide_slider(p_hide_slider);
2594
spin[i]->set_allow_greater(true);
2595
spin[i]->set_allow_lesser(true);
2596
if (i % 4 == 3) {
2597
spin[i]->set_suffix(p_suffix);
2598
}
2599
}
2600
}
2601
2602
EditorPropertyTransform3D::EditorPropertyTransform3D() {
2603
GridContainer *g = memnew(GridContainer);
2604
g->set_columns(4);
2605
add_child(g);
2606
2607
static const char *desc[12] = { "xx", "xy", "xz", "xo", "yx", "yy", "yz", "yo", "zx", "zy", "zz", "zo" };
2608
for (int i = 0; i < 12; i++) {
2609
spin[i] = memnew(EditorSpinSlider);
2610
spin[i]->set_label(desc[i]);
2611
spin[i]->set_accessibility_name(desc[i]);
2612
spin[i]->set_flat(true);
2613
g->add_child(spin[i]);
2614
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2615
add_focusable(spin[i]);
2616
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform3D::_value_changed).bind(desc[i]));
2617
}
2618
set_bottom_editor(g);
2619
}
2620
2621
///////////////////// PROJECTION /////////////////////////
2622
2623
void EditorPropertyProjection::_set_read_only(bool p_read_only) {
2624
for (int i = 0; i < 12; i++) {
2625
spin[i]->set_read_only(p_read_only);
2626
}
2627
}
2628
2629
void EditorPropertyProjection::_value_changed(double val, const String &p_name) {
2630
Projection p;
2631
p.columns[0][0] = spin[0]->get_value();
2632
p.columns[0][1] = spin[1]->get_value();
2633
p.columns[0][2] = spin[2]->get_value();
2634
p.columns[0][3] = spin[3]->get_value();
2635
p.columns[1][0] = spin[4]->get_value();
2636
p.columns[1][1] = spin[5]->get_value();
2637
p.columns[1][2] = spin[6]->get_value();
2638
p.columns[1][3] = spin[7]->get_value();
2639
p.columns[2][0] = spin[8]->get_value();
2640
p.columns[2][1] = spin[9]->get_value();
2641
p.columns[2][2] = spin[10]->get_value();
2642
p.columns[2][3] = spin[11]->get_value();
2643
p.columns[3][0] = spin[12]->get_value();
2644
p.columns[3][1] = spin[13]->get_value();
2645
p.columns[3][2] = spin[14]->get_value();
2646
p.columns[3][3] = spin[15]->get_value();
2647
2648
emit_changed(get_edited_property(), p, p_name);
2649
}
2650
2651
void EditorPropertyProjection::update_property() {
2652
update_using_transform(get_edited_property_value());
2653
}
2654
2655
void EditorPropertyProjection::update_using_transform(Projection p_transform) {
2656
spin[0]->set_value_no_signal(p_transform.columns[0][0]);
2657
spin[1]->set_value_no_signal(p_transform.columns[0][1]);
2658
spin[2]->set_value_no_signal(p_transform.columns[0][2]);
2659
spin[3]->set_value_no_signal(p_transform.columns[0][3]);
2660
spin[4]->set_value_no_signal(p_transform.columns[1][0]);
2661
spin[5]->set_value_no_signal(p_transform.columns[1][1]);
2662
spin[6]->set_value_no_signal(p_transform.columns[1][2]);
2663
spin[7]->set_value_no_signal(p_transform.columns[1][3]);
2664
spin[8]->set_value_no_signal(p_transform.columns[2][0]);
2665
spin[9]->set_value_no_signal(p_transform.columns[2][1]);
2666
spin[10]->set_value_no_signal(p_transform.columns[2][2]);
2667
spin[11]->set_value_no_signal(p_transform.columns[2][3]);
2668
spin[12]->set_value_no_signal(p_transform.columns[3][0]);
2669
spin[13]->set_value_no_signal(p_transform.columns[3][1]);
2670
spin[14]->set_value_no_signal(p_transform.columns[3][2]);
2671
spin[15]->set_value_no_signal(p_transform.columns[3][3]);
2672
}
2673
2674
void EditorPropertyProjection::_notification(int p_what) {
2675
switch (p_what) {
2676
case NOTIFICATION_THEME_CHANGED: {
2677
const Color *colors = _get_property_colors();
2678
for (int i = 0; i < 16; i++) {
2679
spin[i]->add_theme_color_override("label_color", colors[i % 4]);
2680
}
2681
} break;
2682
}
2683
}
2684
2685
void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
2686
for (int i = 0; i < 16; i++) {
2687
spin[i]->set_min(p_min);
2688
spin[i]->set_max(p_max);
2689
spin[i]->set_step(p_step);
2690
spin[i]->set_hide_slider(p_hide_slider);
2691
spin[i]->set_allow_greater(true);
2692
spin[i]->set_allow_lesser(true);
2693
if (i % 4 == 3) {
2694
spin[i]->set_suffix(p_suffix);
2695
}
2696
}
2697
}
2698
2699
EditorPropertyProjection::EditorPropertyProjection() {
2700
GridContainer *g = memnew(GridContainer);
2701
g->set_columns(4);
2702
add_child(g);
2703
2704
static const char *desc[16] = { "xx", "xy", "xz", "xw", "yx", "yy", "yz", "yw", "zx", "zy", "zz", "zw", "wx", "wy", "wz", "ww" };
2705
for (int i = 0; i < 16; i++) {
2706
spin[i] = memnew(EditorSpinSlider);
2707
spin[i]->set_label(desc[i]);
2708
spin[i]->set_accessibility_name(desc[i]);
2709
spin[i]->set_flat(true);
2710
g->add_child(spin[i]);
2711
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
2712
add_focusable(spin[i]);
2713
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyProjection::_value_changed).bind(desc[i]));
2714
}
2715
set_bottom_editor(g);
2716
}
2717
////////////// COLOR PICKER //////////////////////
2718
2719
void EditorPropertyColor::_set_read_only(bool p_read_only) {
2720
picker->set_disabled(p_read_only);
2721
}
2722
2723
void EditorPropertyColor::_color_changed(const Color &p_color) {
2724
if (!live_changes_enabled) {
2725
return;
2726
}
2727
2728
// Cancel the color change if the current color is identical to the new one.
2729
if (((Color)get_edited_property_value()).is_equal_approx(p_color)) {
2730
return;
2731
}
2732
2733
// Preview color change, bypassing undo/redo.
2734
get_edited_object()->set(get_edited_property(), p_color);
2735
}
2736
2737
void EditorPropertyColor::_picker_created() {
2738
picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_popup_opening));
2739
picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED);
2740
}
2741
2742
void EditorPropertyColor::_popup_opening() {
2743
EditorNode::get_singleton()->setup_color_picker(picker->get_picker());
2744
last_color = picker->get_pick_color();
2745
was_checked = !is_checkable() || is_checked();
2746
}
2747
2748
void EditorPropertyColor::_popup_closed() {
2749
get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());
2750
if (!picker->get_pick_color().is_equal_approx(last_color)) {
2751
emit_changed(get_edited_property(), picker->get_pick_color(), "", false);
2752
}
2753
}
2754
2755
void EditorPropertyColor::_notification(int p_what) {
2756
switch (p_what) {
2757
case NOTIFICATION_THEME_CHANGED: {
2758
picker->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), EditorStringName(Editor))));
2759
} break;
2760
}
2761
}
2762
2763
void EditorPropertyColor::update_property() {
2764
picker->set_pick_color(get_edited_property_display_value());
2765
const Color color = picker->get_pick_color();
2766
2767
// Add a tooltip to display each channel's values without having to click the ColorPickerButton
2768
if (picker->is_editing_alpha()) {
2769
picker->set_tooltip_text(vformat(
2770
"R: %s\nG: %s\nB: %s\nA: %s",
2771
rtos(color.r).pad_decimals(2),
2772
rtos(color.g).pad_decimals(2),
2773
rtos(color.b).pad_decimals(2),
2774
rtos(color.a).pad_decimals(2)));
2775
} else {
2776
picker->set_tooltip_text(vformat(
2777
"R: %s\nG: %s\nB: %s",
2778
rtos(color.r).pad_decimals(2),
2779
rtos(color.g).pad_decimals(2),
2780
rtos(color.b).pad_decimals(2)));
2781
}
2782
}
2783
2784
void EditorPropertyColor::setup(bool p_show_alpha) {
2785
picker->set_edit_alpha(p_show_alpha);
2786
}
2787
2788
void EditorPropertyColor::set_live_changes_enabled(bool p_enabled) {
2789
live_changes_enabled = p_enabled;
2790
}
2791
2792
EditorPropertyColor::EditorPropertyColor() {
2793
picker = memnew(ColorPickerButton);
2794
add_child(picker);
2795
picker->set_flat(true);
2796
picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed));
2797
picker->connect("picker_created", callable_mp(this, &EditorPropertyColor::_picker_created), CONNECT_ONE_SHOT);
2798
}
2799
2800
////////////// NODE PATH //////////////////////
2801
2802
void EditorPropertyNodePath::_set_read_only(bool p_read_only) {
2803
assign->set_disabled(p_read_only);
2804
menu->set_disabled(p_read_only);
2805
}
2806
2807
Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const {
2808
if (p_prop == get_edited_property()) {
2809
r_valid = true;
2810
return const_cast<EditorPropertyNodePath *>(this)->get_edited_object()->get(get_edited_property(), &r_valid);
2811
}
2812
return Variant();
2813
}
2814
2815
void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) {
2816
NodePath path = p_path;
2817
Node *base_node = get_base_node();
2818
2819
if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {
2820
Node *to_node = get_node(p_path);
2821
ERR_FAIL_NULL(to_node);
2822
path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
2823
}
2824
2825
if (p_absolute && base_node) { // for AnimationTrackKeyEdit
2826
path = base_node->get_path().rel_path_to(p_path);
2827
}
2828
2829
if (editing_node) {
2830
if (!base_node) {
2831
emit_changed(get_edited_property(), get_tree()->get_edited_scene_root()->get_node(path));
2832
} else {
2833
emit_changed(get_edited_property(), base_node->get_node(path));
2834
}
2835
} else {
2836
emit_changed(get_edited_property(), path);
2837
}
2838
update_property();
2839
}
2840
2841
void EditorPropertyNodePath::_node_assign() {
2842
if (!scene_tree) {
2843
scene_tree = memnew(SceneTreeDialog);
2844
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
2845
scene_tree->set_valid_types(valid_types);
2846
add_child(scene_tree);
2847
scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true));
2848
}
2849
2850
Variant val = get_edited_property_value();
2851
Node *n = nullptr;
2852
if (val.get_type() == Variant::Type::NODE_PATH) {
2853
Node *base_node = get_base_node();
2854
n = base_node == nullptr ? nullptr : base_node->get_node_or_null(val);
2855
} else {
2856
n = Object::cast_to<Node>(val);
2857
}
2858
scene_tree->popup_scenetree_dialog(n, get_base_node());
2859
}
2860
2861
void EditorPropertyNodePath::_assign_draw() {
2862
if (dropping) {
2863
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
2864
assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false);
2865
}
2866
}
2867
2868
void EditorPropertyNodePath::_update_menu() {
2869
const NodePath &np = _get_node_path();
2870
2871
menu->get_popup()->set_item_disabled(ACTION_CLEAR, np.is_empty());
2872
menu->get_popup()->set_item_disabled(ACTION_COPY, np.is_empty());
2873
2874
Node *edited_node = Object::cast_to<Node>(get_edited_object());
2875
menu->get_popup()->set_item_disabled(ACTION_SELECT, !edited_node || !edited_node->has_node(np));
2876
}
2877
2878
void EditorPropertyNodePath::_menu_option(int p_idx) {
2879
switch (p_idx) {
2880
case ACTION_CLEAR: {
2881
if (editing_node) {
2882
emit_changed(get_edited_property(), Variant());
2883
} else {
2884
emit_changed(get_edited_property(), NodePath());
2885
}
2886
update_property();
2887
} break;
2888
2889
case ACTION_COPY: {
2890
DisplayServer::get_singleton()->clipboard_set(String(_get_node_path()));
2891
} break;
2892
2893
case ACTION_EDIT: {
2894
assign->hide();
2895
menu->hide();
2896
2897
const NodePath &np = _get_node_path();
2898
edit->set_text(String(np));
2899
edit->show();
2900
callable_mp((Control *)edit, &Control::grab_focus).call_deferred();
2901
} break;
2902
2903
case ACTION_SELECT: {
2904
const Node *edited_node = get_base_node();
2905
ERR_FAIL_NULL(edited_node);
2906
2907
const NodePath &np = _get_node_path();
2908
Node *target_node = edited_node->get_node_or_null(np);
2909
ERR_FAIL_NULL(target_node);
2910
2911
SceneTreeDock::get_singleton()->set_selected(target_node);
2912
} break;
2913
}
2914
}
2915
2916
void EditorPropertyNodePath::_accept_text() {
2917
_text_submitted(edit->get_text());
2918
}
2919
2920
void EditorPropertyNodePath::_text_submitted(const String &p_text) {
2921
NodePath np = p_text;
2922
_node_selected(np, false);
2923
edit->hide();
2924
assign->show();
2925
menu->show();
2926
}
2927
2928
const NodePath EditorPropertyNodePath::_get_node_path() const {
2929
const Node *base_node = const_cast<EditorPropertyNodePath *>(this)->get_base_node();
2930
2931
Variant val = get_edited_property_value();
2932
Node *n = Object::cast_to<Node>(val);
2933
if (n) {
2934
if (!n->is_inside_tree()) {
2935
return NodePath();
2936
}
2937
if (base_node) {
2938
return base_node->get_path_to(n);
2939
} else {
2940
return get_tree()->get_edited_scene_root()->get_path_to(n);
2941
}
2942
} else {
2943
return val;
2944
}
2945
}
2946
2947
bool EditorPropertyNodePath::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
2948
return !is_read_only() && is_drop_valid(p_data);
2949
}
2950
2951
void EditorPropertyNodePath::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
2952
ERR_FAIL_COND(!is_drop_valid(p_data));
2953
Dictionary data_dict = p_data;
2954
Array nodes = data_dict["nodes"];
2955
Node *node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);
2956
2957
if (node) {
2958
_node_selected(node->get_path());
2959
}
2960
}
2961
2962
bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const {
2963
if (p_drag_data["type"] != "nodes") {
2964
return false;
2965
}
2966
Array nodes = p_drag_data["nodes"];
2967
if (nodes.size() != 1) {
2968
return false;
2969
}
2970
2971
Node *dropped_node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);
2972
ERR_FAIL_NULL_V(dropped_node, false);
2973
2974
if (valid_types.is_empty()) {
2975
// No type requirements specified so any type is valid.
2976
return true;
2977
}
2978
2979
for (const StringName &E : valid_types) {
2980
if (dropped_node->is_class(E) ||
2981
EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, E)) {
2982
return true;
2983
} else {
2984
Ref<Script> dropped_node_script = dropped_node->get_script();
2985
while (dropped_node_script.is_valid()) {
2986
if (dropped_node_script->get_path() == E) {
2987
return true;
2988
}
2989
dropped_node_script = dropped_node_script->get_base_script();
2990
}
2991
}
2992
}
2993
2994
return false;
2995
}
2996
2997
void EditorPropertyNodePath::update_property() {
2998
const Node *base_node = get_base_node();
2999
const NodePath &p = _get_node_path();
3000
assign->set_tooltip_text(String(p));
3001
3002
if (p.is_empty()) {
3003
assign->set_button_icon(Ref<Texture2D>());
3004
assign->set_text(TTR("Assign..."));
3005
assign->set_flat(false);
3006
return;
3007
}
3008
assign->set_flat(true);
3009
3010
if (!base_node || !base_node->has_node(p)) {
3011
assign->set_button_icon(Ref<Texture2D>());
3012
assign->set_text(String(p));
3013
return;
3014
}
3015
3016
const Node *target_node = base_node->get_node(p);
3017
ERR_FAIL_NULL(target_node);
3018
3019
if (String(target_node->get_name()).contains_char('@')) {
3020
assign->set_button_icon(Ref<Texture2D>());
3021
assign->set_text(String(p));
3022
return;
3023
}
3024
3025
assign->set_text(target_node->get_name());
3026
assign->set_button_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));
3027
}
3028
3029
void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) {
3030
valid_types = p_valid_types;
3031
editing_node = p_editing_node;
3032
use_path_from_scene_root = p_use_path_from_scene_root;
3033
}
3034
3035
void EditorPropertyNodePath::_notification(int p_what) {
3036
switch (p_what) {
3037
case NOTIFICATION_THEME_CHANGED: {
3038
menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
3039
menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear")));
3040
menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy")));
3041
menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));
3042
menu->get_popup()->set_item_icon(ACTION_SELECT, get_editor_theme_icon(SNAME("ExternalLink")));
3043
} break;
3044
3045
case NOTIFICATION_DRAG_BEGIN: {
3046
if (!is_read_only() && is_drop_valid(get_viewport()->gui_get_drag_data())) {
3047
dropping = true;
3048
assign->queue_redraw();
3049
}
3050
} break;
3051
3052
case NOTIFICATION_DRAG_END: {
3053
if (dropping) {
3054
dropping = false;
3055
assign->queue_redraw();
3056
}
3057
} break;
3058
}
3059
}
3060
3061
Node *EditorPropertyNodePath::get_base_node() {
3062
Node *base_node = Object::cast_to<Node>(get_edited_object());
3063
3064
if (!base_node) {
3065
base_node = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());
3066
}
3067
if (!base_node) {
3068
// Try a base node within history.
3069
if (EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() > 0) {
3070
Object *base = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(0));
3071
if (base) {
3072
base_node = Object::cast_to<Node>(base);
3073
}
3074
}
3075
}
3076
if (use_path_from_scene_root) {
3077
if (get_edited_object()->has_method("get_root_path")) {
3078
base_node = Object::cast_to<Node>(get_edited_object()->call("get_root_path"));
3079
} else {
3080
base_node = get_tree()->get_edited_scene_root();
3081
}
3082
}
3083
3084
return base_node;
3085
}
3086
3087
EditorPropertyNodePath::EditorPropertyNodePath() {
3088
HBoxContainer *hbc = memnew(HBoxContainer);
3089
hbc->add_theme_constant_override("separation", 0);
3090
add_child(hbc);
3091
assign = memnew(Button);
3092
assign->set_accessibility_name(TTRC("Assign Node"));
3093
assign->set_flat(true);
3094
assign->set_h_size_flags(SIZE_EXPAND_FILL);
3095
assign->set_clip_text(true);
3096
assign->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
3097
assign->set_expand_icon(true);
3098
assign->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyNodePath::_node_assign));
3099
assign->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyNodePath::_assign_draw));
3100
SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);
3101
hbc->add_child(assign);
3102
3103
menu = memnew(MenuButton);
3104
menu->set_flat(true);
3105
menu->connect(SNAME("about_to_popup"), callable_mp(this, &EditorPropertyNodePath::_update_menu));
3106
hbc->add_child(menu);
3107
3108
menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR);
3109
menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY);
3110
menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT);
3111
menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT);
3112
menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyNodePath::_menu_option));
3113
3114
edit = memnew(LineEdit);
3115
edit->set_accessibility_name(TTRC("Node Path"));
3116
edit->set_h_size_flags(SIZE_EXPAND_FILL);
3117
edit->hide();
3118
edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text));
3119
edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyNodePath::_text_submitted));
3120
hbc->add_child(edit);
3121
}
3122
3123
///////////////////// RID /////////////////////////
3124
3125
void EditorPropertyRID::update_property() {
3126
RID rid = get_edited_property_value();
3127
if (rid.is_valid()) {
3128
uint64_t id = rid.get_id();
3129
label->set_text("RID: " + uitos(id));
3130
} else {
3131
label->set_text(TTR("Invalid RID"));
3132
}
3133
}
3134
3135
EditorPropertyRID::EditorPropertyRID() {
3136
label = memnew(Label);
3137
add_child(label);
3138
}
3139
3140
////////////// RESOURCE //////////////////////
3141
3142
void EditorPropertyResource::_set_read_only(bool p_read_only) {
3143
resource_picker->set_editable(!p_read_only);
3144
}
3145
3146
void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {
3147
if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) {
3148
String parent = p_resource->get_path().get_slice("::", 0);
3149
List<String> extensions;
3150
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
3151
3152
if (p_inspect) {
3153
if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) {
3154
// If the resource belongs to another (non-imported) scene, edit it in that scene instead.
3155
if (!FileAccess::exists(parent + ".import")) {
3156
callable_mp(EditorNode::get_singleton(), &EditorNode::edit_foreign_resource).call_deferred(p_resource);
3157
return;
3158
}
3159
}
3160
}
3161
}
3162
3163
if (!p_inspect && use_sub_inspector) {
3164
bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property());
3165
get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold);
3166
update_property();
3167
} else if (!is_checkable() || is_checked()) {
3168
emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);
3169
}
3170
}
3171
3172
static bool _find_recursive_resources(const Variant &v, HashSet<Resource *> &resources_found) {
3173
switch (v.get_type()) {
3174
case Variant::ARRAY: {
3175
Array a = v;
3176
for (int i = 0; i < a.size(); i++) {
3177
Variant v2 = a[i];
3178
if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {
3179
continue;
3180
}
3181
if (_find_recursive_resources(v2, resources_found)) {
3182
return true;
3183
}
3184
}
3185
} break;
3186
case Variant::DICTIONARY: {
3187
Dictionary d = v;
3188
for (const KeyValue<Variant, Variant> &kv : d) {
3189
const Variant &k = kv.key;
3190
const Variant &v2 = kv.value;
3191
if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
3192
if (_find_recursive_resources(k, resources_found)) {
3193
return true;
3194
}
3195
}
3196
if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
3197
if (_find_recursive_resources(v2, resources_found)) {
3198
return true;
3199
}
3200
}
3201
}
3202
} break;
3203
case Variant::OBJECT: {
3204
Ref<Resource> r = v;
3205
3206
if (r.is_null()) {
3207
return false;
3208
}
3209
3210
if (resources_found.has(r.ptr())) {
3211
return true;
3212
}
3213
3214
resources_found.insert(r.ptr());
3215
3216
List<PropertyInfo> plist;
3217
r->get_property_list(&plist);
3218
for (const PropertyInfo &pinfo : plist) {
3219
if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {
3220
continue;
3221
}
3222
3223
if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {
3224
continue;
3225
}
3226
if (_find_recursive_resources(r->get(pinfo.name), resources_found)) {
3227
return true;
3228
}
3229
}
3230
3231
resources_found.erase(r.ptr());
3232
} break;
3233
default: {
3234
}
3235
}
3236
return false;
3237
}
3238
3239
void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) {
3240
Resource *r = Object::cast_to<Resource>(get_edited_object());
3241
if (r) {
3242
// Check for recursive setting of resource
3243
HashSet<Resource *> resources_found;
3244
resources_found.insert(r);
3245
bool found = _find_recursive_resources(p_resource, resources_found);
3246
if (found) {
3247
EditorNode::get_singleton()->show_warning(TTR("Recursion detected, unable to assign resource to property."));
3248
emit_changed(get_edited_property(), Ref<Resource>());
3249
update_property();
3250
return;
3251
}
3252
}
3253
3254
// The bool is_script applies only to an object's main script.
3255
// Changing the value of Script-type exported variables of the main script should not trigger saving/reloading properties.
3256
bool is_script = false;
3257
Ref<Script> s = p_resource;
3258
if (get_edited_object() && s.is_valid() && get_edited_property() == CoreStringName(script)) {
3259
is_script = true;
3260
InspectorDock::get_singleton()->store_script_properties(get_edited_object());
3261
s->call("set_instance_base_type", get_edited_object()->get_class());
3262
}
3263
3264
// Prevent the creation of invalid ViewportTextures when possible.
3265
Ref<ViewportTexture> vpt = p_resource;
3266
if (vpt.is_valid()) {
3267
r = Object::cast_to<Resource>(get_edited_object());
3268
if (Object::cast_to<VisualShaderNodeTexture>(r)) {
3269
EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture in a Texture2D node because the texture will not be bound to a scene.\nUse a Texture2DParameter node instead and set the texture in the \"Shader Parameters\" tab."));
3270
emit_changed(get_edited_property(), Ref<Resource>());
3271
update_property();
3272
return;
3273
}
3274
3275
if (r && r->get_path().is_resource_file()) {
3276
EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene."));
3277
emit_changed(get_edited_property(), Ref<Resource>());
3278
update_property();
3279
return;
3280
}
3281
3282
if (r && !r->is_local_to_scene()) {
3283
EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on this resource because it's not set as local to scene.\nPlease switch on the 'local to scene' property on it (and all resources containing it up to a node)."));
3284
emit_changed(get_edited_property(), Ref<Resource>());
3285
update_property();
3286
return;
3287
}
3288
}
3289
3290
emit_changed(get_edited_property(), p_resource);
3291
update_property();
3292
3293
if (is_script) {
3294
// Restore properties if script was changed.
3295
InspectorDock::get_singleton()->apply_script_properties(get_edited_object());
3296
}
3297
3298
// Automatically suggest setting up the path for a ViewportTexture.
3299
if (vpt.is_valid() && vpt->get_viewport_path_in_scene().is_empty()) {
3300
if (!scene_tree) {
3301
scene_tree = memnew(SceneTreeDialog);
3302
scene_tree->set_title(TTR("Pick a Viewport"));
3303
3304
Vector<StringName> valid_types;
3305
valid_types.push_back("Viewport");
3306
scene_tree->set_valid_types(valid_types);
3307
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
3308
3309
add_child(scene_tree);
3310
scene_tree->connect("selected", callable_mp(this, &EditorPropertyResource::_viewport_selected));
3311
}
3312
scene_tree->popup_scenetree_dialog();
3313
}
3314
}
3315
3316
void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool p_advance) {
3317
// The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it.
3318
const Variant args[3] = { String(get_edited_property()) + ":" + p_property, p_value, p_advance };
3319
const Variant *argp[3] = { &args[0], &args[1], &args[2] };
3320
emit_signalp(SNAME("property_keyed_with_value"), argp, 3);
3321
}
3322
3323
void EditorPropertyResource::_sub_inspector_resource_selected(const Ref<Resource> &p_resource, const String &p_property) {
3324
emit_signal(SNAME("resource_selected"), String(get_edited_property()) + ":" + p_property, p_resource);
3325
}
3326
3327
void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
3328
emit_signal(SNAME("object_id_selected"), get_edited_property(), p_id);
3329
}
3330
3331
void EditorPropertyResource::_open_editor_pressed() {
3332
Ref<Resource> res = get_edited_property_value();
3333
if (res.is_valid()) {
3334
EditorNode::get_singleton()->edit_item(res.ptr(), this);
3335
}
3336
}
3337
3338
void EditorPropertyResource::_update_preferred_shader() {
3339
Node *parent = get_parent();
3340
EditorProperty *parent_property = nullptr;
3341
3342
while (parent && !parent_property) {
3343
parent_property = Object::cast_to<EditorProperty>(parent);
3344
parent = parent->get_parent();
3345
}
3346
3347
if (parent_property) {
3348
EditorShaderPicker *shader_picker = Object::cast_to<EditorShaderPicker>(resource_picker);
3349
Object *ed_object = parent_property->get_edited_object();
3350
const StringName &ed_property = parent_property->get_edited_property();
3351
3352
// Set preferred shader based on edited parent type.
3353
if ((Object::cast_to<GPUParticles2D>(ed_object) || Object::cast_to<GPUParticles3D>(ed_object)) && ed_property == SNAME("process_material")) {
3354
shader_picker->set_preferred_mode(Shader::MODE_PARTICLES);
3355
} else if (Object::cast_to<FogVolume>(ed_object)) {
3356
shader_picker->set_preferred_mode(Shader::MODE_FOG);
3357
} else if (Object::cast_to<CanvasItem>(ed_object)) {
3358
shader_picker->set_preferred_mode(Shader::MODE_CANVAS_ITEM);
3359
} else if (Object::cast_to<Node3D>(ed_object) || Object::cast_to<Mesh>(ed_object)) {
3360
shader_picker->set_preferred_mode(Shader::MODE_SPATIAL);
3361
} else if (Object::cast_to<Sky>(ed_object)) {
3362
shader_picker->set_preferred_mode(Shader::MODE_SKY);
3363
}
3364
}
3365
}
3366
3367
bool EditorPropertyResource::_should_stop_editing() const {
3368
return !resource_picker->is_toggle_pressed();
3369
}
3370
3371
void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
3372
Node *to_node = get_node(p_path);
3373
if (!Object::cast_to<Viewport>(to_node)) {
3374
EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));
3375
return;
3376
}
3377
3378
Ref<ViewportTexture> vt = get_edited_property_value();
3379
ERR_FAIL_COND(vt.is_null());
3380
3381
vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
3382
3383
emit_changed(get_edited_property(), vt);
3384
update_property();
3385
}
3386
3387
void EditorPropertyResource::setup(Object *p_object, const String &p_path, const String &p_base_type) {
3388
if (resource_picker) {
3389
memdelete(resource_picker);
3390
resource_picker = nullptr;
3391
}
3392
3393
if (p_path == "script" && p_base_type == "Script" && Object::cast_to<Node>(p_object)) {
3394
EditorScriptPicker *script_picker = memnew(EditorScriptPicker);
3395
script_picker->set_script_owner(Object::cast_to<Node>(p_object));
3396
resource_picker = script_picker;
3397
} else if (p_path == "shader" && p_base_type == "Shader" && Object::cast_to<ShaderMaterial>(p_object)) {
3398
EditorShaderPicker *shader_picker = memnew(EditorShaderPicker);
3399
shader_picker->set_edited_material(Object::cast_to<ShaderMaterial>(p_object));
3400
resource_picker = shader_picker;
3401
connect(SceneStringName(ready), callable_mp(this, &EditorPropertyResource::_update_preferred_shader));
3402
} else if (ClassDB::is_parent_class(p_base_type, "AudioStream")) {
3403
EditorAudioStreamPicker *astream_picker = memnew(EditorAudioStreamPicker);
3404
resource_picker = astream_picker;
3405
} else {
3406
resource_picker = memnew(EditorResourcePicker);
3407
}
3408
3409
resource_picker->set_base_type(p_base_type);
3410
resource_picker->set_resource_owner(p_object);
3411
resource_picker->set_editable(true);
3412
resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);
3413
add_child(resource_picker);
3414
3415
resource_picker->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_resource_selected));
3416
resource_picker->connect("resource_changed", callable_mp(this, &EditorPropertyResource::_resource_changed));
3417
3418
for (int i = 0; i < resource_picker->get_child_count(); i++) {
3419
Button *b = Object::cast_to<Button>(resource_picker->get_child(i));
3420
if (b) {
3421
add_focusable(b);
3422
}
3423
}
3424
}
3425
3426
void EditorPropertyResource::update_property() {
3427
Ref<Resource> res = get_edited_property_display_value();
3428
3429
if (use_sub_inspector) {
3430
if (res.is_valid() != resource_picker->is_toggle_mode()) {
3431
resource_picker->set_toggle_mode(res.is_valid());
3432
}
3433
3434
if (res.is_valid() && get_edited_object()->editor_is_section_unfolded(get_edited_property())) {
3435
if (!sub_inspector) {
3436
sub_inspector = memnew(EditorInspector);
3437
sub_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
3438
sub_inspector->set_use_doc_hints(true);
3439
3440
EditorInspector *parent_inspector = get_parent_inspector();
3441
if (parent_inspector) {
3442
sub_inspector->set_root_inspector(parent_inspector->get_root_inspector());
3443
sub_inspector->register_text_enter(parent_inspector->search_box);
3444
}
3445
3446
sub_inspector->set_property_name_style(InspectorDock::get_singleton()->get_property_name_style());
3447
3448
sub_inspector->connect("property_keyed", callable_mp(this, &EditorPropertyResource::_sub_inspector_property_keyed));
3449
sub_inspector->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_resource_selected));
3450
sub_inspector->connect("object_id_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_object_id_selected));
3451
sub_inspector->set_keying(is_keying());
3452
sub_inspector->set_read_only(is_read_only());
3453
sub_inspector->set_use_folding(is_using_folding());
3454
3455
sub_inspector->set_draw_focus_border(false);
3456
sub_inspector->set_focus_mode(FocusMode::FOCUS_NONE);
3457
3458
sub_inspector->set_use_filter(use_filter);
3459
3460
add_child(sub_inspector);
3461
set_bottom_editor(sub_inspector);
3462
3463
resource_picker->set_toggle_pressed(true);
3464
3465
Array editor_list;
3466
for (int i = 0; i < EditorNode::get_editor_data().get_editor_plugin_count(); i++) {
3467
EditorPlugin *ep = EditorNode::get_editor_data().get_editor_plugin(i);
3468
if (ep->handles(res.ptr())) {
3469
editor_list.push_back(ep);
3470
}
3471
}
3472
3473
if (!editor_list.is_empty()) {
3474
// Open editor directly.
3475
_open_editor_pressed();
3476
opened_editor = true;
3477
}
3478
}
3479
3480
sub_inspector->set_read_only(is_checkable() && !is_checked());
3481
3482
if (res.ptr() != sub_inspector->get_edited_object()) {
3483
sub_inspector->edit(res.ptr());
3484
_update_property_bg();
3485
}
3486
3487
} else if (sub_inspector) {
3488
set_bottom_editor(nullptr);
3489
memdelete(sub_inspector);
3490
sub_inspector = nullptr;
3491
3492
if (opened_editor) {
3493
EditorNode::get_singleton()->hide_unused_editors();
3494
opened_editor = false;
3495
}
3496
}
3497
}
3498
3499
resource_picker->set_edited_resource_no_check(res);
3500
}
3501
3502
void EditorPropertyResource::collapse_all_folding() {
3503
if (sub_inspector) {
3504
sub_inspector->collapse_all_folding();
3505
}
3506
}
3507
3508
void EditorPropertyResource::expand_all_folding() {
3509
if (sub_inspector) {
3510
sub_inspector->expand_all_folding();
3511
}
3512
}
3513
3514
void EditorPropertyResource::expand_revertable() {
3515
if (sub_inspector) {
3516
sub_inspector->expand_revertable();
3517
}
3518
}
3519
3520
void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {
3521
use_sub_inspector = p_enable;
3522
}
3523
3524
void EditorPropertyResource::set_use_filter(bool p_use) {
3525
use_filter = p_use;
3526
if (sub_inspector) {
3527
update_property();
3528
}
3529
}
3530
3531
void EditorPropertyResource::fold_resource() {
3532
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
3533
if (unfolded) {
3534
resource_picker->set_toggle_pressed(false);
3535
get_edited_object()->editor_set_section_unfold(get_edited_property(), false);
3536
update_property();
3537
}
3538
}
3539
3540
bool EditorPropertyResource::is_colored(ColorationMode p_mode) {
3541
switch (p_mode) {
3542
case COLORATION_CONTAINER_RESOURCE:
3543
return sub_inspector != nullptr;
3544
case COLORATION_RESOURCE:
3545
return true;
3546
case COLORATION_EXTERNAL:
3547
if (sub_inspector) {
3548
Resource *edited_resource = Object::cast_to<Resource>(sub_inspector->get_edited_object());
3549
return edited_resource && !edited_resource->is_built_in();
3550
}
3551
break;
3552
}
3553
return false;
3554
}
3555
3556
void EditorPropertyResource::_notification(int p_what) {
3557
switch (p_what) {
3558
case NOTIFICATION_EXIT_TREE: {
3559
const EditorInspector *ei = get_parent_inspector();
3560
const EditorInspector *main_ei = InspectorDock::get_inspector_singleton();
3561
if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) {
3562
fold_resource();
3563
}
3564
} break;
3565
}
3566
}
3567
3568
void EditorPropertyResource::_bind_methods() {
3569
ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing);
3570
}
3571
3572
EditorPropertyResource::EditorPropertyResource() {
3573
use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
3574
has_borders = true;
3575
}
3576
3577
////////////// DEFAULT PLUGIN //////////////////////
3578
3579
bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {
3580
return true; // Can handle everything.
3581
}
3582
3583
bool EditorInspectorDefaultPlugin::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) {
3584
Control *editor = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide);
3585
if (editor) {
3586
add_property_editor(p_path, editor);
3587
}
3588
return false;
3589
}
3590
3591
struct EditorPropertyRangeHint {
3592
bool or_greater = true;
3593
bool or_less = true;
3594
double min = 0.0;
3595
double max = 0.0;
3596
double step = 1.0;
3597
String suffix;
3598
bool exp_range = false;
3599
bool hide_slider = true;
3600
bool radians_as_degrees = false;
3601
};
3602
3603
static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const String &p_hint_text, double p_default_step, bool is_int = false) {
3604
EditorPropertyRangeHint hint;
3605
hint.step = p_default_step;
3606
if (is_int) {
3607
hint.hide_slider = false; // Always show slider for ints, unless specified in hint range.
3608
}
3609
Vector<String> slices = p_hint_text.split(",");
3610
if (p_hint == PROPERTY_HINT_RANGE) {
3611
ERR_FAIL_COND_V_MSG(slices.size() < 2, hint,
3612
vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Missing required min and/or max values.", p_hint_text));
3613
3614
hint.or_greater = false; // If using ranged, assume false by default.
3615
hint.or_less = false;
3616
3617
hint.min = slices[0].to_float();
3618
hint.max = slices[1].to_float();
3619
3620
if (slices.size() >= 3 && slices[2].is_valid_float()) {
3621
// Step is optional, could be something else if not a number.
3622
hint.step = slices[2].to_float();
3623
}
3624
hint.hide_slider = false;
3625
for (int i = 2; i < slices.size(); i++) {
3626
String slice = slices[i].strip_edges();
3627
if (slice == "or_greater") {
3628
hint.or_greater = true;
3629
} else if (slice == "or_less") {
3630
hint.or_less = true;
3631
} else if (slice == "hide_slider") {
3632
hint.hide_slider = true;
3633
} else if (slice == "exp") {
3634
hint.exp_range = true;
3635
}
3636
}
3637
}
3638
bool degrees = false;
3639
for (int i = 0; i < slices.size(); i++) {
3640
String slice = slices[i].strip_edges();
3641
if (slice == "radians_as_degrees"
3642
#ifndef DISABLE_DEPRECATED
3643
|| slice == "radians"
3644
#endif // DISABLE_DEPRECATED
3645
) {
3646
hint.radians_as_degrees = true;
3647
} else if (slice == "degrees") {
3648
degrees = true;
3649
} else if (slice.begins_with("suffix:")) {
3650
hint.suffix = " " + slice.replace_first("suffix:", "").strip_edges();
3651
}
3652
}
3653
3654
if ((hint.radians_as_degrees || degrees) && hint.suffix.is_empty()) {
3655
hint.suffix = U"\u00B0";
3656
}
3657
3658
ERR_FAIL_COND_V_MSG(hint.step == 0, hint,
3659
vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Step cannot be 0.", p_hint_text));
3660
3661
return hint;
3662
}
3663
3664
static EditorProperty *get_input_action_editor(const String &p_hint_text, bool is_string_name) {
3665
// TODO: Should probably use a better editor GUI with a search bar.
3666
// Said GUI could also handle showing builtin options, requiring 1 less hint.
3667
EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
3668
Vector<String> options;
3669
Vector<String> builtin_options;
3670
List<PropertyInfo> pinfo;
3671
ProjectSettings::get_singleton()->get_property_list(&pinfo);
3672
Vector<String> hints = p_hint_text.remove_char(' ').split(",", false);
3673
3674
HashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins();
3675
bool show_builtin = hints.has("show_builtin");
3676
3677
for (const PropertyInfo &pi : pinfo) {
3678
if (!pi.name.begins_with("input/")) {
3679
continue;
3680
}
3681
3682
const String action_name = pi.name.get_slicec('/', 1);
3683
if (builtins.has(action_name)) {
3684
if (show_builtin) {
3685
builtin_options.append(action_name);
3686
}
3687
} else {
3688
options.append(action_name);
3689
}
3690
}
3691
options.append_array(builtin_options);
3692
editor->setup(options, is_string_name, hints.has("loose_mode"));
3693
return editor;
3694
}
3695
3696
EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_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) {
3697
double default_float_step = EDITOR_GET("interface/inspector/default_float_step");
3698
3699
switch (p_type) {
3700
// atomic types
3701
case Variant::NIL: {
3702
if (p_usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
3703
return memnew(EditorPropertyVariant);
3704
} else {
3705
return memnew(EditorPropertyNil);
3706
}
3707
} break;
3708
case Variant::BOOL: {
3709
EditorPropertyCheck *editor = memnew(EditorPropertyCheck);
3710
return editor;
3711
} break;
3712
case Variant::INT: {
3713
if (p_hint == PROPERTY_HINT_ENUM) {
3714
EditorPropertyEnum *editor = memnew(EditorPropertyEnum);
3715
Vector<String> options = p_hint_text.split(",");
3716
editor->setup(options);
3717
return editor;
3718
3719
} else if (p_hint == PROPERTY_HINT_FLAGS) {
3720
EditorPropertyFlags *editor = memnew(EditorPropertyFlags);
3721
Vector<String> options = p_hint_text.split(",");
3722
editor->setup(options);
3723
return editor;
3724
3725
} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS ||
3726
p_hint == PROPERTY_HINT_LAYERS_2D_RENDER ||
3727
p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||
3728
p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||
3729
p_hint == PROPERTY_HINT_LAYERS_3D_RENDER ||
3730
p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||
3731
p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {
3732
EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;
3733
switch (p_hint) {
3734
case PROPERTY_HINT_LAYERS_2D_RENDER:
3735
lt = EditorPropertyLayers::LAYER_RENDER_2D;
3736
break;
3737
case PROPERTY_HINT_LAYERS_2D_PHYSICS:
3738
lt = EditorPropertyLayers::LAYER_PHYSICS_2D;
3739
break;
3740
case PROPERTY_HINT_LAYERS_2D_NAVIGATION:
3741
lt = EditorPropertyLayers::LAYER_NAVIGATION_2D;
3742
break;
3743
case PROPERTY_HINT_LAYERS_3D_RENDER:
3744
lt = EditorPropertyLayers::LAYER_RENDER_3D;
3745
break;
3746
case PROPERTY_HINT_LAYERS_3D_PHYSICS:
3747
lt = EditorPropertyLayers::LAYER_PHYSICS_3D;
3748
break;
3749
case PROPERTY_HINT_LAYERS_3D_NAVIGATION:
3750
lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;
3751
break;
3752
case PROPERTY_HINT_LAYERS_AVOIDANCE:
3753
lt = EditorPropertyLayers::LAYER_AVOIDANCE;
3754
break;
3755
default: {
3756
} //compiler could be smarter here and realize this can't happen
3757
}
3758
EditorPropertyLayers *editor = memnew(EditorPropertyLayers);
3759
editor->setup(lt);
3760
return editor;
3761
} else if (p_hint == PROPERTY_HINT_OBJECT_ID) {
3762
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
3763
editor->setup(p_hint_text);
3764
return editor;
3765
3766
} else {
3767
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
3768
3769
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
3770
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix);
3771
3772
return editor;
3773
}
3774
} break;
3775
case Variant::FLOAT: {
3776
if (p_hint == PROPERTY_HINT_EXP_EASING) {
3777
EditorPropertyEasing *editor = memnew(EditorPropertyEasing);
3778
bool positive_only = false;
3779
bool flip = false;
3780
const Vector<String> hints = p_hint_text.split(",");
3781
for (int i = 0; i < hints.size(); i++) {
3782
const String hint = hints[i].strip_edges();
3783
if (hint == "attenuation") {
3784
flip = true;
3785
}
3786
if (hint == "positive_only") {
3787
positive_only = true;
3788
}
3789
}
3790
3791
editor->setup(positive_only, flip);
3792
return editor;
3793
3794
} else {
3795
EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
3796
3797
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3798
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.exp_range, hint.or_greater, hint.or_less, hint.suffix, hint.radians_as_degrees);
3799
3800
return editor;
3801
}
3802
} break;
3803
case Variant::STRING: {
3804
if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {
3805
EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
3806
Vector<String> options = p_hint_text.split(",", false);
3807
editor->setup(options, false, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));
3808
return editor;
3809
} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {
3810
return get_input_action_editor(p_hint_text, false);
3811
} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
3812
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
3813
return editor;
3814
} else if (p_hint == PROPERTY_HINT_EXPRESSION) {
3815
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(true));
3816
return editor;
3817
} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {
3818
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
3819
editor->setup(p_hint_text, p_hint_text);
3820
return editor;
3821
} else if (p_hint == PROPERTY_HINT_LOCALE_ID) {
3822
EditorPropertyLocale *editor = memnew(EditorPropertyLocale);
3823
editor->setup(p_hint_text);
3824
return editor;
3825
} else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_FILE_PATH) {
3826
Vector<String> extensions = p_hint_text.split(",");
3827
bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;
3828
bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;
3829
bool save = p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;
3830
bool enable_uid = p_hint == PROPERTY_HINT_FILE;
3831
EditorPropertyPath *editor = memnew(EditorPropertyPath);
3832
editor->setup(extensions, folder, global, enable_uid);
3833
if (save) {
3834
editor->set_save_mode();
3835
}
3836
return editor;
3837
} else {
3838
EditorPropertyText *editor = memnew(EditorPropertyText);
3839
if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {
3840
editor->set_placeholder(p_hint_text);
3841
} else if (p_hint == PROPERTY_HINT_PASSWORD) {
3842
editor->set_secret(true);
3843
editor->set_placeholder(p_hint_text);
3844
}
3845
return editor;
3846
}
3847
} break;
3848
3849
// math types
3850
3851
case Variant::VECTOR2: {
3852
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide));
3853
3854
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3855
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);
3856
return editor;
3857
3858
} break;
3859
case Variant::VECTOR2I: {
3860
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
3861
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
3862
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
3863
return editor;
3864
3865
} break;
3866
case Variant::RECT2: {
3867
EditorPropertyRect2 *editor = memnew(EditorPropertyRect2(p_wide));
3868
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3869
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3870
return editor;
3871
} break;
3872
case Variant::RECT2I: {
3873
EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide));
3874
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
3875
editor->setup(hint.min, hint.max, hint.suffix);
3876
3877
return editor;
3878
} break;
3879
case Variant::VECTOR3: {
3880
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide));
3881
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3882
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);
3883
return editor;
3884
3885
} break;
3886
case Variant::VECTOR3I: {
3887
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
3888
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
3889
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
3890
return editor;
3891
3892
} break;
3893
case Variant::VECTOR4: {
3894
EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
3895
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3896
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);
3897
return editor;
3898
3899
} break;
3900
case Variant::VECTOR4I: {
3901
EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
3902
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
3903
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
3904
return editor;
3905
3906
} break;
3907
case Variant::TRANSFORM2D: {
3908
EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
3909
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3910
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3911
return editor;
3912
} break;
3913
case Variant::PLANE: {
3914
EditorPropertyPlane *editor = memnew(EditorPropertyPlane(p_wide));
3915
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3916
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3917
return editor;
3918
} break;
3919
case Variant::QUATERNION: {
3920
EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion);
3921
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3922
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix, p_hint == PROPERTY_HINT_HIDE_QUATERNION_EDIT);
3923
return editor;
3924
} break;
3925
case Variant::AABB: {
3926
EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
3927
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3928
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3929
return editor;
3930
} break;
3931
case Variant::BASIS: {
3932
EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
3933
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3934
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3935
return editor;
3936
} break;
3937
case Variant::TRANSFORM3D: {
3938
EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D);
3939
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3940
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3941
return editor;
3942
3943
} break;
3944
case Variant::PROJECTION: {
3945
EditorPropertyProjection *editor = memnew(EditorPropertyProjection);
3946
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
3947
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
3948
return editor;
3949
3950
} break;
3951
3952
// misc types
3953
case Variant::COLOR: {
3954
EditorPropertyColor *editor = memnew(EditorPropertyColor);
3955
editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA);
3956
return editor;
3957
} break;
3958
case Variant::STRING_NAME: {
3959
if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {
3960
EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
3961
Vector<String> options = p_hint_text.split(",", false);
3962
editor->setup(options, true, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));
3963
return editor;
3964
} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {
3965
return get_input_action_editor(p_hint_text, true);
3966
} else {
3967
EditorPropertyText *editor = memnew(EditorPropertyText);
3968
if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {
3969
editor->set_placeholder(p_hint_text);
3970
} else if (p_hint == PROPERTY_HINT_PASSWORD) {
3971
editor->set_secret(true);
3972
editor->set_placeholder(p_hint_text);
3973
}
3974
editor->set_string_name(true);
3975
return editor;
3976
}
3977
} break;
3978
case Variant::NODE_PATH: {
3979
EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);
3980
if (p_hint == PROPERTY_HINT_NODE_PATH_VALID_TYPES && !p_hint_text.is_empty()) {
3981
Vector<String> types = p_hint_text.split(",", false);
3982
Vector<StringName> sn = Variant(types); //convert via variant
3983
editor->setup(sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT));
3984
}
3985
return editor;
3986
3987
} break;
3988
case Variant::RID: {
3989
EditorPropertyRID *editor = memnew(EditorPropertyRID);
3990
return editor;
3991
} break;
3992
case Variant::OBJECT: {
3993
if (p_hint == PROPERTY_HINT_NODE_TYPE) {
3994
EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);
3995
Vector<String> types = p_hint_text.split(",", false);
3996
Vector<StringName> sn = Variant(types); //convert via variant
3997
editor->setup(sn, false, true);
3998
return editor;
3999
} else {
4000
EditorPropertyResource *editor = memnew(EditorPropertyResource);
4001
editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
4002
4003
if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
4004
const PackedStringArray open_in_new_inspector = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");
4005
4006
for (const String &type : open_in_new_inspector) {
4007
for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {
4008
const String inherits = p_hint_text.get_slicec(',', j);
4009
if (ClassDB::is_parent_class(inherits, type)) {
4010
editor->set_use_sub_inspector(false);
4011
}
4012
}
4013
}
4014
}
4015
4016
return editor;
4017
}
4018
4019
} break;
4020
case Variant::CALLABLE: {
4021
EditorPropertyCallable *editor = memnew(EditorPropertyCallable);
4022
return editor;
4023
} break;
4024
case Variant::SIGNAL: {
4025
EditorPropertySignal *editor = memnew(EditorPropertySignal);
4026
return editor;
4027
} break;
4028
case Variant::DICTIONARY: {
4029
if (p_hint == PROPERTY_HINT_LOCALIZABLE_STRING) {
4030
EditorPropertyLocalizableString *editor = memnew(EditorPropertyLocalizableString);
4031
return editor;
4032
} else {
4033
EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);
4034
editor->setup(p_hint, p_hint_text);
4035
return editor;
4036
}
4037
} break;
4038
case Variant::ARRAY: {
4039
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4040
editor->setup(Variant::ARRAY, p_hint_text);
4041
return editor;
4042
} break;
4043
case Variant::PACKED_BYTE_ARRAY: {
4044
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4045
editor->setup(Variant::PACKED_BYTE_ARRAY, p_hint_text);
4046
return editor;
4047
} break;
4048
case Variant::PACKED_INT32_ARRAY: {
4049
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4050
editor->setup(Variant::PACKED_INT32_ARRAY, p_hint_text);
4051
return editor;
4052
} break;
4053
case Variant::PACKED_INT64_ARRAY: {
4054
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4055
editor->setup(Variant::PACKED_INT64_ARRAY, p_hint_text);
4056
return editor;
4057
} break;
4058
case Variant::PACKED_FLOAT32_ARRAY: {
4059
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4060
editor->setup(Variant::PACKED_FLOAT32_ARRAY, p_hint_text);
4061
return editor;
4062
} break;
4063
case Variant::PACKED_FLOAT64_ARRAY: {
4064
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4065
editor->setup(Variant::PACKED_FLOAT64_ARRAY, p_hint_text);
4066
return editor;
4067
} break;
4068
case Variant::PACKED_STRING_ARRAY: {
4069
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4070
editor->setup(Variant::PACKED_STRING_ARRAY, p_hint_text);
4071
return editor;
4072
} break;
4073
case Variant::PACKED_VECTOR2_ARRAY: {
4074
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4075
editor->setup(Variant::PACKED_VECTOR2_ARRAY, p_hint_text);
4076
return editor;
4077
} break;
4078
case Variant::PACKED_VECTOR3_ARRAY: {
4079
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4080
editor->setup(Variant::PACKED_VECTOR3_ARRAY, p_hint_text);
4081
return editor;
4082
} break;
4083
case Variant::PACKED_COLOR_ARRAY: {
4084
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4085
editor->setup(Variant::PACKED_COLOR_ARRAY, p_hint_text);
4086
return editor;
4087
} break;
4088
case Variant::PACKED_VECTOR4_ARRAY: {
4089
EditorPropertyArray *editor = memnew(EditorPropertyArray);
4090
editor->setup(Variant::PACKED_VECTOR4_ARRAY, p_hint_text);
4091
return editor;
4092
} break;
4093
default: {
4094
}
4095
}
4096
4097
return nullptr;
4098
}
4099
4100