diff --git a/demo/project.godot b/demo/project.godot index 32ef28a..91d00b8 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="LimboAI Demo" run/main_scene="res://demo/scenes/showcase.tscn" -config/features=PackedStringArray("4.2", "Forward Plus") +config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://demo/assets/icon.svg" [display] diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index be868c6..99da9a9 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -221,6 +221,8 @@ void LimboAIEditor::_load_bt(String p_path) { void LimboAIEditor::_disable_editing() { task_tree->unload(); task_palette->hide(); + task_tree->hide(); + usage_hint->show(); } void LimboAIEditor::edit_bt(Ref p_behavior_tree, bool p_force_refresh) { @@ -235,14 +237,9 @@ void LimboAIEditor::edit_bt(Ref p_behavior_tree, bool p_force_refr p_behavior_tree->notify_property_list_changed(); #endif // LIMBOAI_MODULE - if (task_tree->get_bt().is_valid() && - task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true))) { - task_tree->get_bt()->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); - } - task_tree->load_bt(p_behavior_tree); - if (task_tree->get_bt().is_valid()) { + if (task_tree->get_bt().is_valid() && !task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { task_tree->get_bt()->connect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); } @@ -959,6 +956,10 @@ void LimboAIEditor::_tab_clicked(int p_tab) { void LimboAIEditor::_tab_closed(int p_tab) { ERR_FAIL_INDEX(p_tab, history.size()); + Ref history_bt = history[p_tab]; + if (history_bt.is_valid() && history_bt->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { + history_bt->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty)); + } history.remove_at(p_tab); idx_history = MIN(idx_history, history.size() - 1); if (idx_history < 0) { @@ -1294,12 +1295,19 @@ void LimboAIEditor::_notification(int p_what) { cf.instantiate(); String conf_path = PROJECT_CONFIG_FILE(); cf->load(conf_path); - cf->set_value("bt_editor", "bteditor_hsplit", hsc->get_split_offset()); + int split_offset = hsc->get_split_offset(); + if (editor_layout != (int)EDITOR_GET("limbo_ai/editor/layout")) { + // Editor layout settings changed - flip split offset. + split_offset *= -1; + } + cf->set_value("bt_editor", "bteditor_hsplit", split_offset); cf->save(conf_path); - if (task_tree->get_bt().is_valid() && - task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true))) { - task_tree->get_bt()->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); + task_tree->unload(); + for (int i = 0; i < history.size(); i++) { + if (history[i]->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { + history[i]->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty)); + } } } break; case NOTIFICATION_READY: { @@ -1377,6 +1385,20 @@ LimboAIEditor::LimboAIEditor() { plugin = nullptr; idx_history = 0; +#ifdef LIMBOAI_MODULE + EDITOR_DEF("limbo_ai/editor/layout", 0); + EDITOR_SETTINGS()->add_property_hint(PropertyInfo(Variant::INT, "limbo_ai/editor/layout", PROPERTY_HINT_ENUM, "Classic:0,Widescreen Optimized:1")); + EDITOR_SETTINGS()->set_restart_if_changed("limbo_ai/editor/layout", true); +#elif LIMBOAI_GDEXTENSION + EDITOR_SETTINGS()->set_initial_value("limbo_ai/editor/layout", 0, false); + Dictionary pinfo; + pinfo["name"] = "limbo_ai/editor/layout"; + pinfo["type"] = Variant::INT; + pinfo["hint"] = PROPERTY_HINT_ENUM; + pinfo["hint_string"] = "Classic:0,Widescreen Optimized:1"; + EDITOR_SETTINGS()->add_property_info(pinfo); +#endif + LW_SHORTCUT("limbo_ai/rename_task", TTR("Rename"), LW_KEY(F2)); // Todo: Add override support for shortcuts. // LW_SHORTCUT_OVERRIDE("limbo_ai/rename_task", "macos", Key::ENTER); @@ -1476,14 +1498,14 @@ LimboAIEditor::LimboAIEditor() { misc_btn->set_flat(true); toolbar->add_child(misc_btn); - HBoxContainer *toolbar_end_hbox = memnew(HBoxContainer); - toolbar_end_hbox->set_h_size_flags(SIZE_EXPAND | SIZE_SHRINK_END); - toolbar->add_child(toolbar_end_hbox); + HBoxContainer *version_hbox = memnew(HBoxContainer); + version_hbox->set_h_size_flags(SIZE_EXPAND | SIZE_SHRINK_END); + toolbar->add_child(version_hbox); TextureRect *logo = memnew(TextureRect); logo->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); logo->set_texture(LimboUtility::get_singleton()->get_task_icon("LimboAI")); - toolbar_end_hbox->add_child(logo); + version_hbox->add_child(logo); version_btn = memnew(LinkButton); version_btn->set_text(TTR("v") + String(GET_LIMBOAI_FULL_VERSION())); @@ -1491,11 +1513,11 @@ LimboAIEditor::LimboAIEditor() { version_btn->set_self_modulate(Color(1, 1, 1, 0.65)); version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); version_btn->set_v_size_flags(SIZE_SHRINK_CENTER); - toolbar_end_hbox->add_child(version_btn); + version_hbox->add_child(version_btn); Control *version_spacer = memnew(Control); version_spacer->set_custom_minimum_size(Size2(2, 0) * EDSCALE); - toolbar_end_hbox->add_child(version_spacer); + version_hbox->add_child(version_spacer); tab_bar_panel = memnew(PanelContainer); vbox->add_child(tab_bar_panel); @@ -1544,10 +1566,42 @@ LimboAIEditor::LimboAIEditor() { usage_hint->add_child(usage_label); task_palette = memnew(TaskPalette()); - hsc->set_split_offset(-300); task_palette->hide(); hsc->add_child(task_palette); + editor_layout = (EditorLayout)(int)EDITOR_GET("limbo_ai/editor/layout"); + if (editor_layout == EditorLayout::WIDESCREEN_OPTIMIZED) { + // * Alternative layout optimized for wide screen. + VBoxContainer *sidebar_vbox = memnew(VBoxContainer); + hsc->add_child(sidebar_vbox); + sidebar_vbox->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *header_bar = memnew(HBoxContainer); + sidebar_vbox->add_child(header_bar); + Control *header_spacer = memnew(Control); + header_bar->add_child(header_spacer); + header_spacer->set_custom_minimum_size(Size2(6, 0) * EDSCALE); + TextureRect *header_logo = Object::cast_to(logo->duplicate()); + header_bar->add_child(header_logo); + Label *header_title = memnew(Label); + header_bar->add_child(header_title); + header_title->set_text(TTR("Behavior Tree Editor")); + header_title->set_v_size_flags(SIZE_SHRINK_CENTER); + header_title->set_theme_type_variation("HeaderMedium"); + + task_palette->reparent(sidebar_vbox); + task_palette->set_v_size_flags(SIZE_EXPAND_FILL); + + VBoxContainer *editor_vbox = memnew(VBoxContainer); + hsc->add_child(editor_vbox); + toolbar->reparent(editor_vbox); + tab_bar_panel->reparent(editor_vbox); + task_tree->reparent(editor_vbox); + usage_hint->reparent(editor_vbox); + } + + hsc->set_split_offset((editor_layout == EditorLayout::CLASSIC ? -320 : 320) * EDSCALE); + change_type_popup = memnew(PopupPanel); add_child(change_type_popup); { diff --git a/editor/limbo_ai_editor_plugin.h b/editor/limbo_ai_editor_plugin.h index 8d5f8f1..bda549c 100644 --- a/editor/limbo_ai_editor_plugin.h +++ b/editor/limbo_ai_editor_plugin.h @@ -26,8 +26,8 @@ #include "core/object/object.h" #include "core/templates/hash_set.h" #include "editor/editor_node.h" -#include "editor/plugins/editor_plugin.h" #include "editor/gui/editor_spin_slider.h" +#include "editor/plugins/editor_plugin.h" #include "scene/gui/box_container.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" @@ -111,6 +111,11 @@ private: TAB_CLOSE_ALL, }; + enum EditorLayout { + CLASSIC, + WIDESCREEN_OPTIMIZED, + }; + struct ThemeCache { Ref duplicate_task_icon; Ref edit_script_icon; @@ -132,6 +137,7 @@ private: } theme_cache; EditorPlugin *plugin; + EditorLayout editor_layout; Vector> history; int idx_history; bool updating_tabs = false;