Implement ActionBanner class for actionable editor alerts

This commit is contained in:
Serhii Snitsaruk 2023-08-22 12:20:09 +02:00
parent 4942173e5b
commit 24678e4332
4 changed files with 158 additions and 60 deletions

79
editor/action_banner.cpp Normal file
View File

@ -0,0 +1,79 @@
/**
* action_banner.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 "action_banner.h"
#include "scene/gui/button.h"
void ActionBanner::set_text(const String &p_text) {
message->set_text(p_text);
}
String ActionBanner::get_text() const {
return message->get_text();
}
void ActionBanner::close() {
queue_free();
}
void ActionBanner::add_action(const String &p_name, const Callable &p_action, bool p_auto_close) {
Button *action_btn = memnew(Button);
action_btn->set_text(p_name);
action_btn->connect(SNAME("pressed"), callable_mp(this, &ActionBanner::_execute_action).bind(p_action, p_auto_close));
hbox->add_child(action_btn);
}
void ActionBanner::_execute_action(const Callable &p_action, bool p_auto_close) {
Callable::CallError ce;
Variant ret;
p_action.callp(nullptr, 0, ret, ce);
if (p_auto_close) {
queue_free();
}
}
void ActionBanner::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
} break;
}
}
void ActionBanner::_bind_methods() {
}
ActionBanner::ActionBanner() {
add_theme_constant_override("margin_bottom", 4);
add_theme_constant_override("margin_top", 4);
add_theme_constant_override("margin_left", 10);
add_theme_constant_override("margin_right", 10);
hbox = memnew(HBoxContainer);
hbox->add_theme_constant_override("hseparation", 8);
add_child(hbox);
icon = memnew(TextureRect);
icon->set_expand_mode(TextureRect::ExpandMode::EXPAND_KEEP_SIZE);
icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
hbox->add_child(icon);
message = memnew(Label);
message->set_text(vformat(TTR("User task folder doesn't exist")));
message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
hbox->add_child(message);
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Size2(0, 16));
hbox->add_child(spacer);
}

48
editor/action_banner.h Normal file
View File

@ -0,0 +1,48 @@
/**
* action_banner.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.
* =============================================================================
*/
/* action_banner.h */
#ifndef ACTION_BANNER_H
#define ACTION_BANNER_H
#include "scene/gui/margin_container.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
class ActionBanner : public MarginContainer {
GDCLASS(ActionBanner, MarginContainer);
private:
TextureRect *icon;
Label *message;
HBoxContainer *hbox;
void _execute_action(const Callable &p_action, bool p_auto_close);
protected:
static void _bind_methods();
void _notification(int p_what);
public:
void set_text(const String &p_text);
String get_text() const;
void add_action(const String &p_name, const Callable &p_action, bool p_auto_close = false);
void close();
ActionBanner();
};
#endif // ACTION_BANNER

View File

@ -13,6 +13,7 @@
#include "limbo_ai_editor_plugin.h"
#include "action_banner.h"
#include "modules/limboai/bt/behavior_tree.h"
#include "modules/limboai/bt/tasks/bt_action.h"
#include "modules/limboai/bt/tasks/bt_comment.h"
@ -911,13 +912,14 @@ void LimboAIEditor::_create_user_task_dir() {
err = dir->make_dir_recursive(task_dir);
ERR_FAIL_COND_MSG(err != OK, "LimboAIEditor: Failed to create directory: " + task_dir);
_update_warnings();
EditorFileSystem::get_singleton()->scan_changes();
_update_banners();
}
void LimboAIEditor::_edit_project_settings() {
ProjectSettingsEditor::get_singleton()->set_general_page("limbo_ai/behavior_tree");
ProjectSettingsEditor::get_singleton()->popup_project_settings();
ProjectSettingsEditor::get_singleton()->connect(SNAME("visibility_changed"), callable_mp(this, &LimboAIEditor::_update_banners), CONNECT_ONE_SHOT);
}
void LimboAIEditor::shortcut_input(const Ref<InputEvent> &p_event) {
@ -1393,8 +1395,21 @@ void LimboAIEditor::_update_misc_menu() {
MISC_CREATE_SCRIPT_TEMPLATE);
}
void LimboAIEditor::_update_warnings() {
task_dir_warning->set_visible(!DirAccess::exists(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")));
void LimboAIEditor::_update_banners() {
for (int i = 0; i < banners->get_child_count(); i++) {
banners->get_child(i)->queue_free();
}
for (String dir_setting : { "limbo_ai/behavior_tree/user_task_dir_1", "limbo_ai/behavior_tree/user_task_dir_2", "limbo_ai/behavior_tree/user_task_dir_3" }) {
String task_dir = GLOBAL_GET(dir_setting);
if (!task_dir.is_empty() && !DirAccess::exists(task_dir)) {
ActionBanner *banner = memnew(ActionBanner);
banner->set_text(vformat(TTR("Task folder not found: %s"), task_dir));
banner->add_action(TTR("Create"), callable_mp(this, &LimboAIEditor::_create_user_task_dir), true);
banner->add_action(TTR("Edit Path..."), callable_mp(this, &LimboAIEditor::_edit_project_settings));
banners->call_deferred(SNAME("add_child"), banner);
}
}
}
void LimboAIEditor::_notification(int p_what) {
@ -1423,8 +1438,6 @@ void LimboAIEditor::_notification(int p_what) {
misc_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
warning_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
_update_favorite_tasks();
_update_header();
}
@ -1476,13 +1489,13 @@ LimboAIEditor::LimboAIEditor() {
load_dialog->hide();
add_child(load_dialog);
VBoxContainer *vb = memnew(VBoxContainer);
vb->set_anchor(SIDE_RIGHT, ANCHOR_END);
vb->set_anchor(SIDE_BOTTOM, ANCHOR_END);
add_child(vb);
vbox = memnew(VBoxContainer);
vbox->set_anchor(SIDE_RIGHT, ANCHOR_END);
vbox->set_anchor(SIDE_BOTTOM, ANCHOR_END);
add_child(vbox);
HBoxContainer *toolbar = memnew(HBoxContainer);
vb->add_child(toolbar);
vbox->add_child(toolbar);
Array favorite_tasks_default;
favorite_tasks_default.append("BTSelector");
@ -1567,13 +1580,13 @@ LimboAIEditor::LimboAIEditor() {
header->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
header->add_theme_constant_override("hseparation", 8);
header->connect("pressed", callable_mp(this, &LimboAIEditor::_on_header_pressed));
vb->add_child(header);
vbox->add_child(header);
hsc = memnew(HSplitContainer);
hsc->set_h_size_flags(SIZE_EXPAND_FILL);
hsc->set_v_size_flags(SIZE_EXPAND_FILL);
hsc->set_focus_mode(FOCUS_NONE);
vb->add_child(hsc);
vbox->add_child(hsc);
task_tree = memnew(TaskTree);
task_tree->set_v_size_flags(SIZE_EXPAND_FILL);
@ -1584,7 +1597,7 @@ LimboAIEditor::LimboAIEditor() {
task_tree->connect("task_dragged", callable_mp(this, &LimboAIEditor::_on_task_dragged));
task_tree->connect("task_double_clicked", callable_mp(this, &LimboAIEditor::_on_tree_task_double_clicked));
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_on_visibility_changed));
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_update_warnings));
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_update_banners));
hsc->add_child(task_tree);
usage_hint = memnew(Panel);
@ -1607,50 +1620,8 @@ LimboAIEditor::LimboAIEditor() {
task_panel->hide();
hsc->add_child(task_panel);
task_dir_warning = memnew(MarginContainer);
{
task_dir_warning->add_theme_constant_override("margin_bottom", 4);
task_dir_warning->add_theme_constant_override("margin_top", 4);
task_dir_warning->add_theme_constant_override("margin_left", 4);
task_dir_warning->add_theme_constant_override("margin_right", 4);
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override("hseparation", 8);
task_dir_warning->add_child(hb);
warning_icon = memnew(TextureRect);
warning_icon->set_expand_mode(TextureRect::ExpandMode::EXPAND_KEEP_SIZE);
warning_icon->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED);
hb->add_child(warning_icon);
Label *warning_label = memnew(Label);
warning_label->set_text(vformat(TTR("User task folder doesn't exist")));
warning_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
hb->add_child(warning_label);
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Size2(0, 16));
hb->add_child(spacer);
Button *create_action = memnew(Button);
create_action->set_text(TTR("Create"));
create_action->connect("pressed", callable_mp(this, &LimboAIEditor::_create_user_task_dir));
hb->add_child(create_action);
Button *edit_action = memnew(Button);
edit_action->set_text(TTR("Edit Path..."));
edit_action->connect("pressed", callable_mp(this, &LimboAIEditor::_edit_project_settings));
hb->add_child(edit_action);
Button *refresh_action = memnew(Button);
refresh_action->set_text(TTR("Refresh"));
refresh_action->connect("pressed", callable_mp(task_panel, &TaskPanel::refresh));
refresh_action->connect("pressed", callable_mp(this, &LimboAIEditor::_update_warnings));
hb->add_child(refresh_action);
task_dir_warning->hide();
}
vb->add_child(task_dir_warning);
banners = memnew(VBoxContainer);
vbox->add_child(banners);
menu = memnew(PopupMenu);
add_child(menu);

View File

@ -167,9 +167,11 @@ private:
int idx_history;
HashSet<Ref<BehaviorTree>> dirty;
VBoxContainer *vbox;
Button *header;
HSplitContainer *hsc;
TaskTree *task_tree;
VBoxContainer *banners;
Panel *usage_hint;
PopupMenu *menu;
FileDialog *save_dialog;
@ -178,8 +180,6 @@ private:
Button *history_forward;
TaskPanel *task_panel;
HBoxContainer *fav_tasks_hbox;
MarginContainer *task_dir_warning;
TextureRect *warning_icon;
Button *comment_btn;
Button *new_btn;
@ -203,7 +203,7 @@ private:
void _update_history_buttons();
void _update_favorite_tasks();
void _update_misc_menu();
void _update_warnings();
void _update_banners();
void _new_bt();
void _save_bt(String p_path);
void _load_bt(String p_path);