From b4bef31df57cd2ed829a76bc4c598527863836d9 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 8 Dec 2023 21:56:14 +0100 Subject: [PATCH 1/8] Add property editor for blackboard parameters --- blackboard/bb_param/bb_param.h | 4 +- demo/ai/tasks/arrive_pos.gd | 1 - demo/project.godot | 1 + .../blackboard_parameters/test_bb_params.gd | 37 +++ editor/editor_property_bb_param.cpp | 301 ++++++++++++++++++ editor/editor_property_bb_param.h | 60 ++++ editor/limbo_ai_editor_plugin.cpp | 2 + icons/LimboSpecifyValue.svg | 1 + 8 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 demo/tests/blackboard_parameters/test_bb_params.gd create mode 100644 editor/editor_property_bb_param.cpp create mode 100644 editor/editor_property_bb_param.h create mode 100644 icons/LimboSpecifyValue.svg diff --git a/blackboard/bb_param/bb_param.h b/blackboard/bb_param/bb_param.h index 4a74e2d..65e32cf 100644 --- a/blackboard/bb_param/bb_param.h +++ b/blackboard/bb_param/bb_param.h @@ -41,8 +41,6 @@ private: protected: static void _bind_methods(); - virtual Variant::Type get_type() const { return Variant::NIL; } - _FORCE_INLINE_ void _assign_default_value() { Callable::CallError err; Variant::construct(get_type(), saved_value, nullptr, 0, err); @@ -51,6 +49,8 @@ protected: void _get_property_list(List *p_list) const; public: + virtual Variant::Type get_type() const { return Variant::NIL; } + void set_value_source(ValueSource p_value); ValueSource get_value_source() const { return value_source; } diff --git a/demo/ai/tasks/arrive_pos.gd b/demo/ai/tasks/arrive_pos.gd index 3c04885..e0e361d 100644 --- a/demo/ai/tasks/arrive_pos.gd +++ b/demo/ai/tasks/arrive_pos.gd @@ -11,7 +11,6 @@ @tool @icon("res://icon.png") -class_name ArrivePos extends BTAction @export var target_position_var := "target_position" diff --git a/demo/project.godot b/demo/project.godot index f100ae9..d2acb33 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -11,6 +11,7 @@ config_version=5 [application] config/name="LimboAI Test" +config/tags=PackedStringArray("demo") run/main_scene="res://examples/waypoints/example_waypoints.tscn" config/features=PackedStringArray("4.2") config/icon="res://icon.png" diff --git a/demo/tests/blackboard_parameters/test_bb_params.gd b/demo/tests/blackboard_parameters/test_bb_params.gd new file mode 100644 index 0000000..459fe5b --- /dev/null +++ b/demo/tests/blackboard_parameters/test_bb_params.gd @@ -0,0 +1,37 @@ +@tool +class_name TestBBParams +extends Resource + +@export var bool_param: BBBool +@export var int_param: BBInt +@export var float_param: BBFloat +@export var string_param: BBString +@export var vec2_param: BBVector2 +@export var vec2i_param: BBVector2i +@export var rect2_param: BBRect2 +@export var rect2i_param: BBRect2i +@export var vector3_param: BBVector3 +@export var vector3i_param: BBVector3i +@export var vector4_param: BBVector4 +@export var vector4i_param: BBVector4i +@export var transform2d_param: BBTransform2D +@export var plane_param: BBPlane +@export var quaternion_param: BBQuaternion +@export var aabb_param: BBAabb +@export var basis_param: BBBasis +@export var transform3d_param: BBTransform3D +#@export var projection_param: BBProjection +@export var color_param: BBColor +@export var stringname_param: BBStringName +@export var node_param: BBNode +@export var dictionary_param: BBDictionary +@export var array_param: BBArray +@export var byte_array_param: BBByteArray +@export var int_array_param: BBIntArray +@export var float_array_param: BBFloatArray +@export var string_array_param: BBStringArray +@export var vector2_array_param: BBVector2Array +@export var vector3_array_param: BBVector2Array +@export var color_array_param: BBColorArray +@export var variant_param: BBVariant + diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp new file mode 100644 index 0000000..cd36f63 --- /dev/null +++ b/editor/editor_property_bb_param.cpp @@ -0,0 +1,301 @@ +/** + * editor_property_bb_param.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "editor_property_bb_param.h" + +#include "modules/limboai/blackboard/bb_param/bb_param.h" + +#include "core/error/error_macros.h" +#include "core/object/class_db.h" +#include "core/os/memory.h" +#include "core/string/print_string.h" +#include "editor/editor_inspector.h" +#include "editor/editor_properties.h" +#include "editor/editor_properties_array_dict.h" +#include "editor/editor_properties_vector.h" +#include "editor/editor_settings.h" +#include "scene/gui/base_button.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/line_edit.h" + +Ref EditorPropertyBBParam::_get_edited_param() { + Ref param = get_edited_property_value(); + if (param.is_null()) { + // Create parameter resource if null. + param = ClassDB::instantiate(param_type); + get_edited_object()->set(get_edited_property(), param); + } + return param; +} + +void EditorPropertyBBParam::_create_value_editor(Variant::Type p_type) { + if (value_editor) { + if (value_editor->get_meta(SNAME("_param_type")) == Variant(p_type)) { + return; + } + _remove_value_editor(); + } + + switch (p_type) { + case Variant::BOOL: { + value_editor = memnew(EditorPropertyCheck); + } break; + case Variant::INT: { + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(-100000, 100000, 1, false, true, true); + value_editor = editor; + } break; + case Variant::FLOAT: { + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true, false, true, true); + value_editor = editor; + } break; + case Variant::STRING: { + if (property_hint == PROPERTY_HINT_MULTILINE_TEXT) { + value_editor = memnew(EditorPropertyMultilineText); + } else { + value_editor = memnew(EditorPropertyText); + } + } break; + case Variant::VECTOR2: { + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::VECTOR2I: { + EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i); + editor->setup(-100000, 100000); + value_editor = editor; + } break; + case Variant::RECT2: { + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::RECT2I: { + EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i); + editor->setup(-100000, 100000); + value_editor = editor; + } break; + case Variant::VECTOR3: { + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::VECTOR3I: { + EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i); + editor->setup(-100000, 100000); + value_editor = editor; + } break; + case Variant::VECTOR4: { + EditorPropertyVector4 *editor = memnew(EditorPropertyVector4); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::VECTOR4I: { + EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i); + editor->setup(-100000, 100000); + value_editor = editor; + } break; + case Variant::TRANSFORM2D: { + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::PLANE: { + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::QUATERNION: { + EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::AABB: { + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::BASIS: { + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::TRANSFORM3D: { + EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::PROJECTION: { + EditorPropertyProjection *editor = memnew(EditorPropertyProjection); + editor->setup(-100000, 100000, EDITOR_GET("interface/inspector/default_float_step"), true); + value_editor = editor; + } break; + case Variant::COLOR: { + value_editor = memnew(EditorPropertyColor); + } break; + case Variant::STRING_NAME: { + EditorPropertyText *editor = memnew(EditorPropertyText); + editor->set_string_name(true); + value_editor = editor; + } break; + case Variant::NODE_PATH: { + value_editor = memnew(EditorPropertyNodePath); + } break; + // case Variant::RID: { + // } break; + // case Variant::SIGNAL: { + // } break; + // case Variant::CALLABLE: { + // } break; + // case Variant::OBJECT: { + // } break; + case Variant::DICTIONARY: { + value_editor = memnew(EditorPropertyDictionary); + } break; + + case Variant::ARRAY: + case Variant::PACKED_BYTE_ARRAY: + case Variant::PACKED_INT32_ARRAY: + case Variant::PACKED_FLOAT32_ARRAY: + case Variant::PACKED_INT64_ARRAY: + case Variant::PACKED_FLOAT64_ARRAY: + case Variant::PACKED_STRING_ARRAY: + case Variant::PACKED_VECTOR2_ARRAY: + case Variant::PACKED_VECTOR3_ARRAY: + case Variant::PACKED_COLOR_ARRAY: { + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(p_type); + value_editor = editor; + } break; + + default: { + ERR_PRINT("Unexpected variant type!"); + value_editor = memnew(EditorPropertyNil); + } + } + value_editor->set_name_split_ratio(0.0); + hbox->add_child(value_editor); + value_editor->set_h_size_flags(SIZE_EXPAND_FILL); + value_editor->set_meta(SNAME("_param_type"), p_type); + value_editor->connect(SNAME("property_changed"), callable_mp(this, &EditorPropertyBBParam::_value_edited)); +} + +void EditorPropertyBBParam::_remove_value_editor() { + if (value_editor) { + hbox->remove_child(value_editor); + value_editor->queue_free(); + value_editor = nullptr; + } +} + +void EditorPropertyBBParam::_value_edited(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + _get_edited_param()->set_saved_value(p_value); +} + +void EditorPropertyBBParam::_mode_changed() { + _get_edited_param()->set_value_source(value_mode->is_pressed() ? BBParam::SAVED_VALUE : BBParam::BLACKBOARD_VAR); + update_property(); +} + +void EditorPropertyBBParam::_variable_edited(const String &p_text) { + _get_edited_param()->set_variable(p_text); +} + +void EditorPropertyBBParam::update_property() { + Ref param = _get_edited_param(); + if (param->get_value_source() == BBParam::BLACKBOARD_VAR) { + _remove_value_editor(); + variable_edit->set_text(param->get_variable()); + variable_edit->set_editable(true); + variable_edit->show(); + variable_mode->set_pressed_no_signal(true); + value_mode->set_pressed_no_signal(false); + } else { + variable_edit->hide(); + _create_value_editor(param->get_type()); + value_editor->show(); + value_editor->set_object_and_property(param.ptr(), SNAME("saved_value")); + value_mode->set_pressed_no_signal(true); + variable_mode->set_pressed_no_signal(false); + value_editor->update_property(); + } +} + +void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text) { + param_type = p_hint_text; + property_hint = p_hint; +} + +void EditorPropertyBBParam::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + value_mode->set_icon(get_editor_theme_icon(SNAME("LimboSpecifyValue"))); + variable_mode->set_icon(get_editor_theme_icon(SNAME("BTSetVar"))); + } break; + } +} + +EditorPropertyBBParam::EditorPropertyBBParam() { + hbox = memnew(HBoxContainer); + add_child(hbox); + + HBoxContainer *modes = memnew(HBoxContainer); + hbox->add_child(modes); + modes->add_theme_constant_override("separation", 0); + + Ref modes_group; + modes_group.instantiate(); + + value_mode = memnew(Button); + modes->add_child(value_mode); + value_mode->set_focus_mode(FOCUS_NONE); + value_mode->set_button_group(modes_group); + value_mode->set_toggle_mode(true); + value_mode->set_tooltip_text(TTR("Specify value")); + value_mode->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); + + variable_mode = memnew(Button); + modes->add_child(variable_mode); + variable_mode->set_focus_mode(FOCUS_NONE); + variable_mode->set_button_group(modes_group); + variable_mode->set_toggle_mode(true); + variable_mode->set_tooltip_text(TTR("Bind to a blackboard variable")); + variable_mode->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); + + variable_edit = memnew(LineEdit); + hbox->add_child(variable_edit); + variable_edit->set_placeholder(TTR("Variable")); + variable_edit->set_h_size_flags(SIZE_EXPAND_FILL); + variable_edit->connect(SNAME("text_changed"), callable_mp(this, &EditorPropertyBBParam::_variable_edited)); + + param_type = SNAME("BBString"); +} + +//***** EditorInspectorPluginBBParam + +bool EditorInspectorPluginBBParam::can_handle(Object *p_object) { + return true; // Handles everything. +} + +bool EditorInspectorPluginBBParam::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField p_usage, const bool p_wide) { + if (p_hint == PROPERTY_HINT_RESOURCE_TYPE && p_hint_text.begins_with("BB")) { + // TODO: Add more rigid hint check. + EditorPropertyBBParam *editor = memnew(EditorPropertyBBParam()); + editor->setup(p_hint, p_hint_text); + add_property_editor(p_path, editor); + return true; + } + return false; +} diff --git a/editor/editor_property_bb_param.h b/editor/editor_property_bb_param.h new file mode 100644 index 0000000..63b1130 --- /dev/null +++ b/editor/editor_property_bb_param.h @@ -0,0 +1,60 @@ +/** + * editor_property_bb_param.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef EDITOR_PROPERTY_BB_PARAM_H +#define EDITOR_PROPERTY_BB_PARAM_H + +#include "core/object/object.h" +#include "editor/editor_inspector.h" + +#include "modules/limboai/blackboard/bb_param/bb_param.h" + +class EditorPropertyBBParam : public EditorProperty { + GDCLASS(EditorPropertyBBParam, EditorProperty); + +private: + StringName param_type; + PropertyHint property_hint = PROPERTY_HINT_NONE; + + HBoxContainer *hbox = nullptr; + Button *value_mode = nullptr; + Button *variable_mode = nullptr; + EditorProperty *value_editor = nullptr; + LineEdit *variable_edit = nullptr; + + Ref _get_edited_param(); + + void _create_value_editor(Variant::Type p_type); + void _remove_value_editor(); + + void _value_edited(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false); + void _variable_edited(const String &p_text); + void _mode_changed(); + +protected: + void _notification(int p_what); + +public: + virtual void update_property() override; + void setup(PropertyHint p_hint, const String &p_hint_text); + + EditorPropertyBBParam(); +}; + +class EditorInspectorPluginBBParam : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginBBParam, EditorInspectorPlugin); + +public: + virtual bool can_handle(Object *p_object) override; + virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField p_usage, const bool p_wide = false) override; +}; + +#endif // EDITOR_PROPERTY_BB_PARAM_H diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index 846fcd8..54516b4 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -18,6 +18,7 @@ #include "modules/limboai/bt/tasks/composites/bt_probability_selector.h" #include "modules/limboai/bt/tasks/composites/bt_selector.h" #include "modules/limboai/editor/debugger/limbo_debugger_plugin.h" +#include "modules/limboai/editor/editor_property_bb_param.h" #include "modules/limboai/util/limbo_utility.h" #include "core/config/project_settings.h" @@ -1157,6 +1158,7 @@ LimboAIEditorPlugin::LimboAIEditorPlugin() { EditorNode::get_singleton()->get_main_screen_control()->add_child(limbo_ai_editor); limbo_ai_editor->hide(); add_debugger_plugin(memnew(LimboDebuggerPlugin)); + add_inspector_plugin(memnew(EditorInspectorPluginBBParam)); } LimboAIEditorPlugin::~LimboAIEditorPlugin() { diff --git a/icons/LimboSpecifyValue.svg b/icons/LimboSpecifyValue.svg new file mode 100644 index 0000000..008bee7 --- /dev/null +++ b/icons/LimboSpecifyValue.svg @@ -0,0 +1 @@ + \ No newline at end of file From 91ed8bfe3d2db5ab75f3edbb3ddc8afeb28f60f6 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 9 Dec 2023 19:31:51 +0100 Subject: [PATCH 2/8] Implement mode switch button for BB parameters --- editor/editor_property_bb_param.cpp | 44 ++++++----------- editor/editor_property_bb_param.h | 10 +++- editor/mode_switch_button.cpp | 73 +++++++++++++++++++++++++++++ editor/mode_switch_button.h | 54 +++++++++++++++++++++ 4 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 editor/mode_switch_button.cpp create mode 100644 editor/mode_switch_button.h diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index cd36f63..42ca0cf 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -12,6 +12,7 @@ #include "editor_property_bb_param.h" #include "modules/limboai/blackboard/bb_param/bb_param.h" +#include "modules/limboai/editor/mode_switch_button.h" #include "core/error/error_macros.h" #include "core/object/class_db.h" @@ -204,7 +205,7 @@ void EditorPropertyBBParam::_value_edited(const String &p_property, Variant p_va } void EditorPropertyBBParam::_mode_changed() { - _get_edited_param()->set_value_source(value_mode->is_pressed() ? BBParam::SAVED_VALUE : BBParam::BLACKBOARD_VAR); + _get_edited_param()->set_value_source(mode_button->get_mode() == Mode::SPECIFY_VALUE ? BBParam::SAVED_VALUE : BBParam::BLACKBOARD_VAR); update_property(); } @@ -219,15 +220,13 @@ void EditorPropertyBBParam::update_property() { variable_edit->set_text(param->get_variable()); variable_edit->set_editable(true); variable_edit->show(); - variable_mode->set_pressed_no_signal(true); - value_mode->set_pressed_no_signal(false); + mode_button->set_mode(Mode::BIND_VAR, true); } else { variable_edit->hide(); _create_value_editor(param->get_type()); value_editor->show(); value_editor->set_object_and_property(param.ptr(), SNAME("saved_value")); - value_mode->set_pressed_no_signal(true); - variable_mode->set_pressed_no_signal(false); + mode_button->set_mode(Mode::SPECIFY_VALUE, true); value_editor->update_property(); } } @@ -241,8 +240,13 @@ void EditorPropertyBBParam::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - value_mode->set_icon(get_editor_theme_icon(SNAME("LimboSpecifyValue"))); - variable_mode->set_icon(get_editor_theme_icon(SNAME("BTSetVar"))); + int id = mode_button->get_mode(); + mode_button->clear(); + mode_button->add_mode(Mode::SPECIFY_VALUE, get_editor_theme_icon(SNAME("LimboSpecifyValue")), TTR("Mode: Specify value.\nClick to switch mode.")); + mode_button->add_mode(Mode::BIND_VAR, get_editor_theme_icon(SNAME("BTSetVar")), TTR("Mode: Bind blackboard variable.\nClick to switch mode.")); + if (id >= 0) { + mode_button->set_mode(id); + } } break; } } @@ -251,28 +255,10 @@ EditorPropertyBBParam::EditorPropertyBBParam() { hbox = memnew(HBoxContainer); add_child(hbox); - HBoxContainer *modes = memnew(HBoxContainer); - hbox->add_child(modes); - modes->add_theme_constant_override("separation", 0); - - Ref modes_group; - modes_group.instantiate(); - - value_mode = memnew(Button); - modes->add_child(value_mode); - value_mode->set_focus_mode(FOCUS_NONE); - value_mode->set_button_group(modes_group); - value_mode->set_toggle_mode(true); - value_mode->set_tooltip_text(TTR("Specify value")); - value_mode->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); - - variable_mode = memnew(Button); - modes->add_child(variable_mode); - variable_mode->set_focus_mode(FOCUS_NONE); - variable_mode->set_button_group(modes_group); - variable_mode->set_toggle_mode(true); - variable_mode->set_tooltip_text(TTR("Bind to a blackboard variable")); - variable_mode->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); + mode_button = memnew(ModeSwitchButton); + hbox->add_child(mode_button); + mode_button->set_focus_mode(FOCUS_NONE); + mode_button->connect(SNAME("mode_changed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); variable_edit = memnew(LineEdit); hbox->add_child(variable_edit); diff --git a/editor/editor_property_bb_param.h b/editor/editor_property_bb_param.h index 63b1130..85c2e86 100644 --- a/editor/editor_property_bb_param.h +++ b/editor/editor_property_bb_param.h @@ -16,17 +16,23 @@ #include "editor/editor_inspector.h" #include "modules/limboai/blackboard/bb_param/bb_param.h" +#include "modules/limboai/editor/mode_switch_button.h" class EditorPropertyBBParam : public EditorProperty { GDCLASS(EditorPropertyBBParam, EditorProperty); private: + enum Mode { + SPECIFY_VALUE, + BIND_VAR, + }; + StringName param_type; PropertyHint property_hint = PROPERTY_HINT_NONE; + Mode mode = Mode::SPECIFY_VALUE; HBoxContainer *hbox = nullptr; - Button *value_mode = nullptr; - Button *variable_mode = nullptr; + ModeSwitchButton *mode_button = nullptr; EditorProperty *value_editor = nullptr; LineEdit *variable_edit = nullptr; diff --git a/editor/mode_switch_button.cpp b/editor/mode_switch_button.cpp new file mode 100644 index 0000000..e0051c5 --- /dev/null +++ b/editor/mode_switch_button.cpp @@ -0,0 +1,73 @@ +/** + * mode_switch_button.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "mode_switch_button.h" + +#include "core/error/error_macros.h" +#include "core/object/object.h" +#include "core/variant/variant.h" + +void ModeSwitchButton::add_mode(int p_id, const Ref &p_icon, const String &p_tooltip) { + bool unique_id = true; + for (int i = 0; i < modes.size(); i++) { + if (modes[i].id == p_id) { + unique_id = false; + break; + } + } + ERR_FAIL_COND_MSG(unique_id == false, "ID is already in use by another button state: " + itos(p_id)); + + Mode mode; + mode.id = p_id; + mode.icon = p_icon; + mode.tooltip = p_tooltip; + modes.append(mode); + + if (current_mode_index == -1) { + _set_mode(0); + } +} + +void ModeSwitchButton::set_mode(int p_id, bool p_no_signal) { + ERR_FAIL_COND_MSG(modes.is_empty(), "Cannot set button state with zero states."); + + int idx = -1; + for (int i = 0; i < modes.size(); i++) { + if (modes[i].id == p_id) { + idx = i; + break; + } + } + ERR_FAIL_COND_MSG(idx == -1, "Button state not found with such id: " + itos(p_id)); + + _set_mode(p_id); + if (!p_no_signal) { + emit_signal(SNAME("mode_changed")); + } +} + +void ModeSwitchButton::clear() { + current_mode_index = -1; + modes.clear(); +} + +void ModeSwitchButton::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_mode", "p_id", "p_icon", "p_tooltip"), &ModeSwitchButton::add_mode); + ClassDB::bind_method(D_METHOD("get_mode"), &ModeSwitchButton::get_mode); + ClassDB::bind_method(D_METHOD("set_mode", "p_id"), &ModeSwitchButton::set_mode); + ClassDB::bind_method(D_METHOD("next_mode"), &ModeSwitchButton::next_mode); + + ADD_SIGNAL(MethodInfo("mode_changed")); +} + +ModeSwitchButton::ModeSwitchButton() { + call_deferred(SNAME("connect"), SNAME("pressed"), callable_mp(this, &ModeSwitchButton::next_mode)); +} diff --git a/editor/mode_switch_button.h b/editor/mode_switch_button.h new file mode 100644 index 0000000..e4830b4 --- /dev/null +++ b/editor/mode_switch_button.h @@ -0,0 +1,54 @@ +/** + * mode_switch_button.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef MODE_SWITCH_BUTTON +#define MODE_SWITCH_BUTTON + +#include "scene/gui/button.h" + +#include "core/typedefs.h" +#include "scene/resources/texture.h" + +class ModeSwitchButton : public Button { + GDCLASS(ModeSwitchButton, Button); + +private: + struct Mode { + int id = 0; + Ref icon = nullptr; + String tooltip = ""; + }; + int current_mode_index = -1; + + Vector modes; + + _FORCE_INLINE_ void _set_mode(int p_id) { + current_mode_index = p_id; + set_icon(modes[current_mode_index].icon); + if (!modes[current_mode_index].tooltip.is_empty()) { + set_tooltip_text(modes[current_mode_index].tooltip); + } + } + +protected: + static void _bind_methods(); + +public: + void add_mode(int p_id, const Ref &p_icon, const String &p_tooltip = ""); + int get_mode() const { return modes.size() > 0 ? modes[current_mode_index].id : -1; } + void set_mode(int p_id, bool p_no_signal = false); + _FORCE_INLINE_ void next_mode() { set_mode((current_mode_index + 1) % modes.size()); }; + void clear(); + + ModeSwitchButton(); +}; + +#endif // MODE_SWITCH_BUTTON From 7bce63fbcb297ecd5547ee5b24514ace0fc9f9c8 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 9 Dec 2023 21:28:20 +0100 Subject: [PATCH 3/8] Remove separation in param editor --- editor/editor_property_bb_param.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index 42ca0cf..75898fc 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -254,6 +254,7 @@ void EditorPropertyBBParam::_notification(int p_what) { EditorPropertyBBParam::EditorPropertyBBParam() { hbox = memnew(HBoxContainer); add_child(hbox); + hbox->add_theme_constant_override(SNAME("separation"), 0); mode_button = memnew(ModeSwitchButton); hbox->add_child(mode_button); From 106abe0b5aad0674f69f8a961800be88189ff256 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Dec 2023 14:35:28 +0100 Subject: [PATCH 4/8] Add support for variant param editing and fix bug with ModeSwitchButton --- blackboard/bb_param/bb_variant.h | 2 +- editor/editor_property_bb_param.cpp | 58 ++++++++++++++++++++++++++--- editor/editor_property_bb_param.h | 7 +++- editor/mode_switch_button.cpp | 4 +- editor/mode_switch_button.h | 4 +- 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/blackboard/bb_param/bb_variant.h b/blackboard/bb_param/bb_variant.h index d8664bd..1eb95bc 100644 --- a/blackboard/bb_param/bb_variant.h +++ b/blackboard/bb_param/bb_variant.h @@ -25,10 +25,10 @@ private: protected: static void _bind_methods(); +public: virtual Variant::Type get_type() const override; void set_type(Variant::Type p_type); -public: BBVariant(); }; diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index 75898fc..5cf8064 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -11,11 +11,14 @@ #include "editor_property_bb_param.h" +#include "core/variant/variant.h" #include "modules/limboai/blackboard/bb_param/bb_param.h" +#include "modules/limboai/blackboard/bb_param/bb_variant.h" #include "modules/limboai/editor/mode_switch_button.h" #include "core/error/error_macros.h" #include "core/object/class_db.h" +#include "core/object/ref_counted.h" #include "core/os/memory.h" #include "core/string/print_string.h" #include "editor/editor_inspector.h" @@ -27,6 +30,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/line_edit.h" +#include "scene/gui/menu_button.h" Ref EditorPropertyBBParam::_get_edited_param() { Ref param = get_edited_property_value(); @@ -47,6 +51,9 @@ void EditorPropertyBBParam::_create_value_editor(Variant::Type p_type) { } switch (p_type) { + case Variant::NIL: { + value_editor = memnew(EditorPropertyNil); + } break; case Variant::BOOL: { value_editor = memnew(EditorPropertyCheck); } break; @@ -186,7 +193,7 @@ void EditorPropertyBBParam::_create_value_editor(Variant::Type p_type) { } } value_editor->set_name_split_ratio(0.0); - hbox->add_child(value_editor); + editor_hbox->add_child(value_editor); value_editor->set_h_size_flags(SIZE_EXPAND_FILL); value_editor->set_meta(SNAME("_param_type"), p_type); value_editor->connect(SNAME("property_changed"), callable_mp(this, &EditorPropertyBBParam::_value_edited)); @@ -194,7 +201,7 @@ void EditorPropertyBBParam::_create_value_editor(Variant::Type p_type) { void EditorPropertyBBParam::_remove_value_editor() { if (value_editor) { - hbox->remove_child(value_editor); + editor_hbox->remove_child(value_editor); value_editor->queue_free(); value_editor = nullptr; } @@ -209,6 +216,16 @@ void EditorPropertyBBParam::_mode_changed() { update_property(); } +void EditorPropertyBBParam::_type_selected(int p_index) { + Ref param = _get_edited_param(); + ERR_FAIL_COND(param.is_null()); + Variant::Type t = Variant::Type(p_index); + param->set_type(t); + String type_name = Variant::get_type_name(t); + type_choice->set_icon(get_editor_theme_icon(type_name)); + update_property(); +} + void EditorPropertyBBParam::_variable_edited(const String &p_text) { _get_edited_param()->set_variable(p_text); } @@ -240,12 +257,32 @@ void EditorPropertyBBParam::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - int id = mode_button->get_mode(); + { + String type = Variant::get_type_name(_get_edited_param()->get_type()); + type_choice->set_icon(get_editor_theme_icon(type)); + } + + // Initialize mode button. mode_button->clear(); mode_button->add_mode(Mode::SPECIFY_VALUE, get_editor_theme_icon(SNAME("LimboSpecifyValue")), TTR("Mode: Specify value.\nClick to switch mode.")); mode_button->add_mode(Mode::BIND_VAR, get_editor_theme_icon(SNAME("BTSetVar")), TTR("Mode: Bind blackboard variable.\nClick to switch mode.")); - if (id >= 0) { - mode_button->set_mode(id); + mode_button->set_mode(_get_edited_param()->get_value_source() == BBParam::BLACKBOARD_VAR ? Mode::BIND_VAR : Mode::SPECIFY_VALUE); + + Ref variant_param = _get_edited_param(); + bool is_variant_param = variant_param.is_valid(); + if (is_variant_param) { + // Initialize type choice. + PopupMenu *type_menu = type_choice->get_popup(); + type_menu->clear(); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::RID || i == Variant::CALLABLE || i == Variant::OBJECT || i == Variant::SIGNAL) { + continue; + } + String type = Variant::get_type_name(Variant::Type(i)); + type_menu->add_icon_item(get_editor_theme_icon(type), type, i); + } + } else { // Not a variant param. + type_choice->hide(); } } break; } @@ -261,12 +298,21 @@ EditorPropertyBBParam::EditorPropertyBBParam() { mode_button->set_focus_mode(FOCUS_NONE); mode_button->connect(SNAME("mode_changed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); + editor_hbox = memnew(HBoxContainer); + hbox->add_child(editor_hbox); + editor_hbox->set_h_size_flags(SIZE_EXPAND_FILL); + editor_hbox->add_theme_constant_override(SNAME("separation"), 0); + variable_edit = memnew(LineEdit); - hbox->add_child(variable_edit); + editor_hbox->add_child(variable_edit); variable_edit->set_placeholder(TTR("Variable")); variable_edit->set_h_size_flags(SIZE_EXPAND_FILL); variable_edit->connect(SNAME("text_changed"), callable_mp(this, &EditorPropertyBBParam::_variable_edited)); + type_choice = memnew(MenuButton); + hbox->add_child(type_choice); + type_choice->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &EditorPropertyBBParam::_type_selected)); + param_type = SNAME("BBString"); } diff --git a/editor/editor_property_bb_param.h b/editor/editor_property_bb_param.h index 85c2e86..e1c2d40 100644 --- a/editor/editor_property_bb_param.h +++ b/editor/editor_property_bb_param.h @@ -12,12 +12,14 @@ #ifndef EDITOR_PROPERTY_BB_PARAM_H #define EDITOR_PROPERTY_BB_PARAM_H -#include "core/object/object.h" #include "editor/editor_inspector.h" #include "modules/limboai/blackboard/bb_param/bb_param.h" #include "modules/limboai/editor/mode_switch_button.h" +#include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" + class EditorPropertyBBParam : public EditorProperty { GDCLASS(EditorPropertyBBParam, EditorProperty); @@ -32,9 +34,11 @@ private: Mode mode = Mode::SPECIFY_VALUE; HBoxContainer *hbox = nullptr; + HBoxContainer *editor_hbox = nullptr; ModeSwitchButton *mode_button = nullptr; EditorProperty *value_editor = nullptr; LineEdit *variable_edit = nullptr; + MenuButton *type_choice = nullptr; Ref _get_edited_param(); @@ -44,6 +48,7 @@ private: void _value_edited(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false); void _variable_edited(const String &p_text); void _mode_changed(); + void _type_selected(int p_index); protected: void _notification(int p_what); diff --git a/editor/mode_switch_button.cpp b/editor/mode_switch_button.cpp index e0051c5..b50fa6e 100644 --- a/editor/mode_switch_button.cpp +++ b/editor/mode_switch_button.cpp @@ -32,7 +32,7 @@ void ModeSwitchButton::add_mode(int p_id, const Ref &p_icon, const St modes.append(mode); if (current_mode_index == -1) { - _set_mode(0); + _set_mode_by_index(0); } } @@ -48,7 +48,7 @@ void ModeSwitchButton::set_mode(int p_id, bool p_no_signal) { } ERR_FAIL_COND_MSG(idx == -1, "Button state not found with such id: " + itos(p_id)); - _set_mode(p_id); + _set_mode_by_index(idx); if (!p_no_signal) { emit_signal(SNAME("mode_changed")); } diff --git a/editor/mode_switch_button.h b/editor/mode_switch_button.h index e4830b4..b27e92e 100644 --- a/editor/mode_switch_button.h +++ b/editor/mode_switch_button.h @@ -30,8 +30,8 @@ private: Vector modes; - _FORCE_INLINE_ void _set_mode(int p_id) { - current_mode_index = p_id; + _FORCE_INLINE_ void _set_mode_by_index(int p_index) { + current_mode_index = p_index; set_icon(modes[current_mode_index].icon); if (!modes[current_mode_index].tooltip.is_empty()) { set_tooltip_text(modes[current_mode_index].tooltip); From 949583a6b47ad28138ea78c014ba536117b9466a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Dec 2023 14:41:00 +0100 Subject: [PATCH 5/8] Change type choice position, appearance and tooltip --- editor/editor_property_bb_param.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index 5cf8064..6a66f04 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -298,6 +298,12 @@ EditorPropertyBBParam::EditorPropertyBBParam() { mode_button->set_focus_mode(FOCUS_NONE); mode_button->connect(SNAME("mode_changed"), callable_mp(this, &EditorPropertyBBParam::_mode_changed)); + type_choice = memnew(MenuButton); + hbox->add_child(type_choice); + type_choice->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &EditorPropertyBBParam::_type_selected)); + type_choice->set_tooltip_text(TTR("Click to choose type")); + type_choice->set_flat(false); + editor_hbox = memnew(HBoxContainer); hbox->add_child(editor_hbox); editor_hbox->set_h_size_flags(SIZE_EXPAND_FILL); @@ -309,10 +315,6 @@ EditorPropertyBBParam::EditorPropertyBBParam() { variable_edit->set_h_size_flags(SIZE_EXPAND_FILL); variable_edit->connect(SNAME("text_changed"), callable_mp(this, &EditorPropertyBBParam::_variable_edited)); - type_choice = memnew(MenuButton); - hbox->add_child(type_choice); - type_choice->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &EditorPropertyBBParam::_type_selected)); - param_type = SNAME("BBString"); } From 2fc5732ed82ce5ea9ea7fbc0bfadaba495c0d328 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Dec 2023 15:50:03 +0100 Subject: [PATCH 6/8] Fix param type button not updated when property is reset --- editor/editor_property_bb_param.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index 6a66f04..4a6cfc8 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -219,10 +219,7 @@ void EditorPropertyBBParam::_mode_changed() { void EditorPropertyBBParam::_type_selected(int p_index) { Ref param = _get_edited_param(); ERR_FAIL_COND(param.is_null()); - Variant::Type t = Variant::Type(p_index); - param->set_type(t); - String type_name = Variant::get_type_name(t); - type_choice->set_icon(get_editor_theme_icon(type_name)); + param->set_type(Variant::Type(p_index)); update_property(); } @@ -246,6 +243,12 @@ void EditorPropertyBBParam::update_property() { mode_button->set_mode(Mode::SPECIFY_VALUE, true); value_editor->update_property(); } + + if (param->is_class_ptr(BBVariant::get_class_ptr_static())) { + Variant::Type t = Variant::Type(param->get_type()); + String type_name = Variant::get_type_name(t); + type_choice->set_icon(get_editor_theme_icon(type_name)); + } } void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text) { From 678df76c650c98b25f11ec53c592f08bc2a56cd2 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Dec 2023 15:58:57 +0100 Subject: [PATCH 7/8] Hide type choice when editing variable name --- editor/editor_property_bb_param.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index 4a6cfc8..df5b6b2 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -229,12 +229,15 @@ void EditorPropertyBBParam::_variable_edited(const String &p_text) { void EditorPropertyBBParam::update_property() { Ref param = _get_edited_param(); + bool is_variant_param = param->is_class_ptr(BBVariant::get_class_ptr_static()); + if (param->get_value_source() == BBParam::BLACKBOARD_VAR) { _remove_value_editor(); variable_edit->set_text(param->get_variable()); variable_edit->set_editable(true); variable_edit->show(); mode_button->set_mode(Mode::BIND_VAR, true); + type_choice->hide(); } else { variable_edit->hide(); _create_value_editor(param->get_type()); @@ -242,9 +245,10 @@ void EditorPropertyBBParam::update_property() { value_editor->set_object_and_property(param.ptr(), SNAME("saved_value")); mode_button->set_mode(Mode::SPECIFY_VALUE, true); value_editor->update_property(); + type_choice->set_visible(is_variant_param); } - if (param->is_class_ptr(BBVariant::get_class_ptr_static())) { + if (is_variant_param) { Variant::Type t = Variant::Type(param->get_type()); String type_name = Variant::get_type_name(t); type_choice->set_icon(get_editor_theme_icon(type_name)); @@ -271,8 +275,7 @@ void EditorPropertyBBParam::_notification(int p_what) { mode_button->add_mode(Mode::BIND_VAR, get_editor_theme_icon(SNAME("BTSetVar")), TTR("Mode: Bind blackboard variable.\nClick to switch mode.")); mode_button->set_mode(_get_edited_param()->get_value_source() == BBParam::BLACKBOARD_VAR ? Mode::BIND_VAR : Mode::SPECIFY_VALUE); - Ref variant_param = _get_edited_param(); - bool is_variant_param = variant_param.is_valid(); + bool is_variant_param = _get_edited_param()->is_class_ptr(BBVariant::get_class_ptr_static()); if (is_variant_param) { // Initialize type choice. PopupMenu *type_menu = type_choice->get_popup(); From dfa332490a4094e3a12fe8774891f86c0680babf Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Dec 2023 16:32:41 +0100 Subject: [PATCH 8/8] Add object(resource) support for variant param editor --- editor/editor_property_bb_param.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/editor/editor_property_bb_param.cpp b/editor/editor_property_bb_param.cpp index df5b6b2..9d37ef6 100644 --- a/editor/editor_property_bb_param.cpp +++ b/editor/editor_property_bb_param.cpp @@ -17,6 +17,7 @@ #include "modules/limboai/editor/mode_switch_button.h" #include "core/error/error_macros.h" +#include "core/io/marshalls.h" #include "core/object/class_db.h" #include "core/object/ref_counted.h" #include "core/os/memory.h" @@ -160,14 +161,18 @@ void EditorPropertyBBParam::_create_value_editor(Variant::Type p_type) { case Variant::NODE_PATH: { value_editor = memnew(EditorPropertyNodePath); } break; - // case Variant::RID: { - // } break; - // case Variant::SIGNAL: { - // } break; - // case Variant::CALLABLE: { - // } break; - // case Variant::OBJECT: { - // } break; + // case Variant::RID: { + // } break; + // case Variant::SIGNAL: { + // } break; + // case Variant::CALLABLE: { + // } break; + case Variant::OBJECT: { + // Only resources are supported. + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup(_get_edited_param().ptr(), SNAME("saved_value"), "Resource"); + value_editor = editor; + } break; case Variant::DICTIONARY: { value_editor = memnew(EditorPropertyDictionary); } break; @@ -281,7 +286,7 @@ void EditorPropertyBBParam::_notification(int p_what) { PopupMenu *type_menu = type_choice->get_popup(); type_menu->clear(); for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (i == Variant::RID || i == Variant::CALLABLE || i == Variant::OBJECT || i == Variant::SIGNAL) { + if (i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) { continue; } String type = Variant::get_type_name(Variant::Type(i));