TachyonUI 0.1.0
High-performance UI for building tools.
Loading...
Searching...
No Matches
gui_tree_utils.h
Go to the documentation of this file.
1#pragma once
2
3// Flat-list tree helpers shared by VirtualTreeView / tree-table views: rows
4// are a flat vector in depth-first order, each row carrying its depth.
5// RebuildTreeIndex derives skipCount / ancestorLineMask / isLastSibling;
6// ReorderTreeRow moves a subtree (with reparenting) for drag-drop.
7
8#include "gui_application.h"
9
10#include <concepts>
11#include <iterator>
12#include <stdint.h>
13#include <vector>
14
16{
19
21 template <typename Row>
22 concept TreeRowLike = requires(Row r)
23 {
24 { r.depth } -> std::convertible_to<int>;
25 { r.skipCount } -> std::convertible_to<int>;
26 { r.isOpen } -> std::convertible_to<bool>;
27 { r.ancestorLineMask } -> std::convertible_to<uint32_t>;
28 { r.isLastSibling } -> std::convertible_to<bool>;
29 };
30
34 template <TreeRowLike Row>
35 void RebuildTreeIndex(std::vector<Row>& rows)
36 {
37 const int n = static_cast<int>(rows.size());
38 for (int i = 0; i < n; i++)
39 {
40 const int D = rows[i].depth;
41
42 int j = i + 1;
43 while (j < n && rows[j].depth > D) j++;
44 rows[i].skipCount = j - i - 1;
45
46 uint32_t mask = 0;
47 for (int d = 0; d <= D - 2 && d < 32; d++) // the mask is 32 bits wide
48 {
49 const int target = d + 1;
50 for (int k = i + 1; k < n; k++)
51 {
52 if (rows[k].depth < target) break;
53 if (rows[k].depth == target) { mask |= (1u << d); break; }
54 }
55 }
56 rows[i].ancestorLineMask = mask;
57
58 bool last = true;
59 for (int k = i + 1; k < n; k++)
60 {
61 if (rows[k].depth < D) break;
62 if (rows[k].depth == D) { last = false; break; }
63 }
64 rows[i].isLastSibling = last;
65 }
66 }
67
72 template <TreeRowLike Row>
73 int ReorderTreeRow(std::vector<Row>& rows, int srcDs, int dstDs,
75 {
76 const int n = static_cast<int>(rows.size());
77 if (srcDs < 0 || srcDs >= n) return -1;
78 if (dstDs < 0 || dstDs >= n) return -1;
79 if (srcDs == dstDs) return -1;
80
81 const int srcStart = srcDs;
82 const int srcSliceCount = 1 + rows[srcStart].skipCount;
83 const int srcEnd = srcStart + srcSliceCount;
84
85 // Cycle guard: dropping into the source's own subtree is invalid.
86 if (dstDs >= srcStart && dstDs < srcEnd) return -1;
87
88 int targetBaseDepth = rows[dstDs].depth;
89 int insertAt = dstDs;
90 switch (pos)
91 {
93 insertAt = dstDs;
94 targetBaseDepth = rows[dstDs].depth;
95 break;
97 // First child: splice in right after the parent row.
98 insertAt = dstDs + 1;
99 targetBaseDepth = rows[dstDs].depth + 1;
100 rows[dstDs].isOpen = true;
101 break;
103 // Last child: splice in past the parent's whole subtree.
104 insertAt = dstDs + 1 + rows[dstDs].skipCount;
105 targetBaseDepth = rows[dstDs].depth + 1;
106 rows[dstDs].isOpen = true;
107 break;
109 insertAt = dstDs + 1 + rows[dstDs].skipCount;
110 targetBaseDepth = rows[dstDs].depth;
111 break;
112 default: return -1;
113 }
114
115 // Lift the source slice into a temp, adjust depth on each lifted row.
116 const int srcBaseDepth = rows[srcStart].depth;
117 const int deltaDepth = targetBaseDepth - srcBaseDepth;
118 std::vector<Row> lifted;
119 lifted.reserve(srcSliceCount);
120 lifted.insert(lifted.end(),
121 std::make_move_iterator(rows.begin() + srcStart),
122 std::make_move_iterator(rows.begin() + srcEnd));
123 if (deltaDepth != 0)
124 for (auto& r : lifted) r.depth += deltaDepth;
125
126 rows.erase(rows.begin() + srcStart, rows.begin() + srcEnd);
127 if (insertAt > srcStart) insertAt -= srcSliceCount;
128 rows.insert(rows.begin() + insertAt,
129 std::make_move_iterator(lifted.begin()),
130 std::make_move_iterator(lifted.end()));
131 return insertAt; // new flat index of the moved row's root
132 }
133
134}
A flat tree row: the caller sets depth and isOpen; RebuildTreeIndex derives the rest.
Definition gui_tree_utils.h:22
int ReorderTreeRow(std::vector< Row > &rows, int srcDs, int dstDs, tachyon::TreeDropPosition pos)
Moves the subtree rooted at flat index srcDs to pos relative to row dstDs, shifting depths when the t...
Definition gui_tree_utils.h:73
void RebuildTreeIndex(std::vector< Row > &rows)
Recomputes the derived fields (skipCount, ancestorLineMask, isLastSibling) from each row's depth.
Definition gui_tree_utils.h:35
TreeDropPosition
Where a tree reorder drop lands relative to the target row.
Definition gui_widget_state_types.h:44
@ InsideLast
last child of the target (rectangle around the row)
Definition gui_widget_state_types.h:48
@ After
next sibling of the target (line at the row bottom)
Definition gui_widget_state_types.h:49
@ Before
previous sibling of the target (line at the row top)
Definition gui_widget_state_types.h:46
@ Inside
first child of an open target branch (indented line at the row bottom)
Definition gui_widget_state_types.h:47
@ Row
Horizontal, left to right.
Definition gui_style.h:165
Definition gui_tree_utils.h:16