4#include <wx/filename.h>
15 : wxPanel(parent,
id), m_titleFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL),
16 m_typeFont(9, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL),
17 m_headerFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD) {
18 SetBackgroundStyle(wxBG_STYLE_PAINT);
19 SetBackgroundColour(m_config.background_color);
23 Bind(wxEVT_KEY_DOWN, [
this](wxKeyEvent &e) { OnKeyDown(e); });
24 Bind(wxEVT_KEY_UP, [
this](wxKeyEvent &e) { OnKeyUp(e); });
26 m_lastFrameTime = wxGetUTCTimeMillis();
28 m_refreshTimer =
new wxTimer(
this, wxID_ANY);
29 m_refreshTimer->Start(16);
31 Bind(wxEVT_TIMER, [
this](wxTimerEvent &) {
32 wxLongLong currentTime = wxGetUTCTimeMillis();
33 double deltaTime = (currentTime - m_lastFrameTime).ToDouble() / 1000.0;
34 m_lastFrameTime = currentTime;
39 if (m_config.enable_smooth_panning) {
40 float dx =
static_cast<float>(m_targetOffset.x - m_viewOffset.x);
41 float dy =
static_cast<float>(m_targetOffset.y - m_viewOffset.y);
43 if (std::abs(dx) > 0.5f || std::abs(dy) > 0.5f) {
44 float speed = std::max(5.0f, 100.0f - m_config.pan_smoothness);
45 float alpha = 1.0f - std::exp(-speed *
static_cast<float>(deltaTime));
46 int stepX =
static_cast<int>(dx * alpha);
47 int stepY =
static_cast<int>(dy * alpha);
48 if (stepX == 0 && dx != 0.0f)
49 stepX = dx > 0 ? 1 : -1;
50 if (stepY == 0 && dy != 0.0f)
51 stepY = dy > 0 ? 1 : -1;
52 m_viewOffset.x += stepX;
53 m_viewOffset.y += stepY;
56 m_viewOffset = m_targetOffset;
59 m_viewOffset = m_targetOffset;
81 wxString dir =
m_config.icon_directory;
82 if (!dir.EndsWith(
"/") && !dir.EndsWith(
"\\"))
90 IconEntry entries[] = {
98 int iconSize = std::max(8,
m_scaled.type_header_height - 4);
100 for (
const auto &entry : entries) {
101 wxString path = dir + entry.filename;
102 if (wxFileName::Exists(path)) {
103 wxImage img(path, wxBITMAP_TYPE_PNG);
105 if (img.GetWidth() != iconSize || img.GetHeight() != iconSize) {
106 img = img.Scale(iconSize, iconSize, wxIMAGE_QUALITY_BICUBIC);
154 if (relativeDepth <= 0 || node->GetChildCount() == 0) {
176 m_tree->GetNodeCount() >
static_cast<size_t>(
m_config.autoCollapseThreshold)) {
205 wxColour fill =
m_config.node_color;
215 wxColour border =
m_config.border_color;
216 int64_t nodeId =
static_cast<int64_t
>(node->
GetId());
227 border =
m_config.selected_border_color;
229 border =
m_config.hovered_border_color;
235 wxColour text =
m_config.text_color;
246 wxSize panel_size = GetSize();
255 wxSize panelSize = GetSize();
257 if (!
m_tree || !
m_tree->HasRootNode() || panelSize.GetWidth() <= 0) {
264 if (treeWidth <= panelSize.GetWidth()) {
269 m_zoomFactor = std::max(
m_minZoom,
static_cast<float>(panelSize.GetWidth()) /
static_cast<float>(treeWidth) * 0.9f);
279 wxSize panel_size = GetSize();
282 if (node_world_pos.x != -1 && node_world_pos.y != -1) {
283 wxPoint dest = {panel_size.x / 2 -
static_cast<int>(node_world_pos.x *
m_zoomFactor),
284 panel_size.y / 2 -
static_cast<int>(node_world_pos.y *
m_zoomFactor)};
285 if (
m_config.enable_smooth_panning) {
329 wxBufferedPaintDC dc(
this);
331 dc.SetBackground(wxBrush(
m_config.background_color));
335 dc.SetUserScale(1.0, 1.0);
336 dc.SetDeviceOrigin(0, 0);
337 wxSize sz = GetSize();
338 dc.SetTextForeground(wxColour(128, 128, 128));
339 dc.SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
340 wxString msg =
"Waiting for tree data...";
342 dc.GetTextExtent(msg, &tw, &th);
343 dc.DrawText(msg, (sz.x - tw) / 2, sz.y / 2 - th);
370 DrawNode(dc, effectiveRoot, root_pos.x, root_pos.y, 0, viewport);
375 dc.SetPen(wxPen(pathColor, 4));
377 dc.DrawLine(seg.x1, seg.y1, seg.x2, seg.y2);
382 dc.SetPen(wxPen(
m_config.path_highlight_color, 4));
384 dc.DrawLine(seg.x1, seg.y1, seg.x2, seg.y2);
417 wxPoint mouse_pos =
event.GetPosition();
424 int treeMinX = rootPos.x;
425 int treeMaxX = rootPos.x;
426 int treeMaxY = rootPos.y +
m_scaled.node_height;
429 int treeW = std::max(1, treeMaxX - treeMinX);
430 int treeH = std::max(1, treeMaxY - rootPos.y);
433 float scX =
static_cast<float>(
m_minimapRect.GetWidth() - 2 * pad) /
static_cast<float>(treeW);
434 float scY =
static_cast<float>(
m_minimapRect.GetHeight() - 2 * pad) /
static_cast<float>(treeH);
435 float sc = std::min(scX, scY);
438 static_cast<int>((
m_minimapRect.GetWidth() - 2 * pad - treeW * sc) / 2.0f);
441 float worldX = (mouse_pos.x - ofsX) / sc + treeMinX;
442 float worldY = (mouse_pos.y - ofsY) / sc + rootPos.y;
444 wxSize panelSize = GetSize();
454 if (bc.first.Contains(mouse_pos)) {
455 if (bc.second ==
nullptr) {
500 wxPoint mouse_pos =
event.GetPosition();
518 wxPoint mouse_pos =
event.GetPosition();
520 bool shouldPan =
false;
523 shouldPan =
event.LeftIsDown();
524 else if (
m_config.pan_mouse_button == 2)
525 shouldPan =
event.MiddleIsDown();
526 else if (
m_config.pan_mouse_button == 3)
527 shouldPan =
event.RightIsDown();
538 delta.x =
static_cast<int>(delta.x *
m_config.pan_sensitivity);
539 delta.y =
static_cast<int>(delta.y *
m_config.pan_sensitivity);
588 wxPoint mouse_screen =
event.GetPosition();
589 wxPoint mouse_world = {
static_cast<int>((mouse_screen.x -
m_viewOffset.x) / old_zoom),
590 static_cast<int>((mouse_screen.y -
m_viewOffset.y) / old_zoom)};
608 int keyCode =
event.GetKeyCode();
664 int keyCode =
event.GetKeyCode();
679 wxPoint mouse_pos =
event.GetPosition();
692 ID_EXPAND_ALL = wxID_HIGHEST + 100,
706 menu.Append(ID_EXPAND_ALL,
"Expand All");
707 menu.Append(ID_COLLAPSE_ALL,
"Collapse All");
708 menu.AppendSeparator();
709 menu.Append(ID_EXPAND_DEPTH_1,
"Expand to Depth 1");
710 menu.Append(ID_EXPAND_DEPTH_2,
"Expand to Depth 2");
711 menu.Append(ID_EXPAND_DEPTH_3,
"Expand to Depth 3");
712 menu.AppendSeparator();
713 menu.Append(ID_FOCUS_HERE,
"Focus Here");
714 menu.Append(ID_CENTER_NODE,
"Center on Node");
718 [
this, clicked_node](wxCommandEvent &) {
730 [
this, clicked_node](wxCommandEvent &) {
742 [
this, clicked_node](wxCommandEvent &) {
754 [
this, clicked_node](wxCommandEvent &) {
766 [
this, clicked_node](wxCommandEvent &) {
777 wxEVT_MENU, [
this, clicked_node](wxCommandEvent &) {
EnterFocusMode(clicked_node); }, ID_FOCUS_HERE);
780 wxEVT_MENU, [
this, clicked_node](wxCommandEvent &) {
CenterOnNode(clicked_node); }, ID_CENTER_NODE);
784 menu.Append(ID_EXPAND_ALL,
"Expand All");
785 menu.Append(ID_COLLAPSE_ALL,
"Collapse All");
786 menu.AppendSeparator();
787 menu.Append(ID_FIT_VIEW,
"Fit Tree in View");
788 menu.Append(ID_RESET_VIEW,
"Reset View");
792 [
this, root](wxCommandEvent &) {
805 [
this, root](wxCommandEvent &) {
817 wxEVT_MENU, [
this](wxCommandEvent &) {
FitTreeInView(); }, ID_FIT_VIEW);
820 wxEVT_MENU, [
this](wxCommandEvent &) {
ResetView(); }, ID_RESET_VIEW);
823 PopupMenu(&menu, mouse_pos);
829 dc.SetBrush(wxBrush(
m_config.background_color));
830 dc.SetPen(*wxTRANSPARENT_PEN);
831 dc.DrawRectangle(viewport);
833 dc.SetPen(wxPen(
m_config.grid_color, 1));
834 dc.SetBrush(*wxTRANSPARENT_BRUSH);
836 int gridSpacing =
m_scaled.grid_size;
838 gridSpacing =
m_scaled.grid_size * 4;
840 gridSpacing =
m_scaled.grid_size * 2;
842 gridSpacing =
m_scaled.grid_size / 2;
844 int startX = (viewport.GetLeft() / gridSpacing) * gridSpacing;
845 for (
int x = startX; x <= viewport.GetRight(); x += gridSpacing) {
846 dc.DrawLine(x, viewport.GetTop(), x, viewport.GetBottom());
849 int startY = (viewport.GetTop() / gridSpacing) * gridSpacing;
850 for (
int y = startY; y <= viewport.GetBottom(); y += gridSpacing) {
851 dc.DrawLine(viewport.GetLeft(), y, viewport.GetRight(), y);
856 const wxRect &viewport) {
863 if (y > viewport.GetBottom() + margin) {
869 if (
m_config.enable_viewport_culling) {
870 if (x + subtreeWidth / 2 < viewport.GetLeft() - margin || x - subtreeWidth / 2 > viewport.GetRight() + margin) {
875 bool nodeVisible = (y +
m_scaled.node_height >= viewport.GetTop() - margin);
891 int total_width = subtreeWidth;
892 wxCoord child_x = x - total_width / 2;
899 wxCoord center_x = child_x + child_subtree_width / 2;
900 DrawNode(dc, child, center_x, child_y, level + 1, viewport);
901 child_x += child_subtree_width;
914 int border_width = 1;
915 int64_t nodeId =
static_cast<int64_t
>(node->
GetId());
925 dc.SetBrush(wxBrush(fill_color));
926 dc.SetPen(wxPen(border_color, border_width));
931 dc.DrawRectangle(node_rect);
933 dc.DrawRoundedRectangle(node_rect, 5);
941 int headerH =
m_scaled.type_header_height;
942 wxRect headerRect(nodeRect.GetLeft() + 1, nodeRect.GetTop() + 1, nodeRect.GetWidth() - 2, headerH);
944 dc.SetBrush(wxBrush(typeColor));
945 dc.SetPen(*wxTRANSPARENT_PEN);
946 dc.DrawRectangle(headerRect);
951 int contentX = headerRect.GetLeft() + 4;
952 int contentCenterY = headerRect.GetTop() + headerH / 2;
956 const wxBitmap &icon = iconIt->second;
957 int iconH = icon.GetHeight();
959 double savedScaleX, savedScaleY;
960 dc.GetUserScale(&savedScaleX, &savedScaleY);
961 wxPoint savedOrigin = dc.GetDeviceOrigin();
963 dc.SetUserScale(1.0, 1.0);
964 dc.SetDeviceOrigin(0, 0);
966 int screenX =
static_cast<int>(contentX * savedScaleX) + savedOrigin.x;
967 int screenY =
static_cast<int>((contentCenterY - iconH / 2) * savedScaleY) + savedOrigin.y;
969 dc.DrawBitmap(icon, screenX, screenY,
true);
971 dc.SetUserScale(savedScaleX, savedScaleY);
972 dc.SetDeviceOrigin(savedOrigin.x, savedOrigin.y);
974 contentX +=
static_cast<int>(icon.GetWidth() / savedScaleX) + 3;
979 dc.SetTextForeground(
m_config.type_header_text_color);
983 dc.GetTextExtent(typeStr, &tw, &th);
985 int maxTextW = headerRect.GetRight() - contentX - 4;
986 if (tw > maxTextW && maxTextW > 0) {
987 size_t lo = 0, hi = typeStr.Length();
989 size_t mid = (lo + hi + 1) / 2;
990 dc.GetTextExtent(typeStr.Left(mid) +
"..", &tw, &th);
996 typeStr = (lo > 0 ? typeStr.Left(lo) : wxString()) +
"..";
997 dc.GetTextExtent(typeStr, &tw, &th);
1000 dc.DrawText(typeStr, contentX, contentCenterY - th / 2);
1011 dc.SetTextForeground(text_color);
1013 int textStartY = y +
m_scaled.type_header_height + 2;
1018 dc.GetTextExtent(text, &tw, &th);
1020 int maxW =
m_scaled.node_width - 20;
1021 if (tw >
m_scaled.node_width - 10 && maxW > 0) {
1022 size_t lo = 0, hi = text.length();
1024 size_t mid = (lo + hi + 1) / 2;
1026 dc.GetTextExtent(wxString(text.substr(0, mid) +
"..."), &mw, &mh);
1033 dc.GetTextExtent(text, &tw, &th);
1036 dc.DrawText(text, x - tw / 2, textStartY + 2);
1046 int64_t parentId =
static_cast<int64_t
>(node->
GetId());
1050 dc.SetPen(wxPen(
m_config.connection_color, 2));
1051 dc.DrawLine(x, y +
m_scaled.node_height, x, child_y - 10);
1053 if (parentInExecPath)
1055 if (parentInSelectedPath)
1058 wxCoord child_x = x - total_width / 2;
1064 wxCoord center_x = child_x + subtree_width / 2;
1066 int64_t childId =
static_cast<int64_t
>(child->
GetId());
1070 dc.SetPen(wxPen(
m_config.connection_color, 2));
1071 dc.DrawLine(x, child_y - 10, center_x, child_y - 10);
1072 dc.DrawLine(center_x, child_y - 10, center_x, child_y);
1074 if (parentInExecPath && childInExecPath) {
1078 if (parentInSelectedPath && childInSelectedPath) {
1083 child_x += subtree_width;
1095 int circle_radius = 8;
1096 wxCoord arrow_x = x;
1097 wxCoord arrow_y = y +
m_scaled.node_height;
1099 wxColour circleFill(70, 70, 70), circlePen(100, 100, 100);
1101 circleFill = wxColour(50, 50, 50);
1102 circlePen = wxColour(80, 80, 80);
1103 }
else if (isHovered) {
1104 circleFill = wxColour(90, 90, 90);
1105 circlePen = wxColour(130, 130, 130);
1107 dc.SetBrush(wxBrush(circleFill));
1108 dc.SetPen(wxPen(circlePen, 1));
1109 dc.DrawCircle(arrow_x, arrow_y, circle_radius);
1111 wxPoint triangle[3];
1112 wxColour triFill, triPen;
1116 triangle[0] = {arrow_x - sz, arrow_y - 2};
1117 triangle[1] = {arrow_x + sz, arrow_y - 2};
1118 triangle[2] = {arrow_x, arrow_y + 3};
1119 triFill = isPressed ? wxColour(100, 180, 235) : (isHovered ? wxColour(150, 220, 255) : wxColour(120, 200, 255));
1123 triangle[0] = {arrow_x - 2, arrow_y - sz};
1124 triangle[1] = {arrow_x - 2, arrow_y + sz};
1125 triangle[2] = {arrow_x + 3, arrow_y};
1126 triFill = isPressed ? wxColour(140, 140, 140) : (isHovered ? wxColour(220, 220, 220) : wxColour(180, 180, 180));
1129 dc.SetPen(wxPen(triPen, 1));
1130 dc.SetBrush(wxBrush(triFill));
1131 dc.DrawPolygon(3, triangle);
1138 dc.SetUserScale(1.0, 1.0);
1139 dc.SetDeviceOrigin(0, 0);
1141 wxSize panel_size = GetSize();
1145 dc.SetTextForeground(wxColour(150, 150, 150));
1146 dc.SetFont(wxFont(9, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1151 zoomText +=
" | FOCUS MODE";
1153 dc.DrawText(zoomText, 10, topOffset);
1158 dc.DrawText(selectedText, 10, topOffset + 18);
1162 wxString statusNames[] = {
"Idle",
"Running",
"Success",
"Failure",
"Halted"};
1163 wxString statusText =
"Status: " + (status >= 0 && status <= 4 ? statusNames[status] :
"Unknown");
1164 dc.DrawText(statusText, 10, topOffset + 36);
1169 wxString statsText = wxString::Format(
"Nodes: %lu",
static_cast<unsigned long>(
m_tree->GetNodeCount()));
1171 dc.GetTextExtent(statsText, &tw, &th);
1172 dc.DrawText(statsText, panel_size.x - tw - 10, topOffset);
1175 dc.SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1176 dc.SetTextForeground(wxColour(100, 100, 100));
1177 wxString hints =
"Click=Select | DblClick=Center | Wheel=Zoom | "
1178 "R=Reset | E=Collapse | F=Focus";
1180 hints +=
" | Esc=Exit Focus";
1181 dc.DrawText(hints, 10, panel_size.y - 20);
1210 return m_tree->GetRootNode();
1219 return m_config.control_type_color;
1221 return m_config.condition_type_color;
1223 return m_config.decorator_type_color;
1225 return m_config.behavior_tree_type_color;
1235 int halfW =
m_scaled.node_width / 2;
1236 if (x - halfW < minX)
1238 if (x + halfW > maxX)
1240 if (y +
m_scaled.node_height > maxY)
1245 wxCoord cx = x - totalW / 2;
1262 dc.SetUserScale(1.0, 1.0);
1263 dc.SetDeviceOrigin(0, 0);
1265 wxSize panelSize = GetSize();
1266 int mmW = 200, mmH = 150;
1268 int mmY = panelSize.y - mmH - 30;
1277 memDC.SetBackground(wxBrush(wxColour(30, 30, 30)));
1283 int treeMinX = rootPos.x;
1284 int treeMaxX = rootPos.x;
1285 int treeMaxY = rootPos.y +
m_scaled.node_height;
1288 int treeW = std::max(1, treeMaxX - treeMinX);
1289 int treeH = std::max(1, treeMaxY - rootPos.y);
1292 float scaleX =
static_cast<float>(mmW - 2 * pad) /
static_cast<float>(treeW);
1293 float scaleY =
static_cast<float>(mmH - 2 * pad) /
static_cast<float>(treeH);
1294 float scale = std::min(scaleX, scaleY);
1296 int ofsX = pad +
static_cast<int>((mmW - 2 * pad - treeW * scale) / 2.0f);
1299 DrawMinimapNode(memDC, root, rootPos.x, rootPos.y, scale, scale, ofsX -
static_cast<int>(treeMinX * scale),
1300 ofsY -
static_cast<int>(rootPos.y * scale), wxRect(0, 0, mmW, mmH));
1309 memDC.SelectObject(wxNullBitmap);
1313 dc.SetPen(wxPen(wxColour(80, 80, 80), 1));
1314 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1326 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1327 dc.SetPen(wxPen(wxColour(255, 255, 255, 180), 1));
1328 dc.DrawRectangle(std::max(vx, mmX), std::max(vy, mmY), std::min(vw, mmW), std::min(vh, mmH));
1332 int offsetX,
int offsetY,
const wxRect &minimapArea) {
1336 int sx = offsetX +
static_cast<int>(x * scaleX);
1337 int sy = offsetY +
static_cast<int>(y * scaleY);
1338 int sw = std::max(2,
static_cast<int>(
m_scaled.node_width * scaleX));
1339 int sh = std::max(2,
static_cast<int>(
m_scaled.node_height * scaleY));
1349 dc.SetBrush(wxBrush(fill));
1350 dc.SetPen(*wxTRANSPARENT_PEN);
1351 dc.DrawRectangle(sx - sw / 2, sy, sw, sh);
1355 wxCoord cx = x - totalW / 2;
1361 DrawMinimapNode(dc, child, cx + csw / 2, cy, scaleX, scaleY, offsetX, offsetY, minimapArea);
1372 dc.SetUserScale(1.0, 1.0);
1373 dc.SetDeviceOrigin(0, 0);
1375 wxSize panelSize = GetSize();
1378 dc.SetBrush(wxBrush(wxColour(35, 35, 35, 220)));
1379 dc.SetPen(*wxTRANSPARENT_PEN);
1380 dc.DrawRectangle(0, 0, panelSize.x, barH);
1382 std::vector<EmberCore::ITreeNode *> ancestors;
1385 ancestors.push_back(curr);
1388 std::reverse(ancestors.begin(), ancestors.end());
1391 bool foundFocus =
false;
1392 std::vector<EmberCore::ITreeNode *> trimmed;
1393 for (
auto *a : ancestors) {
1397 trimmed.push_back(a);
1399 if (!trimmed.empty())
1400 ancestors = trimmed;
1405 wxFont bcFont(9, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1409 int yCenter = barH / 2;
1412 wxString label =
"Full Tree";
1414 dc.GetTextExtent(label, &tw, &th);
1416 dc.SetTextForeground(wxColour(100, 180, 255));
1417 dc.DrawText(label, xPos, yCenter - th / 2);
1421 dc.SetTextForeground(wxColour(100, 100, 100));
1422 dc.DrawText(
" > ", xPos, yCenter - th / 2);
1426 for (
size_t i = 0; i < ancestors.size(); ++i) {
1428 bool isLast = (i == ancestors.size() - 1);
1430 wxString name = node->
GetName();
1431 if (name.length() > 20)
1432 name = name.Left(17) +
"...";
1435 dc.GetTextExtent(name, &tw, &th);
1437 if (xPos + tw > panelSize.x - 20) {
1438 dc.SetTextForeground(wxColour(100, 100, 100));
1439 dc.DrawText(
"...", xPos, yCenter - th / 2);
1443 dc.SetTextForeground(isLast ? wxColour(255, 255, 255) : wxColour(150, 200, 255));
1444 dc.DrawText(name, xPos, yCenter - th / 2);
1449 dc.SetTextForeground(wxColour(100, 100, 100));
1450 dc.DrawText(
" > ", xPos, yCenter - th / 2);
1471 int total_width = 0;
1478 width = std::max(total_width,
m_scaled.node_width +
m_scaled.horizontal_spacing);
1493 if (node_rect.Contains(target_pos)) {
1499 wxCoord child_x = node_pos.x - total_width / 2;
1500 wxCoord child_y = node_pos.y +
m_scaled.node_height +
m_scaled.vertical_spacing;
1506 wxCoord center_x = child_x + subtree_width / 2;
1513 child_x += subtree_width;
1522 const int hit_radius = 10;
1524 wxCoord arrow_x = it->x;
1525 wxCoord arrow_y = it->y +
m_scaled.node_height;
1526 int dx = world_pos.x - arrow_x;
1527 int dy = world_pos.y - arrow_y;
1528 if (dx * dx + dy * dy <= hit_radius * hit_radius)
1536 if (!target_node || !root)
1549 wxCoord child_x = node_pos.x - total_width / 2;
1550 wxCoord child_y = node_pos.y +
m_scaled.node_height +
m_scaled.vertical_spacing;
1556 wxCoord center_x = child_x + subtree_width / 2;
1558 wxPoint found = findPosition(child, {center_x, child_y}, level + 1);
1559 if (found.x != -1 && found.y != -1) {
1563 child_x += subtree_width;
1572 return findPosition(root, root_pos, 0);
1586 wxSize panel_size = GetSize();
1588 wxPoint bottomRight =
ScreenToWorld({panel_size.x, panel_size.y});
1589 return wxRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
BehaviorTreeProjectDialog::OnProjectNameChanged BehaviorTreeProjectDialog::OnRemoveFiles wxEND_EVENT_TABLE() BehaviorTreeProjectDialog
Abstract interface for tree nodes that can be visualized.
virtual String GetTypeString() const
virtual size_t GetId() const =0
virtual size_t GetChildCount() const =0
virtual NodeType GetType() const =0
virtual ITreeNode * GetParent() const =0
virtual bool AreChildrenVisible() const =0
virtual void SetChildrenVisible(bool visible)=0
virtual const String & GetName() const =0
virtual ITreeNode * GetChild(size_t index) const =0
NodeType
Node types for visualization categorization.
Shared tree rendering canvas usable by both EmberForge and EmberMonitor.
void CenterOnNode(EmberCore::ITreeNode *node)
Centers the view on the given node.
void DrawOverlayInfo(wxDC &dc)
void OnMouseMotion(wxMouseEvent &event)
void DrawMinimapNode(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y, float scaleX, float scaleY, int offsetX, int offsetY, const wxRect &minimapArea)
std::set< int64_t > m_executionPathIds
virtual ~TreeCanvas()
Destructor.
void OnMouseWheel(wxMouseEvent &event)
wxPoint FindNodeWorldPosition(EmberCore::ITreeNode *target_node)
void EnterFocusMode(EmberCore::ITreeNode *node)
Enters focus mode, showing only the subtree rooted at the given node.
std::unordered_map< EmberCore::ITreeNode *, int > m_widthCache
std::vector< CollapseArrowInfo > m_collapseArrows
EmberCore::ITreeNode * FindNodeAtPosition(EmberCore::ITreeNode *node, wxPoint node_pos, wxPoint target_pos)
std::shared_ptr< EmberCore::ITreeStructure > m_tree
void OnSize(wxSizeEvent &event)
void DrawTypeHeader(wxDC &dc, EmberCore::ITreeNode *node, const wxRect &nodeRect)
EmberCore::ITreeNode * m_hoveredArrowNode
virtual void OnNodeSelected(EmberCore::ITreeNode *node)
Called when a node is selected; override for app-specific behavior.
wxColour GetNodeTypeColor(EmberCore::ITreeNode::NodeType type) const
int m_minimapCacheTreeMinX
void OnMouseLeftDClick(wxMouseEvent &event)
void OnMouseRightUp(wxMouseEvent &event)
TreeCanvasConfig m_scaled
std::map< EmberCore::ITreeNode::NodeType, wxBitmap > m_typeIcons
void DrawNodeText(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y)
void DrawGrid(wxDC &dc, const wxRect &viewport)
void DrawNodeBox(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y)
virtual void OnKeyUp(wxKeyEvent &event)
Key up handler; override for custom key handling.
void DrawNodeConnections(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y)
std::vector< std::pair< wxRect, EmberCore::ITreeNode * > > m_breadcrumbHitTargets
wxPoint WorldToScreen(const wxPoint &world_pos) const
void ExpandAllChildren(EmberCore::ITreeNode *node)
void SetTree(std::shared_ptr< EmberCore::ITreeStructure > tree)
Sets the tree structure to display.
virtual wxColour GetNodeFillColor(EmberCore::ITreeNode *node, bool selected, bool hovered)
Returns the fill color for a node; override for custom coloring.
void OnMouseMiddleUp(wxMouseEvent &event)
void DrawBreadcrumb(wxDC &dc)
void AutoCollapseTree(EmberCore::ITreeNode *node, int depth)
float m_minimapCacheScale
void ResetView()
Resets zoom and pan to default values.
virtual void OnKeyDown(wxKeyEvent &event)
Key down handler; override for custom key handling.
EmberCore::ITreeNode * m_hoveredNode
NodeSelectionCallback m_selectionCallback
void UpdateScaledConfig()
void MarkDirty()
Marks the canvas for repaint.
std::vector< LineSegment > m_selectedPathSegments
void BuildPathToSelected()
void DrawNode(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y, int level, const wxRect &viewport)
EmberCore::ITreeNode * m_selectedNode
void LoadTypeIcons()
Loads type icons from the configured icon directory.
VisibilityChangeCallback m_visibilityChangeCallback
void ExpandToDepth(EmberCore::ITreeNode *node, int relativeDepth)
void SetZoom(float zoom)
Sets the zoom factor.
TreeCanvasConfig m_config
void OnMouseLeftUp(wxMouseEvent &event)
virtual wxColour GetNodeBorderColor(EmberCore::ITreeNode *node, bool selected, bool hovered)
Returns the border color for a node; override for custom coloring.
void CollapseAllChildren(EmberCore::ITreeNode *node)
void ComputeTreeExtent(EmberCore::ITreeNode *node, wxCoord x, wxCoord y, int &minX, int &maxX, int &maxY)
IStatusProvider * m_statusProvider
EmberCore::ITreeNode * GetEffectiveRoot() const
std::set< int > m_pathToSelectedIds
void FitTreeInView()
Adjusts view to fit the entire tree.
void DrawMinimap(wxDC &dc)
wxPoint CalculateRootPosition()
EmberCore::ITreeNode * m_focusRoot
wxPoint ScreenToWorld(const wxPoint &screen_pos) const
void OnPaint(wxPaintEvent &event)
void ExitFocusMode()
Exits focus mode.
int CalculateSubtreeWidth(EmberCore::ITreeNode *node)
void OnMouseMiddleDown(wxMouseEvent &event)
void OnMouseLeftDown(wxMouseEvent &event)
virtual void OnBeforePaintOverlays(wxDC &dc)
Called before painting overlays; override to draw custom overlays.
EmberCore::ITreeNode * m_pressedArrowNode
wxRect GetViewportBounds() const
void SetSelectedNode(EmberCore::ITreeNode *node)
Sets the selected node.
virtual wxColour GetNodeTextColor(EmberCore::ITreeNode *node, bool selected, bool hovered)
Returns the text color for a node; override for custom coloring.
std::vector< LineSegment > m_highlightedSegments
void DrawCollapseArrow(wxDC &dc, EmberCore::ITreeNode *node, wxCoord x, wxCoord y)
EmberCore::ITreeNode * FindArrowAtPosition(const wxPoint &world_pos) const
std::string String
Framework-agnostic string type.
int Scale(wxWindow *win, int px)
Scales a pixel value from logical to physical units for the given window.
wxBEGIN_EVENT_TABLE(Panel, wxPanel) EVT_SIZE(Panel
static wxColour GetExecutionPathColor()
Color for nodes in the execution path.
static wxColour GetBorderColor(int status)
Returns border color for the given status code.
static wxColour GetTextColor(int status)
Returns text color for the given status code.
static wxColour GetFillColor(int status)
Returns fill color for the given status code.