2024-09-19 21:35:14 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2024-09-28 15:45:52 +00:00
|
|
|
#include "core/templates/hash_map.h"
|
2024-09-25 17:03:46 +00:00
|
|
|
#include "scene/gui/check_box.h"
|
2024-09-25 16:15:17 +00:00
|
|
|
#include "scene/gui/flow_container.h"
|
2024-09-25 15:32:03 +00:00
|
|
|
#include "scene/gui/label.h"
|
|
|
|
#include "scene/gui/line_edit.h"
|
|
|
|
#include "scene/gui/tree.h"
|
2024-09-19 21:35:14 +00:00
|
|
|
#endif // LIMBOAI_MODULE
|
|
|
|
|
2024-09-29 00:00:17 +00:00
|
|
|
#ifdef LIMBOAI_GDEXTENSION
|
|
|
|
#include <godot_cpp/classes/check_box.hpp>
|
|
|
|
#include <godot_cpp/classes/h_flow_container.hpp>
|
|
|
|
#include <godot_cpp/classes/label.hpp>
|
|
|
|
#include <godot_cpp/classes/line_edit.hpp>
|
|
|
|
#include <godot_cpp/classes/tree.hpp>
|
|
|
|
#include <godot_cpp/templates/hash_map.hpp>
|
|
|
|
#endif // LIMBOAI_GDEXTENSION
|
|
|
|
|
2024-09-19 21:35:14 +00:00
|
|
|
using namespace godot;
|
|
|
|
|
2024-09-28 15:58:42 +00:00
|
|
|
class TreeSearchPanel;
|
2024-09-19 21:35:14 +00:00
|
|
|
|
2024-09-25 15:32:03 +00:00
|
|
|
class TreeSearch : public RefCounted {
|
|
|
|
GDCLASS(TreeSearch, RefCounted)
|
2024-09-19 21:35:14 +00:00
|
|
|
private:
|
2024-09-25 15:32:03 +00:00
|
|
|
struct StringSearchIndices {
|
|
|
|
// initialize to opposite bounds.
|
|
|
|
int lower = -1;
|
|
|
|
int upper = -1;
|
|
|
|
|
|
|
|
bool hit() {
|
|
|
|
return 0 <= lower && lower < upper;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-09-27 15:02:21 +00:00
|
|
|
TreeSearchPanel *search_panel;
|
2024-09-25 15:32:03 +00:00
|
|
|
|
2024-09-27 16:07:42 +00:00
|
|
|
// For TaskTree: These are updated when the tree is updated through TaskTree::_create_tree.
|
2024-09-27 15:02:21 +00:00
|
|
|
Tree *tree_reference;
|
2024-09-29 14:48:57 +00:00
|
|
|
// linearized ordering of tree items.
|
2024-09-25 15:32:03 +00:00
|
|
|
Vector<TreeItem *> ordered_tree_items;
|
2024-09-29 14:48:57 +00:00
|
|
|
// entires that match the search mask.
|
2024-09-29 16:26:18 +00:00
|
|
|
// TODO: Decide if this can be removed. It can be implicitly inferred from number_matches.
|
2024-09-25 15:32:03 +00:00
|
|
|
Vector<TreeItem *> matching_entries;
|
2024-09-29 14:48:57 +00:00
|
|
|
// number of descendant matches for each tree item.
|
2024-09-25 15:32:03 +00:00
|
|
|
HashMap<TreeItem *, int> number_matches;
|
2024-09-29 14:48:57 +00:00
|
|
|
// custom draw-callbacks for each tree item.
|
2024-09-25 15:32:03 +00:00
|
|
|
HashMap<TreeItem *, Callable> callable_cache;
|
2024-09-25 16:17:44 +00:00
|
|
|
|
2024-09-29 12:50:07 +00:00
|
|
|
bool was_searched_recently = false; // Performance
|
|
|
|
bool was_filtered_recently = false; // Performance
|
|
|
|
|
|
|
|
void _clean_callable_cache();
|
|
|
|
|
2024-09-27 16:07:42 +00:00
|
|
|
// Update_search() calls these
|
2024-09-25 16:54:33 +00:00
|
|
|
void _filter_tree(const String &p_search_mask);
|
2024-09-29 12:50:07 +00:00
|
|
|
void _clear_filter();
|
|
|
|
|
|
|
|
void _highlight_tree();
|
|
|
|
void _highlight_tree_item(TreeItem *p_tree_item);
|
2024-09-27 16:07:42 +00:00
|
|
|
|
|
|
|
// Custom draw-Callback (bind inherited Callable).
|
2024-09-29 15:57:13 +00:00
|
|
|
void _draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect, const Callable p_parent_draw_method);
|
2024-09-28 15:45:52 +00:00
|
|
|
|
2024-09-26 05:28:59 +00:00
|
|
|
void _update_matching_entries(const String &p_search_mask);
|
|
|
|
void _update_ordered_tree_items(TreeItem *p_tree_item);
|
2024-09-25 15:32:03 +00:00
|
|
|
void _update_number_matches();
|
2024-09-25 16:54:33 +00:00
|
|
|
|
2024-09-29 15:57:13 +00:00
|
|
|
void _find_matching_entries(TreeItem *p_tree_item, const String &p_search_mask, Vector<TreeItem *> &p_accum) const;
|
|
|
|
String _get_search_mask() const;
|
2024-09-26 05:28:59 +00:00
|
|
|
StringSearchIndices _substring_bounds(const String &p_searchable, const String &p_search_mask) const;
|
2024-09-25 15:32:03 +00:00
|
|
|
|
2024-09-26 05:28:59 +00:00
|
|
|
void _select_item(TreeItem *p_item);
|
2024-09-25 15:32:03 +00:00
|
|
|
void _select_first_match();
|
|
|
|
void _select_next_match();
|
2024-09-19 21:35:14 +00:00
|
|
|
|
2024-09-30 14:13:36 +00:00
|
|
|
void _on_search_panel_closed();
|
2024-09-29 19:47:38 +00:00
|
|
|
|
2024-09-29 16:26:18 +00:00
|
|
|
// TODO: make p_vec ref `const` once Vector::bsearch is const. See: https://github.com/godotengine/godot/pull/90341
|
2024-09-26 15:36:59 +00:00
|
|
|
template <typename T>
|
2024-09-29 15:57:13 +00:00
|
|
|
bool _vector_has_bsearch(Vector<T *> &p_vec, T *element) const;
|
2024-09-26 15:36:59 +00:00
|
|
|
|
2024-09-25 15:32:03 +00:00
|
|
|
protected:
|
2024-09-25 16:17:44 +00:00
|
|
|
static void _bind_methods() {}
|
2024-09-19 21:35:14 +00:00
|
|
|
|
|
|
|
public:
|
2024-09-28 15:58:42 +00:00
|
|
|
enum TreeSearchMode {
|
|
|
|
HIGHLIGHT = 0,
|
|
|
|
FILTER = 1
|
|
|
|
};
|
|
|
|
|
2024-09-30 14:13:36 +00:00
|
|
|
struct SearchInfo {
|
|
|
|
String search_mask;
|
|
|
|
TreeSearchMode search_mode;
|
|
|
|
bool visible;
|
|
|
|
};
|
|
|
|
|
2024-09-26 05:28:59 +00:00
|
|
|
void update_search(Tree *p_tree);
|
2024-09-28 15:45:52 +00:00
|
|
|
void notify_item_edited(TreeItem *p_item);
|
2024-09-19 21:35:14 +00:00
|
|
|
|
2024-09-29 00:08:24 +00:00
|
|
|
TreeSearch() { ERR_FAIL_MSG("TreeSearch needs a TreeSearchPanel to work properly."); }
|
2024-09-28 15:45:52 +00:00
|
|
|
TreeSearch(TreeSearchPanel *p_search_panel);
|
2024-09-19 21:35:14 +00:00
|
|
|
};
|
|
|
|
|
2024-09-29 00:00:17 +00:00
|
|
|
// --------------------------------------------
|
|
|
|
|
2024-09-28 15:58:42 +00:00
|
|
|
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);
|
2024-09-29 16:12:18 +00:00
|
|
|
|
2024-09-28 15:58:42 +00:00
|
|
|
void _notification(int p_what);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
static void _bind_methods();
|
|
|
|
|
|
|
|
public:
|
2024-09-29 15:57:13 +00:00
|
|
|
String get_text() const;
|
2024-09-30 14:22:23 +00:00
|
|
|
TreeSearch::TreeSearchMode get_search_mode() const;
|
2024-09-30 14:13:36 +00:00
|
|
|
TreeSearch::SearchInfo get_search_info() const;
|
2024-09-30 14:22:23 +00:00
|
|
|
void set_search_info(const TreeSearch::SearchInfo &p_search_info);
|
2024-09-30 14:13:36 +00:00
|
|
|
void focus_editor();
|
2024-09-28 15:58:42 +00:00
|
|
|
TreeSearchPanel();
|
|
|
|
};
|
|
|
|
|
2024-09-19 21:35:14 +00:00
|
|
|
#endif // TREE_SEARCH_H
|
2024-09-25 16:54:33 +00:00
|
|
|
#endif // ! TOOLS_ENABLED
|