| 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 | |