MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Surface.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Context.hpp"
4#include "Layer.hpp"
5
6namespace MayaFlux::Core {
7class Window;
8}
9
11
12/**
13 * @struct SurfaceConfig
14 * @brief The single argument every Surface-creating function in this module
15 * takes, at every level of abstraction.
16 *
17 * Surface, Atelier::create_surface, and Portal::Forma::create_surface all
18 * take this instead of a loose (window, name) pair: when @c layer/@c ctx are
19 * unset, a fresh Layer and Context are built from @c window/@c name against
20 * the global EventManager and filled in; when both are already set (the
21 * power-tinkerer case) they are used as-is and @c name is ignored. This type
22 * must not itself reach into internal::atelier(). Only Forma.hpp/.cpp touch
23 * that singleton. Building stays the job of whichever function is given
24 * the config.
25 */
27 /// @brief Target window. Must outlive the Surface.
28 std::shared_ptr<Core::Window> window;
29
30 /**
31 * @brief Unique name scoping the Context's event coroutines.
32 *
33 * Only consulted when @c layer/@c ctx are unset and need to be built.
34 * Ignored when both are already supplied.
35 */
36 std::string name;
37
38 /// @brief Pre-built Layer, constructed against @c window. Optional,
39 /// built automatically from @c name when unset.
40 std::shared_ptr<Layer> layer;
41
42 /// @brief Pre-built Context, already wired to @c window. Optional,
43 /// built automatically from @c name when unset.
44 std::shared_ptr<Context> ctx;
45
46 /**
47 * @brief Whether the built-in close handling (Bridge::stop_sync plus a
48 * deferred window release, see Surface::Surface) runs. Forced
49 * true whenever on_close is set. See should_detach_on_close().
50 */
51 bool detach_on_close { true };
52
53 /**
54 * @brief Runs in addition to, never instead of, the built-in close
55 * handling. Setting this forces should_detach_on_close() true
56 * regardless of detach_on_close, so supplying a hook can never
57 * silently lose the window-release cleanup.
58 *
59 * Context::on_close is a single slot per id, and the last write
60 * wins, so a bare replacement field here would silently clobber
61 * Surface's own close handler on first use. This field exists so
62 * that cannot happen.
63 */
64 std::function<void()> on_close;
65
66 /// @brief Whether the built-in detach behavior actually runs.
67 [[nodiscard]] bool should_detach_on_close() const noexcept
68 {
69 return detach_on_close || static_cast<bool>(on_close);
70 }
71};
72
73/**
74 * @class Surface
75 * @brief Named owner of a (Window, Layer, Context) triple: the Forma canvas.
76 *
77 * In Forma, three things always travel together: a Window (the rendering
78 * target and coordinate space), a Layer (the spatial registry of elements),
79 * and a Context (the event router that hit-tests against the layer). They
80 * are the three faces of one concept: the canvas. Surface names that
81 * concept and owns the triple, so that downstream construction functions
82 * accept one argument instead of three.
83 *
84 * Surface is not a widget toolkit, not a layout engine, not an event
85 * dispatcher. It does not know what an element looks like, how a buffer
86 * is built, or where bindings are routed. Construction of FormaBuffers,
87 * Mapped<T>s, and Bridge registrations is the job of the free functions
88 * in Forma.hpp, which take a Surface& as their canvas argument and read
89 * the BufferManager, Bridge, and other module-level state from the
90 * singletons established by Portal::Forma::initialize().
91 *
92 * The Layer and Context owned by Surface remain fully accessible through
93 * the layer() and ctx() accessors. Surface is a named owner, not a wall.
94 * Anything that worked against Layer or Context before continues to work
95 * against surface.layer() and surface.ctx().
96 *
97 * Surface(SurfaceConfig) never creates the Layer or Context itself. It only
98 * ever wraps ones already built. Portal::Forma::create_surface (in
99 * Forma.hpp) also takes a SurfaceConfig: the default path leaves layer/ctx
100 * unset, so a fresh Layer and Context are built against the global
101 * EventManager from window/name and the Surface is constructed from those.
102 * Set layer/ctx yourself in the same SurfaceConfig for the power-tinkerer
103 * path, when you need a custom Context subclass, want to share one Layer
104 * across multiple Contexts (split-pane editing), or are constructing in a
105 * test against a non-global EventManager.
106 *
107 * @code
108 * // Default path
109 * auto surface = Portal::Forma::create_surface(
110 * SurfaceConfig { .window = window, .name = "plot_live" });
111 *
112 * auto el = Portal::Forma::create_element<float>(surface, geom, 0.5F);
113 * surface.ctx().on_press(el.element.id, IO::MouseButtons::Left, ...);
114 *
115 * // Power-tinkerer path: pre-built layer and context
116 * auto layer = std::make_shared<Layer>();
117 * auto ctx = std::make_shared<MyCustomContext>(layer, window, em, "custom");
118 * Surface surface(SurfaceConfig { .window = window, .layer = layer, .ctx = ctx });
119 * @endcode
120 */
121class MAYAFLUX_API Surface {
122public:
123 // =========================================================================
124 // Construction
125 // =========================================================================
126
127 /**
128 * @brief Construct a Surface from a SurfaceConfig.
129 *
130 * Surface takes shared ownership of all three components. The caller is
131 * responsible for ensuring the Layer and Context were constructed
132 * against @c config.window. Wires the built-in close handling
133 * (Bridge::stop_sync plus a deferred window release) per
134 * @c config.should_detach_on_close(), then runs @c config.on_close if
135 * set. See SurfaceConfig's own doc for the composition rule between
136 * the two.
137 *
138 * @param config Window, Layer, Context, and close-handling behavior.
139 */
140 explicit Surface(SurfaceConfig config);
141
142 ~Surface() = default;
143
144 Surface(const Surface&) = default;
145 Surface& operator=(const Surface&) = default;
146 Surface(Surface&&) noexcept = default;
147 Surface& operator=(Surface&&) noexcept = default;
148
149 // =========================================================================
150 // Accessors: the canvas is never walled off
151 // =========================================================================
152
153 /**
154 * @brief Access the spatial registry.
155 *
156 * Anything supported by Layer is reachable here: add, remove, relate,
157 * set_visible, set_bounds, set_contains, hit_test, etc.
158 */
159 [[nodiscard]] Layer& layer() noexcept { return *m_layer; }
160
161 /// @copydoc layer()
162 [[nodiscard]] const Layer& layer() const noexcept { return *m_layer; }
163
164 /**
165 * @brief Access the event router.
166 *
167 * Anything supported by Context is reachable here: on_press,
168 * on_release, on_move, on_enter, on_leave, on_scroll.
169 */
170 [[nodiscard]] Context& ctx() noexcept { return *m_ctx; }
171
172 /// @copydoc ctx()
173 [[nodiscard]] const Context& ctx() const noexcept { return *m_ctx; }
174
175 /**
176 * @brief Access the rendering target window.
177 */
178 [[nodiscard]] const std::shared_ptr<Core::Window>& window() const noexcept
179 {
180 return m_window_ownership->window;
181 }
182
183 /**
184 * @brief Shared handles for callers that need to keep components alive
185 * independently of the Surface (background tasks, escapes into
186 * coroutines, etc.).
187 */
188 [[nodiscard]] const std::shared_ptr<Layer>& layer_ptr() const noexcept { return m_layer; }
189
190 /// @copydoc layer_ptr()
191 [[nodiscard]] const std::shared_ptr<Context>& ctx_ptr() const noexcept { return m_ctx; }
192
193 // =========================================================================
194 // Element registration passthrough
195 // =========================================================================
196
197 /**
198 * @brief Forward an Element to layer().add() and return the Slot.
199 *
200 * Pure passthrough. Present so that existing patterns like
201 * @code
202 * const uint32_t id = layer->add(std::move(el)).relate_to(parent).id();
203 * @endcode
204 * read as
205 * @code
206 * const uint32_t id = surface.add(std::move(el)).relate_to(parent).id();
207 * @endcode
208 * without forcing the caller to reach through layer() at every step.
209 */
210 Layer::Slot add(Element element) { return m_layer->add(std::move(element)); }
211
212 /**
213 * @brief Forward an id to layer().remove().
214 * @param id Element id to remove from the layer.
215 */
216 void remove(uint32_t id) { m_layer->remove(id); }
217
218 // =========================================================================
219 // Rendering
220 // =========================================================================
221
222 /**
223 * @brief @p base targeting this Surface's window.
224 *
225 * Only the window is set; every other field of @p base is kept.
226 */
228 Portal::Graphics::RenderConfig base = {}) const
229 {
230 base.target_window = window();
231 return base;
232 }
233
234 // =========================================================================
235 // Named regions
236 // =========================================================================
237
238 /**
239 * @brief Rect of size (@p w, @p h) at the top-left corner, inset by @p margin.
240 * @param w Width in NDC units.
241 * @param h Height in NDC units.
242 * @param margin Inset from both screen edges in NDC units.
243 */
244 [[nodiscard]] Kinesis::AABB2D top_left(float w, float h, float margin = 0.02F) const noexcept
245 {
246 return { .min = { -1.F + margin, 1.F - margin - h },
247 .max = { -1.F + margin + w, 1.F - margin } };
248 }
249
250 /// @copydoc top_left
251 [[nodiscard]] Kinesis::AABB2D top_right(float w, float h, float margin = 0.02F) const noexcept
252 {
253 return { .min = { 1.F - margin - w, 1.F - margin - h },
254 .max = { 1.F - margin, 1.F - margin } };
255 }
256
257 /// @copydoc top_left
258 [[nodiscard]] Kinesis::AABB2D bottom_left(float w, float h, float margin = 0.02F) const noexcept
259 {
260 return { .min = { -1.F + margin, -1.F + margin },
261 .max = { -1.F + margin + w, -1.F + margin + h } };
262 }
263
264 /// @copydoc top_left
265 [[nodiscard]] Kinesis::AABB2D bottom_right(float w, float h, float margin = 0.02F) const noexcept
266 {
267 return { .min = { 1.F - margin - w, -1.F + margin },
268 .max = { 1.F - margin, -1.F + margin + h } };
269 }
270
271 /**
272 * @brief Full-width strip of height @p h along the top edge.
273 * @param h Strip height in NDC units.
274 * @param margin Inset from the top and side edges in NDC units.
275 */
276 [[nodiscard]] Kinesis::AABB2D top_strip(float h, float margin = 0.F) const noexcept
277 {
278 return { .min = { -1.F + margin, 1.F - margin - h },
279 .max = { 1.F - margin, 1.F - margin } };
280 }
281
282 /// @copydoc top_strip
283 [[nodiscard]] Kinesis::AABB2D bottom_strip(float h, float margin = 0.F) const noexcept
284 {
285 return { .min = { -1.F + margin, -1.F + margin },
286 .max = { 1.F - margin, -1.F + margin + h } };
287 }
288
289 /**
290 * @brief Full-height strip of width @p w along the left edge.
291 * @param w Strip width in NDC units.
292 * @param margin Inset from the left and vertical edges in NDC units.
293 */
294 [[nodiscard]] Kinesis::AABB2D left_strip(float w, float margin = 0.F) const noexcept
295 {
296 return { .min = { -1.F + margin, -1.F + margin },
297 .max = { -1.F + margin + w, 1.F - margin } };
298 }
299
300 /// @copydoc left_strip
301 [[nodiscard]] Kinesis::AABB2D right_strip(float w, float margin = 0.F) const noexcept
302 {
303 return { .min = { 1.F - margin - w, -1.F + margin },
304 .max = { 1.F - margin, 1.F - margin } };
305 }
306
307 /// @brief Rect of size (@p w, @p h) centered on the NDC origin.
308 [[nodiscard]] Kinesis::AABB2D center_rect(float w, float h) const noexcept
309 {
310 return { .min = { -w * 0.5F, -h * 0.5F }, .max = { w * 0.5F, h * 0.5F } };
311 }
312
313 /// @brief The entire NDC surface.
314 [[nodiscard]] Kinesis::AABB2D full() const noexcept
315 {
316 return { .min = glm::vec2(-1.F), .max = glm::vec2(1.F) };
317 }
318
319private:
321 explicit WindowOwnership(std::shared_ptr<Core::Window> value)
322 : window(std::move(value))
323 {
324 }
325
326 std::shared_ptr<Core::Window> window;
327 };
328
329 std::shared_ptr<WindowOwnership> m_window_ownership;
330 std::shared_ptr<Layer> m_layer;
331 std::shared_ptr<Context> m_ctx;
332};
333
334} // namespace MayaFlux::Portal::Forma
uint32_t h
Definition InkPress.cpp:29
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
glm::vec3 value
Event wiring between a Layer and a window surface.
Definition Context.hpp:41
Handle returned by Layer::add, carrying the assigned element id.
Definition Layer.hpp:52
Flat registry of Elements on a surface.
Definition Layer.hpp:24
const std::shared_ptr< Layer > & layer_ptr() const noexcept
Shared handles for callers that need to keep components alive independently of the Surface (backgroun...
Definition Surface.hpp:188
Kinesis::AABB2D top_left(float w, float h, float margin=0.02F) const noexcept
Rect of size (w, h) at the top-left corner, inset by margin.
Definition Surface.hpp:244
const Context & ctx() const noexcept
Access the event router.
Definition Surface.hpp:173
Surface(Surface &&) noexcept=default
Kinesis::AABB2D bottom_strip(float h, float margin=0.F) const noexcept
Full-width strip of height h along the top edge.
Definition Surface.hpp:283
const Layer & layer() const noexcept
Access the spatial registry.
Definition Surface.hpp:162
Kinesis::AABB2D bottom_right(float w, float h, float margin=0.02F) const noexcept
Rect of size (w, h) at the top-left corner, inset by margin.
Definition Surface.hpp:265
Kinesis::AABB2D full() const noexcept
The entire NDC surface.
Definition Surface.hpp:314
Surface & operator=(const Surface &)=default
Portal::Graphics::RenderConfig render_config(Portal::Graphics::RenderConfig base={}) const
base targeting this Surface's window.
Definition Surface.hpp:227
const std::shared_ptr< Context > & ctx_ptr() const noexcept
Shared handles for callers that need to keep components alive independently of the Surface (backgroun...
Definition Surface.hpp:191
const std::shared_ptr< Core::Window > & window() const noexcept
Access the rendering target window.
Definition Surface.hpp:178
Kinesis::AABB2D center_rect(float w, float h) const noexcept
Rect of size (w, h) centered on the NDC origin.
Definition Surface.hpp:308
Context & ctx() noexcept
Access the event router.
Definition Surface.hpp:170
Layer::Slot add(Element element)
Forward an Element to layer().add() and return the Slot.
Definition Surface.hpp:210
Surface(const Surface &)=default
std::shared_ptr< Context > m_ctx
Definition Surface.hpp:331
Kinesis::AABB2D left_strip(float w, float margin=0.F) const noexcept
Full-height strip of width w along the left edge.
Definition Surface.hpp:294
void remove(uint32_t id)
Forward an id to layer().remove().
Definition Surface.hpp:216
std::shared_ptr< Layer > m_layer
Definition Surface.hpp:330
Kinesis::AABB2D top_strip(float h, float margin=0.F) const noexcept
Full-width strip of height h along the top edge.
Definition Surface.hpp:276
Kinesis::AABB2D top_right(float w, float h, float margin=0.02F) const noexcept
Rect of size (w, h) at the top-left corner, inset by margin.
Definition Surface.hpp:251
Kinesis::AABB2D bottom_left(float w, float h, float margin=0.02F) const noexcept
Rect of size (w, h) at the top-left corner, inset by margin.
Definition Surface.hpp:258
Kinesis::AABB2D right_strip(float w, float margin=0.F) const noexcept
Full-height strip of width w along the left edge.
Definition Surface.hpp:301
std::shared_ptr< WindowOwnership > m_window_ownership
Definition Surface.hpp:329
Named owner of a (Window, Layer, Context) triple: the Forma canvas.
Definition Surface.hpp:121
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
bool should_detach_on_close() const noexcept
Whether the built-in detach behavior actually runs.
Definition Surface.hpp:67
bool detach_on_close
Whether the built-in close handling (Bridge::stop_sync plus a deferred window release,...
Definition Surface.hpp:51
std::shared_ptr< Core::Window > window
Target window. Must outlive the Surface.
Definition Surface.hpp:28
std::shared_ptr< Layer > layer
Pre-built Layer, constructed against window.
Definition Surface.hpp:40
std::function< void()> on_close
Runs in addition to, never instead of, the built-in close handling.
Definition Surface.hpp:64
std::string name
Unique name scoping the Context's event coroutines.
Definition Surface.hpp:36
std::shared_ptr< Context > ctx
Pre-built Context, already wired to window.
Definition Surface.hpp:44
The single argument every Surface-creating function in this module takes, at every level of abstracti...
Definition Surface.hpp:26
std::shared_ptr< Core::Window > window
Definition Surface.hpp:326
WindowOwnership(std::shared_ptr< Core::Window > value)
Definition Surface.hpp:321
std::shared_ptr< Core::Window > target_window
Unified rendering configuration for graphics buffers.