MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Layer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Element.hpp"
4
6
7/**
8 * @class Layer
9 * @brief Flat registry of Elements on a surface.
10 *
11 * Owns a collection of Elements and provides spatial queries against them
12 * in NDC space. Does not drive rendering, scheduling, or event wiring.
13 *
14 * Elements are stored and traversed back-to-front so later-added elements
15 * are considered on top during hit testing, matching typical draw order.
16 *
17 * Pixel-space query overloads accept window dimensions and convert to NDC
18 * internally using the same convention as Kinesis::to_ndc: top-left origin,
19 * +Y down in pixel space, +Y up in NDC.
20 *
21 * Element ids are unique only within one Layer. A Layer used with Bridge
22 * must be owned by a shared_ptr, which Bridge uses to tell layers apart.
23 */
24class MAYAFLUX_API Layer : public std::enable_shared_from_this<Layer> {
25public:
26 // =========================================================================
27 // Slot
28 // =========================================================================
29
30 /**
31 * @class Slot
32 * @brief Handle returned by Layer::add, carrying the assigned element id.
33 *
34 * Provides chained post-registration mutations — relation, visibility,
35 * z-order — without requiring the caller to keep the id in a separate
36 * variable before acting on it.
37 *
38 * Slot holds a non-owning reference to its Layer. It must not outlive
39 * the Layer. The recommended pattern is to extract the id immediately
40 * after configuration is complete and store that instead:
41 *
42 * @code
43 * const uint32_t id = layer.add(std::move(el))
44 * .relate_to(parent_id)
45 * .hidden()
46 * .id();
47 * @endcode
48 *
49 * When the id is not needed, Slot converts implicitly to uint32_t so
50 * existing callsites (el.element.id = layer.add(...)) compile unchanged.
51 */
52 class Slot {
53 public:
54 Slot(Layer& layer, uint32_t id) noexcept
55 : m_layer(layer)
56 , m_id(id)
57 {
58 }
59
60 /**
61 * @brief Implicit conversion to the element id.
62 *
63 * Allows Slot to substitute for uint32_t in existing call sites:
64 * @code
65 * mapped.element.id = layer.add(mapped.element);
66 * @endcode
67 */
68 operator uint32_t() const noexcept { return m_id; }
69
70 /**
71 * @brief Explicit id accessor, for clarity in chained expressions.
72 */
73 [[nodiscard]] uint32_t id() const noexcept { return m_id; }
74
75 // =====================================================================
76 // Post-registration mutations — each returns Slot& for chaining
77 // =====================================================================
78
79 /**
80 * @brief Record that this element belongs with @p primary_id.
81 *
82 * Visibility, z-order, and remove operations on @p primary_id cascade
83 * to this element. Used to group body elements under a collapsible
84 * header or to tie a label element to its controller.
85 */
86 Slot& relate_to(uint32_t primary_id)
87 {
88 m_layer.relate(primary_id, m_id);
89 return *this;
90 }
91
92 /**
93 * @brief Hide this element immediately after registration.
94 *
95 * Equivalent to layer.set_visible(id, false). Use when the element
96 * starts hidden and should be revealed by a relation cascade or
97 * an explicit state change.
98 */
100 {
101 m_layer.set_visible(m_id, false);
102 return *this;
103 }
104
105 /**
106 * @brief Disable hit testing for this element immediately.
107 *
108 * Equivalent to layer.set_interactive(id, false).
109 */
111 {
112 m_layer.set_interactive(m_id, false);
113 return *this;
114 }
115
116 /**
117 * @brief Move to the top of the hit-test order (drawn last, hit first).
118 */
120 {
121 m_layer.bring_to_front(m_id);
122 return *this;
123 }
124
125 /**
126 * @brief Move to the bottom of the hit-test order (drawn first, hit last).
127 */
129 {
130 m_layer.send_to_back(m_id);
131 return *this;
132 }
133
134 private:
136 uint32_t m_id;
137 };
138
139 // =========================================================================
140 // Lifecycle
141 // =========================================================================
142
143 Layer() = default;
144 ~Layer() = default;
145
146 Layer(const Layer&) = delete;
147 Layer& operator=(const Layer&) = delete;
148 Layer(Layer&&) = default;
149 Layer& operator=(Layer&&) = default;
150
151 // =========================================================================
152 // Element registration
153 // =========================================================================
154
155 /**
156 * @brief Add an element to the layer.
157 *
158 * Assigns a stable id and returns a Slot carrying that id. The Slot
159 * supports chained post-registration mutations (relate_to, hidden,
160 * non_interactive, to_front, to_back) and converts implicitly to
161 * uint32_t, preserving all existing callsites.
162 *
163 * @return Slot for the registered element.
164 */
165 [[nodiscard]] Slot add(Element element);
166
167 /**
168 * @brief Remove an element by id, cascading to every related id.
169 * Marks each removed element's buffer for removal.
170 * @return True if @p id was found.
171 */
172 bool remove(uint32_t id);
173
174 /**
175 * @brief Remove all elements.
176 */
177 void clear();
178
179 // =========================================================================
180 // Element mutation
181 // =========================================================================
182
183 /**
184 * @brief Replace the bounds_hint on an existing element.
185 * @return True if found.
186 */
187 bool set_bounds(uint32_t id, Kinesis::AABB2D bounds);
188
189 /**
190 * @brief Replace the contains callable on an existing element.
191 * @return True if found.
192 */
193 bool set_contains(uint32_t id, std::function<bool(glm::vec2)> fn);
194
195 /**
196 * @brief Confine hit testing of an element to a region, or lift the
197 * confinement with std::nullopt.
198 *
199 * The clip survives set_bounds, set_contains and Mapped::sync, which
200 * rewrite the element's own shape but not where it may be hit.
201 * @return True if found.
202 */
203 bool set_clip(uint32_t id, std::optional<Kinesis::AABB2D> clip);
204
205 bool set_interactive(uint32_t id, bool interactive);
206 bool set_visible(uint32_t id, bool visible);
207
208 /**
209 * @brief Set every element in the layer visible or hidden.
210 *
211 * Same effect as calling set_visible(id, visible) for every element
212 * currently registered. Each element's render processor changes visibility
213 * and requests a presentation refresh. Buffers retain their processing state,
214 * pipelines, and window associations while hidden.
215 */
216 void set_all_visible(bool visible);
217
218 /**
219 * @brief Move element to the top of the hit-test and paint order
220 * (drawn last), cascading to every related element.
221 */
222 bool bring_to_front(uint32_t id);
223
224 /**
225 * @brief Move element to the bottom of the hit-test and paint order
226 * (drawn first), cascading to every related element.
227 */
228 bool send_to_back(uint32_t id);
229
230 // =========================================================================
231 // Spatial queries - NDC space
232 // =========================================================================
233
234 /**
235 * @brief Return the topmost interactive, visible element containing @p ndc.
236 *
237 * Traverses back-to-front. An element passes if:
238 * 1. bounds_hint is absent OR bounds_hint.contains(ndc) is true.
239 * 2. contains is absent OR contains(ndc) is true.
240 * An element with neither set never hits.
241 *
242 * @param ndc Point in NDC space (x, y in [-1, +1]).
243 * @return Element id, or nullopt if no element hit.
244 */
245 [[nodiscard]] std::optional<uint32_t> hit_test(glm::vec2 ndc) const;
246
247 /**
248 * @brief Return all interactive, visible elements containing @p ndc,
249 * ordered front-to-back (topmost first, matching hit_test).
250 */
251 [[nodiscard]] std::vector<uint32_t> hit_test_all(glm::vec2 ndc) const;
252
253 // =========================================================================
254 // Spatial queries - pixel space
255 // =========================================================================
256
257 /**
258 * @brief hit_test with pixel-space input, converted to NDC internally.
259 * @param px Cursor x in pixels (top-left origin).
260 * @param py Cursor y in pixels (top-left origin).
261 * @param win_w Window width in pixels.
262 * @param win_h Window height in pixels.
263 */
264 [[nodiscard]] std::optional<uint32_t> hit_test(
265 double px, double py, uint32_t win_w, uint32_t win_h) const;
266
267 [[nodiscard]] std::vector<uint32_t> hit_test_all(
268 double px, double py, uint32_t win_w, uint32_t win_h) const;
269
270 // =========================================================================
271 // Relations
272 // =========================================================================
273
274 /**
275 * @brief Record that @p related_id belongs with @p primary_id.
276 *
277 * Cascaded lifecycle operations (remove, set_visible, bring_to_front,
278 * send_to_back) on @p primary_id propagate to @p related_id.
279 * Returns false if either id is not found or the ids are equal. Two
280 * elements may be related to each other to form one group; every
281 * cascade follows closure() and so terminates on such cycles.
282 */
283 bool relate(uint32_t primary_id, uint32_t related_id);
284
285 /**
286 * @brief Remove a specific relation between two elements.
287 * Does not remove either element from the layer.
288 */
289 bool unrelate(uint32_t primary_id, uint32_t related_id);
290
291 /**
292 * @brief Return the ids related to @p primary_id, or empty if none.
293 */
294 [[nodiscard]] std::vector<uint32_t> related_ids(uint32_t primary_id) const;
295
296 /**
297 * @brief Return @p id plus every id transitively related to it.
298 */
299 [[nodiscard]] std::vector<uint32_t> closure(uint32_t id) const;
300
301 // =========================================================================
302 // Introspection
303 // =========================================================================
304
305 [[nodiscard]] const Element* get(uint32_t id) const;
306 [[nodiscard]] Element* get(uint32_t id);
307
308 [[nodiscard]] const std::vector<Element>& elements() const { return m_elements; }
309 [[nodiscard]] size_t size() const { return m_elements.size(); }
310 [[nodiscard]] bool empty() const { return m_elements.empty(); }
311
312private:
313 std::vector<Element> m_elements;
314 mutable std::unordered_map<uint32_t, size_t> m_index;
315 mutable bool m_index_stale {};
316 uint32_t m_next_id { 1 };
317
318 int64_t m_next_front_priority {};
319 int64_t m_next_back_priority {};
320
321 std::unordered_map<uint32_t, std::vector<uint32_t>> m_relations;
322
323 void reindex() const;
324
325 [[nodiscard]] static glm::vec2 to_ndc(
326 double px, double py,
327 uint32_t win_w, uint32_t win_h) noexcept;
328
329 [[nodiscard]] static bool test_element(
330 const Element& el, glm::vec2 ndc) noexcept;
331};
332
333} // namespace MayaFlux::Portal::Forma
uint32_t py
uint32_t px
Slot & non_interactive()
Disable hit testing for this element immediately.
Definition Layer.hpp:110
Slot & to_back()
Move to the bottom of the hit-test order (drawn first, hit last).
Definition Layer.hpp:128
Slot(Layer &layer, uint32_t id) noexcept
Definition Layer.hpp:54
uint32_t id() const noexcept
Explicit id accessor, for clarity in chained expressions.
Definition Layer.hpp:73
Slot & relate_to(uint32_t primary_id)
Record that this element belongs with primary_id.
Definition Layer.hpp:86
Slot & hidden()
Hide this element immediately after registration.
Definition Layer.hpp:99
Slot & to_front()
Move to the top of the hit-test order (drawn last, hit first).
Definition Layer.hpp:119
Handle returned by Layer::add, carrying the assigned element id.
Definition Layer.hpp:52
Layer(const Layer &)=delete
Layer & operator=(Layer &&)=default
Layer & operator=(const Layer &)=delete
std::vector< Element > m_elements
Definition Layer.hpp:313
std::unordered_map< uint32_t, size_t > m_index
Definition Layer.hpp:314
const std::vector< Element > & elements() const
Definition Layer.hpp:308
std::unordered_map< uint32_t, std::vector< uint32_t > > m_relations
Definition Layer.hpp:321
Flat registry of Elements on a surface.
Definition Layer.hpp:24
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
A bounded, renderable region on a window surface.
Definition Element.hpp:61