1 | /**************************************************************************/ |
2 | /* property_selector.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 "property_selector.h" |
32 | |
33 | #include "core/os/keyboard.h" |
34 | #include "editor/doc_tools.h" |
35 | #include "editor/editor_help.h" |
36 | #include "editor/editor_node.h" |
37 | #include "editor/editor_scale.h" |
38 | #include "scene/gui/line_edit.h" |
39 | #include "scene/gui/rich_text_label.h" |
40 | #include "scene/gui/tree.h" |
41 | |
42 | void PropertySelector::_text_changed(const String &p_newtext) { |
43 | _update_search(); |
44 | } |
45 | |
46 | void PropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { |
47 | Ref<InputEventKey> k = p_ie; |
48 | |
49 | if (k.is_valid()) { |
50 | switch (k->get_keycode()) { |
51 | case Key::UP: |
52 | case Key::DOWN: |
53 | case Key::PAGEUP: |
54 | case Key::PAGEDOWN: { |
55 | search_options->gui_input(k); |
56 | search_box->accept_event(); |
57 | |
58 | TreeItem *root = search_options->get_root(); |
59 | if (!root->get_first_child()) { |
60 | break; |
61 | } |
62 | |
63 | TreeItem *current = search_options->get_selected(); |
64 | |
65 | TreeItem *item = search_options->get_next_selected(root); |
66 | while (item) { |
67 | item->deselect(0); |
68 | item = search_options->get_next_selected(item); |
69 | } |
70 | |
71 | current->select(0); |
72 | |
73 | } break; |
74 | default: |
75 | break; |
76 | } |
77 | } |
78 | } |
79 | |
80 | void PropertySelector::_update_search() { |
81 | if (properties) { |
82 | set_title(TTR("Select Property" )); |
83 | } else if (virtuals_only) { |
84 | set_title(TTR("Select Virtual Method" )); |
85 | } else { |
86 | set_title(TTR("Select Method" )); |
87 | } |
88 | |
89 | search_options->clear(); |
90 | help_bit->set_text("" ); |
91 | |
92 | TreeItem *root = search_options->create_item(); |
93 | |
94 | // Allow using spaces in place of underscores in the search string (makes the search more fault-tolerant). |
95 | const String search_text = search_box->get_text().replace(" " , "_" ); |
96 | |
97 | if (properties) { |
98 | List<PropertyInfo> props; |
99 | |
100 | if (instance) { |
101 | instance->get_property_list(&props, true); |
102 | } else if (type != Variant::NIL) { |
103 | Variant v; |
104 | Callable::CallError ce; |
105 | Variant::construct(type, v, nullptr, 0, ce); |
106 | |
107 | v.get_property_list(&props); |
108 | } else { |
109 | Object *obj = ObjectDB::get_instance(script); |
110 | if (Object::cast_to<Script>(obj)) { |
111 | props.push_back(PropertyInfo(Variant::NIL, "Script Variables" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_CATEGORY)); |
112 | Object::cast_to<Script>(obj)->get_script_property_list(&props); |
113 | } |
114 | |
115 | StringName base = base_type; |
116 | while (base) { |
117 | props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_CATEGORY)); |
118 | ClassDB::get_property_list(base, &props, true); |
119 | base = ClassDB::get_parent_class(base); |
120 | } |
121 | } |
122 | |
123 | TreeItem *category = nullptr; |
124 | |
125 | bool found = false; |
126 | |
127 | Ref<Texture2D> type_icons[] = { |
128 | search_options->get_editor_theme_icon(SNAME("Variant" )), |
129 | search_options->get_editor_theme_icon(SNAME("bool" )), |
130 | search_options->get_editor_theme_icon(SNAME("int" )), |
131 | search_options->get_editor_theme_icon(SNAME("float" )), |
132 | search_options->get_editor_theme_icon(SNAME("String" )), |
133 | search_options->get_editor_theme_icon(SNAME("Vector2" )), |
134 | search_options->get_editor_theme_icon(SNAME("Vector2i" )), |
135 | search_options->get_editor_theme_icon(SNAME("Rect2" )), |
136 | search_options->get_editor_theme_icon(SNAME("Rect2i" )), |
137 | search_options->get_editor_theme_icon(SNAME("Vector3" )), |
138 | search_options->get_editor_theme_icon(SNAME("Vector3i" )), |
139 | search_options->get_editor_theme_icon(SNAME("Transform2D" )), |
140 | search_options->get_editor_theme_icon(SNAME("Vector4" )), |
141 | search_options->get_editor_theme_icon(SNAME("Vector4i" )), |
142 | search_options->get_editor_theme_icon(SNAME("Plane" )), |
143 | search_options->get_editor_theme_icon(SNAME("Quaternion" )), |
144 | search_options->get_editor_theme_icon(SNAME("AABB" )), |
145 | search_options->get_editor_theme_icon(SNAME("Basis" )), |
146 | search_options->get_editor_theme_icon(SNAME("Transform3D" )), |
147 | search_options->get_editor_theme_icon(SNAME("Projection" )), |
148 | search_options->get_editor_theme_icon(SNAME("Color" )), |
149 | search_options->get_editor_theme_icon(SNAME("StringName" )), |
150 | search_options->get_editor_theme_icon(SNAME("NodePath" )), |
151 | search_options->get_editor_theme_icon(SNAME("RID" )), |
152 | search_options->get_editor_theme_icon(SNAME("MiniObject" )), |
153 | search_options->get_editor_theme_icon(SNAME("Callable" )), |
154 | search_options->get_editor_theme_icon(SNAME("Signal" )), |
155 | search_options->get_editor_theme_icon(SNAME("Dictionary" )), |
156 | search_options->get_editor_theme_icon(SNAME("Array" )), |
157 | search_options->get_editor_theme_icon(SNAME("PackedByteArray" )), |
158 | search_options->get_editor_theme_icon(SNAME("PackedInt32Array" )), |
159 | search_options->get_editor_theme_icon(SNAME("PackedInt64Array" )), |
160 | search_options->get_editor_theme_icon(SNAME("PackedFloat32Array" )), |
161 | search_options->get_editor_theme_icon(SNAME("PackedFloat64Array" )), |
162 | search_options->get_editor_theme_icon(SNAME("PackedStringArray" )), |
163 | search_options->get_editor_theme_icon(SNAME("PackedVector2Array" )), |
164 | search_options->get_editor_theme_icon(SNAME("PackedVector3Array" )), |
165 | search_options->get_editor_theme_icon(SNAME("PackedColorArray" )) |
166 | }; |
167 | static_assert((sizeof(type_icons) / sizeof(type_icons[0])) == Variant::VARIANT_MAX, "Number of type icons doesn't match the number of Variant types." ); |
168 | |
169 | for (const PropertyInfo &E : props) { |
170 | if (E.usage == PROPERTY_USAGE_CATEGORY) { |
171 | if (category && category->get_first_child() == nullptr) { |
172 | memdelete(category); //old category was unused |
173 | } |
174 | category = search_options->create_item(root); |
175 | category->set_text(0, E.name); |
176 | category->set_selectable(0, false); |
177 | |
178 | Ref<Texture2D> icon; |
179 | if (E.name == "Script Variables" ) { |
180 | icon = search_options->get_editor_theme_icon(SNAME("Script" )); |
181 | } else { |
182 | icon = EditorNode::get_singleton()->get_class_icon(E.name); |
183 | } |
184 | category->set_icon(0, icon); |
185 | continue; |
186 | } |
187 | |
188 | if (!(E.usage & PROPERTY_USAGE_EDITOR) && !(E.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) { |
189 | continue; |
190 | } |
191 | |
192 | if (!search_box->get_text().is_empty() && E.name.findn(search_text) == -1) { |
193 | continue; |
194 | } |
195 | |
196 | if (type_filter.size() && !type_filter.has(E.type)) { |
197 | continue; |
198 | } |
199 | |
200 | TreeItem *item = search_options->create_item(category ? category : root); |
201 | item->set_text(0, E.name); |
202 | item->set_metadata(0, E.name); |
203 | item->set_icon(0, type_icons[E.type]); |
204 | |
205 | if (!found && !search_box->get_text().is_empty() && E.name.findn(search_text) != -1) { |
206 | item->select(0); |
207 | found = true; |
208 | } |
209 | |
210 | item->set_selectable(0, true); |
211 | } |
212 | |
213 | if (category && category->get_first_child() == nullptr) { |
214 | memdelete(category); //old category was unused |
215 | } |
216 | } else { |
217 | List<MethodInfo> methods; |
218 | |
219 | if (type != Variant::NIL) { |
220 | Variant v; |
221 | Callable::CallError ce; |
222 | Variant::construct(type, v, nullptr, 0, ce); |
223 | v.get_method_list(&methods); |
224 | } else { |
225 | Ref<Script> script_ref = Object::cast_to<Script>(ObjectDB::get_instance(script)); |
226 | if (script_ref.is_valid()) { |
227 | methods.push_back(MethodInfo("*Script Methods" )); |
228 | if (script_ref->is_built_in()) { |
229 | script_ref->reload(true); |
230 | } |
231 | script_ref->get_script_method_list(&methods); |
232 | } |
233 | |
234 | StringName base = base_type; |
235 | while (base) { |
236 | methods.push_back(MethodInfo("*" + String(base))); |
237 | ClassDB::get_method_list(base, &methods, true, true); |
238 | base = ClassDB::get_parent_class(base); |
239 | } |
240 | } |
241 | |
242 | TreeItem *category = nullptr; |
243 | |
244 | bool found = false; |
245 | bool script_methods = false; |
246 | |
247 | for (MethodInfo &mi : methods) { |
248 | if (mi.name.begins_with("*" )) { |
249 | if (category && category->get_first_child() == nullptr) { |
250 | memdelete(category); //old category was unused |
251 | } |
252 | category = search_options->create_item(root); |
253 | category->set_text(0, mi.name.replace_first("*" , "" )); |
254 | category->set_selectable(0, false); |
255 | |
256 | Ref<Texture2D> icon; |
257 | script_methods = false; |
258 | String rep = mi.name.replace("*" , "" ); |
259 | if (mi.name == "*Script Methods" ) { |
260 | icon = search_options->get_editor_theme_icon(SNAME("Script" )); |
261 | script_methods = true; |
262 | } else { |
263 | icon = EditorNode::get_singleton()->get_class_icon(rep); |
264 | } |
265 | category->set_icon(0, icon); |
266 | |
267 | continue; |
268 | } |
269 | |
270 | String name = mi.name.get_slice(":" , 0); |
271 | if (!script_methods && name.begins_with("_" ) && !(mi.flags & METHOD_FLAG_VIRTUAL)) { |
272 | continue; |
273 | } |
274 | |
275 | if (virtuals_only && !(mi.flags & METHOD_FLAG_VIRTUAL)) { |
276 | continue; |
277 | } |
278 | |
279 | if (!virtuals_only && (mi.flags & METHOD_FLAG_VIRTUAL)) { |
280 | continue; |
281 | } |
282 | |
283 | if (!search_box->get_text().is_empty() && name.findn(search_text) == -1) { |
284 | continue; |
285 | } |
286 | |
287 | TreeItem *item = search_options->create_item(category ? category : root); |
288 | |
289 | String desc; |
290 | if (mi.name.contains(":" )) { |
291 | desc = mi.name.get_slice(":" , 1) + " " ; |
292 | mi.name = mi.name.get_slice(":" , 0); |
293 | } else if (mi.return_val.type != Variant::NIL) { |
294 | desc = Variant::get_type_name(mi.return_val.type); |
295 | } else { |
296 | desc = "void" ; |
297 | } |
298 | |
299 | desc += vformat(" %s(" , mi.name); |
300 | |
301 | for (int i = 0; i < mi.arguments.size(); i++) { |
302 | if (i > 0) { |
303 | desc += ", " ; |
304 | } |
305 | |
306 | desc += mi.arguments[i].name; |
307 | |
308 | if (mi.arguments[i].type == Variant::NIL) { |
309 | desc += ": Variant" ; |
310 | } else if (mi.arguments[i].name.contains(":" )) { |
311 | desc += vformat(": %s" , mi.arguments[i].name.get_slice(":" , 1)); |
312 | mi.arguments[i].name = mi.arguments[i].name.get_slice(":" , 0); |
313 | } else { |
314 | desc += vformat(": %s" , Variant::get_type_name(mi.arguments[i].type)); |
315 | } |
316 | } |
317 | |
318 | desc += ")" ; |
319 | |
320 | if (mi.flags & METHOD_FLAG_CONST) { |
321 | desc += " const" ; |
322 | } |
323 | |
324 | if (mi.flags & METHOD_FLAG_VIRTUAL) { |
325 | desc += " virtual" ; |
326 | } |
327 | |
328 | item->set_text(0, desc); |
329 | item->set_metadata(0, name); |
330 | item->set_selectable(0, true); |
331 | |
332 | if (!found && !search_box->get_text().is_empty() && name.findn(search_text) != -1) { |
333 | item->select(0); |
334 | found = true; |
335 | } |
336 | } |
337 | |
338 | if (category && category->get_first_child() == nullptr) { |
339 | memdelete(category); //old category was unused |
340 | } |
341 | } |
342 | |
343 | get_ok_button()->set_disabled(root->get_first_child() == nullptr); |
344 | } |
345 | |
346 | void PropertySelector::_confirmed() { |
347 | TreeItem *ti = search_options->get_selected(); |
348 | if (!ti) { |
349 | return; |
350 | } |
351 | emit_signal(SNAME("selected" ), ti->get_metadata(0)); |
352 | hide(); |
353 | } |
354 | |
355 | void PropertySelector::_item_selected() { |
356 | help_bit->set_text("" ); |
357 | |
358 | TreeItem *item = search_options->get_selected(); |
359 | if (!item) { |
360 | return; |
361 | } |
362 | String name = item->get_metadata(0); |
363 | |
364 | String class_type; |
365 | if (type != Variant::NIL) { |
366 | class_type = Variant::get_type_name(type); |
367 | } else if (!base_type.is_empty()) { |
368 | class_type = base_type; |
369 | } else if (instance) { |
370 | class_type = instance->get_class(); |
371 | } |
372 | |
373 | DocTools *dd = EditorHelp::get_doc_data(); |
374 | String text; |
375 | if (properties) { |
376 | while (!class_type.is_empty()) { |
377 | HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(class_type); |
378 | if (E) { |
379 | for (int i = 0; i < E->value.properties.size(); i++) { |
380 | if (E->value.properties[i].name == name) { |
381 | text = DTR(E->value.properties[i].description); |
382 | break; |
383 | } |
384 | } |
385 | } |
386 | |
387 | if (!text.is_empty()) { |
388 | break; |
389 | } |
390 | |
391 | // The property may be from a parent class, keep looking. |
392 | class_type = ClassDB::get_parent_class(class_type); |
393 | } |
394 | } else { |
395 | while (!class_type.is_empty()) { |
396 | HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(class_type); |
397 | if (E) { |
398 | for (int i = 0; i < E->value.methods.size(); i++) { |
399 | if (E->value.methods[i].name == name) { |
400 | text = DTR(E->value.methods[i].description); |
401 | break; |
402 | } |
403 | } |
404 | } |
405 | |
406 | if (!text.is_empty()) { |
407 | break; |
408 | } |
409 | |
410 | // The method may be from a parent class, keep looking. |
411 | class_type = ClassDB::get_parent_class(class_type); |
412 | } |
413 | } |
414 | |
415 | if (!text.is_empty()) { |
416 | // Display both property name and description, since the help bit may be displayed |
417 | // far away from the location (especially if the dialog was resized to be taller). |
418 | help_bit->set_text(vformat("[b]%s[/b]: %s" , name, text)); |
419 | help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1)); |
420 | } else { |
421 | // Use nested `vformat()` as translators shouldn't interfere with BBCode tags. |
422 | help_bit->set_text(vformat(TTR("No description available for %s." ), vformat("[b]%s[/b]" , name))); |
423 | help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5)); |
424 | } |
425 | } |
426 | |
427 | void PropertySelector::_hide_requested() { |
428 | _cancel_pressed(); // From AcceptDialog. |
429 | } |
430 | |
431 | void PropertySelector::_notification(int p_what) { |
432 | switch (p_what) { |
433 | case NOTIFICATION_ENTER_TREE: { |
434 | connect("confirmed" , callable_mp(this, &PropertySelector::_confirmed)); |
435 | } break; |
436 | |
437 | case NOTIFICATION_EXIT_TREE: { |
438 | disconnect("confirmed" , callable_mp(this, &PropertySelector::_confirmed)); |
439 | } break; |
440 | } |
441 | } |
442 | |
443 | void PropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only) { |
444 | base_type = p_base; |
445 | selected = p_current; |
446 | type = Variant::NIL; |
447 | script = ObjectID(); |
448 | properties = false; |
449 | instance = nullptr; |
450 | virtuals_only = p_virtuals_only; |
451 | |
452 | popup_centered_ratio(0.6); |
453 | search_box->set_text("" ); |
454 | search_box->grab_focus(); |
455 | _update_search(); |
456 | } |
457 | |
458 | void PropertySelector::select_method_from_script(const Ref<Script> &p_script, const String &p_current) { |
459 | ERR_FAIL_COND(p_script.is_null()); |
460 | base_type = p_script->get_instance_base_type(); |
461 | selected = p_current; |
462 | type = Variant::NIL; |
463 | script = p_script->get_instance_id(); |
464 | properties = false; |
465 | instance = nullptr; |
466 | virtuals_only = false; |
467 | |
468 | popup_centered_ratio(0.6); |
469 | search_box->set_text("" ); |
470 | search_box->grab_focus(); |
471 | _update_search(); |
472 | } |
473 | |
474 | void PropertySelector::select_method_from_basic_type(Variant::Type p_type, const String &p_current) { |
475 | ERR_FAIL_COND(p_type == Variant::NIL); |
476 | base_type = "" ; |
477 | selected = p_current; |
478 | type = p_type; |
479 | script = ObjectID(); |
480 | properties = false; |
481 | instance = nullptr; |
482 | virtuals_only = false; |
483 | |
484 | popup_centered_ratio(0.6); |
485 | search_box->set_text("" ); |
486 | search_box->grab_focus(); |
487 | _update_search(); |
488 | } |
489 | |
490 | void PropertySelector::select_method_from_instance(Object *p_instance, const String &p_current) { |
491 | base_type = p_instance->get_class(); |
492 | selected = p_current; |
493 | type = Variant::NIL; |
494 | script = ObjectID(); |
495 | { |
496 | Ref<Script> scr = p_instance->get_script(); |
497 | if (scr.is_valid()) { |
498 | script = scr->get_instance_id(); |
499 | } |
500 | } |
501 | properties = false; |
502 | instance = nullptr; |
503 | virtuals_only = false; |
504 | |
505 | popup_centered_ratio(0.6); |
506 | search_box->set_text("" ); |
507 | search_box->grab_focus(); |
508 | _update_search(); |
509 | } |
510 | |
511 | void PropertySelector::select_property_from_base_type(const String &p_base, const String &p_current) { |
512 | base_type = p_base; |
513 | selected = p_current; |
514 | type = Variant::NIL; |
515 | script = ObjectID(); |
516 | properties = true; |
517 | instance = nullptr; |
518 | virtuals_only = false; |
519 | |
520 | popup_centered_ratio(0.6); |
521 | search_box->set_text("" ); |
522 | search_box->grab_focus(); |
523 | _update_search(); |
524 | } |
525 | |
526 | void PropertySelector::select_property_from_script(const Ref<Script> &p_script, const String &p_current) { |
527 | ERR_FAIL_COND(p_script.is_null()); |
528 | |
529 | base_type = p_script->get_instance_base_type(); |
530 | selected = p_current; |
531 | type = Variant::NIL; |
532 | script = p_script->get_instance_id(); |
533 | properties = true; |
534 | instance = nullptr; |
535 | virtuals_only = false; |
536 | |
537 | popup_centered_ratio(0.6); |
538 | search_box->set_text("" ); |
539 | search_box->grab_focus(); |
540 | _update_search(); |
541 | } |
542 | |
543 | void PropertySelector::select_property_from_basic_type(Variant::Type p_type, const String &p_current) { |
544 | ERR_FAIL_COND(p_type == Variant::NIL); |
545 | base_type = "" ; |
546 | selected = p_current; |
547 | type = p_type; |
548 | script = ObjectID(); |
549 | properties = true; |
550 | instance = nullptr; |
551 | virtuals_only = false; |
552 | |
553 | popup_centered_ratio(0.6); |
554 | search_box->set_text("" ); |
555 | search_box->grab_focus(); |
556 | _update_search(); |
557 | } |
558 | |
559 | void PropertySelector::select_property_from_instance(Object *p_instance, const String &p_current) { |
560 | base_type = "" ; |
561 | selected = p_current; |
562 | type = Variant::NIL; |
563 | script = ObjectID(); |
564 | properties = true; |
565 | instance = p_instance; |
566 | virtuals_only = false; |
567 | |
568 | popup_centered_ratio(0.6); |
569 | search_box->set_text("" ); |
570 | search_box->grab_focus(); |
571 | _update_search(); |
572 | } |
573 | |
574 | void PropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) { |
575 | type_filter = p_type_filter; |
576 | } |
577 | |
578 | void PropertySelector::_bind_methods() { |
579 | ADD_SIGNAL(MethodInfo("selected" , PropertyInfo(Variant::STRING, "name" ))); |
580 | } |
581 | |
582 | PropertySelector::PropertySelector() { |
583 | VBoxContainer *vbc = memnew(VBoxContainer); |
584 | add_child(vbc); |
585 | //set_child_rect(vbc); |
586 | search_box = memnew(LineEdit); |
587 | vbc->add_margin_child(TTR("Search:" ), search_box); |
588 | search_box->connect("text_changed" , callable_mp(this, &PropertySelector::_text_changed)); |
589 | search_box->connect("gui_input" , callable_mp(this, &PropertySelector::_sbox_input)); |
590 | search_options = memnew(Tree); |
591 | vbc->add_margin_child(TTR("Matches:" ), search_options, true); |
592 | set_ok_button_text(TTR("Open" )); |
593 | get_ok_button()->set_disabled(true); |
594 | register_text_enter(search_box); |
595 | set_hide_on_ok(false); |
596 | search_options->connect("item_activated" , callable_mp(this, &PropertySelector::_confirmed)); |
597 | search_options->connect("cell_selected" , callable_mp(this, &PropertySelector::_item_selected)); |
598 | search_options->set_hide_root(true); |
599 | search_options->set_hide_folding(true); |
600 | |
601 | help_bit = memnew(EditorHelpBit); |
602 | vbc->add_margin_child(TTR("Description:" ), help_bit); |
603 | help_bit->connect("request_hide" , callable_mp(this, &PropertySelector::_hide_requested)); |
604 | } |
605 | |