Implement BlackboardPlan editor
This commit is contained in:
parent
20a995d1d2
commit
15e0323919
|
@ -108,11 +108,13 @@ void BlackboardPlan::set_base_plan(const Ref<BlackboardPlan> &p_base) {
|
|||
base = p_base;
|
||||
sync_with_base_plan();
|
||||
emit_changed();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlan::set_value(const String &p_name, const Variant &p_value) {
|
||||
ERR_FAIL_COND(!data.has(p_name));
|
||||
data.get(p_name).set_value(p_value);
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
Variant BlackboardPlan::get_value(const String &p_name) const {
|
||||
|
@ -124,12 +126,16 @@ void BlackboardPlan::add_var(const String &p_name, const BBVariable &p_var) {
|
|||
ERR_FAIL_COND(data.has(p_name));
|
||||
ERR_FAIL_COND(base.is_valid());
|
||||
data.insert(p_name, p_var);
|
||||
notify_property_list_changed();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlan::remove_var(const String &p_name) {
|
||||
ERR_FAIL_COND(!data.has(p_name));
|
||||
ERR_FAIL_COND(base.is_valid());
|
||||
data.erase(p_name);
|
||||
notify_property_list_changed();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
BBVariable BlackboardPlan::get_var(const String &p_name) {
|
||||
|
@ -137,6 +143,22 @@ BBVariable BlackboardPlan::get_var(const String &p_name) {
|
|||
return data.get(p_name);
|
||||
}
|
||||
|
||||
Pair<String, BBVariable> BlackboardPlan::get_var_by_index(int p_index) {
|
||||
Pair<String, BBVariable> ret;
|
||||
ERR_FAIL_INDEX_V(p_index, (int)data.size(), ret);
|
||||
|
||||
int i = 0;
|
||||
for (const KeyValue<String, BBVariable> &kv : data) {
|
||||
if (i == p_index) {
|
||||
ret.first = kv.key;
|
||||
ret.second = kv.value;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedStringArray BlackboardPlan::list_vars() const {
|
||||
PackedStringArray ret;
|
||||
for (const KeyValue<String, BBVariable> &kv : data) {
|
||||
|
@ -145,24 +167,66 @@ PackedStringArray BlackboardPlan::list_vars() const {
|
|||
return ret;
|
||||
}
|
||||
|
||||
String BlackboardPlan::get_var_name(const BBVariable &p_var) const {
|
||||
for (const KeyValue<String, BBVariable> &kv : data) {
|
||||
if (kv.value == p_var) {
|
||||
return kv.key;
|
||||
}
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void BlackboardPlan::rename_var(const String &p_name, const String &p_new_name) {
|
||||
ERR_FAIL_COND(p_new_name.is_empty());
|
||||
ERR_FAIL_COND(data.has(p_new_name));
|
||||
ERR_FAIL_COND(!data.has(p_name));
|
||||
|
||||
data.replace_key(p_name, p_new_name);
|
||||
notify_property_list_changed();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlan::swap_vars(int p_idx_a, int p_idx_b) {
|
||||
ERR_FAIL_INDEX(p_idx_a, (int)data.size());
|
||||
ERR_FAIL_INDEX(p_idx_b, (int)data.size());
|
||||
|
||||
Pair<String, BBVariable> a = get_var_by_index(p_idx_a);
|
||||
Pair<String, BBVariable> b = get_var_by_index(p_idx_b);
|
||||
|
||||
data.replace_key(a.first, "__tmp__");
|
||||
data.replace_key(b.first, a.first);
|
||||
data.replace_key("__tmp__", b.first);
|
||||
|
||||
data[b.first] = b.second;
|
||||
data[a.first] = a.second;
|
||||
|
||||
notify_property_list_changed();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlan::sync_with_base_plan() {
|
||||
if (base.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
// Sync variables with the base plan.
|
||||
for (const KeyValue<String, BBVariable> &kv : base->data) {
|
||||
if (!data.has(kv.key)) {
|
||||
data.insert(kv.key, kv.value.duplicate());
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
BBVariable var = data.get(kv.key);
|
||||
if (!var.is_same_prop_info(kv.value)) {
|
||||
var.copy_prop_info(kv.value);
|
||||
changed = true;
|
||||
}
|
||||
if (var.get_value().get_type() != kv.value.get_type()) {
|
||||
var.set_value(kv.value.get_value());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,8 +234,14 @@ void BlackboardPlan::sync_with_base_plan() {
|
|||
for (const KeyValue<String, BBVariable> &kv : data) {
|
||||
if (!base->data.has(kv.key)) {
|
||||
data.erase(kv.key);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
notify_property_list_changed();
|
||||
emit_changed();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Blackboard> BlackboardPlan::create_blackboard() {
|
||||
|
@ -196,11 +266,4 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
|
|||
}
|
||||
|
||||
BlackboardPlan::BlackboardPlan() {
|
||||
// TODO: REMOVE THE TEST DATA BELOW.
|
||||
data.insert("speed", BBVariable(Variant::Type::FLOAT));
|
||||
data["speed"].set_value(200.0);
|
||||
data.insert("limit_speed", BBVariable(Variant::Type::BOOL));
|
||||
data["limit_speed"].set_value(500.0);
|
||||
data.insert("about", BBVariable(Variant::Type::STRING, PropertyHint::PROPERTY_HINT_MULTILINE_TEXT, ""));
|
||||
data["about"].set_value("Hello, World!");
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ private:
|
|||
HashMap<String, BBVariable> data;
|
||||
|
||||
// When base is not null, the plan is considered to be derived from the base plan.
|
||||
// A derived plan can only have variables that exist in the base plan.
|
||||
// A derived plan can only have variables that exist in the base plan,
|
||||
// and only the values can be different in those variables.
|
||||
Ref<BlackboardPlan> base;
|
||||
|
||||
protected:
|
||||
|
@ -40,11 +41,19 @@ public:
|
|||
|
||||
void set_value(const String &p_name, const Variant &p_value);
|
||||
Variant get_value(const String &p_name) const;
|
||||
|
||||
void add_var(const String &p_name, const BBVariable &p_var);
|
||||
void remove_var(const String &p_name);
|
||||
BBVariable get_var(const String &p_name);
|
||||
PackedStringArray list_vars() const;
|
||||
Pair<String, BBVariable> get_var_by_index(int p_index);
|
||||
bool has_var(const String &p_name) { return data.has(p_name); }
|
||||
bool is_empty() const { return data.is_empty(); }
|
||||
int get_var_count() const { return data.size(); }
|
||||
|
||||
PackedStringArray list_vars() const;
|
||||
String get_var_name(const BBVariable &p_var) const;
|
||||
void rename_var(const String &p_name, const String &p_new_name);
|
||||
void swap_vars(int idx_a, int idx_b);
|
||||
|
||||
void sync_with_base_plan();
|
||||
bool is_derived() { return base.is_valid(); }
|
||||
|
|
|
@ -67,20 +67,18 @@ void BTPlayer::_update_blackboard_plan() {
|
|||
if (blackboard_plan.is_null()) {
|
||||
blackboard_plan = Ref<BlackboardPlan>(memnew(BlackboardPlan));
|
||||
}
|
||||
|
||||
if (behavior_tree.is_valid()) {
|
||||
if (blackboard_plan == behavior_tree->get_blackboard_plan()) {
|
||||
blackboard_plan->sync_with_base_plan();
|
||||
} else {
|
||||
blackboard_plan->set_base_plan(behavior_tree->get_blackboard_plan());
|
||||
}
|
||||
}
|
||||
blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr);
|
||||
}
|
||||
|
||||
void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
|
||||
if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) {
|
||||
behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
|
||||
}
|
||||
behavior_tree = p_tree;
|
||||
if (Engine::get_singleton()->is_editor_hint() == false && get_owner()) {
|
||||
if (!Engine::get_singleton()->is_editor_hint() && get_owner()) {
|
||||
_load_tree();
|
||||
} else if (behavior_tree.is_valid()) {
|
||||
behavior_tree->connect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
|
||||
}
|
||||
_update_blackboard_plan();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
/**
|
||||
* blackboard_plan_editor.cpp
|
||||
* =============================================================================
|
||||
* Copyright 2021-2024 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 "blackboard_plan_editor.h"
|
||||
|
||||
#include "../util/limbo_string_names.h"
|
||||
#include "../util/limbo_utility.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
#endif // LIMBOAI_MODULE
|
||||
|
||||
void BlackboardPlanEditor::_add_var() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
|
||||
int suffix = 1;
|
||||
String name = "var" + itos(suffix);
|
||||
while (plan->has_var(name)) {
|
||||
suffix += 1;
|
||||
name = "var" + itos(suffix);
|
||||
}
|
||||
|
||||
BBVariable var(Variant::Type::FLOAT);
|
||||
plan->add_var(name, var);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_trash_var(int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
String var_name = plan->get_var_by_index(p_index).first;
|
||||
plan->remove_var(var_name);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_rename_var(const String &p_new_name, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
plan->rename_var(plan->get_var_by_index(p_index).first, p_new_name);
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
plan->get_var_by_index(p_index).second.set_type(p_new_type);
|
||||
plan->notify_property_list_changed();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_hint(PropertyHint p_new_hint, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
plan->get_var_by_index(p_index).second.set_hint(p_new_hint);
|
||||
plan->notify_property_list_changed();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_hint_string(const String &p_new_hint_string, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
plan->get_var_by_index(p_index).second.set_hint_string(p_new_hint_string);
|
||||
plan->notify_property_list_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::edit_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||
plan = p_plan;
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) {
|
||||
ERR_FAIL_NULL(p_button);
|
||||
ERR_FAIL_NULL(p_popup);
|
||||
|
||||
Rect2 rect = p_button->get_screen_rect();
|
||||
rect.position.y += rect.size.height;
|
||||
rect.size.height = 0;
|
||||
p_popup->set_size(rect.size);
|
||||
p_popup->set_position(rect.position);
|
||||
|
||||
last_index = p_index;
|
||||
p_popup->popup();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_type_chosen(int id) {
|
||||
_change_var_type(Variant::Type(id), last_index);
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_hint_chosen(int id) {
|
||||
_change_var_hint(PropertyHint(id), last_index);
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_drag_button_down(Control *p_row) {
|
||||
drag_index = p_row->get_index();
|
||||
drag_mouse_y_delta = 0.0;
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_drag_button_up() {
|
||||
drag_index = -1;
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_drag_button_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (drag_index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
drag_mouse_y_delta += mm->get_relative().y;
|
||||
|
||||
if ((drag_index == 0 && drag_mouse_y_delta < 0.0) || (drag_index == (plan->get_var_count() - 1) && drag_mouse_y_delta > 0.0)) {
|
||||
drag_mouse_y_delta = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
float required_distance = 20.0f * EDSCALE;
|
||||
if (ABS(drag_mouse_y_delta) > required_distance) {
|
||||
int drag_dir = drag_mouse_y_delta > 0.0f ? 1 : -1;
|
||||
drag_mouse_y_delta -= required_distance * drag_dir;
|
||||
|
||||
plan->swap_vars(drag_index, drag_index + drag_dir);
|
||||
|
||||
Control *row = Object::cast_to<Control>(rows_vbox->get_child(drag_index));
|
||||
Control *other_row = Object::cast_to<Control>(rows_vbox->get_child(drag_index + drag_dir));
|
||||
ERR_FAIL_NULL(row);
|
||||
ERR_FAIL_NULL(other_row);
|
||||
rows_vbox->move_child(row, drag_index + drag_dir);
|
||||
row->add_theme_style_override(LW_NAME(panel), row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style);
|
||||
other_row->add_theme_style_override(LW_NAME(panel), other_row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style);
|
||||
|
||||
drag_index += drag_dir;
|
||||
}
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_visibility_changed() {
|
||||
if (!is_visible() && plan.is_valid()) {
|
||||
plan->notify_property_list_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_refresh() {
|
||||
for (int i = 0; i < rows_vbox->get_child_count(); i++) {
|
||||
Control *child = Object::cast_to<Control>(rows_vbox->get_child(i));
|
||||
ERR_FAIL_NULL(child);
|
||||
child->hide();
|
||||
child->queue_free();
|
||||
}
|
||||
|
||||
// TODO: Name validation
|
||||
|
||||
PackedStringArray names = plan->list_vars();
|
||||
int idx = 0;
|
||||
for (const String &var_name : names) {
|
||||
BBVariable var = plan->get_var(var_name);
|
||||
|
||||
PanelContainer *row_panel = memnew(PanelContainer);
|
||||
rows_vbox->add_child(row_panel);
|
||||
row_panel->add_theme_style_override(LW_NAME(panel), idx % 2 ? theme_cache.odd_style : theme_cache.even_style);
|
||||
row_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
HBoxContainer *props_hbox = memnew(HBoxContainer);
|
||||
row_panel->add_child(props_hbox);
|
||||
props_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
Button *drag_button = memnew(Button);
|
||||
props_hbox->add_child(drag_button);
|
||||
drag_button->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE);
|
||||
drag_button->set_icon(theme_cache.grab_icon);
|
||||
drag_button->connect(LW_NAME(gui_input), callable_mp(this, &BlackboardPlanEditor::_drag_button_gui_input));
|
||||
drag_button->connect(LW_NAME(button_down), callable_mp(this, &BlackboardPlanEditor::_drag_button_down).bind(row_panel));
|
||||
drag_button->connect(LW_NAME(button_up), callable_mp(this, &BlackboardPlanEditor::_drag_button_up));
|
||||
|
||||
LineEdit *name_edit = memnew(LineEdit);
|
||||
props_hbox->add_child(name_edit);
|
||||
name_edit->set_text(var_name);
|
||||
name_edit->set_placeholder(TTR("Variable name"));
|
||||
name_edit->set_flat(true);
|
||||
name_edit->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE);
|
||||
name_edit->connect(LW_NAME(text_changed), callable_mp(this, &BlackboardPlanEditor::_rename_var).bind(idx));
|
||||
name_edit->connect(LW_NAME(text_submitted), callable_mp(this, &BlackboardPlanEditor::_refresh).unbind(1));
|
||||
|
||||
Button *type_choice = memnew(Button);
|
||||
props_hbox->add_child(type_choice);
|
||||
type_choice->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE);
|
||||
type_choice->set_text(Variant::get_type_name(var.get_type()));
|
||||
type_choice->set_tooltip_text(Variant::get_type_name(var.get_type()));
|
||||
type_choice->set_icon(get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons)));
|
||||
type_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
type_choice->set_flat(true);
|
||||
type_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
type_choice->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_show_button_popup).bind(type_choice, type_menu, idx));
|
||||
|
||||
Button *hint_choice = memnew(Button);
|
||||
props_hbox->add_child(hint_choice);
|
||||
hint_choice->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
|
||||
hint_choice->set_text(LimboUtility::get_singleton()->get_property_hint_text(var.get_hint()));
|
||||
hint_choice->set_tooltip_text(LimboUtility::get_singleton()->get_property_hint_text(var.get_hint()));
|
||||
hint_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
hint_choice->set_flat(true);
|
||||
hint_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
hint_choice->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_show_button_popup).bind(hint_choice, hint_menu, idx));
|
||||
|
||||
LineEdit *hint_string_edit = memnew(LineEdit);
|
||||
props_hbox->add_child(hint_string_edit);
|
||||
hint_string_edit->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE);
|
||||
hint_string_edit->set_text(var.get_hint_string());
|
||||
hint_string_edit->set_placeholder(TTR("Hint string"));
|
||||
hint_string_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
hint_string_edit->set_flat(true);
|
||||
hint_string_edit->connect(LW_NAME(text_changed), callable_mp(this, &BlackboardPlanEditor::_change_var_hint_string).bind(idx));
|
||||
hint_string_edit->connect(LW_NAME(text_submitted), callable_mp(this, &BlackboardPlanEditor::_refresh).unbind(1));
|
||||
|
||||
Button *trash_button = memnew(Button);
|
||||
props_hbox->add_child(trash_button);
|
||||
trash_button->set_custom_minimum_size(Size2(24.0, 0.0) * EDSCALE);
|
||||
trash_button->set_icon(theme_cache.trash_icon);
|
||||
trash_button->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_trash_var).bind(idx));
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
theme_cache.trash_icon = get_theme_icon(LW_NAME(Remove), LW_NAME(EditorIcons));
|
||||
theme_cache.grab_icon = get_theme_icon(LW_NAME(TripleBar), LW_NAME(EditorIcons));
|
||||
|
||||
add_var_tool->set_icon(get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons)));
|
||||
|
||||
type_menu->clear();
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) {
|
||||
continue;
|
||||
}
|
||||
String type = Variant::get_type_name(Variant::Type(i));
|
||||
type_menu->add_icon_item(get_theme_icon(type, LW_NAME(EditorIcons)), type, i);
|
||||
}
|
||||
|
||||
scroll_container->add_theme_style_override(LW_NAME(panel), get_theme_stylebox(LW_NAME(panel), LW_NAME(Tree)));
|
||||
|
||||
Color bg_color = get_theme_color(LW_NAME(dark_color_2), LW_NAME(Editor));
|
||||
theme_cache.odd_style->set_bg_color(bg_color.darkened(-0.05));
|
||||
theme_cache.even_style->set_bg_color(bg_color.darkened(0.05));
|
||||
theme_cache.header_style->set_bg_color(bg_color.darkened(-0.2));
|
||||
|
||||
header_row->add_theme_style_override(LW_NAME(panel), theme_cache.header_style);
|
||||
} break;
|
||||
case NOTIFICATION_READY: {
|
||||
add_var_tool->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_add_var));
|
||||
connect(LW_NAME(visibility_changed), callable_mp(this, &BlackboardPlanEditor::_visibility_changed));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
BlackboardPlanEditor::BlackboardPlanEditor() {
|
||||
set_title(TTR("Edit Blackboard Plan"));
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
vbox->add_theme_constant_override(LW_NAME(separation), 8 * EDSCALE);
|
||||
add_child(vbox);
|
||||
|
||||
HBoxContainer *toolbar = memnew(HBoxContainer);
|
||||
vbox->add_child(toolbar);
|
||||
|
||||
add_var_tool = memnew(Button);
|
||||
toolbar->add_child(add_var_tool);
|
||||
add_var_tool->set_focus_mode(Control::FOCUS_NONE);
|
||||
add_var_tool->set_text(TTR("Add variable"));
|
||||
|
||||
{
|
||||
// * Header
|
||||
header_row = memnew(PanelContainer);
|
||||
vbox->add_child(header_row);
|
||||
header_row->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
HBoxContainer *labels_hbox = memnew(HBoxContainer);
|
||||
header_row->add_child(labels_hbox);
|
||||
labels_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
Control *offset = memnew(Control);
|
||||
labels_hbox->add_child(offset);
|
||||
offset->set_custom_minimum_size(Size2(2.0, 0.0) * EDSCALE);
|
||||
|
||||
Label *drag_header = memnew(Label);
|
||||
labels_hbox->add_child(drag_header);
|
||||
drag_header->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE);
|
||||
|
||||
Label *name_header = memnew(Label);
|
||||
labels_hbox->add_child(name_header);
|
||||
name_header->set_text(TTR("Name"));
|
||||
name_header->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE);
|
||||
name_header->set_theme_type_variation(LW_NAME(HeaderSmall));
|
||||
|
||||
Label *type_header = memnew(Label);
|
||||
labels_hbox->add_child(type_header);
|
||||
type_header->set_text(TTR("Type"));
|
||||
type_header->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE);
|
||||
type_header->set_theme_type_variation(LW_NAME(HeaderSmall));
|
||||
|
||||
Label *hint_header = memnew(Label);
|
||||
labels_hbox->add_child(hint_header);
|
||||
hint_header->set_text(TTR("Hint"));
|
||||
hint_header->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
|
||||
hint_header->set_theme_type_variation(LW_NAME(HeaderSmall));
|
||||
|
||||
Label *hint_string_header = memnew(Label);
|
||||
labels_hbox->add_child(hint_string_header);
|
||||
hint_string_header->set_text(TTR("Hint string"));
|
||||
hint_string_header->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE);
|
||||
hint_string_header->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
hint_string_header->set_theme_type_variation(LW_NAME(HeaderSmall));
|
||||
}
|
||||
|
||||
scroll_container = memnew(ScrollContainer);
|
||||
vbox->add_child(scroll_container);
|
||||
scroll_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
scroll_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
scroll_container->set_custom_minimum_size(Size2(0.0, 600.0) * EDSCALE);
|
||||
|
||||
rows_vbox = memnew(VBoxContainer);
|
||||
scroll_container->add_child(rows_vbox);
|
||||
rows_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
rows_vbox->add_theme_constant_override(LW_NAME(separation), 0);
|
||||
|
||||
type_menu = memnew(PopupMenu);
|
||||
add_child(type_menu);
|
||||
type_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_type_chosen));
|
||||
|
||||
hint_menu = memnew(PopupMenu);
|
||||
add_child(hint_menu);
|
||||
hint_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_hint_chosen));
|
||||
for (int i = 0; i < PropertyHint::PROPERTY_HINT_MAX; i++) {
|
||||
hint_menu->add_item(LimboUtility::get_singleton()->get_property_hint_text(PropertyHint(i)), i);
|
||||
}
|
||||
|
||||
theme_cache.odd_style.instantiate();
|
||||
theme_cache.even_style.instantiate();
|
||||
theme_cache.header_style.instantiate();
|
||||
}
|
||||
|
||||
// *****
|
||||
|
||||
void EditorInspectorPluginBBPlan::_edit_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||
ERR_FAIL_NULL(p_plan);
|
||||
plan_editor->edit_plan(p_plan);
|
||||
plan_editor->popup_centered();
|
||||
}
|
||||
|
||||
void EditorInspectorPluginBBPlan::_open_base_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||
ERR_FAIL_NULL(p_plan);
|
||||
ERR_FAIL_NULL(p_plan->get_base_plan());
|
||||
EditorInterface::get_singleton()->call_deferred("edit_resource", p_plan->get_base_plan());
|
||||
}
|
||||
|
||||
bool EditorInspectorPluginBBPlan::can_handle(Object *p_object) {
|
||||
Ref<BlackboardPlan> plan = Object::cast_to<BlackboardPlan>(p_object);
|
||||
if (plan.is_valid()) {
|
||||
plan->sync_with_base_plan();
|
||||
}
|
||||
return plan.is_valid();
|
||||
}
|
||||
|
||||
void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) {
|
||||
Ref<BlackboardPlan> plan = Object::cast_to<BlackboardPlan>(p_object);
|
||||
ERR_FAIL_NULL(plan);
|
||||
|
||||
MarginContainer *margin_container = memnew(MarginContainer);
|
||||
margin_container->set_theme_type_variation("MarginContainer4px");
|
||||
|
||||
VBoxContainer *toolbar = memnew(VBoxContainer);
|
||||
margin_container->add_child(toolbar);
|
||||
toolbar->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
|
||||
if (plan->is_derived()) {
|
||||
Button *goto_btn = memnew(Button);
|
||||
toolbar->add_child(goto_btn);
|
||||
goto_btn->set_text(TTR("Open Base Plan"));
|
||||
goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
goto_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE);
|
||||
goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan));
|
||||
} else {
|
||||
Button *edit_btn = memnew(Button);
|
||||
toolbar->add_child(edit_btn);
|
||||
edit_btn->set_text(TTR("Edit..."));
|
||||
edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
edit_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE);
|
||||
edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan));
|
||||
}
|
||||
|
||||
add_custom_control(margin_container);
|
||||
}
|
||||
|
||||
EditorInspectorPluginBBPlan::EditorInspectorPluginBBPlan() {
|
||||
plan_editor = memnew(BlackboardPlanEditor);
|
||||
EditorInterface::get_singleton()->get_base_control()->add_child(plan_editor);
|
||||
plan_editor->hide();
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* blackboard_plan_editor.h
|
||||
* =============================================================================
|
||||
* Copyright 2021-2024 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 BLACKBOARD_PLAN_EDITOR_H
|
||||
#define BLACKBOARD_PLAN_EDITOR_H
|
||||
|
||||
#include "../blackboard/blackboard_plan.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#endif // LIMBOAI_MODULE
|
||||
|
||||
// *****
|
||||
|
||||
class BlackboardPlanEditor : public AcceptDialog {
|
||||
GDCLASS(BlackboardPlanEditor, AcceptDialog);
|
||||
|
||||
private:
|
||||
struct ThemeCache {
|
||||
Ref<Texture2D> trash_icon;
|
||||
Ref<Texture2D> grab_icon;
|
||||
Ref<StyleBoxFlat> odd_style;
|
||||
Ref<StyleBoxFlat> even_style;
|
||||
Ref<StyleBoxFlat> header_style;
|
||||
} theme_cache;
|
||||
|
||||
int last_index = 0;
|
||||
|
||||
int drag_mouse_y_delta = 0;
|
||||
int drag_index = 0;
|
||||
|
||||
Ref<BlackboardPlan> plan;
|
||||
|
||||
VBoxContainer *rows_vbox;
|
||||
Button *add_var_tool;
|
||||
PanelContainer *header_row;
|
||||
ScrollContainer *scroll_container;
|
||||
PopupMenu *type_menu;
|
||||
PopupMenu *hint_menu;
|
||||
|
||||
void _add_var();
|
||||
void _trash_var(int p_index);
|
||||
void _rename_var(const String &p_new_name, int p_index);
|
||||
void _change_var_type(Variant::Type p_new_type, int p_index);
|
||||
void _change_var_hint(PropertyHint p_new_hint, int p_index);
|
||||
void _change_var_hint_string(const String &p_new_hint_string, int p_index);
|
||||
|
||||
void _show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index);
|
||||
void _type_chosen(int id);
|
||||
void _hint_chosen(int id);
|
||||
|
||||
void _drag_button_down(Control *p_row);
|
||||
void _drag_button_up();
|
||||
void _drag_button_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _refresh();
|
||||
void _visibility_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void edit_plan(const Ref<BlackboardPlan> &p_plan);
|
||||
|
||||
BlackboardPlanEditor();
|
||||
};
|
||||
|
||||
// *****
|
||||
|
||||
class EditorInspectorPluginBBPlan : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginBBPlan, EditorInspectorPlugin);
|
||||
|
||||
private:
|
||||
BlackboardPlanEditor *plan_editor;
|
||||
|
||||
void _edit_plan(const Ref<BlackboardPlan> &p_plan);
|
||||
void _open_base_plan(const Ref<BlackboardPlan> &p_plan);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
|
||||
EditorInspectorPluginBBPlan();
|
||||
};
|
||||
|
||||
#endif // BLACKBOARD_PLAN_EDITOR_H
|
|
@ -18,11 +18,12 @@
|
|||
#include "../bt/tasks/composites/bt_probability_selector.h"
|
||||
#include "../bt/tasks/composites/bt_selector.h"
|
||||
#include "../bt/tasks/decorators/bt_subtree.h"
|
||||
#include "../editor/debugger/limbo_debugger_plugin.h"
|
||||
#include "../editor/editor_property_bb_param.h"
|
||||
#include "../util/limbo_compat.h"
|
||||
#include "../util/limbo_utility.h"
|
||||
#include "action_banner.h"
|
||||
#include "blackboard_plan_editor.h"
|
||||
#include "debugger/limbo_debugger_plugin.h"
|
||||
#include "editor_property_bb_param.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "core/config/project_settings.h"
|
||||
|
@ -1447,6 +1448,7 @@ LimboAIEditorPlugin::LimboAIEditorPlugin() {
|
|||
limbo_ai_editor->hide();
|
||||
limbo_ai_editor->set_plugin(this);
|
||||
|
||||
add_inspector_plugin(memnew(EditorInspectorPluginBBPlan));
|
||||
#ifdef LIMBOAI_MODULE
|
||||
// ! Only used in the module version.
|
||||
add_inspector_plugin(memnew(EditorInspectorPluginBBParam));
|
||||
|
|
|
@ -42,6 +42,7 @@ LimboStringNames::LimboStringNames() {
|
|||
_update_banners = SN("_update_banners");
|
||||
_weight_ = SN("_weight_");
|
||||
accent_color = SN("accent_color");
|
||||
Add = SN("Add");
|
||||
add_child = SN("add_child");
|
||||
add_child_at_index = SN("add_child_at_index");
|
||||
AnimationFilter = SN("AnimationFilter");
|
||||
|
@ -52,8 +53,12 @@ LimboStringNames::LimboStringNames() {
|
|||
bold = SN("bold");
|
||||
BTAlwaysFail = SN("BTAlwaysFail");
|
||||
BTAlwaysSucceed = SN("BTAlwaysSucceed");
|
||||
button_down = SN("button_down");
|
||||
button_up = SN("button_up");
|
||||
changed = SN("changed");
|
||||
connect = SN("connect");
|
||||
dark_color_1 = SN("dark_color_1");
|
||||
dark_color_2 = SN("dark_color_2");
|
||||
Debug = SN("Debug");
|
||||
disabled_font_color = SN("disabled_font_color");
|
||||
doc_italic = SN("doc_italic");
|
||||
|
@ -76,6 +81,7 @@ LimboStringNames::LimboStringNames() {
|
|||
gui_input = SN("gui_input");
|
||||
GuiTreeArrowDown = SN("GuiTreeArrowDown");
|
||||
GuiTreeArrowRight = SN("GuiTreeArrowRight");
|
||||
HeaderSmall = SN("HeaderSmall");
|
||||
Help = SN("Help");
|
||||
icon_max_width = SN("icon_max_width");
|
||||
id_pressed = SN("id_pressed");
|
||||
|
@ -97,6 +103,7 @@ LimboStringNames::LimboStringNames() {
|
|||
NodeWarning = SN("NodeWarning");
|
||||
NonFavorite = SN("NonFavorite");
|
||||
normal = SN("normal");
|
||||
panel = SN("panel");
|
||||
popup_hide = SN("popup_hide");
|
||||
pressed = SN("pressed");
|
||||
probability_clicked = SN("probability_clicked");
|
||||
|
@ -111,6 +118,7 @@ LimboStringNames::LimboStringNames() {
|
|||
Script = SN("Script");
|
||||
ScriptCreate = SN("ScriptCreate");
|
||||
Search = SN("Search");
|
||||
separation = SN("separation");
|
||||
set_custom_name = SN("set_custom_name");
|
||||
set_root_task = SN("set_root_task");
|
||||
setup = SN("setup");
|
||||
|
@ -125,9 +133,12 @@ LimboStringNames::LimboStringNames() {
|
|||
task_meta = SN("task_meta");
|
||||
task_selected = SN("task_selected");
|
||||
text_changed = SN("text_changed");
|
||||
text_submitted = SN("text_submitted");
|
||||
timeout = SN("timeout");
|
||||
toggled = SN("toggled");
|
||||
Tools = SN("Tools");
|
||||
Tree = SN("Tree");
|
||||
TripleBar = SN("TripleBar");
|
||||
update_task = SN("update_task");
|
||||
update_tree = SN("update_tree");
|
||||
updated = SN("updated");
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
StringName accent_color;
|
||||
StringName add_child_at_index;
|
||||
StringName add_child;
|
||||
StringName Add;
|
||||
StringName AnimationFilter;
|
||||
StringName Back;
|
||||
StringName behavior_tree_finished;
|
||||
|
@ -66,8 +67,12 @@ public:
|
|||
StringName bold;
|
||||
StringName BTAlwaysFail;
|
||||
StringName BTAlwaysSucceed;
|
||||
StringName button_down;
|
||||
StringName button_up;
|
||||
StringName changed;
|
||||
StringName connect;
|
||||
StringName dark_color_1;
|
||||
StringName dark_color_2;
|
||||
StringName Debug;
|
||||
StringName disabled_font_color;
|
||||
StringName doc_italic;
|
||||
|
@ -90,6 +95,7 @@ public:
|
|||
StringName gui_input;
|
||||
StringName GuiTreeArrowDown;
|
||||
StringName GuiTreeArrowRight;
|
||||
StringName HeaderSmall;
|
||||
StringName Help;
|
||||
StringName icon_max_width;
|
||||
StringName id_pressed;
|
||||
|
@ -112,6 +118,7 @@ public:
|
|||
StringName NodeWarning;
|
||||
StringName NonFavorite;
|
||||
StringName normal;
|
||||
StringName panel;
|
||||
StringName popup_hide;
|
||||
StringName pressed;
|
||||
StringName probability_clicked;
|
||||
|
@ -126,6 +133,7 @@ public:
|
|||
StringName Script;
|
||||
StringName ScriptCreate;
|
||||
StringName Search;
|
||||
StringName separation;
|
||||
StringName set_custom_name;
|
||||
StringName set_root_task;
|
||||
StringName setup;
|
||||
|
@ -140,9 +148,12 @@ public:
|
|||
StringName task_meta;
|
||||
StringName task_selected;
|
||||
StringName text_changed;
|
||||
StringName text_submitted;
|
||||
StringName timeout;
|
||||
StringName toggled;
|
||||
StringName Tools;
|
||||
StringName Tree;
|
||||
StringName TripleBar;
|
||||
StringName update_task;
|
||||
StringName update_tree;
|
||||
StringName updated;
|
||||
|
|
|
@ -288,6 +288,129 @@ Variant LimboUtility::perform_operation(Operation p_operation, const Variant &le
|
|||
return ret;
|
||||
}
|
||||
|
||||
String LimboUtility::get_property_hint_text(PropertyHint p_hint) const {
|
||||
switch (p_hint) {
|
||||
case PROPERTY_HINT_NONE: {
|
||||
return "NONE";
|
||||
}
|
||||
case PROPERTY_HINT_RANGE: {
|
||||
return "RANGE";
|
||||
}
|
||||
case PROPERTY_HINT_ENUM: {
|
||||
return "ENUM";
|
||||
}
|
||||
case PROPERTY_HINT_ENUM_SUGGESTION: {
|
||||
return "SUGGESTION";
|
||||
}
|
||||
case PROPERTY_HINT_EXP_EASING: {
|
||||
return "EXP_EASING";
|
||||
}
|
||||
case PROPERTY_HINT_LINK: {
|
||||
return "LINK";
|
||||
}
|
||||
case PROPERTY_HINT_FLAGS: {
|
||||
return "FLAGS";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_2D_RENDER: {
|
||||
return "LAYERS_2D_RENDER";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_2D_PHYSICS: {
|
||||
return "LAYERS_2D_PHYSICS";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_2D_NAVIGATION: {
|
||||
return "LAYERS_2D_NAVIGATION";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_3D_RENDER: {
|
||||
return "LAYERS_3D_RENDER";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_3D_PHYSICS: {
|
||||
return "LAYERS_3D_PHYSICS";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_3D_NAVIGATION: {
|
||||
return "LAYERS_3D_NAVIGATION";
|
||||
}
|
||||
case PROPERTY_HINT_FILE: {
|
||||
return "FILE";
|
||||
}
|
||||
case PROPERTY_HINT_DIR: {
|
||||
return "DIR";
|
||||
}
|
||||
case PROPERTY_HINT_GLOBAL_FILE: {
|
||||
return "GLOBAL_FILE";
|
||||
}
|
||||
case PROPERTY_HINT_GLOBAL_DIR: {
|
||||
return "GLOBAL_DIR";
|
||||
}
|
||||
case PROPERTY_HINT_RESOURCE_TYPE: {
|
||||
return "RESOURCE_TYPE";
|
||||
}
|
||||
case PROPERTY_HINT_MULTILINE_TEXT: {
|
||||
return "MULTILINE_TEXT";
|
||||
}
|
||||
case PROPERTY_HINT_EXPRESSION: {
|
||||
return "EXPRESSION";
|
||||
}
|
||||
case PROPERTY_HINT_PLACEHOLDER_TEXT: {
|
||||
return "PLACEHOLDER_TEXT";
|
||||
}
|
||||
case PROPERTY_HINT_COLOR_NO_ALPHA: {
|
||||
return "COLOR_NO_ALPHA";
|
||||
}
|
||||
case PROPERTY_HINT_OBJECT_ID: {
|
||||
return "OBJECT_ID";
|
||||
}
|
||||
case PROPERTY_HINT_TYPE_STRING: {
|
||||
return "TYPE_STRING";
|
||||
}
|
||||
case PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE: {
|
||||
return "NODE_PATH_TO_EDITED_NODE";
|
||||
}
|
||||
case PROPERTY_HINT_OBJECT_TOO_BIG: {
|
||||
return "OBJECT_TOO_BIG";
|
||||
}
|
||||
case PROPERTY_HINT_NODE_PATH_VALID_TYPES: {
|
||||
return "NODE_PATH_VALID_TYPES";
|
||||
}
|
||||
case PROPERTY_HINT_SAVE_FILE: {
|
||||
return "SAVE_FILE";
|
||||
}
|
||||
case PROPERTY_HINT_GLOBAL_SAVE_FILE: {
|
||||
return "GLOBAL_SAVE_FILE";
|
||||
}
|
||||
case PROPERTY_HINT_INT_IS_OBJECTID: {
|
||||
return "INT_IS_OBJECTID";
|
||||
}
|
||||
case PROPERTY_HINT_INT_IS_POINTER: {
|
||||
return "INT_IS_POINTER";
|
||||
}
|
||||
case PROPERTY_HINT_ARRAY_TYPE: {
|
||||
return "ARRAY_TYPE";
|
||||
}
|
||||
case PROPERTY_HINT_LOCALE_ID: {
|
||||
return "LOCALE_ID";
|
||||
}
|
||||
case PROPERTY_HINT_LOCALIZABLE_STRING: {
|
||||
return "LOCALIZABLE_STRING";
|
||||
}
|
||||
case PROPERTY_HINT_NODE_TYPE: {
|
||||
return "NODE_TYPE";
|
||||
}
|
||||
case PROPERTY_HINT_HIDE_QUATERNION_EDIT: {
|
||||
return "HIDE_QUATERNION_EDIT";
|
||||
}
|
||||
case PROPERTY_HINT_PASSWORD: {
|
||||
return "PASSWORD";
|
||||
}
|
||||
case PROPERTY_HINT_LAYERS_AVOIDANCE: {
|
||||
return "LAYERS_AVOIDANCE";
|
||||
}
|
||||
case PROPERTY_HINT_MAX: {
|
||||
return "MAX";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
Ref<Shortcut> LimboUtility::add_shortcut(const String &p_path, const String &p_name, Key p_keycode) {
|
||||
|
|
|
@ -88,6 +88,8 @@ public:
|
|||
String get_operation_string(Operation p_operation) const;
|
||||
Variant perform_operation(Operation p_operation, const Variant &left_value, const Variant &right_value);
|
||||
|
||||
String get_property_hint_text(PropertyHint p_hint) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<Shortcut> add_shortcut(const String &p_path, const String &p_name, Key p_keycode = LW_KEY(NONE));
|
||||
bool is_shortcut(const String &p_path, const Ref<InputEvent> &p_event) const;
|
||||
|
|
Loading…
Reference in New Issue