1 /*************************************************************************/
2 /* property_editor.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 "property_editor.h"
32
33 #include "core/class_db.h"
34 #include "core/io/image_loader.h"
35 #include "core/io/marshalls.h"
36 #include "core/io/resource_loader.h"
37 #include "core/math/expression.h"
38 #include "core/os/input.h"
39 #include "core/os/keyboard.h"
40 #include "core/pair.h"
41 #include "core/print_string.h"
42 #include "core/project_settings.h"
43 #include "editor/array_property_edit.h"
44 #include "editor/create_dialog.h"
45 #include "editor/dictionary_property_edit.h"
46 #include "editor/editor_export.h"
47 #include "editor/editor_file_system.h"
48 #include "editor/editor_help.h"
49 #include "editor/editor_node.h"
50 #include "editor/editor_scale.h"
51 #include "editor/editor_settings.h"
52 #include "editor/filesystem_dock.h"
53 #include "editor/multi_node_edit.h"
54 #include "editor/property_selector.h"
55 #include "scene/gui/label.h"
56 #include "scene/main/viewport.h"
57 #include "scene/resources/font.h"
58 #include "scene/resources/packed_scene.h"
59 #include "scene/scene_string_names.h"
60
_bind_methods()61 void EditorResourceConversionPlugin::_bind_methods() {
62
63 MethodInfo mi;
64 mi.name = "_convert";
65 mi.return_val.type = Variant::OBJECT;
66 mi.return_val.class_name = "Resource";
67 mi.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE;
68 mi.return_val.hint_string = "Resource";
69 mi.arguments.push_back(mi.return_val);
70 mi.arguments[0].name = "resource";
71
72 BIND_VMETHOD(mi)
73
74 mi.name = "_handles";
75 mi.return_val = PropertyInfo(Variant::BOOL, "");
76
77 BIND_VMETHOD(MethodInfo(Variant::STRING, "_converts_to"));
78 }
79
converts_to() const80 String EditorResourceConversionPlugin::converts_to() const {
81
82 if (get_script_instance())
83 return get_script_instance()->call("_converts_to");
84
85 return "";
86 }
87
handles(const Ref<Resource> & p_resource) const88 bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) const {
89
90 if (get_script_instance())
91 return get_script_instance()->call("_handles", p_resource);
92
93 return false;
94 }
95
convert(const Ref<Resource> & p_resource) const96 Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const {
97
98 if (get_script_instance())
99 return get_script_instance()->call("_convert", p_resource);
100
101 return Ref<Resource>();
102 }
103
_notification(int p_what)104 void CustomPropertyEditor::_notification(int p_what) {
105
106 if (p_what == NOTIFICATION_DRAW) {
107
108 RID ci = get_canvas_item();
109 get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size()));
110 }
111 if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) {
112 hide();
113 }
114 }
115
_menu_option(int p_which)116 void CustomPropertyEditor::_menu_option(int p_which) {
117
118 switch (type) {
119
120 case Variant::INT: {
121
122 if (hint == PROPERTY_HINT_FLAGS) {
123
124 int val = v;
125
126 if (val & (1 << p_which)) {
127
128 val &= ~(1 << p_which);
129 } else {
130 val |= (1 << p_which);
131 }
132
133 v = val;
134 emit_signal("variant_changed");
135 } else if (hint == PROPERTY_HINT_ENUM) {
136
137 v = menu->get_item_metadata(p_which);
138 emit_signal("variant_changed");
139 }
140 } break;
141 case Variant::STRING: {
142
143 if (hint == PROPERTY_HINT_ENUM) {
144
145 v = hint_text.get_slice(",", p_which);
146 emit_signal("variant_changed");
147 }
148 } break;
149 case Variant::OBJECT: {
150
151 switch (p_which) {
152 case OBJ_MENU_LOAD: {
153
154 file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
155 String type = (hint == PROPERTY_HINT_RESOURCE_TYPE) ? hint_text : String();
156
157 List<String> extensions;
158 for (int i = 0; i < type.get_slice_count(","); i++) {
159
160 ResourceLoader::get_recognized_extensions_for_type(type.get_slice(",", i), &extensions);
161 }
162
163 Set<String> valid_extensions;
164 for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
165 valid_extensions.insert(E->get());
166 }
167
168 file->clear_filters();
169 for (Set<String>::Element *E = valid_extensions.front(); E; E = E->next()) {
170
171 file->add_filter("*." + E->get() + " ; " + E->get().to_upper());
172 }
173
174 file->popup_centered_ratio();
175 } break;
176
177 case OBJ_MENU_EDIT: {
178
179 RefPtr RefPtr = v;
180
181 if (!RefPtr.is_null()) {
182
183 emit_signal("resource_edit_request");
184 hide();
185 }
186 } break;
187 case OBJ_MENU_CLEAR: {
188
189 v = Variant();
190 emit_signal("variant_changed");
191 hide();
192 } break;
193
194 case OBJ_MENU_MAKE_UNIQUE: {
195
196 RefPtr RefPtr = v;
197 Ref<Resource> res_orig = RefPtr;
198 if (res_orig.is_null())
199 return;
200
201 List<PropertyInfo> property_list;
202 res_orig->get_property_list(&property_list);
203 List<Pair<String, Variant> > propvalues;
204
205 for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
206
207 Pair<String, Variant> p;
208 PropertyInfo &pi = E->get();
209 if (pi.usage & PROPERTY_USAGE_STORAGE) {
210
211 p.first = pi.name;
212 p.second = res_orig->get(pi.name);
213 }
214
215 propvalues.push_back(p);
216 }
217
218 String orig_type = res_orig->get_class();
219
220 Object *inst = ClassDB::instance(orig_type);
221
222 Ref<Resource> res = Ref<Resource>(Object::cast_to<Resource>(inst));
223
224 ERR_FAIL_COND(res.is_null());
225
226 for (List<Pair<String, Variant> >::Element *E = propvalues.front(); E; E = E->next()) {
227
228 Pair<String, Variant> &p = E->get();
229 res->set(p.first, p.second);
230 }
231
232 v = res.get_ref_ptr();
233 emit_signal("variant_changed");
234 hide();
235 } break;
236
237 case OBJ_MENU_COPY: {
238
239 EditorSettings::get_singleton()->set_resource_clipboard(v);
240
241 } break;
242 case OBJ_MENU_PASTE: {
243
244 v = EditorSettings::get_singleton()->get_resource_clipboard();
245 emit_signal("variant_changed");
246
247 } break;
248 case OBJ_MENU_NEW_SCRIPT: {
249
250 if (Object::cast_to<Node>(owner))
251 EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), false);
252
253 } break;
254 case OBJ_MENU_EXTEND_SCRIPT: {
255
256 if (Object::cast_to<Node>(owner))
257 EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), true);
258
259 } break;
260 case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
261 RES r = v;
262 FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
263 file_system_dock->navigate_to_path(r->get_path());
264 // Ensure that the FileSystem dock is visible.
265 TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
266 tab_container->set_current_tab(file_system_dock->get_position_in_parent());
267 } break;
268 default: {
269
270 if (p_which >= CONVERT_BASE_ID) {
271
272 int to_type = p_which - CONVERT_BASE_ID;
273
274 Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(RES(v));
275
276 ERR_FAIL_INDEX(to_type, conversions.size());
277
278 Ref<Resource> new_res = conversions[to_type]->convert(v);
279
280 v = new_res;
281 emit_signal("variant_changed");
282 break;
283 }
284 ERR_FAIL_COND(inheritors_array.empty());
285
286 String intype = inheritors_array[p_which - TYPE_BASE_ID];
287
288 if (intype == "ViewportTexture") {
289
290 scene_tree->set_title(TTR("Pick a Viewport"));
291 scene_tree->popup_centered_ratio();
292 picking_viewport = true;
293 return;
294 }
295
296 Object *obj = ClassDB::instance(intype);
297
298 if (!obj) {
299 if (ScriptServer::is_global_class(intype)) {
300 obj = EditorNode::get_editor_data().script_class_instance(intype);
301 } else {
302 obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
303 }
304 }
305
306 ERR_BREAK(!obj);
307 Resource *res = Object::cast_to<Resource>(obj);
308 ERR_BREAK(!res);
309 if (owner && hint == PROPERTY_HINT_RESOURCE_TYPE && hint_text == "Script") {
310 //make visual script the right type
311 res->call("set_instance_base_type", owner->get_class());
312 }
313
314 v = Ref<Resource>(res).get_ref_ptr();
315 emit_signal("variant_changed");
316
317 } break;
318 }
319
320 } break;
321 default: {
322 }
323 }
324 }
325
hide_menu()326 void CustomPropertyEditor::hide_menu() {
327 menu->hide();
328 }
329
get_variant() const330 Variant CustomPropertyEditor::get_variant() const {
331
332 return v;
333 }
334
get_name() const335 String CustomPropertyEditor::get_name() const {
336
337 return name;
338 }
339
edit(Object * p_owner,const String & p_name,Variant::Type p_type,const Variant & p_variant,int p_hint,String p_hint_text)340 bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::Type p_type, const Variant &p_variant, int p_hint, String p_hint_text) {
341
342 owner = p_owner;
343 updating = true;
344 name = p_name;
345 v = p_variant;
346 field_names.clear();
347 hint = p_hint;
348 hint_text = p_hint_text;
349 type_button->hide();
350 if (color_picker)
351 color_picker->hide();
352 texture_preview->hide();
353 inheritors_array.clear();
354 text_edit->hide();
355 easing_draw->hide();
356 spinbox->hide();
357 slider->hide();
358 menu->clear();
359 menu->set_size(Size2(1, 1) * EDSCALE);
360
361 for (int i = 0; i < MAX_VALUE_EDITORS; i++) {
362
363 value_editor[i]->hide();
364 value_label[i]->hide();
365 if (i < 4)
366 scroll[i]->hide();
367 }
368
369 for (int i = 0; i < MAX_ACTION_BUTTONS; i++) {
370
371 action_buttons[i]->hide();
372 }
373
374 checks20gc->hide();
375 for (int i = 0; i < 20; i++)
376 checks20[i]->hide();
377
378 type = (p_variant.get_type() != Variant::NIL && p_variant.get_type() != Variant::_RID && p_type != Variant::OBJECT) ? p_variant.get_type() : p_type;
379
380 switch (type) {
381
382 case Variant::BOOL: {
383
384 checks20gc->show();
385
386 CheckBox *c = checks20[0];
387 c->set_text("True");
388 checks20gc->set_position(Vector2(4, 4) * EDSCALE);
389 c->set_pressed(v);
390 c->show();
391
392 checks20gc->set_size(checks20gc->get_minimum_size());
393 set_size(checks20gc->get_position() + checks20gc->get_size() + c->get_size() + Vector2(4, 4) * EDSCALE);
394
395 } break;
396 case Variant::INT:
397 case Variant::REAL: {
398
399 if (hint == PROPERTY_HINT_RANGE) {
400
401 int c = hint_text.get_slice_count(",");
402 float min = 0, max = 100, step = type == Variant::REAL ? .01 : 1;
403 if (c >= 1) {
404
405 if (!hint_text.get_slice(",", 0).empty())
406 min = hint_text.get_slice(",", 0).to_double();
407 }
408 if (c >= 2) {
409
410 if (!hint_text.get_slice(",", 1).empty())
411 max = hint_text.get_slice(",", 1).to_double();
412 }
413
414 if (c >= 3) {
415
416 if (!hint_text.get_slice(",", 2).empty())
417 step = hint_text.get_slice(",", 2).to_double();
418 }
419
420 if (c >= 4 && hint_text.get_slice(",", 3) == "slider") {
421 slider->set_min(min);
422 slider->set_max(max);
423 slider->set_step(step);
424 slider->set_value(v);
425 slider->show();
426 set_size(Size2(110, 30) * EDSCALE);
427 } else {
428 spinbox->set_min(min);
429 spinbox->set_max(max);
430 spinbox->set_step(step);
431 spinbox->set_value(v);
432 spinbox->show();
433 set_size(Size2(70, 35) * EDSCALE);
434 }
435
436 } else if (hint == PROPERTY_HINT_ENUM) {
437
438 Vector<String> options = hint_text.split(",");
439 int current_val = 0;
440 for (int i = 0; i < options.size(); i++) {
441 Vector<String> text_split = options[i].split(":");
442 if (text_split.size() != 1)
443 current_val = text_split[1].to_int();
444 menu->add_item(text_split[0]);
445 menu->set_item_metadata(i, current_val);
446 current_val += 1;
447 }
448 menu->set_position(get_position());
449 menu->popup();
450 hide();
451 updating = false;
452 return false;
453
454 } else if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER) {
455
456 String basename;
457 switch (hint) {
458 case PROPERTY_HINT_LAYERS_2D_RENDER:
459 basename = "layer_names/2d_render";
460 break;
461 case PROPERTY_HINT_LAYERS_2D_PHYSICS:
462 basename = "layer_names/2d_physics";
463 break;
464 case PROPERTY_HINT_LAYERS_3D_RENDER:
465 basename = "layer_names/3d_render";
466 break;
467 case PROPERTY_HINT_LAYERS_3D_PHYSICS:
468 basename = "layer_names/3d_physics";
469 break;
470 }
471
472 checks20gc->show();
473 uint32_t flgs = v;
474 for (int i = 0; i < 2; i++) {
475
476 Point2 ofs(4, 4);
477 ofs.y += 22 * i;
478 for (int j = 0; j < 10; j++) {
479
480 int idx = i * 10 + j;
481 CheckBox *c = checks20[idx];
482 c->set_text(ProjectSettings::get_singleton()->get(basename + "/layer_" + itos(idx + 1)));
483 c->set_pressed(flgs & (1 << (i * 10 + j)));
484 c->show();
485 }
486 }
487
488 show();
489
490 checks20gc->set_position(Vector2(4, 4) * EDSCALE);
491 checks20gc->set_size(checks20gc->get_minimum_size());
492
493 set_size(Vector2(4, 4) * EDSCALE + checks20gc->get_position() + checks20gc->get_size());
494
495 } else if (hint == PROPERTY_HINT_EXP_EASING) {
496
497 easing_draw->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 5 * EDSCALE);
498 easing_draw->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -5 * EDSCALE);
499 easing_draw->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 5 * EDSCALE);
500 easing_draw->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -30 * EDSCALE);
501 type_button->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 3 * EDSCALE);
502 type_button->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -3 * EDSCALE);
503 type_button->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -25 * EDSCALE);
504 type_button->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -7 * EDSCALE);
505 type_button->set_text(TTR("Preset..."));
506 type_button->get_popup()->clear();
507 type_button->get_popup()->add_item(TTR("Linear"), EASING_LINEAR);
508 type_button->get_popup()->add_item(TTR("Ease In"), EASING_EASE_IN);
509 type_button->get_popup()->add_item(TTR("Ease Out"), EASING_EASE_OUT);
510 if (hint_text != "attenuation") {
511 type_button->get_popup()->add_item(TTR("Zero"), EASING_ZERO);
512 type_button->get_popup()->add_item(TTR("Easing In-Out"), EASING_IN_OUT);
513 type_button->get_popup()->add_item(TTR("Easing Out-In"), EASING_OUT_IN);
514 }
515
516 type_button->show();
517 easing_draw->show();
518 set_size(Size2(200, 150) * EDSCALE);
519 } else if (hint == PROPERTY_HINT_FLAGS) {
520 Vector<String> flags = hint_text.split(",");
521 for (int i = 0; i < flags.size(); i++) {
522 String flag = flags[i];
523 if (flag == "")
524 continue;
525 menu->add_check_item(flag, i);
526 int f = v;
527 if (f & (1 << i))
528 menu->set_item_checked(menu->get_item_index(i), true);
529 }
530 menu->set_position(get_position());
531 menu->popup();
532 hide();
533 updating = false;
534 return false;
535
536 } else {
537 List<String> names;
538 names.push_back("value:");
539 config_value_editors(1, 1, 50, names);
540 value_editor[0]->set_text(String::num(v));
541 }
542
543 } break;
544 case Variant::STRING: {
545
546 if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
547
548 List<String> names;
549 names.push_back(TTR("File..."));
550 names.push_back(TTR("Clear"));
551 config_action_buttons(names);
552
553 } else if (hint == PROPERTY_HINT_DIR || hint == PROPERTY_HINT_GLOBAL_DIR) {
554
555 List<String> names;
556 names.push_back(TTR("Dir..."));
557 names.push_back(TTR("Clear"));
558 config_action_buttons(names);
559 } else if (hint == PROPERTY_HINT_ENUM) {
560
561 Vector<String> options = hint_text.split(",");
562 for (int i = 0; i < options.size(); i++) {
563 menu->add_item(options[i], i);
564 }
565 menu->set_position(get_position());
566 menu->popup();
567 hide();
568 updating = false;
569 return false;
570
571 } else if (hint == PROPERTY_HINT_MULTILINE_TEXT) {
572
573 text_edit->show();
574 text_edit->set_text(v);
575 text_edit->deselect();
576
577 int button_margin = get_constant("button_margin", "Dialogs");
578 int margin = get_constant("margin", "Dialogs");
579
580 action_buttons[0]->set_anchor(MARGIN_LEFT, ANCHOR_END);
581 action_buttons[0]->set_anchor(MARGIN_TOP, ANCHOR_END);
582 action_buttons[0]->set_anchor(MARGIN_RIGHT, ANCHOR_END);
583 action_buttons[0]->set_anchor(MARGIN_BOTTOM, ANCHOR_END);
584 action_buttons[0]->set_begin(Point2(-70 * EDSCALE, -button_margin + 5 * EDSCALE));
585 action_buttons[0]->set_end(Point2(-margin, -margin));
586 action_buttons[0]->set_text(TTR("Close"));
587 action_buttons[0]->show();
588
589 } else if (hint == PROPERTY_HINT_TYPE_STRING) {
590
591 if (!create_dialog) {
592 create_dialog = memnew(CreateDialog);
593 create_dialog->connect("create", this, "_create_dialog_callback");
594 add_child(create_dialog);
595 }
596
597 if (hint_text != String()) {
598 create_dialog->set_base_type(hint_text);
599 } else {
600 create_dialog->set_base_type("Object");
601 }
602
603 create_dialog->popup_create(false);
604 hide();
605 updating = false;
606 return false;
607
608 } else if (hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE) {
609 #define MAKE_PROPSELECT \
610 if (!property_select) { \
611 property_select = memnew(PropertySelector); \
612 property_select->connect("selected", this, "_create_selected_property"); \
613 add_child(property_select); \
614 } \
615 hide();
616
617 MAKE_PROPSELECT;
618
619 Variant::Type type = Variant::NIL;
620 for (int i = 0; i < Variant::VARIANT_MAX; i++) {
621 if (hint_text == Variant::get_type_name(Variant::Type(i))) {
622 type = Variant::Type(i);
623 }
624 }
625 if (type != Variant::NIL)
626 property_select->select_method_from_basic_type(type, v);
627 updating = false;
628 return false;
629
630 } else if (hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE) {
631 MAKE_PROPSELECT
632
633 property_select->select_method_from_base_type(hint_text, v);
634
635 updating = false;
636 return false;
637
638 } else if (hint == PROPERTY_HINT_METHOD_OF_INSTANCE) {
639
640 MAKE_PROPSELECT
641
642 Object *instance = ObjectDB::get_instance(hint_text.to_int64());
643 if (instance)
644 property_select->select_method_from_instance(instance, v);
645 updating = false;
646 return false;
647
648 } else if (hint == PROPERTY_HINT_METHOD_OF_SCRIPT) {
649 MAKE_PROPSELECT
650
651 Object *obj = ObjectDB::get_instance(hint_text.to_int64());
652 if (Object::cast_to<Script>(obj)) {
653 property_select->select_method_from_script(Object::cast_to<Script>(obj), v);
654 }
655
656 updating = false;
657 return false;
658
659 } else if (hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE) {
660
661 MAKE_PROPSELECT
662 Variant::Type type = Variant::NIL;
663 String tname = hint_text;
664 if (tname.find(".") != -1)
665 tname = tname.get_slice(".", 0);
666 for (int i = 0; i < Variant::VARIANT_MAX; i++) {
667 if (tname == Variant::get_type_name(Variant::Type(i))) {
668 type = Variant::Type(Variant::Type(i));
669 }
670 }
671
672 if (type != Variant::NIL)
673 property_select->select_property_from_basic_type(type, v);
674
675 updating = false;
676 return false;
677
678 } else if (hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE) {
679
680 MAKE_PROPSELECT
681
682 property_select->select_property_from_base_type(hint_text, v);
683
684 updating = false;
685 return false;
686
687 } else if (hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE) {
688
689 MAKE_PROPSELECT
690
691 Object *instance = ObjectDB::get_instance(hint_text.to_int64());
692 if (instance)
693 property_select->select_property_from_instance(instance, v);
694
695 updating = false;
696 return false;
697
698 } else if (hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) {
699 MAKE_PROPSELECT
700
701 Object *obj = ObjectDB::get_instance(hint_text.to_int64());
702 if (Object::cast_to<Script>(obj)) {
703 property_select->select_property_from_script(Object::cast_to<Script>(obj), v);
704 }
705
706 updating = false;
707 return false;
708
709 } else {
710 List<String> names;
711 names.push_back("string:");
712 config_value_editors(1, 1, 50, names);
713 value_editor[0]->set_text(v);
714 }
715
716 } break;
717 case Variant::VECTOR2: {
718
719 field_names.push_back("x");
720 field_names.push_back("y");
721 config_value_editors(2, 2, 10, field_names);
722 Vector2 vec = v;
723 value_editor[0]->set_text(String::num(vec.x));
724 value_editor[1]->set_text(String::num(vec.y));
725 } break;
726 case Variant::RECT2: {
727
728 field_names.push_back("x");
729 field_names.push_back("y");
730 field_names.push_back("w");
731 field_names.push_back("h");
732 config_value_editors(4, 4, 10, field_names);
733 Rect2 r = v;
734 value_editor[0]->set_text(String::num(r.position.x));
735 value_editor[1]->set_text(String::num(r.position.y));
736 value_editor[2]->set_text(String::num(r.size.x));
737 value_editor[3]->set_text(String::num(r.size.y));
738 } break;
739 case Variant::VECTOR3: {
740
741 field_names.push_back("x");
742 field_names.push_back("y");
743 field_names.push_back("z");
744 config_value_editors(3, 3, 10, field_names);
745 Vector3 vec = v;
746 value_editor[0]->set_text(String::num(vec.x));
747 value_editor[1]->set_text(String::num(vec.y));
748 value_editor[2]->set_text(String::num(vec.z));
749 } break;
750 case Variant::PLANE: {
751
752 field_names.push_back("x");
753 field_names.push_back("y");
754 field_names.push_back("z");
755 field_names.push_back("d");
756 config_value_editors(4, 4, 10, field_names);
757 Plane plane = v;
758 value_editor[0]->set_text(String::num(plane.normal.x));
759 value_editor[1]->set_text(String::num(plane.normal.y));
760 value_editor[2]->set_text(String::num(plane.normal.z));
761 value_editor[3]->set_text(String::num(plane.d));
762
763 } break;
764 case Variant::QUAT: {
765
766 field_names.push_back("x");
767 field_names.push_back("y");
768 field_names.push_back("z");
769 field_names.push_back("w");
770 config_value_editors(4, 4, 10, field_names);
771 Quat q = v;
772 value_editor[0]->set_text(String::num(q.x));
773 value_editor[1]->set_text(String::num(q.y));
774 value_editor[2]->set_text(String::num(q.z));
775 value_editor[3]->set_text(String::num(q.w));
776
777 } break;
778 case Variant::AABB: {
779
780 field_names.push_back("px");
781 field_names.push_back("py");
782 field_names.push_back("pz");
783 field_names.push_back("sx");
784 field_names.push_back("sy");
785 field_names.push_back("sz");
786 config_value_editors(6, 3, 16, field_names);
787
788 AABB aabb = v;
789 value_editor[0]->set_text(String::num(aabb.position.x));
790 value_editor[1]->set_text(String::num(aabb.position.y));
791 value_editor[2]->set_text(String::num(aabb.position.z));
792 value_editor[3]->set_text(String::num(aabb.size.x));
793 value_editor[4]->set_text(String::num(aabb.size.y));
794 value_editor[5]->set_text(String::num(aabb.size.z));
795
796 } break;
797 case Variant::TRANSFORM2D: {
798
799 field_names.push_back("xx");
800 field_names.push_back("xy");
801 field_names.push_back("yx");
802 field_names.push_back("yy");
803 field_names.push_back("ox");
804 field_names.push_back("oy");
805 config_value_editors(6, 2, 16, field_names);
806
807 Transform2D basis = v;
808 for (int i = 0; i < 6; i++) {
809
810 value_editor[i]->set_text(String::num(basis.elements[i / 2][i % 2]));
811 }
812
813 } break;
814 case Variant::BASIS: {
815
816 field_names.push_back("xx");
817 field_names.push_back("xy");
818 field_names.push_back("xz");
819 field_names.push_back("yx");
820 field_names.push_back("yy");
821 field_names.push_back("yz");
822 field_names.push_back("zx");
823 field_names.push_back("zy");
824 field_names.push_back("zz");
825 config_value_editors(9, 3, 16, field_names);
826
827 Basis basis = v;
828 for (int i = 0; i < 9; i++) {
829
830 value_editor[i]->set_text(String::num(basis.elements[i / 3][i % 3]));
831 }
832
833 } break;
834 case Variant::TRANSFORM: {
835
836 field_names.push_back("xx");
837 field_names.push_back("xy");
838 field_names.push_back("xz");
839 field_names.push_back("xo");
840 field_names.push_back("yx");
841 field_names.push_back("yy");
842 field_names.push_back("yz");
843 field_names.push_back("yo");
844 field_names.push_back("zx");
845 field_names.push_back("zy");
846 field_names.push_back("zz");
847 field_names.push_back("zo");
848 config_value_editors(12, 4, 16, field_names);
849
850 Transform tr = v;
851 for (int i = 0; i < 9; i++) {
852
853 value_editor[(i / 3) * 4 + i % 3]->set_text(String::num(tr.basis.elements[i / 3][i % 3]));
854 }
855
856 value_editor[3]->set_text(String::num(tr.origin.x));
857 value_editor[7]->set_text(String::num(tr.origin.y));
858 value_editor[11]->set_text(String::num(tr.origin.z));
859
860 } break;
861 case Variant::COLOR: {
862
863 if (!color_picker) {
864 //late init for performance
865 color_picker = memnew(ColorPicker);
866 color_picker->set_deferred_mode(true);
867 add_child(color_picker);
868 color_picker->hide();
869 color_picker->connect("color_changed", this, "_color_changed");
870
871 // get default color picker mode from editor settings
872 int default_color_mode = EDITOR_GET("interface/inspector/default_color_picker_mode");
873 if (default_color_mode == 1)
874 color_picker->set_hsv_mode(true);
875 else if (default_color_mode == 2)
876 color_picker->set_raw_mode(true);
877 }
878
879 color_picker->show();
880 color_picker->set_edit_alpha(hint != PROPERTY_HINT_COLOR_NO_ALPHA);
881 color_picker->set_pick_color(v);
882 color_picker->set_focus_on_line_edit();
883
884 } break;
885
886 case Variant::NODE_PATH: {
887
888 List<String> names;
889 names.push_back(TTR("Assign"));
890 names.push_back(TTR("Clear"));
891
892 if (owner && owner->is_class("Node") && (v.get_type() == Variant::NODE_PATH) && Object::cast_to<Node>(owner)->has_node(v))
893 names.push_back(TTR("Select Node"));
894
895 config_action_buttons(names);
896
897 } break;
898 case Variant::OBJECT: {
899
900 if (hint != PROPERTY_HINT_RESOURCE_TYPE)
901 break;
902
903 if (p_name == "script" && hint_text == "Script" && Object::cast_to<Node>(owner)) {
904 menu->add_icon_item(get_icon("Script", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT);
905 menu->add_separator();
906 } else if (hint_text != "") {
907 int idx = 0;
908
909 Vector<EditorData::CustomType> custom_resources;
910
911 if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
912 custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
913 }
914
915 for (int i = 0; i < hint_text.get_slice_count(","); i++) {
916
917 String base = hint_text.get_slice(",", i);
918
919 Set<String> valid_inheritors;
920 valid_inheritors.insert(base);
921 List<StringName> inheritors;
922 ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors);
923
924 for (int j = 0; j < custom_resources.size(); j++) {
925 inheritors.push_back(custom_resources[j].name);
926 }
927
928 List<StringName>::Element *E = inheritors.front();
929 while (E) {
930 valid_inheritors.insert(E->get());
931 E = E->next();
932 }
933
934 for (Set<String>::Element *j = valid_inheritors.front(); j; j = j->next()) {
935 const String &t = j->get();
936
937 bool is_custom_resource = false;
938 Ref<Texture> icon;
939 if (!custom_resources.empty()) {
940 for (int k = 0; k < custom_resources.size(); k++) {
941 if (custom_resources[k].name == t) {
942 is_custom_resource = true;
943 if (custom_resources[k].icon.is_valid())
944 icon = custom_resources[k].icon;
945 break;
946 }
947 }
948 }
949
950 if (!is_custom_resource && !ClassDB::can_instance(t))
951 continue;
952
953 inheritors_array.push_back(t);
954
955 int id = TYPE_BASE_ID + idx;
956
957 if (!icon.is_valid() && has_icon(t, "EditorIcons")) {
958 icon = get_icon(t, "EditorIcons");
959 }
960
961 if (icon.is_valid()) {
962
963 menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);
964 } else {
965
966 menu->add_item(vformat(TTR("New %s"), t), id);
967 }
968
969 idx++;
970 }
971 }
972
973 if (menu->get_item_count())
974 menu->add_separator();
975 }
976
977 menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Load"), OBJ_MENU_LOAD);
978
979 if (!RES(v).is_null()) {
980
981 menu->add_icon_item(get_icon("Edit", "EditorIcons"), TTR("Edit"), OBJ_MENU_EDIT);
982 menu->add_icon_item(get_icon("Clear", "EditorIcons"), TTR("Clear"), OBJ_MENU_CLEAR);
983 menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
984 RES r = v;
985 if (r.is_valid() && r->get_path().is_resource_file()) {
986 menu->add_separator();
987 menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
988 }
989 }
990
991 RES cb = EditorSettings::get_singleton()->get_resource_clipboard();
992 bool paste_valid = false;
993 if (cb.is_valid()) {
994 if (hint_text == "")
995 paste_valid = true;
996 else
997 for (int i = 0; i < hint_text.get_slice_count(","); i++)
998 if (ClassDB::is_parent_class(cb->get_class(), hint_text.get_slice(",", i))) {
999 paste_valid = true;
1000 break;
1001 }
1002 }
1003
1004 if (!RES(v).is_null() || paste_valid) {
1005 menu->add_separator();
1006
1007 if (!RES(v).is_null()) {
1008
1009 menu->add_item(TTR("Copy"), OBJ_MENU_COPY);
1010 }
1011
1012 if (paste_valid) {
1013
1014 menu->add_item(TTR("Paste"), OBJ_MENU_PASTE);
1015 }
1016 }
1017
1018 if (!RES(v).is_null()) {
1019
1020 Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(RES(v));
1021 if (conversions.size()) {
1022 menu->add_separator();
1023 }
1024 for (int i = 0; i < conversions.size(); i++) {
1025 String what = conversions[i]->converts_to();
1026 Ref<Texture> icon;
1027 if (has_icon(what, "EditorIcons")) {
1028
1029 icon = get_icon(what, "EditorIcons");
1030 } else {
1031
1032 icon = get_icon(what, "Resource");
1033 }
1034
1035 menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i);
1036 }
1037 }
1038
1039 menu->set_position(get_position());
1040 menu->popup();
1041 hide();
1042 updating = false;
1043 return false;
1044
1045 } break;
1046 case Variant::DICTIONARY: {
1047
1048 } break;
1049 case Variant::POOL_BYTE_ARRAY: {
1050
1051 } break;
1052 case Variant::POOL_INT_ARRAY: {
1053
1054 } break;
1055 case Variant::POOL_REAL_ARRAY: {
1056
1057 } break;
1058 case Variant::POOL_STRING_ARRAY: {
1059
1060 } break;
1061 case Variant::POOL_VECTOR3_ARRAY: {
1062
1063 } break;
1064 case Variant::POOL_COLOR_ARRAY: {
1065
1066 } break;
1067 default: {
1068 }
1069 }
1070
1071 updating = false;
1072 return true;
1073 }
1074
_file_selected(String p_file)1075 void CustomPropertyEditor::_file_selected(String p_file) {
1076
1077 switch (type) {
1078
1079 case Variant::STRING: {
1080
1081 if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_DIR) {
1082
1083 v = ProjectSettings::get_singleton()->localize_path(p_file);
1084 emit_signal("variant_changed");
1085 hide();
1086 }
1087
1088 if (hint == PROPERTY_HINT_GLOBAL_FILE || hint == PROPERTY_HINT_GLOBAL_DIR) {
1089
1090 v = p_file;
1091 emit_signal("variant_changed");
1092 hide();
1093 }
1094
1095 } break;
1096 case Variant::OBJECT: {
1097
1098 String type = (hint == PROPERTY_HINT_RESOURCE_TYPE) ? hint_text : String();
1099
1100 RES res = ResourceLoader::load(p_file, type);
1101 if (res.is_null()) {
1102 error->set_text(TTR("Error loading file: Not a resource!"));
1103 error->popup_centered_minsize();
1104 break;
1105 }
1106 v = res.get_ref_ptr();
1107 emit_signal("variant_changed");
1108 hide();
1109 } break;
1110 default: {
1111 }
1112 }
1113 }
1114
_type_create_selected(int p_idx)1115 void CustomPropertyEditor::_type_create_selected(int p_idx) {
1116
1117 if (type == Variant::INT || type == Variant::REAL) {
1118
1119 float newval = 0;
1120 switch (p_idx) {
1121
1122 case EASING_LINEAR: {
1123
1124 newval = 1;
1125 } break;
1126 case EASING_EASE_IN: {
1127
1128 newval = 2.0;
1129 } break;
1130 case EASING_EASE_OUT: {
1131 newval = 0.5;
1132 } break;
1133 case EASING_ZERO: {
1134
1135 newval = 0;
1136 } break;
1137 case EASING_IN_OUT: {
1138
1139 newval = -0.5;
1140 } break;
1141 case EASING_OUT_IN: {
1142 newval = -2.0;
1143 } break;
1144 }
1145
1146 v = newval;
1147 emit_signal("variant_changed");
1148 easing_draw->update();
1149
1150 } else if (type == Variant::OBJECT) {
1151
1152 ERR_FAIL_INDEX(p_idx, inheritors_array.size());
1153
1154 String intype = inheritors_array[p_idx];
1155
1156 Object *obj = ClassDB::instance(intype);
1157
1158 if (!obj) {
1159 if (ScriptServer::is_global_class(intype)) {
1160 obj = EditorNode::get_editor_data().script_class_instance(intype);
1161 } else {
1162 obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
1163 }
1164 }
1165
1166 ERR_FAIL_COND(!obj);
1167
1168 Resource *res = Object::cast_to<Resource>(obj);
1169 ERR_FAIL_COND(!res);
1170
1171 v = Ref<Resource>(res).get_ref_ptr();
1172 emit_signal("variant_changed");
1173 hide();
1174 }
1175 }
1176
_color_changed(const Color & p_color)1177 void CustomPropertyEditor::_color_changed(const Color &p_color) {
1178
1179 v = p_color;
1180 emit_signal("variant_changed");
1181 }
1182
_node_path_selected(NodePath p_path)1183 void CustomPropertyEditor::_node_path_selected(NodePath p_path) {
1184
1185 if (picking_viewport) {
1186
1187 Node *to_node = get_node(p_path);
1188 if (!Object::cast_to<Viewport>(to_node)) {
1189 EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));
1190 return;
1191 }
1192
1193 Ref<ViewportTexture> vt;
1194 vt.instance();
1195 vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
1196 vt->setup_local_to_scene();
1197 v = vt;
1198 emit_signal("variant_changed");
1199 return;
1200 }
1201
1202 if (hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && hint_text != String()) {
1203
1204 Node *node = get_node(hint_text);
1205 if (node) {
1206
1207 Node *tonode = node->get_node(p_path);
1208 if (tonode) {
1209 p_path = node->get_path_to(tonode);
1210 }
1211 }
1212
1213 } else if (owner) {
1214
1215 Node *node = NULL;
1216
1217 if (owner->is_class("Node"))
1218 node = Object::cast_to<Node>(owner);
1219 else if (owner->is_class("ArrayPropertyEdit"))
1220 node = Object::cast_to<ArrayPropertyEdit>(owner)->get_node();
1221 else if (owner->is_class("DictionaryPropertyEdit"))
1222 node = Object::cast_to<DictionaryPropertyEdit>(owner)->get_node();
1223 if (!node) {
1224 v = p_path;
1225 emit_signal("variant_changed");
1226 call_deferred("hide"); //to not mess with dialogs
1227 return;
1228 }
1229
1230 Node *tonode = node->get_node(p_path);
1231 if (tonode) {
1232 p_path = node->get_path_to(tonode);
1233 }
1234 }
1235
1236 v = p_path;
1237 emit_signal("variant_changed");
1238 call_deferred("hide"); //to not mess with dialogs
1239 }
1240
_action_pressed(int p_which)1241 void CustomPropertyEditor::_action_pressed(int p_which) {
1242
1243 if (updating)
1244 return;
1245
1246 switch (type) {
1247 case Variant::BOOL: {
1248 v = checks20[0]->is_pressed();
1249 emit_signal("variant_changed");
1250 } break;
1251 case Variant::INT: {
1252
1253 if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER) {
1254
1255 uint32_t f = v;
1256 if (checks20[p_which]->is_pressed())
1257 f |= (1 << p_which);
1258 else
1259 f &= ~(1 << p_which);
1260
1261 v = f;
1262 emit_signal("variant_changed");
1263 }
1264
1265 } break;
1266 case Variant::STRING: {
1267
1268 if (hint == PROPERTY_HINT_MULTILINE_TEXT) {
1269
1270 hide();
1271
1272 } else if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
1273 if (p_which == 0) {
1274
1275 if (hint == PROPERTY_HINT_FILE)
1276 file->set_access(EditorFileDialog::ACCESS_RESOURCES);
1277 else
1278 file->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1279
1280 file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1281 file->clear_filters();
1282
1283 file->clear_filters();
1284
1285 if (hint_text != "") {
1286 Vector<String> extensions = hint_text.split(",");
1287 for (int i = 0; i < extensions.size(); i++) {
1288
1289 String filter = extensions[i];
1290 if (filter.begins_with("."))
1291 filter = "*" + extensions[i];
1292 else if (!filter.begins_with("*"))
1293 filter = "*." + extensions[i];
1294
1295 file->add_filter(filter + " ; " + extensions[i].to_upper());
1296 }
1297 }
1298 file->popup_centered_ratio();
1299 } else {
1300
1301 v = "";
1302 emit_signal("variant_changed");
1303 hide();
1304 }
1305
1306 } else if (hint == PROPERTY_HINT_DIR || hint == PROPERTY_HINT_GLOBAL_DIR) {
1307
1308 if (p_which == 0) {
1309
1310 if (hint == PROPERTY_HINT_DIR)
1311 file->set_access(EditorFileDialog::ACCESS_RESOURCES);
1312 else
1313 file->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1314 file->set_mode(EditorFileDialog::MODE_OPEN_DIR);
1315 file->clear_filters();
1316 file->popup_centered_ratio();
1317 } else {
1318
1319 v = "";
1320 emit_signal("variant_changed");
1321 hide();
1322 }
1323 }
1324
1325 } break;
1326 case Variant::NODE_PATH: {
1327
1328 if (p_which == 0) {
1329
1330 picking_viewport = false;
1331 scene_tree->set_title(TTR("Pick a Node"));
1332 scene_tree->popup_centered_ratio();
1333
1334 } else if (p_which == 1) {
1335
1336 v = NodePath();
1337 emit_signal("variant_changed");
1338 hide();
1339 } else if (p_which == 2) {
1340
1341 if (owner->is_class("Node") && (v.get_type() == Variant::NODE_PATH) && Object::cast_to<Node>(owner)->has_node(v)) {
1342
1343 Node *target_node = Object::cast_to<Node>(owner)->get_node(v);
1344 EditorNode::get_singleton()->get_editor_selection()->clear();
1345 EditorNode::get_singleton()->get_scene_tree_dock()->set_selected(target_node);
1346 }
1347
1348 hide();
1349 }
1350
1351 } break;
1352 case Variant::OBJECT: {
1353
1354 if (p_which == 0) {
1355
1356 ERR_FAIL_COND(inheritors_array.empty());
1357
1358 String intype = inheritors_array[0];
1359
1360 if (hint == PROPERTY_HINT_RESOURCE_TYPE) {
1361
1362 Object *obj = ClassDB::instance(intype);
1363
1364 if (!obj) {
1365 if (ScriptServer::is_global_class(intype)) {
1366 obj = EditorNode::get_editor_data().script_class_instance(intype);
1367 } else {
1368 obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
1369 }
1370 }
1371
1372 ERR_BREAK(!obj);
1373 Resource *res = Object::cast_to<Resource>(obj);
1374 ERR_BREAK(!res);
1375
1376 v = Ref<Resource>(res).get_ref_ptr();
1377 emit_signal("variant_changed");
1378 hide();
1379 }
1380 } else if (p_which == 1) {
1381
1382 file->set_access(EditorFileDialog::ACCESS_RESOURCES);
1383 file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1384 List<String> extensions;
1385 String type = (hint == PROPERTY_HINT_RESOURCE_TYPE) ? hint_text : String();
1386
1387 ResourceLoader::get_recognized_extensions_for_type(type, &extensions);
1388 file->clear_filters();
1389 for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
1390
1391 file->add_filter("*." + E->get() + " ; " + E->get().to_upper());
1392 }
1393
1394 file->popup_centered_ratio();
1395
1396 } else if (p_which == 2) {
1397
1398 RefPtr RefPtr = v;
1399
1400 if (!RefPtr.is_null()) {
1401
1402 emit_signal("resource_edit_request");
1403 hide();
1404 }
1405
1406 } else if (p_which == 3) {
1407
1408 v = Variant();
1409 emit_signal("variant_changed");
1410 hide();
1411 } else if (p_which == 4) {
1412
1413 RefPtr RefPtr = v;
1414 Ref<Resource> res_orig = RefPtr;
1415 if (res_orig.is_null())
1416 return;
1417
1418 List<PropertyInfo> property_list;
1419 res_orig->get_property_list(&property_list);
1420 List<Pair<String, Variant> > propvalues;
1421
1422 for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
1423
1424 Pair<String, Variant> p;
1425 PropertyInfo &pi = E->get();
1426 if (pi.usage & PROPERTY_USAGE_STORAGE) {
1427
1428 p.first = pi.name;
1429 p.second = res_orig->get(pi.name);
1430 }
1431
1432 propvalues.push_back(p);
1433 }
1434
1435 Ref<Resource> res = Ref<Resource>(ClassDB::instance(res_orig->get_class()));
1436
1437 ERR_FAIL_COND(res.is_null());
1438
1439 for (List<Pair<String, Variant> >::Element *E = propvalues.front(); E; E = E->next()) {
1440
1441 Pair<String, Variant> &p = E->get();
1442 res->set(p.first, p.second);
1443 }
1444
1445 v = res.get_ref_ptr();
1446 emit_signal("variant_changed");
1447 hide();
1448 }
1449
1450 } break;
1451
1452 default: {
1453 };
1454 }
1455 }
1456
_drag_easing(const Ref<InputEvent> & p_ev)1457 void CustomPropertyEditor::_drag_easing(const Ref<InputEvent> &p_ev) {
1458
1459 Ref<InputEventMouseMotion> mm = p_ev;
1460
1461 if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
1462
1463 float rel = mm->get_relative().x;
1464 if (rel == 0)
1465 return;
1466
1467 bool flip = hint_text == "attenuation";
1468
1469 if (flip)
1470 rel = -rel;
1471
1472 float val = v;
1473 if (val == 0)
1474 return;
1475 bool sg = val < 0;
1476 val = Math::absf(val);
1477
1478 val = Math::log(val) / Math::log((float)2.0);
1479 //logspace
1480 val += rel * 0.05;
1481
1482 val = Math::pow(2.0f, val);
1483 if (sg)
1484 val = -val;
1485
1486 v = val;
1487 easing_draw->update();
1488 emit_signal("variant_changed");
1489 }
1490 }
1491
_draw_easing()1492 void CustomPropertyEditor::_draw_easing() {
1493
1494 RID ci = easing_draw->get_canvas_item();
1495
1496 Size2 s = easing_draw->get_size();
1497 Rect2 r(Point2(), s);
1498 r = r.grow(3);
1499 get_stylebox("normal", "LineEdit")->draw(ci, r);
1500
1501 int points = 48;
1502
1503 float prev = 1.0;
1504 float exp = v;
1505 bool flip = hint_text == "attenuation";
1506
1507 Ref<Font> f = get_font("font", "Label");
1508 Color color = get_color("font_color", "Label");
1509
1510 for (int i = 1; i <= points; i++) {
1511
1512 float ifl = i / float(points);
1513 float iflp = (i - 1) / float(points);
1514
1515 float h = 1.0 - Math::ease(ifl, exp);
1516
1517 if (flip) {
1518 ifl = 1.0 - ifl;
1519 iflp = 1.0 - iflp;
1520 }
1521
1522 VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(iflp * s.width, prev * s.height), Point2(ifl * s.width, h * s.height), color);
1523 prev = h;
1524 }
1525
1526 f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color);
1527 }
1528
_text_edit_changed()1529 void CustomPropertyEditor::_text_edit_changed() {
1530
1531 v = text_edit->get_text();
1532 emit_signal("variant_changed");
1533 }
1534
_create_dialog_callback()1535 void CustomPropertyEditor::_create_dialog_callback() {
1536
1537 v = create_dialog->get_selected_type();
1538 emit_signal("variant_changed");
1539 }
1540
_create_selected_property(const String & p_prop)1541 void CustomPropertyEditor::_create_selected_property(const String &p_prop) {
1542
1543 v = p_prop;
1544 emit_signal("variant_changed");
1545 }
1546
_modified(String p_string)1547 void CustomPropertyEditor::_modified(String p_string) {
1548
1549 if (updating)
1550 return;
1551
1552 updating = true;
1553 switch (type) {
1554 case Variant::INT: {
1555 String text = value_editor[0]->get_text();
1556 Ref<Expression> expr;
1557 expr.instance();
1558 Error err = expr->parse(text);
1559 if (err != OK) {
1560 v = value_editor[0]->get_text().to_int();
1561 return;
1562 } else {
1563 v = expr->execute(Array(), NULL, false);
1564 }
1565 emit_signal("variant_changed");
1566
1567 } break;
1568 case Variant::REAL: {
1569
1570 if (hint != PROPERTY_HINT_EXP_EASING) {
1571 String text = value_editor[0]->get_text();
1572 v = _parse_real_expression(text);
1573 emit_signal("variant_changed");
1574 }
1575
1576 } break;
1577 case Variant::STRING: {
1578
1579 v = value_editor[0]->get_text();
1580 emit_signal("variant_changed");
1581 } break;
1582 case Variant::VECTOR2: {
1583
1584 Vector2 vec;
1585 vec.x = _parse_real_expression(value_editor[0]->get_text());
1586 vec.y = _parse_real_expression(value_editor[1]->get_text());
1587 v = vec;
1588 _emit_changed_whole_or_field();
1589
1590 } break;
1591 case Variant::RECT2: {
1592
1593 Rect2 r2;
1594
1595 r2.position.x = _parse_real_expression(value_editor[0]->get_text());
1596 r2.position.y = _parse_real_expression(value_editor[1]->get_text());
1597 r2.size.x = _parse_real_expression(value_editor[2]->get_text());
1598 r2.size.y = _parse_real_expression(value_editor[3]->get_text());
1599 v = r2;
1600 _emit_changed_whole_or_field();
1601
1602 } break;
1603
1604 case Variant::VECTOR3: {
1605
1606 Vector3 vec;
1607 vec.x = _parse_real_expression(value_editor[0]->get_text());
1608 vec.y = _parse_real_expression(value_editor[1]->get_text());
1609 vec.z = _parse_real_expression(value_editor[2]->get_text());
1610 v = vec;
1611 _emit_changed_whole_or_field();
1612
1613 } break;
1614 case Variant::PLANE: {
1615
1616 Plane pl;
1617 pl.normal.x = _parse_real_expression(value_editor[0]->get_text());
1618 pl.normal.y = _parse_real_expression(value_editor[1]->get_text());
1619 pl.normal.z = _parse_real_expression(value_editor[2]->get_text());
1620 pl.d = _parse_real_expression(value_editor[3]->get_text());
1621 v = pl;
1622 _emit_changed_whole_or_field();
1623
1624 } break;
1625 case Variant::QUAT: {
1626
1627 Quat q;
1628 q.x = _parse_real_expression(value_editor[0]->get_text());
1629 q.y = _parse_real_expression(value_editor[1]->get_text());
1630 q.z = _parse_real_expression(value_editor[2]->get_text());
1631 q.w = _parse_real_expression(value_editor[3]->get_text());
1632 v = q;
1633 _emit_changed_whole_or_field();
1634
1635 } break;
1636 case Variant::AABB: {
1637
1638 Vector3 pos;
1639 Vector3 size;
1640
1641 pos.x = _parse_real_expression(value_editor[0]->get_text());
1642 pos.y = _parse_real_expression(value_editor[1]->get_text());
1643 pos.z = _parse_real_expression(value_editor[2]->get_text());
1644 size.x = _parse_real_expression(value_editor[3]->get_text());
1645 size.y = _parse_real_expression(value_editor[4]->get_text());
1646 size.z = _parse_real_expression(value_editor[5]->get_text());
1647 v = AABB(pos, size);
1648 _emit_changed_whole_or_field();
1649
1650 } break;
1651 case Variant::TRANSFORM2D: {
1652
1653 Transform2D m;
1654 for (int i = 0; i < 6; i++) {
1655 m.elements[i / 2][i % 2] = _parse_real_expression(value_editor[i]->get_text());
1656 }
1657
1658 v = m;
1659 _emit_changed_whole_or_field();
1660
1661 } break;
1662 case Variant::BASIS: {
1663
1664 Basis m;
1665 for (int i = 0; i < 9; i++) {
1666 m.elements[i / 3][i % 3] = _parse_real_expression(value_editor[i]->get_text());
1667 }
1668
1669 v = m;
1670 _emit_changed_whole_or_field();
1671
1672 } break;
1673 case Variant::TRANSFORM: {
1674
1675 Basis basis;
1676 for (int i = 0; i < 9; i++) {
1677 basis.elements[i / 3][i % 3] = _parse_real_expression(value_editor[(i / 3) * 4 + i % 3]->get_text());
1678 }
1679
1680 Vector3 origin;
1681
1682 origin.x = _parse_real_expression(value_editor[3]->get_text());
1683 origin.y = _parse_real_expression(value_editor[7]->get_text());
1684 origin.z = _parse_real_expression(value_editor[11]->get_text());
1685
1686 v = Transform(basis, origin);
1687 _emit_changed_whole_or_field();
1688
1689 } break;
1690 case Variant::COLOR: {
1691
1692 } break;
1693
1694 case Variant::NODE_PATH: {
1695
1696 v = NodePath(value_editor[0]->get_text());
1697 emit_signal("variant_changed");
1698 } break;
1699 case Variant::DICTIONARY: {
1700
1701 } break;
1702 case Variant::POOL_BYTE_ARRAY: {
1703
1704 } break;
1705 case Variant::POOL_INT_ARRAY: {
1706
1707 } break;
1708 case Variant::POOL_REAL_ARRAY: {
1709
1710 } break;
1711 case Variant::POOL_STRING_ARRAY: {
1712
1713 } break;
1714 case Variant::POOL_VECTOR3_ARRAY: {
1715
1716 } break;
1717 case Variant::POOL_COLOR_ARRAY: {
1718
1719 } break;
1720 default: {
1721 }
1722 }
1723
1724 updating = false;
1725 }
1726
_parse_real_expression(String text)1727 real_t CustomPropertyEditor::_parse_real_expression(String text) {
1728 Ref<Expression> expr;
1729 expr.instance();
1730 Error err = expr->parse(text);
1731 real_t out;
1732 if (err != OK) {
1733 out = value_editor[0]->get_text().to_double();
1734 } else {
1735 out = expr->execute(Array(), NULL, false);
1736 }
1737 return out;
1738 }
1739
_emit_changed_whole_or_field()1740 void CustomPropertyEditor::_emit_changed_whole_or_field() {
1741
1742 if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
1743 emit_signal("variant_changed");
1744 } else {
1745 emit_signal("variant_field_changed", field_names[focused_value_editor]);
1746 }
1747 }
1748
_range_modified(double p_value)1749 void CustomPropertyEditor::_range_modified(double p_value) {
1750 v = p_value;
1751 emit_signal("variant_changed");
1752 }
1753
_focus_enter()1754 void CustomPropertyEditor::_focus_enter() {
1755 switch (type) {
1756 case Variant::REAL:
1757 case Variant::STRING:
1758 case Variant::VECTOR2:
1759 case Variant::RECT2:
1760 case Variant::VECTOR3:
1761 case Variant::PLANE:
1762 case Variant::QUAT:
1763 case Variant::AABB:
1764 case Variant::TRANSFORM2D:
1765 case Variant::BASIS:
1766 case Variant::TRANSFORM: {
1767 for (int i = 0; i < MAX_VALUE_EDITORS; ++i) {
1768 if (value_editor[i]->has_focus()) {
1769 focused_value_editor = i;
1770 value_editor[i]->select_all();
1771 break;
1772 }
1773 }
1774 } break;
1775 default: {
1776 }
1777 }
1778 }
1779
_focus_exit()1780 void CustomPropertyEditor::_focus_exit() {
1781 switch (type) {
1782 case Variant::REAL:
1783 case Variant::STRING:
1784 case Variant::VECTOR2:
1785 case Variant::RECT2:
1786 case Variant::VECTOR3:
1787 case Variant::PLANE:
1788 case Variant::QUAT:
1789 case Variant::AABB:
1790 case Variant::TRANSFORM2D:
1791 case Variant::BASIS:
1792 case Variant::TRANSFORM: {
1793 for (int i = 0; i < MAX_VALUE_EDITORS; ++i) {
1794 value_editor[i]->select(0, 0);
1795 }
1796 } break;
1797 default: {
1798 }
1799 }
1800 }
1801
config_action_buttons(const List<String> & p_strings)1802 void CustomPropertyEditor::config_action_buttons(const List<String> &p_strings) {
1803
1804 Ref<StyleBox> sb = get_stylebox("panel");
1805 int margin_top = sb->get_margin(MARGIN_TOP);
1806 int margin_left = sb->get_margin(MARGIN_LEFT);
1807 int margin_bottom = sb->get_margin(MARGIN_BOTTOM);
1808 int margin_right = sb->get_margin(MARGIN_RIGHT);
1809
1810 int max_width = 0;
1811 int height = 0;
1812
1813 for (int i = 0; i < MAX_ACTION_BUTTONS; i++) {
1814
1815 if (i < p_strings.size()) {
1816
1817 action_buttons[i]->show();
1818 action_buttons[i]->set_text(p_strings[i]);
1819
1820 Size2 btn_m_size = action_buttons[i]->get_minimum_size();
1821 if (btn_m_size.width > max_width)
1822 max_width = btn_m_size.width;
1823
1824 } else {
1825 action_buttons[i]->hide();
1826 }
1827 }
1828
1829 for (int i = 0; i < p_strings.size(); i++) {
1830
1831 Size2 btn_m_size = action_buttons[i]->get_size();
1832 action_buttons[i]->set_position(Point2(0, height) + Point2(margin_left, margin_top));
1833 action_buttons[i]->set_size(Size2(max_width, btn_m_size.height));
1834
1835 height += btn_m_size.height;
1836 }
1837 set_size(Size2(max_width, height) + Size2(margin_left + margin_right, margin_top + margin_bottom));
1838 }
1839
config_value_editors(int p_amount,int p_columns,int p_label_w,const List<String> & p_strings)1840 void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns, int p_label_w, const List<String> &p_strings) {
1841
1842 int cell_width = 95;
1843 int cell_height = 25;
1844 int cell_margin = 5;
1845 int hor_spacing = 5; // Spacing between labels and their values
1846
1847 int rows = ((p_amount - 1) / p_columns) + 1;
1848
1849 set_size(Size2(cell_margin + p_label_w + (cell_width + cell_margin + p_label_w) * p_columns, cell_margin + (cell_height + cell_margin) * rows) * EDSCALE);
1850
1851 for (int i = 0; i < MAX_VALUE_EDITORS; i++) {
1852
1853 int c = i % p_columns;
1854 int r = i / p_columns;
1855
1856 if (i < p_amount) {
1857 value_editor[i]->show();
1858 value_label[i]->show();
1859 value_label[i]->set_text(i < p_strings.size() ? p_strings[i] : String(""));
1860 value_editor[i]->set_position(Point2(cell_margin + p_label_w + hor_spacing + (cell_width + cell_margin + p_label_w + hor_spacing) * c, cell_margin + (cell_height + cell_margin) * r) * EDSCALE);
1861 value_editor[i]->set_size(Size2(cell_width, cell_height));
1862 value_label[i]->set_position(Point2(cell_margin + (cell_width + cell_margin + p_label_w + hor_spacing) * c, cell_margin + (cell_height + cell_margin) * r) * EDSCALE);
1863 value_editor[i]->set_editable(!read_only);
1864 } else {
1865 value_editor[i]->hide();
1866 value_label[i]->hide();
1867 }
1868 }
1869 }
1870
_bind_methods()1871 void CustomPropertyEditor::_bind_methods() {
1872
1873 ClassDB::bind_method("_focus_enter", &CustomPropertyEditor::_focus_enter);
1874 ClassDB::bind_method("_focus_exit", &CustomPropertyEditor::_focus_exit);
1875 ClassDB::bind_method("_modified", &CustomPropertyEditor::_modified);
1876 ClassDB::bind_method("_range_modified", &CustomPropertyEditor::_range_modified);
1877 ClassDB::bind_method("_action_pressed", &CustomPropertyEditor::_action_pressed);
1878 ClassDB::bind_method("_file_selected", &CustomPropertyEditor::_file_selected);
1879 ClassDB::bind_method("_type_create_selected", &CustomPropertyEditor::_type_create_selected);
1880 ClassDB::bind_method("_node_path_selected", &CustomPropertyEditor::_node_path_selected);
1881 ClassDB::bind_method("_color_changed", &CustomPropertyEditor::_color_changed);
1882 ClassDB::bind_method("_draw_easing", &CustomPropertyEditor::_draw_easing);
1883 ClassDB::bind_method("_drag_easing", &CustomPropertyEditor::_drag_easing);
1884 ClassDB::bind_method("_text_edit_changed", &CustomPropertyEditor::_text_edit_changed);
1885 ClassDB::bind_method("_menu_option", &CustomPropertyEditor::_menu_option);
1886 ClassDB::bind_method("_create_dialog_callback", &CustomPropertyEditor::_create_dialog_callback);
1887 ClassDB::bind_method("_create_selected_property", &CustomPropertyEditor::_create_selected_property);
1888
1889 ADD_SIGNAL(MethodInfo("variant_changed"));
1890 ADD_SIGNAL(MethodInfo("variant_field_changed", PropertyInfo(Variant::STRING, "field")));
1891 ADD_SIGNAL(MethodInfo("resource_edit_request"));
1892 }
1893
CustomPropertyEditor()1894 CustomPropertyEditor::CustomPropertyEditor() {
1895
1896 read_only = false;
1897 updating = false;
1898
1899 for (int i = 0; i < MAX_VALUE_EDITORS; i++) {
1900
1901 value_editor[i] = memnew(LineEdit);
1902 add_child(value_editor[i]);
1903 value_label[i] = memnew(Label);
1904 add_child(value_label[i]);
1905 value_editor[i]->hide();
1906 value_label[i]->hide();
1907 value_editor[i]->connect("text_entered", this, "_modified");
1908 value_editor[i]->connect("focus_entered", this, "_focus_enter");
1909 value_editor[i]->connect("focus_exited", this, "_focus_exit");
1910 }
1911 focused_value_editor = -1;
1912
1913 for (int i = 0; i < 4; i++) {
1914
1915 scroll[i] = memnew(HScrollBar);
1916 scroll[i]->hide();
1917 scroll[i]->set_min(0);
1918 scroll[i]->set_max(1.0);
1919 scroll[i]->set_step(0.01);
1920 add_child(scroll[i]);
1921 }
1922
1923 checks20gc = memnew(GridContainer);
1924 add_child(checks20gc);
1925 checks20gc->set_columns(11);
1926
1927 for (int i = 0; i < 20; i++) {
1928 if (i == 5 || i == 15) {
1929 Control *space = memnew(Control);
1930 space->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
1931 checks20gc->add_child(space);
1932 }
1933
1934 checks20[i] = memnew(CheckBox);
1935 checks20[i]->set_toggle_mode(true);
1936 checks20[i]->set_focus_mode(FOCUS_NONE);
1937 checks20gc->add_child(checks20[i]);
1938 checks20[i]->hide();
1939 checks20[i]->connect("pressed", this, "_action_pressed", make_binds(i));
1940 checks20[i]->set_tooltip(vformat(TTR("Bit %d, val %d."), i, 1 << i));
1941 }
1942
1943 text_edit = memnew(TextEdit);
1944 add_child(text_edit);
1945 text_edit->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
1946 text_edit->set_margin(MARGIN_BOTTOM, -30);
1947
1948 text_edit->hide();
1949 text_edit->connect("text_changed", this, "_text_edit_changed");
1950
1951 for (int i = 0; i < MAX_ACTION_BUTTONS; i++) {
1952
1953 action_buttons[i] = memnew(Button);
1954 action_buttons[i]->hide();
1955 add_child(action_buttons[i]);
1956 Vector<Variant> binds;
1957 binds.push_back(i);
1958 action_buttons[i]->connect("pressed", this, "_action_pressed", binds);
1959 action_buttons[i]->set_flat(true);
1960 }
1961
1962 color_picker = NULL;
1963
1964 set_as_toplevel(true);
1965 file = memnew(EditorFileDialog);
1966 add_child(file);
1967 file->hide();
1968
1969 file->connect("file_selected", this, "_file_selected");
1970 file->connect("dir_selected", this, "_file_selected");
1971
1972 error = memnew(ConfirmationDialog);
1973 error->set_title(TTR("Error!"));
1974 add_child(error);
1975
1976 scene_tree = memnew(SceneTreeDialog);
1977 add_child(scene_tree);
1978 scene_tree->connect("selected", this, "_node_path_selected");
1979 scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
1980
1981 texture_preview = memnew(TextureRect);
1982 add_child(texture_preview);
1983 texture_preview->hide();
1984
1985 easing_draw = memnew(Control);
1986 add_child(easing_draw);
1987 easing_draw->hide();
1988 easing_draw->connect("draw", this, "_draw_easing");
1989 easing_draw->connect("gui_input", this, "_drag_easing");
1990 easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);
1991
1992 type_button = memnew(MenuButton);
1993 add_child(type_button);
1994 type_button->hide();
1995 type_button->get_popup()->connect("id_pressed", this, "_type_create_selected");
1996
1997 menu = memnew(PopupMenu);
1998 menu->set_pass_on_modal_close_click(false);
1999 add_child(menu);
2000 menu->connect("id_pressed", this, "_menu_option");
2001
2002 evaluator = NULL;
2003
2004 spinbox = memnew(SpinBox);
2005 add_child(spinbox);
2006 spinbox->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
2007 spinbox->connect("value_changed", this, "_range_modified");
2008
2009 slider = memnew(HSlider);
2010 add_child(slider);
2011 slider->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 5);
2012 slider->connect("value_changed", this, "_range_modified");
2013
2014 create_dialog = NULL;
2015 property_select = NULL;
2016 }
2017