9#include <wx/dcbuffer.h>
46 : wxVListBox(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE),
m_owner(owner) {
47 SetBackgroundColour(wxColour(45, 45, 50));
54 const auto &items =
m_owner->GetFlatList();
55 if (n >= items.size())
58 const auto &entry = items[n];
61 dc.SetBrush(wxBrush(wxColour(38, 40, 45)));
62 dc.SetPen(*wxTRANSPARENT_PEN);
63 dc.DrawRectangle(rect);
67 bool isSelected = IsSelected(n);
68 bool isCurrent = (entry.id ==
m_owner->GetCurrentTreeId());
71 if (isSelected && isBlackboard) {
72 dc.SetBrush(wxBrush(wxColour(70, 55, 100)));
73 }
else if (isSelected) {
74 dc.SetBrush(wxBrush(wxColour(55, 75, 100)));
75 }
else if (isCurrent) {
76 dc.SetBrush(wxBrush(wxColour(50, 60, 75)));
77 }
else if (n % 2 == 1) {
78 dc.SetBrush(wxBrush(wxColour(48, 48, 53)));
80 dc.SetBrush(wxBrush(wxColour(45, 45, 50)));
82 dc.SetPen(*wxTRANSPARENT_PEN);
83 dc.DrawRectangle(rect);
86 dc.SetBrush(wxBrush(wxColour(80, 160, 220)));
87 dc.SetPen(*wxTRANSPARENT_PEN);
88 dc.DrawRectangle(rect.x, rect.y + 4, 3, rect.height - 8);
93 const auto &items =
m_owner->GetFlatList();
94 if (n >= items.size())
97 const auto &entry = items[n];
102 int toggleX = leftPad;
103 int toggleY = rect.y + (rect.height / 2) - (
TOGGLE_SIZE / 2);
105 dc.SetPen(wxPen(wxColour(180, 185, 195), 1));
106 dc.SetBrush(wxBrush(wxColour(180, 185, 195)));
108 if (entry.isCollapsed) {
109 wxPoint tri[3] = {wxPoint(toggleX, toggleY), wxPoint(toggleX, toggleY +
TOGGLE_SIZE),
111 dc.DrawPolygon(3, tri);
113 wxPoint tri[3] = {wxPoint(toggleX, toggleY + 1), wxPoint(toggleX +
TOGGLE_SIZE, toggleY + 1),
115 dc.DrawPolygon(3, tri);
119 wxFont headerFont = GetFont();
120 headerFont.SetPointSize(11);
121 headerFont.SetWeight(wxFONTWEIGHT_BOLD);
122 dc.SetFont(headerFont);
123 dc.SetTextForeground(wxColour(200, 205, 215));
125 wxString headerText = wxString::FromUTF8(entry.displayName);
126 int textY = rect.y + (rect.height - dc.GetTextExtent(headerText).GetHeight()) / 2;
127 dc.DrawText(headerText, rect.x + leftPad, textY);
129 wxFont countFont = GetFont();
130 countFont.SetPointSize(9);
131 dc.SetFont(countFont);
132 dc.SetTextForeground(wxColour(130, 135, 145));
134 wxString countText = wxString::Format(
"(%d)", entry.sectionCount);
135 int countW = dc.GetTextExtent(countText).GetWidth();
136 int countX = rect.x + rect.width - countW - 10;
137 dc.DrawText(countText, countX, textY + 1);
139 dc.SetPen(wxPen(wxColour(60, 65, 75), 1));
140 dc.DrawLine(rect.x + 4, rect.y + rect.height - 1, rect.x + rect.width - 4, rect.y + rect.height - 1);
145 bool isCurrent = (entry.id ==
m_owner->GetCurrentTreeId());
146 int depth = entry.depth;
148 int leftPad = basePad + depth *
INDENT_PX;
151 wxPen connPen(wxColour(80, 85, 95), 1);
154 for (
int d = 1; d < depth; ++d) {
155 if (d - 1 <
static_cast<int>(entry.parentHasMore.size()) && entry.parentHasMore[d - 1]) {
157 dc.DrawLine(lineX, rect.y, lineX, rect.y + rect.height);
162 int midY = rect.y + rect.height / 2;
163 dc.DrawLine(connX, rect.y, connX, entry.isLastChild ? midY : rect.y + rect.height);
164 dc.DrawLine(connX, midY, connX + 8, midY);
167 if (entry.hasChildren) {
168 int toggleX = leftPad;
169 int toggleY = rect.y + (rect.height / 2) - (
TOGGLE_SIZE / 2);
171 dc.SetPen(wxPen(wxColour(150, 155, 165), 1));
173 if (entry.isCollapsed) {
174 wxPoint tri[3] = {wxPoint(toggleX, toggleY), wxPoint(toggleX, toggleY +
TOGGLE_SIZE),
176 dc.SetBrush(wxBrush(wxColour(150, 155, 165)));
177 dc.DrawPolygon(3, tri);
179 wxPoint tri[3] = {wxPoint(toggleX, toggleY + 1), wxPoint(toggleX +
TOGGLE_SIZE, toggleY + 1),
181 dc.SetBrush(wxBrush(wxColour(150, 155, 165)));
182 dc.DrawPolygon(3, tri);
189 wxFont nameFont = GetFont();
190 nameFont.SetPointSize(10);
192 nameFont.SetWeight(wxFONTWEIGHT_BOLD);
193 dc.SetFont(nameFont);
195 wxColour nameColour = isCurrent ? wxColour(230, 240, 255) : wxColour(210, 210, 215);
196 if (!entry.isImplemented)
197 nameColour = wxColour(140, 140, 155);
199 nameColour = wxColour(180, 210, 255);
200 dc.SetTextForeground(nameColour);
202 wxString name = wxString::FromUTF8(entry.displayName);
203 int nameY = rect.y + 7;
204 dc.DrawText(name, rect.x + leftPad, nameY);
206 wxFont subFont = GetFont();
207 subFont.SetPointSize(8);
209 dc.SetTextForeground(wxColour(110, 115, 125));
213 info = wxString::Format(
"%d entr%s", entry.nodeCount, entry.nodeCount == 1 ?
"y" :
"ies");
215 if (entry.nodeCount > 0) {
216 info = wxString::Format(
"%d node%s", entry.nodeCount, entry.nodeCount == 1 ?
"" :
"s");
218 if (!entry.isImplemented) {
221 info +=
"unimplemented";
226 info +=
"not in main tree";
234 if (!info.IsEmpty()) {
235 int infoY = nameY + dc.GetCharHeight() + 1;
236 dc.DrawText(info, rect.x + leftPad, infoY);
241 const auto &items =
m_owner->GetFlatList();
242 if (n < items.size() && items[n].isHeader) {
249 int item = VirtualHitTest(event.GetPosition().y);
250 if (item == wxNOT_FOUND) {
255 const auto &items =
m_owner->GetFlatList();
256 if (item < 0 || item >=
static_cast<int>(items.size())) {
261 const auto &entry = items[item];
263 if (entry.isHeader) {
264 m_owner->ToggleSectionCollapse(entry.section);
271 m_owner->OnBlackboardClicked(entry.id);
275 if (entry.hasChildren) {
276 bool isCurrent = (entry.id ==
m_owner->GetCurrentTreeId());
277 int toggleHitX =
m_owner->GetToggleHitX(entry.depth, isCurrent);
278 int clickX =
event.GetPosition().x;
280 if (clickX >= toggleHitX && clickX < toggleHitX +
TOGGLE_SIZE + 6) {
281 m_owner->ToggleCollapse(entry.id);
290 int item = VirtualHitTest(event.GetPosition().y);
291 if (item == wxNOT_FOUND)
294 const auto &items =
m_owner->GetFlatList();
295 if (item < 0 || item >=
static_cast<int>(items.size()))
298 const auto &entry = items[item];
303 m_owner->OnBlackboardClicked(entry.id);
307 m_owner->DrillIntoTree(entry.id);
311 int item = VirtualHitTest(event.GetPosition().y);
312 if (item == wxNOT_FOUND) {
317 const auto &items =
m_owner->GetFlatList();
318 if (item < 0 || item >=
static_cast<int>(items.size())) {
330 m_owner->ShowTreeContextMenu(item, event.GetPosition());
338 : wxVListBox(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE),
m_owner(owner) {
339 SetBackgroundColour(wxColour(45, 45, 50));
345 bool isSelected = IsSelected(n);
347 dc.SetBrush(wxBrush(wxColour(55, 75, 100)));
348 }
else if (n % 2 == 1) {
349 dc.SetBrush(wxBrush(wxColour(48, 48, 53)));
351 dc.SetBrush(wxBrush(wxColour(45, 45, 50)));
353 dc.SetPen(*wxTRANSPARENT_PEN);
354 dc.DrawRectangle(rect);
358 const auto &results =
m_owner->GetSearchResults();
359 if (n >= results.size())
362 const auto &result = results[n];
365 wxFont nameFont = GetFont();
366 nameFont.SetPointSize(10);
367 dc.SetFont(nameFont);
370 dc.SetTextForeground(wxColour(100, 180, 255));
371 wxString label = wxString::FromUTF8(result.treeId);
372 int nameY = rect.y + 7;
373 dc.DrawText(label, rect.x + leftPad, nameY);
375 wxFont subFont = GetFont();
376 subFont.SetPointSize(8);
378 dc.SetTextForeground(wxColour(110, 115, 125));
379 dc.DrawText(
"tree", rect.x + leftPad, nameY + dc.GetCharHeight() + 1);
381 dc.SetTextForeground(wxColour(210, 210, 215));
382 wxString label = wxString::FromUTF8(result.nodeName);
383 int nameY = rect.y + 7;
384 dc.DrawText(label, rect.x + leftPad, nameY);
386 wxFont subFont = GetFont();
387 subFont.SetPointSize(8);
389 dc.SetTextForeground(wxColour(110, 115, 125));
390 wxString info =
"in " + wxString::FromUTF8(result.treeId);
391 dc.DrawText(info, rect.x + leftPad, nameY + dc.GetCharHeight() + 1);
398 int item = VirtualHitTest(event.GetPosition().y);
399 if (item == wxNOT_FOUND)
402 const auto &results =
m_owner->GetSearchResults();
403 if (item < 0 || item >=
static_cast<int>(results.size()))
406 const auto &result = results[item];
408 m_owner->DrillIntoTree(result.treeId);
411 m_owner->SelectNodeById(result.nodeId);
416 int item = VirtualHitTest(event.GetPosition().y);
417 if (item == wxNOT_FOUND)
422 m_owner->ShowSearchContextMenu(item, event.GetPosition());
437 : wxPanel(parent, wxID_ANY) {
438 SetBackgroundColour(wxColour(45, 45, 50));
443 SetBackgroundColour(wxColour(45, 45, 50));
453 wxBoxSizer *mainSizer =
new wxBoxSizer(wxVERTICAL);
456 new wxSearchCtrl(
this,
ID_SEARCH_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
457 m_searchCtrl->SetDescriptiveText(
"Search trees/nodes...");
467 wxBoxSizer *breadSizer =
new wxBoxSizer(wxHORIZONTAL);
470 wxDefaultSize, wxBU_EXACTFIT | wxBORDER_NONE);
471 m_backBtn->SetBackgroundColour(wxColour(50, 52, 58));
472 m_backBtn->SetForegroundColour(wxColour(140, 180, 220));
478 bcFont.SetPointSize(9);
481 breadSizer->AddSpacer(6);
482 breadSizer->Add(
m_backBtn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
484 breadSizer->AddSpacer(6);
488 m_book =
new wxSimplebook(
this, wxID_ANY);
506 mainSizer->Add(
m_book, 1, wxEXPAND | wxLEFT | wxRIGHT, 2);
508 m_statusLabel =
new wxStaticText(
this, wxID_ANY,
"No project loaded");
511 statusFont.SetPointSize(8);
535 auto adapter = std::make_shared<EmberCore::DirectTreeAdapter>(it->second);
592 int count = (it !=
m_treeInfoMap.end()) ? it->second.nodeCount : 0;
606 const std::string ¤tTreeId) {
611 for (
const auto &pair : trees) {
613 info.
id = pair.first;
614 info.
nodeCount = pair.second ?
static_cast<int>(pair.second->GetNodeCount()) : 0;
615 info.
isImplemented = pair.second && pair.second->HasRootNode() && pair.second->GetNodeCount() > 0;
617 if (pair.second && pair.second->GetRootNode()) {
626 if (!pair.second.subtreeRefs.empty()) {
708 LOG_INFO(
"Navigator",
"SetMainTreeId called with: '" +
id +
"'");
732 std::string subtreeRef = node->
GetAttribute(
"__subtree_ref__",
"");
733 if (!subtreeRef.empty()) {
734 refs.insert(subtreeRef);
747 for (
const auto &ref : it->second.subtreeRefs) {
748 if (reachable.insert(ref).second) {
763 std::set<std::string> mainTreeRefs;
767 "' hasMainTree=" + (hasMainTree ?
"true" :
"false") +
768 " treeInfoMap.size()=" + std::to_string(
m_treeInfoMap.size()));
772 LOG_INFO(
"Navigator",
"BuildFlatList: mainTreeRefs.size()=" + std::to_string(mainTreeRefs.size()));
775 std::vector<std::string> otherTrees;
777 if (hasMainTree && (pair.first ==
m_mainTreeId || mainTreeRefs.count(pair.first)))
779 otherTrees.push_back(pair.first);
781 std::sort(otherTrees.begin(), otherTrees.end());
785 header.
id =
"__section_main_tree__";
795 header.
sectionCount = 1 +
static_cast<int>(mainTreeRefs.size());
799 std::set<std::string> visited;
804 if (!otherTrees.empty()) {
806 header.
id =
"__section_other__";
816 header.
sectionCount =
static_cast<int>(otherTrees.size());
820 for (
size_t i = 0; i < otherTrees.size(); ++i) {
821 const auto &
id = otherTrees[i];
842 header.
id =
"__section_blackboards__";
856 std::vector<std::string> bbIds;
859 bbIds.push_back(kv.first);
861 std::sort(bbIds.begin(), bbIds.end());
863 for (
size_t i = 0; i < bbIds.size(); ++i) {
864 const auto &
id = bbIds[i];
869 e.
nodeCount = bb ?
static_cast<int>(bb->GetEntryCount()) : 0;
896 std::vector<bool> parentHasMore) {
904 bool isCircular = visited.find(treeId) != visited.end();
905 visited.insert(treeId);
908 bool hasKids = !isCircular && !info.
subtreeRefs.empty();
927 if (hasKids && !collapsed) {
929 std::sort(sortedRefs.begin(), sortedRefs.end());
931 std::vector<bool> childParentHasMore = parentHasMore;
932 childParentHasMore.push_back(!isLastChild);
934 for (
size_t i = 0; i < sortedRefs.size(); ++i) {
935 bool childIsLast = (i == sortedRefs.size() - 1);
936 FlattenTree(sortedRefs[i], depth + 1, visited, childIsLast, childParentHasMore);
940 visited.erase(treeId);
944 for (
size_t i = 0; i <
m_flatList.size(); ++i) {
946 return static_cast<int>(i);
961 if (firstVisible < m_treeListBox->GetItemCount()) {
980 if (firstVisible < m_treeListBox->GetItemCount()) {
1003 if (!pair.second.subtreeRefs.empty()) {
1029 wxString lower = query.Lower();
1032 wxString treeIdLower = wxString::FromUTF8(pair.first).Lower();
1033 if (treeIdLower.Find(lower) != wxNOT_FOUND) {
1042 static const size_t MAX_NODE_RESULTS = 200;
1043 size_t nodeResults = 0;
1045 if (!pair.second || !pair.second->HasRootNode() || nodeResults >= MAX_NODE_RESULTS)
1048 auto adapter = std::make_shared<EmberCore::DirectTreeAdapter>(pair.second);
1049 bool limitReached =
false;
1053 wxString nameLower = wxString(node->
GetName().c_str()).Lower();
1054 if (nameLower.Find(lower) != wxNOT_FOUND) {
1062 if (nodeResults >= MAX_NODE_RESULTS)
1063 limitReached =
true;
1088 menu.AppendSeparator();
1091 menu.AppendSeparator();
1115 mutableNode->
AddChild(std::move(newNode));
1124 int result = wxMessageBox(wxString::Format(
"Delete '%s' and all its children?", mutableNode->
GetName()),
1125 "Confirm Delete", wxYES_NO | wxICON_QUESTION,
this);
1126 if (result == wxYES) {
1131 CallAfter([
this]() {
1140 wxString currentName = mutableNode->
GetName();
1141 wxString newName = wxGetTextFromUser(
"Enter new name:",
"Rename Node", currentName,
this);
1142 if (!newName.IsEmpty() && newName != currentName) {
1143 mutableNode->
SetName(newName.ToStdString());
1144 CallAfter([
this]() {
1155 auto dup = mutableNode->
DeepCopy();
1156 dup->SetName(mutableNode->
GetName() +
" Copy");
1158 wxTheApp->CallAfter([
this]() {
1189 if (itemIndex < 0 || itemIndex >=
static_cast<int>(
m_flatList.size()))
1201 menu.AppendSeparator();
1204 if (entry.hasChildren) {
1205 if (entry.isCollapsed)
1216 if (resultIndex < 0 || resultIndex >=
static_cast<int>(results.size()))
1219 const auto &result = results[resultIndex];
BehaviorTreeProjectDialog::OnProjectNameChanged BehaviorTreeProjectDialog::OnRemoveFiles wxEND_EVENT_TABLE() BehaviorTreeProjectDialog
#define LOG_INFO(category, message)
MainFrame::OnExit MainFrame::OnNewProject MainFrame::OnCloseProject MainFrame::OnToggleMaximize MainFrame::OnPreviousScene MainFrame::OnPreferences MainFrame::OnEditorTool EVT_BUTTON(ID_MonitorTool, MainFrame::OnMonitorTool) EVT_PAINT(MainFrame
MainFrame::OnExit EVT_MENU(wxID_ABOUT, MainFrame::OnAbout) EVT_MENU(ID_NewProject
Abstract interface for tree nodes that can be visualized.
virtual size_t GetId() const =0
virtual const String & GetName() const =0
Adapter class that wraps the current Node implementation to work with ITreeNode interface.
Node * GetWrappedNode() const
static std::unique_ptr< Node > CreateActionNode(const String &name)
Represents a node in a behavior tree structure.
void RemoveChild(size_t index)
void SetName(const String &name)
String GetAttribute(const String &name, const String &default_value="") const
Node * GetChild(size_t index) const
const String & GetName() const
void AddChild(std::unique_ptr< Node > child)
size_t GetChildCount() const
std::unique_ptr< Node > DeepCopy() const
TreeHierarchyTab subclass with context menu and node activation hooks.
NodeActivatedHandler m_activatedHandler
void OnPopulateContextMenu(EmberCore::ITreeNode *node, wxMenu &menu) override
Hook to populate context menu for a node.
ContextMenuPopulator m_contextPopulator
void OnNodeActivated(EmberCore::ITreeNode *node) override
Hook called when a node is double-clicked/activated.
ContextMenuHandler m_contextHandler
void OnContextMenuCommand(int id, EmberCore::ITreeNode *node) override
Hook to handle context menu command.
NavigatorHierarchyView(wxWindow *parent, const EmberUI::TreeHierarchyConfig &config)
Main navigator tab with tree list, hierarchy view, search, and breadcrumb navigation.
void ToggleCollapse(const std::string &treeId)
std::vector< SearchResult > m_searchResults
std::string m_contextMenuTreeId
EmberCore::Node * GetMutableNode(EmberCore::ITreeNode *inode) const
NavigatorCallbacks m_callbacks
void OnHierarchyContextCommand(int id, EmberCore::ITreeNode *node)
NavigatorHierarchyView * m_hierarchyView
wxStaticText * m_breadcrumbLabel
TreeSelectionCallback m_treeSelectionCallback
void SetBlackboards(const std::map< std::string, std::shared_ptr< EmberCore::Blackboard > > &bbs)
std::function< void(EmberCore::ITreeNode *)> m_nodeSelectionCallback
std::string m_browsedTreeId
void ShowTreeContextMenu(int itemIndex, const wxPoint &pos)
bool m_mainTreeSectionCollapsed
std::vector< FlatEntry > m_flatList
void OnClosed() override
Called when the tab is closed.
void SetMainTreeId(const std::string &id)
SearchResultsListBox * m_searchListBox
wxStaticText * m_statusLabel
void OnTreeListContextExpand(wxCommandEvent &event)
bool IsCollapsed(const std::string &treeId) const
void PerformGlobalSearch(const wxString &query)
std::shared_ptr< EmberCore::ITreeStructure > m_activeTreeAdapter
ViewPage m_pageBeforeSearch
NavigatorTreeListBox * m_treeListBox
void OnTreeListContextBrowse(wxCommandEvent &event)
void SetCurrentTree(const std::string &treeId)
Sets the currently selected tree in the list.
void FlattenTree(const std::string &treeId, int depth, std::set< std::string > &visited, bool isLastChild, std::vector< bool > parentHasMore)
void OnHierarchySelectionChanged(EmberCore::ITreeNode *node)
bool IsSectionCollapsed(SectionType section) const
void SetNodeSelectionCallback(std::function< void(EmberCore::ITreeNode *)> cb)
void OnTreeListContextOpenNewScene(wxCommandEvent &event)
void SelectNodeById(size_t nodeId)
Selects the node with the given ID in the hierarchy.
std::map< std::string, std::shared_ptr< EmberCore::Blackboard > > m_blackboards
bool m_blackboardsSectionCollapsed
wxSearchCtrl * m_searchCtrl
void CollectAllReachableSubtrees(const std::string &treeId, std::set< std::string > &reachable) const
void OnBackClicked(wxCommandEvent &event)
void OnActivated() override
Called when the tab becomes active.
void OnSearchEnter(wxCommandEvent &event)
void SetActiveTree(std::shared_ptr< EmberCore::ITreeStructure > tree, const std::string &treeId)
Sets the active tree for hierarchy view and drills into it.
void OnSearchTextChanged(wxCommandEvent &event)
void OnHierarchyNodeActivated(EmberCore::ITreeNode *node)
void OnTreeListContextCollapse(wxCommandEvent &event)
void UpdateTreeList(const std::map< std::string, std::shared_ptr< EmberCore::BehaviorTree > > &trees, const std::string ¤tTreeId)
Updates tree list from project trees and sets current tree.
void CollectSubTreeRefs(EmberCore::Node *node, std::set< std::string > &refs) const
void SetBlackboardSelectionCallback(std::function< void(const std::string &)> cb)
void OnBlackboardClicked(const std::string &bbId)
wxPanel * m_breadcrumbPanel
std::map< std::string, std::shared_ptr< EmberCore::BehaviorTree > > m_projectTrees
std::string m_currentTreeId
std::set< std::string > m_collapsedTrees
void NavigateBack()
Navigates back in breadcrumb history.
void OnHierarchyContextMenu(EmberCore::ITreeNode *node, wxMenu &menu)
void DrillIntoTree(const std::string &treeId)
Drills into a subtree (updates breadcrumb and hierarchy).
int GetToggleHitX(int depth, bool isCurrent) const
std::map< std::string, TreeInfo > m_treeInfoMap
void OnTreeListContextViewInCurrent(wxCommandEvent &event)
void ToggleSectionCollapse(SectionType section)
virtual NavigatorHierarchyView * CreateHierarchyView(wxWindow *parent, const TreeHierarchyConfig &cfg)
NavigatorTab(wxWindow *parent)
void SetLayoutInvalidationCallback(std::function< void()> cb)
std::function< void(const std::string &)> m_blackboardSelectionCallback
int FindItemByTreeId(const std::string &treeId) const
bool m_otherTreesSectionCollapsed
void ShowSearchContextMenu(int resultIndex, const wxPoint &pos)
Virtual list box for the tree list (main tree, other trees, blackboards).
void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const override
wxCoord OnMeasureItem(size_t n) const override
void OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const override
void OnLeftDown(wxMouseEvent &event)
void OnRightDown(wxMouseEvent &event)
NavigatorTreeListBox(NavigatorTab *owner, wxWindow *parent, wxWindowID id)
void OnLeftDClick(wxMouseEvent &event)
Virtual list box for search results (trees and nodes).
void OnLeftDClick(wxMouseEvent &event)
void OnRightDown(wxMouseEvent &event)
void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const override
void OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const override
wxCoord OnMeasureItem(size_t n) const override
SearchResultsListBox(NavigatorTab *owner, wxWindow *parent, wxWindowID id)
TreeHierarchyTab(wxWindow *parent, const TreeHierarchyConfig &config={})
static const int TOGGLE_SIZE
static const int CONNECTOR_X_OFFSET
static const int INDENT_PX
static const int BASE_LEFT_PAD
wxBEGIN_EVENT_TABLE(Panel, wxPanel) EVT_SIZE(Panel
Flattened tree list entry for display in NavigatorTreeListBox.
std::vector< bool > parentHasMore
Search result (tree or node) for display in SearchResultsListBox.
std::set< std::string > subtreeRefs
Configuration for TreeHierarchyTab appearance and behavior.
bool autoExpandOnLoad
Auto-expand tree on load.
bool syncSelectionWithCanvas
Sync selection with canvas.
bool showSearchBar
Show search bar above tree.
int autoExpandDepth
Depth to auto-expand.
wxColour backgroundColour
Tree background.
wxColour panelColour
Panel background.