TachyonUI 0.1.0
High-performance UI for building tools.
Loading...
Searching...
No Matches
gui_window_registry.h
Go to the documentation of this file.
1#pragma once
2
3#include <gui_core/elements/gui_element.h> // ElementRef (member by value, used inline)
4#include <gui_core/gui_id.h> // ID (member by value, map key)
5
6#include <algorithm>
7#include <stdint.h>
8#include <vector>
9#include <unordered_map>
10
11namespace tachyon
12{
15
23
33 {
34 static constexpr uint32_t kInitialWindowCapacity = 16;
35 static constexpr uint32_t kInitialWindowStackCapacity = 8;
36 public:
37 static constexpr uint32_t kInvalidIndex = ~(uint32_t)0;
38
42 {
44 uint32_t subtreeSkip = 0;
45 uint32_t _pad = 0;
46 };
47
50 {
54
55 std::vector<RenderNode> members;
56 };
57
59 {
60 m_windows.reserve(kInitialWindowCapacity);
61 m_windowStack.reserve(kInitialWindowStackCapacity);
62 }
63
65 void Clear()
66 {
67 // Keep each window's member list so its capacity is reused next frame
68 for (auto& window : m_windows)
69 {
70 window.members.clear();
71 m_spareMembers.push_back(std::move(window.members));
72 }
73 m_windows.clear();
74 m_windowStack.clear();
75 m_windowIDToIndexMap.clear();
76 }
77
79 uint32_t WindowCount() const { return static_cast<uint32_t>(m_windows.size()); }
80
82 const ID GetWindowID(uint32_t windowIndex) const
83 {
84 if (windowIndex >= m_windows.size())
85 return ID::Invalid();
86 return m_windows[windowIndex].id;
87 }
88
90 ElementRef GetWindowRef(uint32_t windowIndex) const
91 {
92 if (windowIndex >= m_windows.size()) return ElementRef::Invalid();
93 return m_windows[windowIndex].windowRef;
94 }
95
98 {
99 auto it = m_windowIDToIndexMap.find(id);
100 if (it == m_windowIDToIndexMap.end())
101 return ElementRef::Invalid();
102 return m_windows[it->second].windowRef;
103 }
104
106 uint32_t GetWindowIndex(ID id) const
107 {
108 auto it = m_windowIDToIndexMap.find(id);
109 if (it == m_windowIDToIndexMap.end())
110 return kInvalidIndex;
111 return it->second;
112 }
113
115 inline bool Contains(ID id) const
116 {
117 auto it = m_windowIDToIndexMap.find(id);
118 return it != m_windowIDToIndexMap.end();
119 }
120
123 const std::vector<RenderNode>* GetWindowMembers(uint32_t windowIndex) const
124 {
125 if (windowIndex >= m_windows.size()) return nullptr;
126 return &m_windows[windowIndex].members;
127 }
128
130 uint32_t GetWindowStackDepth() const
131 {
132 return static_cast<uint32_t>(m_windowStack.size());
133 }
134
137 {
138 return m_windowStack.empty() ? ID::Invalid() : GetWindowID(m_windowStack.back());
139 }
140
142 bool HasActiveWindow() const
143 {
144 return !m_windowStack.empty();
145 }
146
150 {
151 if (!ref.IsValid()) return RegistryResult::InvalidRef;
152
153 // Create a new window entry
154 uint32_t newIndex = static_cast<uint32_t>(m_windows.size());
155 std::vector<RenderNode> members;
156 if (!m_spareMembers.empty())
157 {
158 members = std::move(m_spareMembers.back());
159 m_spareMembers.pop_back();
160 }
161 m_windows.push_back(
163 {
164 .parentWindowEntry = m_activeWindowIndex,
165 .id = id,
166 .windowRef = ref,
167 .members = std::move(members)
168 });
169 m_windowIDToIndexMap[id] = newIndex;
170
171 // Become the active window (supports nested windows)
172 m_windowStack.push_back(newIndex);
173 m_activeWindowIndex = newIndex;
175 }
176
179 {
180 if (!ref.IsValid()) return RegistryResult::InvalidRef;
181 if (m_windowStack.empty()) return RegistryResult::NoActiveWindow;
182
183 m_windows[m_activeWindowIndex].members.push_back(RenderNode{ ref, 0, 0 });
185 }
186
189 void MoveSubtreeToEnd(const ElementRef& nodeRef)
190 {
191 if (m_windowStack.empty()) return;
192 auto& members = m_windows[m_activeWindowIndex].members;
193
194 // Find the node in the members list
195 uint32_t nodeStart = UINT32_MAX;
196 for (uint32_t i = 0; i < static_cast<uint32_t>(members.size()); i++)
197 {
198 if (members[i].ref == nodeRef) { nodeStart = i; break; }
199 }
200 if (nodeStart == UINT32_MAX) return;
201
202 uint32_t nodeDepth = nodeRef.depth;
203
204 // Find the end of this node's subtree: next node at same or shallower depth
205 uint32_t subtreeEnd = nodeStart + 1;
206 while (subtreeEnd < members.size() && members[subtreeEnd].ref.depth > nodeDepth)
207 subtreeEnd++;
208
209 // Find the end of the sibling group: scan forward past all remaining siblings
210 // and their subtrees (any node at depth >= nodeDepth belongs to the group)
211 uint32_t siblingGroupEnd = subtreeEnd;
212 while (siblingGroupEnd < members.size() && members[siblingGroupEnd].ref.depth >= nodeDepth)
213 siblingGroupEnd++;
214
215 // Rotate [nodeStart, subtreeEnd) to just before siblingGroupEnd
216 if (subtreeEnd < siblingGroupEnd)
217 std::rotate(members.begin() + nodeStart, members.begin() + subtreeEnd, members.begin() + siblingGroupEnd);
218 }
219
223 {
224 for (auto& window : m_windows)
225 {
226 auto& members = window.members;
227 for (RenderNode& node : members)
228 node.ref = move.Remap(node.ref);
229
230 auto isMovedSibling = [&](const RenderNode& node)
231 {
232 return node.ref.depth == move.childDepth &&
233 node.ref.index >= move.siblingsBegin && node.ref.index < move.siblingsEnd;
234 };
235 auto first = std::find_if(members.begin(), members.end(), isMovedSibling);
236 if (first == members.end())
237 continue;
238 auto last = std::find_if(members.rbegin(), members.rend(), isMovedSibling).base();
239 while (last != members.end() && last->ref.depth > move.childDepth)
240 ++last; // include the last sibling's subtree
241
242 // Split the region into sibling subtrees and put them in slot order.
243 struct Chunk { uint32_t index; size_t begin, end; };
244 std::vector<Chunk> chunks;
245 for (auto it = first; it != last; )
246 {
247 auto end = it + 1;
248 while (end != last && end->ref.depth > move.childDepth)
249 ++end;
250 chunks.push_back({ it->ref.index, size_t(it - members.begin()), size_t(end - members.begin()) });
251 it = end;
252 }
253 std::stable_sort(chunks.begin(), chunks.end(), [](const Chunk& a, const Chunk& b) { return a.index < b.index; });
254
255 std::vector<RenderNode> reordered;
256 reordered.reserve(size_t(last - first));
257 for (const Chunk& chunk : chunks)
258 reordered.insert(reordered.end(), members.begin() + chunk.begin, members.begin() + chunk.end);
259 std::copy(reordered.begin(), reordered.end(), first);
260 }
261 }
262
266 {
267 for (auto& window : m_windows)
268 {
269 auto& nodes = window.members;
270 const uint32_t count = static_cast<uint32_t>(nodes.size());
271
272 m_skipLinkStack.clear();
273 for (uint32_t i = 0; i < count; i++)
274 {
275 const uint32_t myDepth = nodes[i].ref.depth;
276 // Close any open ancestors that are at the same depth or deeper -
277 // their subtrees end here.
278 while (!m_skipLinkStack.empty() &&
279 nodes[m_skipLinkStack.back()].ref.depth >= myDepth)
280 {
281 nodes[m_skipLinkStack.back()].subtreeSkip = i;
282 m_skipLinkStack.pop_back();
283 }
284 m_skipLinkStack.push_back(i);
285 }
286 // Remaining open nodes have subtrees that extend to the end of the list.
287 for (uint32_t idx : m_skipLinkStack)
288 nodes[idx].subtreeSkip = count;
289 }
290 }
291
294 {
295 if (m_windowStack.empty())
296 {
297 m_activeWindowIndex = kInvalidIndex;
299 }
300
301 m_windowStack.pop_back();
302
303 // Update active window index
304 if (!m_windowStack.empty())
305 {
306 m_activeWindowIndex = m_windowStack.back();
307 } else
308 m_activeWindowIndex = kInvalidIndex;
309
311 }
312
315 auto begin() const { return m_windows.begin(); }
316 auto end() const { return m_windows.end(); }
317 const std::vector<WindowEntry>& Windows() const { return m_windows; }
319
320 private:
321 std::vector<WindowEntry> m_windows;
322 std::vector<uint32_t> m_windowStack;
323 std::vector<uint32_t> m_skipLinkStack;
324 std::vector<std::vector<RenderNode>> m_spareMembers;
325
326 uint32_t m_activeWindowIndex = kInvalidIndex;
327 std::unordered_map<ID, uint32_t> m_windowIDToIndexMap;
328
329 };
330
332} // namespace tachyon
const std::vector< RenderNode > * GetWindowMembers(uint32_t windowIndex) const
A window's render list (nullptr for an invalid index), for building its render data.
Definition gui_window_registry.h:123
uint32_t GetWindowStackDepth() const
How many window scopes are open.
Definition gui_window_registry.h:130
const std::vector< WindowEntry > & Windows() const
The windows, in registration order.
Definition gui_window_registry.h:317
void MoveSubtreeToEnd(const ElementRef &nodeRef)
Moves an element's subtree, in the innermost window scope, to render last among its siblings (on top)...
Definition gui_window_registry.h:189
bool HasActiveWindow() const
True inside a window scope.
Definition gui_window_registry.h:142
uint32_t WindowCount() const
The number of registered windows.
Definition gui_window_registry.h:79
RegistryResult BeginWindowScope(ID id, const ElementRef &ref)
Registers a window element just added to the ElementStorage and opens its scope: following Adds go to...
Definition gui_window_registry.h:149
bool Contains(ID id) const
True when a window with id is registered.
Definition gui_window_registry.h:115
auto end() const
The windows, in registration order.
Definition gui_window_registry.h:316
RegistryResult Add(const ElementRef &ref)
Adds a non-window element to the innermost window scope's render list.
Definition gui_window_registry.h:178
WindowRegistry()
Definition gui_window_registry.h:58
const ID GetWindowID(uint32_t windowIndex) const
The ID of the window at windowIndex; Invalid when out of range.
Definition gui_window_registry.h:82
uint32_t GetWindowIndex(ID id) const
The index of the window with id; kInvalidIndex when unknown.
Definition gui_window_registry.h:106
static constexpr uint32_t kInvalidIndex
Definition gui_window_registry.h:37
ElementRef GetWindowRef(uint32_t windowIndex) const
The element of the window at windowIndex; Invalid when out of range.
Definition gui_window_registry.h:90
void BuildRenderSkipLinks()
Computes subtreeSkip for every RenderNode in every window.
Definition gui_window_registry.h:265
void Clear()
Forgets all windows, keeping capacity; call at BeginConstruction.
Definition gui_window_registry.h:65
ElementRef GetWindowRef(ID id) const
The element of the window with id; Invalid when unknown.
Definition gui_window_registry.h:97
ID GetCurrentWindowID() const
The window being built (innermost window scope); Invalid outside one.
Definition gui_window_registry.h:136
void ApplyElementMove(const ElementMove &move)
Follows the elements an ElementStorage::MoveChild moved: remaps every ref, then reorders the moved si...
Definition gui_window_registry.h:222
RegistryResult EndWindowScope()
Closes the innermost window scope; NoActiveWindow when none is open.
Definition gui_window_registry.h:293
auto begin() const
The windows, in registration order.
Definition gui_window_registry.h:315
@ Success
Definition gui_element_storage.h:44
RegistryResult
Outcome of a WindowRegistry call.
Definition gui_window_registry.h:18
@ InvalidRef
Definition gui_window_registry.h:20
@ Success
Definition gui_window_registry.h:19
@ NoActiveWindow
Definition gui_window_registry.h:21
Definition gui_logging.h:24
What a MoveChild did: the slot rotations it applied at each depth, so code that stores ElementRefs (e...
Definition gui_element.h:162
ElementRef Remap(ElementRef ref) const
Where ref sits after the move.
Definition gui_element.h:170
uint32_t siblingsEnd
Definition gui_element.h:167
uint32_t siblingsBegin
absolute slot range of the siblings that changed order
Definition gui_element.h:166
uint32_t childDepth
depth of the moved siblings
Definition gui_element.h:165
An element's slot in ElementStorage: its depth and index within that depth.
Definition gui_element.h:127
static constexpr ElementRef Invalid()
Definition gui_element.h:135
constexpr bool IsValid() const
Definition gui_element.h:134
uint32_t depth
Definition gui_element.h:128
A 64-bit element ID whose bit layout depends on its IDMode.
Definition gui_id.h:23
static constexpr ID Invalid()
The "no element" ID (all bits zero); also the default for optional ID parameters.
Definition gui_id.h:66
One entry in a window's flat pre-order render list.
Definition gui_window_registry.h:42
uint32_t _pad
explicit padding -> 16 bytes, 4 nodes per cache line
Definition gui_window_registry.h:45
uint32_t subtreeSkip
flat-list index past this subtree
Definition gui_window_registry.h:44
ElementRef ref
Definition gui_window_registry.h:43
A window of this frame and its render list.
Definition gui_window_registry.h:50
ElementRef windowRef
Definition gui_window_registry.h:53
std::vector< RenderNode > members
pre-order, populated during construction
Definition gui_window_registry.h:55
uint32_t parentWindowEntry
index of the enclosing window, if any
Definition gui_window_registry.h:51