/** * tree_search.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. * ============================================================================= */ #ifdef TOOLS_ENABLED #ifndef 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 #include #include #include #include #include #include #endif // LIMBOAI_GDEXTENSION using namespace godot; class TreeSearchPanel; class TreeSearch : public RefCounted { GDCLASS(TreeSearch, RefCounted) private: struct StringSearchIndices { // initialize to opposite bounds. int lower = -1; int upper = -1; bool hit() { return 0 <= lower && lower < upper; } }; TreeSearchPanel *search_panel; // For TaskTree: These are updated when the tree is updated through TaskTree::_create_tree. Tree *tree_reference; // linearized ordering of tree items. Vector ordered_tree_items; // entires that match the search mask. // TODO: Decide if this can be removed. It can be implicitly inferred from number_matches. Vector matching_entries; // number of descendant matches for each tree item. HashMap number_matches; // custom draw-callbacks for each tree item. HashMap callable_cache; bool was_searched_recently = false; // Performance bool was_filtered_recently = false; // Performance void _clean_callable_cache(); // Update_search() calls these void _filter_tree(const String &p_search_mask); void _clear_filter(); void _highlight_tree(); void _highlight_tree_item(TreeItem *p_tree_item); // Custom draw-Callback (bind inherited Callable). void _draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect, const Callable p_parent_draw_method); void _update_matching_entries(const String &p_search_mask); void _update_ordered_tree_items(TreeItem *p_tree_item); void _update_number_matches(); void _find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector &p_accum) const; String _get_search_mask() const; StringSearchIndices _substring_bounds(const String &p_searchable, const String &p_search_mask) const; void _select_item(TreeItem *p_item); void _select_first_match(); void _select_next_match(); void _on_search_panel_closed(); // TODO: make p_vec ref `const` once Vector::bsearch is const. See: https://github.com/godotengine/godot/pull/90341 template bool _vector_has_bsearch(Vector &p_vec, T *element) const; protected: static void _bind_methods() {} public: enum TreeSearchMode { HIGHLIGHT = 0, FILTER = 1 }; struct SearchInfo { String search_mask; TreeSearchMode search_mode; bool visible; }; void update_search(Tree *p_tree); void notify_item_edited(TreeItem *p_item); TreeSearch() { ERR_FAIL_MSG("TreeSearch needs a TreeSearchPanel to work properly."); } TreeSearch(TreeSearchPanel *p_search_panel); }; // -------------------------------------------- class TreeSearchPanel : public HFlowContainer { GDCLASS(TreeSearchPanel, HFlowContainer) private: Button *toggle_button_filter_highlight; Button *close_button; Label *label_filter; LineEdit *line_edit_search; CheckBox *check_button_filter_highlight; void _initialize_controls(); void _add_spacer(float width_multiplier = 1.f); void _notification(int p_what); protected: static void _bind_methods(); public: String get_text() const; TreeSearch::TreeSearchMode get_search_mode() const; TreeSearch::SearchInfo get_search_info() const; void set_search_info(const TreeSearch::SearchInfo &p_search_info); void focus_editor(); TreeSearchPanel(); }; #endif // TREE_SEARCH_H #endif // ! TOOLS_ENABLED