MayaFlux 0.4.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 */
21class MAYAFLUX_API Layer {
22public:
23 // =========================================================================
24 // Slot
25 // =========================================================================
26
27 /**
28 * @class Slot
29 * @brief Handle returned by Layer::add, carrying the assigned element id.
30 *
31 * Provides chained post-registration mutations — relation, visibility,
32 * z-order — without requiring the caller to keep the id in a separate
33 * variable before acting on it.
34 *
35 * Slot holds a non-owning reference to its Layer. It must not outlive
36 * the Layer. The recommended pattern is to extract the id immediately
37 * after configuration is complete and store that instead:
38 *
39 * @code
40 * const uint32_t id = layer.add(std::move(el))
41 * .relate_to(parent_id)
42 * .hidden()
43 * .id();
44 * @endcode
45 *
46 * When the id is not needed, Slot converts implicitly to uint32_t so
47 * existing callsites (el.element.id = layer.add(...)) compile unchanged.
48 */
49 class Slot {
50 public:
51 Slot(Layer& layer, uint32_t id) noexcept
52 : m_layer(layer)
53 , m_id(id)
54 {
55 }
56
57 /**
58 * @brief Implicit conversion to the element id.
59 *
60 * Allows Slot to substitute for uint32_t in existing call sites:
61 * @code
62 * mapped.element.id = layer.add(mapped.element);
63 * @endcode
64 */
65 operator uint32_t() const noexcept { return m_id; }
66
67 /**
68 * @brief Explicit id accessor, for clarity in chained expressions.
69 */
70 [[nodiscard]] uint32_t id() const noexcept { return m_id; }
71
72 // =====================================================================
73 // Post-registration mutations — each returns Slot& for chaining
74 // =====================================================================
75
76 /**
77 * @brief Record that this element belongs with @p primary_id.
78 *
79 * Visibility, z-order, and remove operations on @p primary_id cascade
80 * to this element. Used to group body elements under a collapsible
81 * header or to tie a label element to its controller.
82 */
83 Slot& relate_to(uint32_t primary_id)
84 {
85 m_layer.relate(primary_id, m_id);
86 return *this;
87 }
88
89 /**
90 * @brief Hide this element immediately after registration.
91 *
92 * Equivalent to layer.set_visible(id, false). Use when the element
93 * starts hidden and should be revealed by a relation cascade or
94 * an explicit state change.
95 */
97 {
98 m_layer.set_visible(m_id, false);
99 return *this;
100 }
101
102 /**
103 * @brief Disable hit testing for this element immediately.
104 *
105 * Equivalent to layer.set_interactive(id, false).
106 */
108 {
109 m_layer.set_interactive(m_id, false);
110 return *this;
111 }
112
113 /**
114 * @brief Move to the top of the hit-test order (drawn last, hit first).
115 */
117 {
118 m_layer.bring_to_front(m_id);
119 return *this;
120 }
121
122 /**
123 * @brief Move to the bottom of the hit-test order (drawn first, hit last).
124 */
126 {
127 m_layer.send_to_back(m_id);
128 return *this;
129 }
130
131 private:
133 uint32_t m_id;
134 };
135
136 // =========================================================================
137 // Lifecycle
138 // =========================================================================
139
140 Layer() = default;
141 ~Layer() = default;
142
143 Layer(const Layer&) = delete;
144 Layer& operator=(const Layer&) = delete;
145 Layer(Layer&&) = default;
146 Layer& operator=(Layer&&) = default;
147
148 // =========================================================================
149 // Element registration
150 // =========================================================================
151
152 /**
153 * @brief Add an element to the layer.
154 *
155 * Assigns a stable id and returns a Slot carrying that id. The Slot
156 * supports chained post-registration mutations (relate_to, hidden,
157 * non_interactive, to_front, to_back) and converts implicitly to
158 * uint32_t, preserving all existing callsites.
159 *
160 * @return Slot for the registered element.
161 */
162 [[nodiscard]] Slot add(Element element);
163
164 /**
165 * @brief Remove an element by id.
166 * @return True if found and removed.
167 */
168 bool remove(uint32_t id);
169
170 /**
171 * @brief Remove all elements.
172 */
173 void clear();
174
175 // =========================================================================
176 // Element mutation
177 // =========================================================================
178
179 /**
180 * @brief Replace the bounds_hint on an existing element.
181 * @return True if found.
182 */
183 bool set_bounds(uint32_t id, Kinesis::AABB2D bounds);
184
185 /**
186 * @brief Replace the contains callable on an existing element.
187 * @return True if found.
188 */
189 bool set_contains(uint32_t id, std::function<bool(glm::vec2)> fn);
190
191 bool set_interactive(uint32_t id, bool interactive);
192 bool set_visible(uint32_t id, bool visible);
193
194 /**
195 * @brief Move element to the top of the hit-test order (drawn last).
196 */
197 bool bring_to_front(uint32_t id);
198
199 /**
200 * @brief Move element to the bottom of the hit-test order (drawn first).
201 */
202 bool send_to_back(uint32_t id);
203
204 // =========================================================================
205 // Spatial queries - NDC space
206 // =========================================================================
207
208 /**
209 * @brief Return the topmost interactive, visible element containing @p ndc.
210 *
211 * Traverses back-to-front. An element passes if:
212 * 1. bounds_hint is absent OR bounds_hint.contains(ndc) is true.
213 * 2. contains is absent OR contains(ndc) is true.
214 * An element with neither set never hits.
215 *
216 * @param ndc Point in NDC space (x, y in [-1, +1]).
217 * @return Element id, or nullopt if no element hit.
218 */
219 [[nodiscard]] std::optional<uint32_t> hit_test(glm::vec2 ndc) const;
220
221 /**
222 * @brief Return all interactive, visible elements containing @p ndc,
223 * ordered back-to-front.
224 */
225 [[nodiscard]] std::vector<uint32_t> hit_test_all(glm::vec2 ndc) const;
226
227 // =========================================================================
228 // Spatial queries - pixel space
229 // =========================================================================
230
231 /**
232 * @brief hit_test with pixel-space input, converted to NDC internally.
233 * @param px Cursor x in pixels (top-left origin).
234 * @param py Cursor y in pixels (top-left origin).
235 * @param win_w Window width in pixels.
236 * @param win_h Window height in pixels.
237 */
238 [[nodiscard]] std::optional<uint32_t> hit_test(
239 double px, double py, uint32_t win_w, uint32_t win_h) const;
240
241 [[nodiscard]] std::vector<uint32_t> hit_test_all(
242 double px, double py, uint32_t win_w, uint32_t win_h) const;
243
244 // =========================================================================
245 // Relations
246 // =========================================================================
247
248 /**
249 * @brief Record that @p related_id belongs with @p primary_id.
250 *
251 * Cascaded lifecycle operations (remove, set_visible, bring_to_front,
252 * send_to_back) on @p primary_id propagate to @p related_id.
253 * Returns false if either id is not found.
254 */
255 bool relate(uint32_t primary_id, uint32_t related_id);
256
257 /**
258 * @brief Remove a specific relation between two elements.
259 * Does not remove either element from the layer.
260 */
261 bool unrelate(uint32_t primary_id, uint32_t related_id);
262
263 /**
264 * @brief Return the ids related to @p primary_id, or empty if none.
265 */
266 [[nodiscard]] std::vector<uint32_t> related_ids(uint32_t primary_id) const;
267
268 // =========================================================================
269 // Introspection
270 // =========================================================================
271
272 [[nodiscard]] const Element* get(uint32_t id) const;
273 [[nodiscard]] Element* get(uint32_t id);
274
275 [[nodiscard]] const std::vector<Element>& elements() const { return m_elements; }
276 [[nodiscard]] size_t size() const { return m_elements.size(); }
277 [[nodiscard]] bool empty() const { return m_elements.empty(); }
278
279private:
280 std::vector<Element> m_elements;
281 uint32_t m_next_id { 1 };
282
283 std::unordered_map<uint32_t, std::vector<uint32_t>> m_relations;
284
285 [[nodiscard]] static glm::vec2 to_ndc(
286 double px, double py,
287 uint32_t win_w, uint32_t win_h) noexcept;
288
289 [[nodiscard]] static bool test_element(
290 const Element& el, glm::vec2 ndc) noexcept;
291};
292
293} // namespace MayaFlux::Portal::Forma
Slot & non_interactive()
Disable hit testing for this element immediately.
Definition Layer.hpp:107
Slot & to_back()
Move to the bottom of the hit-test order (drawn first, hit last).
Definition Layer.hpp:125
Slot(Layer &layer, uint32_t id) noexcept
Definition Layer.hpp:51
uint32_t id() const noexcept
Explicit id accessor, for clarity in chained expressions.
Definition Layer.hpp:70
Slot & relate_to(uint32_t primary_id)
Record that this element belongs with primary_id.
Definition Layer.hpp:83
Slot & hidden()
Hide this element immediately after registration.
Definition Layer.hpp:96
Slot & to_front()
Move to the top of the hit-test order (drawn last, hit first).
Definition Layer.hpp:116
Handle returned by Layer::add, carrying the assigned element id.
Definition Layer.hpp:49
Layer(const Layer &)=delete
Layer & operator=(Layer &&)=default
Layer & operator=(const Layer &)=delete
std::vector< Element > m_elements
Definition Layer.hpp:280
const std::vector< Element > & elements() const
Definition Layer.hpp:275
std::unordered_map< uint32_t, std::vector< uint32_t > > m_relations
Definition Layer.hpp:283
Flat registry of Elements on a surface.
Definition Layer.hpp:21
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:21
A bounded, renderable region on a window surface.
Definition Element.hpp:58