Compare commits

..

1 Commits

Author SHA1 Message Date
monxa 031e589c07
Merge cd85e6dd30 into 60a767032e 2024-09-28 16:45:18 +00:00
4 changed files with 125 additions and 133 deletions

View File

@ -39,6 +39,96 @@
#define UPPER_BOUND (1 << 15) // for substring search. #define UPPER_BOUND (1 << 15) // for substring search.
/* ------- TreeSearchPanel ------- */
void TreeSearchPanel::_initialize_controls() {
line_edit_search = memnew(LineEdit);
check_button_filter_highlight = memnew(CheckBox);
close_button = memnew(Button);
label_filter = memnew(Label);
line_edit_search->set_placeholder(TTR("Search tree"));
label_filter->set_text(TTR("Filter"));
close_button->set_theme_type_variation("FlatButton");
// positioning and sizing
set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_WIDE);
set_v_size_flags(SIZE_SHRINK_CENTER); // Do not expand vertically
line_edit_search->set_h_size_flags(SIZE_EXPAND_FILL);
_add_spacer(0.25); // otherwise the lineedits expand margin touches the left border.
add_child(line_edit_search);
_add_spacer(0.25);
add_child(check_button_filter_highlight);
add_child(label_filter);
_add_spacer(0.25);
add_child(close_button);
_add_spacer(0.25);
}
void TreeSearchPanel::_add_spacer(float p_width_multiplier) {
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Vector2(8.0 * EDSCALE * p_width_multiplier, 0.0));
add_child(spacer);
}
void TreeSearchPanel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
BUTTON_SET_ICON(close_button, get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
// close callbacks
close_button->connect("pressed", Callable(this, "set_visible").bind(false));
close_button->set_shortcut(LW_GET_SHORTCUT("limbo_ai/hide_tree_search"));
// search callbacks
Callable c_update_requested = Callable(this, "emit_signal").bind("update_requested");
Callable c_text_submitted = Callable((Object *)this, "emit_signal").bind("text_submitted");
line_edit_search->connect("text_changed", c_update_requested.unbind(1));
check_button_filter_highlight->connect("pressed", c_update_requested);
line_edit_search->connect("text_submitted", c_text_submitted.unbind(1));
break;
}
}
}
void TreeSearchPanel::_bind_methods() {
ADD_SIGNAL(MethodInfo("update_requested"));
ADD_SIGNAL(MethodInfo("text_submitted"));
}
TreeSearchPanel::TreeSearchPanel() {
_initialize_controls();
set_visible(false);
}
TreeSearch::TreeSearchMode TreeSearchPanel::get_search_mode() {
if (!check_button_filter_highlight || !check_button_filter_highlight->is_pressed()) {
return TreeSearch::TreeSearchMode::HIGHLIGHT;
}
return TreeSearch::TreeSearchMode::FILTER;
}
String TreeSearchPanel::get_text() {
if (!line_edit_search) {
return String();
}
return line_edit_search->get_text();
}
void TreeSearchPanel::show_and_focus() {
set_visible(true);
line_edit_search->grab_focus();
}
/* !TreeSearchPanel */
/* ------- TreeSearch ------- */ /* ------- TreeSearch ------- */
void TreeSearch::_filter_tree(const String &p_search_mask) { void TreeSearch::_filter_tree(const String &p_search_mask) {
@ -131,11 +221,11 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, Rect2 p_rect, Calla
Vector2 substring_before_size = font->get_string_size(substring_before, HORIZONTAL_ALIGNMENT_LEFT, -1.f, font_size); Vector2 substring_before_size = font->get_string_size(substring_before, HORIZONTAL_ALIGNMENT_LEFT, -1.f, font_size);
// stylebox // stylebox
Ref<StyleBox> stylebox = p_tree_item->get_tree()->get_theme_stylebox(LW_NAME(Focus)); Ref<StyleBox> stylebox = p_tree_item->get_tree()->get_theme_stylebox("Focus");
ERR_FAIL_NULL(stylebox); ERR_FAIL_NULL(stylebox);
// extract separation // extract separation
float h_sep = p_tree_item->get_tree()->get_theme_constant(LW_NAME(h_separation)); float h_sep = p_tree_item->get_tree()->get_theme_constant("h_separation");
// compose draw rect // compose draw rect
const Vector2 PADDING = Vector2(4., 2.); const Vector2 PADDING = Vector2(4., 2.);
@ -156,9 +246,9 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, Rect2 p_rect, Calla
// second part: draw number // second part: draw number
int num_mat = number_matches.has(p_tree_item) ? number_matches.get(p_tree_item) : 0; int num_mat = number_matches.has(p_tree_item) ? number_matches.get(p_tree_item) : 0;
if (num_mat > 0) { if (num_mat > 0) {
float h_sep = p_tree_item->get_tree()->get_theme_constant(LW_NAME(h_separation)); float h_sep = p_tree_item->get_tree()->get_theme_constant("h_separation");
Ref<Font> font = tree_reference->get_theme_font(LW_NAME(font)); Ref<Font> font = tree_reference->get_theme_font("font");
float font_size = tree_reference->get_theme_font_size(LW_NAME(font)) * 0.75; float font_size = tree_reference->get_theme_font_size("font") * 0.75;
String num_string = String::num_int64(num_mat); String num_string = String::num_int64(num_mat);
Vector2 string_size = font->get_string_size(num_string, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size); Vector2 string_size = font->get_string_size(num_string, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size);
@ -173,8 +263,7 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, Rect2 p_rect, Calla
void TreeSearch::_update_matching_entries(const String &p_search_mask) { void TreeSearch::_update_matching_entries(const String &p_search_mask) {
Vector<TreeItem *> accum; Vector<TreeItem *> accum;
_find_matching_entries(tree_reference->get_root(), p_search_mask, accum); matching_entries = _find_matching_entries(tree_reference->get_root(), p_search_mask, accum);
matching_entries = accum;
} }
/* this linearizes the tree into [ordered_tree_items] like so: /* this linearizes the tree into [ordered_tree_items] like so:
@ -205,8 +294,8 @@ void TreeSearch::_update_number_matches() {
for (int i = 0; i < matching_entries.size(); i++) { for (int i = 0; i < matching_entries.size(); i++) {
TreeItem *item = matching_entries[i]; TreeItem *item = matching_entries[i];
while (item) { while (item) {
int previous_match_cnt = number_matches.has(item) ? number_matches.get(item) : 0; int old_num_value = number_matches.has(item) ? number_matches.get(item) : 0;
number_matches[item] = previous_match_cnt + 1; number_matches[item] = old_num_value + 1;
item = item->get_parent(); item = item->get_parent();
} }
} }
@ -217,14 +306,13 @@ String TreeSearch::_get_search_mask() {
return search_panel->get_text(); return search_panel->get_text();
} }
void TreeSearch::_find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector<TreeItem *> &p_accum) { Vector<TreeItem *> TreeSearch::_find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector<TreeItem *> &p_accum) {
if (!p_tree_item) { if (!p_tree_item)
return; return p_accum;
}
StringSearchIndices item_search_indices = _substring_bounds(p_tree_item->get_text(0), p_search_mask); StringSearchIndices item_search_indices = _substring_bounds(p_tree_item->get_text(0), p_search_mask);
if (item_search_indices.hit()) { if (item_search_indices.hit())
p_accum.push_back(p_tree_item); p_accum.push_back(p_tree_item);
}
for (int i = 0; i < p_tree_item->get_child_count(); i++) { for (int i = 0; i < p_tree_item->get_child_count(); i++) {
TreeItem *child = p_tree_item->get_child(i); TreeItem *child = p_tree_item->get_child(i);
_find_matching_entries(child, p_search_mask, p_accum); _find_matching_entries(child, p_search_mask, p_accum);
@ -235,7 +323,7 @@ void TreeSearch::_find_matching_entries(TreeItem *p_tree_item, const String &p_s
p_accum.sort(); p_accum.sort();
} }
return; return p_accum;
} }
// Returns the lower and upper bounds of a substring. Does fuzzy search: Simply looks if words exist in right ordering. // Returns the lower and upper bounds of a substring. Does fuzzy search: Simply looks if words exist in right ordering.
@ -253,10 +341,8 @@ TreeSearch::StringSearchIndices TreeSearch::_substring_bounds(const String &p_se
// Determine if the search should be case-insensitive. // Determine if the search should be case-insensitive.
bool is_case_insensitive = (p_search_mask == p_search_mask.to_lower()); bool is_case_insensitive = (p_search_mask == p_search_mask.to_lower());
String searchable_processed = is_case_insensitive ? p_searchable.to_lower() : p_searchable; String searchable_processed = is_case_insensitive ? p_searchable.to_lower() : p_searchable;
PackedStringArray words = p_search_mask.split(" "); PackedStringArray words = p_search_mask.split(" ");
int word_position = 0; int word_position = 0;
for (const String &word : words) { for (const String &word : words) {
if (word.is_empty()) { if (word.is_empty()) {
continue; // Skip empty words. continue; // Skip empty words.
@ -308,6 +394,7 @@ void TreeSearch::_select_first_match() {
if (!_vector_has_bsearch(matching_entries, item)) { if (!_vector_has_bsearch(matching_entries, item)) {
continue; continue;
} }
String debug_string = "[";
_select_item(item); _select_item(item);
return; return;
} }
@ -332,12 +419,14 @@ void TreeSearch::_select_next_match() {
} }
// find the best fitting entry. // find the best fitting entry.
for (int i = MAX(0, selected_idx) + 1; i < ordered_tree_items.size(); i++) { for (int i = 0; i < ordered_tree_items.size(); i++) {
TreeItem *item = ordered_tree_items[i]; TreeItem *item = ordered_tree_items[i];
if (_vector_has_bsearch(matching_entries, item)) { if (!_vector_has_bsearch(matching_entries, item) || selected_idx >= i) {
_select_item(item); continue;
return;
} }
_select_item(item);
return;
} }
_select_first_match(); // wrap around. _select_first_match(); // wrap around.
} }
@ -381,6 +470,9 @@ void TreeSearch::update_search(Tree *p_tree) {
_update_number_matches(); _update_number_matches();
_highlight_tree(search_mask); _highlight_tree(search_mask);
if (!search_panel->is_connected("text_submitted", callable_mp(this, &TreeSearch::_select_next_match))) {
search_panel->connect("text_submitted", callable_mp(this, &TreeSearch::_select_next_match));
}
if (search_mode == TreeSearchMode::FILTER) { if (search_mode == TreeSearchMode::FILTER) {
_filter_tree(search_mask); _filter_tree(search_mask);
@ -389,100 +481,8 @@ void TreeSearch::update_search(Tree *p_tree) {
TreeSearch::TreeSearch(TreeSearchPanel *p_search_panel) { TreeSearch::TreeSearch(TreeSearchPanel *p_search_panel) {
search_panel = p_search_panel; search_panel = p_search_panel;
search_panel->connect("text_submitted", callable_mp(this, &TreeSearch::_select_next_match));
} }
/* !TreeSearch */ /* !TreeSearch */
/* ------- TreeSearchPanel ------- */
void TreeSearchPanel::_initialize_controls() {
line_edit_search = memnew(LineEdit);
check_button_filter_highlight = memnew(CheckBox);
close_button = memnew(Button);
label_filter = memnew(Label);
line_edit_search->set_placeholder(TTR("Search tree"));
close_button->set_theme_type_variation(LW_NAME(FlatButton));
// positioning and sizing
set_anchors_and_offsets_preset(LayoutPreset::PRESET_BOTTOM_WIDE);
set_v_size_flags(SIZE_SHRINK_CENTER); // Do not expand vertically
line_edit_search->set_h_size_flags(SIZE_EXPAND_FILL);
_add_spacer(0.25); // otherwise the lineedits expand margin touches the left border.
add_child(line_edit_search);
_add_spacer(0.25);
add_child(check_button_filter_highlight);
add_child(label_filter);
_add_spacer(0.25);
add_child(close_button);
_add_spacer(0.25);
}
void TreeSearchPanel::_add_spacer(float p_width_multiplier) {
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Vector2(8.0 * EDSCALE * p_width_multiplier, 0.0));
add_child(spacer);
}
void TreeSearchPanel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
// close callbacks
close_button->connect("pressed", Callable(this, "set_visible").bind(false));
close_button->set_shortcut(LW_GET_SHORTCUT("limbo_ai/hide_tree_search"));
// search callbacks
Callable c_update_requested = Callable(this, "emit_signal").bind("update_requested");
Callable c_text_submitted = Callable((Object *)this, "emit_signal").bind("text_submitted");
line_edit_search->connect("text_changed", c_update_requested.unbind(1));
check_button_filter_highlight->connect("pressed", c_update_requested);
line_edit_search->connect("text_submitted", c_text_submitted.unbind(1));
break;
}
case NOTIFICATION_THEME_CHANGED: {
BUTTON_SET_ICON(close_button, get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
label_filter->set_text(TTR("Filter"));
break;
}
}
}
void TreeSearchPanel::_bind_methods() {
ADD_SIGNAL(MethodInfo("update_requested"));
ADD_SIGNAL(MethodInfo("text_submitted"));
}
TreeSearchPanel::TreeSearchPanel() {
_initialize_controls();
set_visible(false);
}
TreeSearch::TreeSearchMode TreeSearchPanel::get_search_mode() {
if (!check_button_filter_highlight || !check_button_filter_highlight->is_pressed()) {
return TreeSearch::TreeSearchMode::HIGHLIGHT;
}
return TreeSearch::TreeSearchMode::FILTER;
}
String TreeSearchPanel::get_text() {
if (!line_edit_search) {
return String();
}
return line_edit_search->get_text();
}
void TreeSearchPanel::show_and_focus() {
set_visible(true);
line_edit_search->grab_focus();
}
/* !TreeSearchPanel */
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED

View File

@ -14,15 +14,6 @@
#ifndef TREE_SEARCH_H #ifndef TREE_SEARCH_H
#define TREE_SEARCH_H #define TREE_SEARCH_H
#ifdef LIMBOAI_MODULE
#include "core/templates/hash_map.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/tree.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/check_box.hpp> #include <godot_cpp/classes/check_box.hpp>
#include <godot_cpp/classes/h_flow_container.hpp> #include <godot_cpp/classes/h_flow_container.hpp>
@ -32,6 +23,15 @@
#include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/templates/hash_map.hpp>
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
#ifdef LIMBOAI_MODULE
#include "core/templates/hash_map.h"
#include "scene/gui/check_box.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/tree.h"
#endif // LIMBOAI_MODULE
using namespace godot; using namespace godot;
class TreeSearchPanel; class TreeSearchPanel;
@ -69,7 +69,7 @@ private:
void _update_ordered_tree_items(TreeItem *p_tree_item); void _update_ordered_tree_items(TreeItem *p_tree_item);
void _update_number_matches(); void _update_number_matches();
void _find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector<TreeItem *> &p_accum); Vector<TreeItem *> _find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector<TreeItem *> &p_buffer);
String _get_search_mask(); String _get_search_mask();
StringSearchIndices _substring_bounds(const String &p_searchable, const String &p_search_mask) const; StringSearchIndices _substring_bounds(const String &p_searchable, const String &p_search_mask) const;
@ -92,12 +92,10 @@ public:
void update_search(Tree *p_tree); void update_search(Tree *p_tree);
void notify_item_edited(TreeItem *p_item); void notify_item_edited(TreeItem *p_item);
TreeSearch() { ERR_FAIL_MSG("TreeSearch needs a TreeSearchPanel to work properly."); } TreeSearch() { ERR_FAIL_MSG("TreeSearch needs a TreeSearchPanel to work properly"); }
TreeSearch(TreeSearchPanel *p_search_panel); TreeSearch(TreeSearchPanel *p_search_panel);
}; };
// --------------------------------------------
class TreeSearchPanel : public HFlowContainer { class TreeSearchPanel : public HFlowContainer {
GDCLASS(TreeSearchPanel, HFlowContainer) GDCLASS(TreeSearchPanel, HFlowContainer)

View File

@ -67,8 +67,6 @@ LimboStringNames::LimboStringNames() {
exited = SN("exited"); exited = SN("exited");
favorite_tasks_changed = SN("favorite_tasks_changed"); favorite_tasks_changed = SN("favorite_tasks_changed");
Favorites = SN("Favorites"); Favorites = SN("Favorites");
FlatButton = SN("FlatButton");
Focus = SN("Focus");
focus_exited = SN("focus_exited"); focus_exited = SN("focus_exited");
font = SN("font"); font = SN("font");
font_color = SN("font_color"); font_color = SN("font_color");
@ -80,7 +78,6 @@ LimboStringNames::LimboStringNames() {
GuiTreeArrowRight = SN("GuiTreeArrowRight"); GuiTreeArrowRight = SN("GuiTreeArrowRight");
HeaderSmall = SN("HeaderSmall"); HeaderSmall = SN("HeaderSmall");
Help = SN("Help"); Help = SN("Help");
h_separation = SN("h_separation");
icon_max_width = SN("icon_max_width"); icon_max_width = SN("icon_max_width");
class_icon_size = SN("class_icon_size"); class_icon_size = SN("class_icon_size");
id_pressed = SN("id_pressed"); id_pressed = SN("id_pressed");

View File

@ -83,8 +83,6 @@ public:
StringName exited; StringName exited;
StringName favorite_tasks_changed; StringName favorite_tasks_changed;
StringName Favorites; StringName Favorites;
StringName FlatButton;
StringName Focus;
StringName focus_exited; StringName focus_exited;
StringName font_color; StringName font_color;
StringName font_size; StringName font_size;
@ -96,7 +94,6 @@ public:
StringName GuiTreeArrowRight; StringName GuiTreeArrowRight;
StringName HeaderSmall; StringName HeaderSmall;
StringName Help; StringName Help;
StringName h_separation;
StringName icon_max_width; StringName icon_max_width;
StringName class_icon_size; StringName class_icon_size;
StringName id_pressed; StringName id_pressed;