MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Tend.hpp
Go to the documentation of this file.
1#pragma once
2
6#include "Surface.hpp"
7
9
10/**
11 * @brief Realize a Form on @p surface as a slice of @p batch.
12 *
13 * Like create(), but the element draws into a slice of the shared buffer
14 * instead of a buffer of its own, with the Form's topology. Make the buffer
15 * with create_buffer() and pass it to every Form that should draw together;
16 * the Form's capacity is not used, the batch grows. A Form that needs its own
17 * buffer, such as one scissored by a Scrollable, uses create() instead.
18 *
19 * @tparam T MappedState value type.
20 * @param surface Canvas to register on.
21 * @param batch Shared buffer from create_buffer().
22 * @param form Geometry, topology and interaction.
23 * @param initial Starting value written into MappedState.
24 * @param project Optional T to float projection for outbound readers.
25 * @return Fully constructed Mapped<T>, wired.
26 */
27template <typename T>
28[[nodiscard]] Mapped<T> create(
29 Surface& surface,
30 const std::shared_ptr<Buffers::FormaBuffer>& batch,
32 T initial,
33 std::function<float(T)> project = {})
34{
35 auto mapped = internal::atelier().create_element<T>(
36 surface, FormaTarget::slice_of(batch, form.topology),
37 std::move(form.geometry), std::move(initial), std::move(project));
38
39 if (form.wire)
40 form.wire(surface.ctx(), mapped.element.id, mapped.state);
41
42 return mapped;
43}
44
45/**
46 * @brief Realize a Form on @p surface.
47 *
48 * Builds a buffer of its own and draws the Form as a slice of it with the
49 * Form's topology, registers the element, syncs so the geometry function's
50 * bounds_hint and contains reach the Layer before the first frame, then runs
51 * the Form's interaction wiring.
52 *
53 * A bare GeometryFn converts to a Form, so this also serves as the simplest
54 * element construction path for a caller's own geometry.
55 *
56 * @tparam T MappedState value type.
57 * @param surface Canvas to register on.
58 * @param form Geometry, topology and interaction.
59 * @param initial Starting value written into MappedState.
60 * @param project Optional T to float projection for outbound readers.
61 * @return Fully constructed Mapped<T>, wired.
62 */
63template <typename T>
64[[nodiscard]] Mapped<T> create(
65 Surface& surface,
67 T initial,
68 std::function<float(T)> project = {})
69{
70 auto mapped = internal::atelier().create_element<T>(
71 surface, std::move(form.geometry), std::move(initial),
72 form.topology, std::move(project));
73
74 if (form.wire)
75 form.wire(surface.ctx(), mapped.element.id, mapped.state);
76
77 return mapped;
78}
79
80/**
81 * @brief Build a FormaBuffer, register it, construct a Mapped<T>, add the
82 * element to @p surface's layer, and register it with the
83 * application Bridge.
84 *
85 * Reads the layer and window from @p surface.
86 *
87 * After registration, one sync() is run so that bounds_hint and contains
88 * populated by the geometry function are visible on the Element before
89 * the first frame. This removes the manual
90 * @code
91 * layer->set_bounds(el.element.id, ...);
92 * layer->set_contains(el.element.id, ...);
93 * @endcode
94 * boilerplate seen at fader-style call sites: those values now arrive
95 * directly from the geometry function on construction. The geometry
96 * function remains the user's; the sync is the same one that runs every
97 * frame.
98 *
99 * @tparam T MappedState value type.
100 * @param surface Canvas to register the element on.
101 * @param geom Geometry function producing vertex bytes from T.
102 * @param initial Starting value written into MappedState.
103 * @param topology Primitive topology for the FormaBuffer.
104 * @param project Optional T -> float projection for outbound readers.
105 * @return Fully constructed Mapped<T> with element registered.
106 */
107template <typename T>
109 Surface& surface,
110 GeometryFn<T> geom,
111 T initial,
113 std::function<float(T)> project = {})
114{
116 surface, std::move(geom), std::move(initial), topology, std::move(project));
117}
118
119/**
120 * @brief create_element drawing into @p target instead of a buffer of its own.
121 *
122 * Give several elements slices of one buffer, from FormaTarget::slice_of on a
123 * create_buffer buffer, and they draw together. Otherwise matches the
124 * Surface overload above.
125 *
126 * @param target Where the element's geometry is written.
127 */
128template <typename T>
130 Surface& surface,
131 FormaTarget target,
132 GeometryFn<T> geom,
133 T initial,
134 std::function<float(T)> project = {})
135{
137 surface, std::move(target), std::move(geom), std::move(initial), std::move(project));
138}
139
140/**
141 * @brief Build a text-capable FormaBuffer, register a TextField, and wire
142 * text editing onto it in one call.
143 *
144 * The one-call convenience over TextField::place(), the same split
145 * Portal::Forma::create<T> already has with a bare Form<T>. This builds
146 * the buffer (create_buffer, which TextField.cpp itself may never call;
147 * see TextField.hpp) and delegates. Build the buffer and TextField by hand
148 * instead for anything needing a non-default buffer setup (a shared
149 * texture slot alongside a background quad, for instance).
150 *
151 * @p scrollable composes a second primitive in, the same way: false (the
152 * default) matches every prior call exactly, and @p bounds is the field's
153 * own region. True builds a plain Scrollable and places its viewport and
154 * the field text in slices of one text-capable buffer, then calls
155 * TextField::scrollable() instead of place(). This is the same composition
156 * test_text_input() (test_8.hpp) once wired by hand,
157 * now living at the TextField primitive level; this is only the one-call
158 * convenience over it. That Scrollable is local to this call and not
159 * returned, so no indicator is added and nothing else can be tracked into
160 * the same viewport. Build the Scrollable yourself and call
161 * TextField::scrollable() directly (or place() + Scrollable::track() by
162 * hand) for either of those.
163 *
164 * @param surface Surface whose window, layer, and context own the field.
165 * @param bounds NDC region for both hit testing and the text quad,
166 * or, when scrollable, the fixed clipped viewport.
167 * @param params Shared render params. A default-constructed one is
168 * allocated if null.
169 * @param initial_text Starting text. Cursor starts at its end.
170 * @param scrollable When true, wraps the field in a Scrollable viewport.
171 * @return Registered, wired TextField. Portal::Forma::destroy(surface,
172 * field.element_id) is sufficient to tear the whole thing down,
173 * scrollable viewport included when @p scrollable is true. No
174 * manual multi-id cleanup is needed.
175 */
176[[nodiscard]] MAYAFLUX_API TextField create_text_field(
177 Surface& surface,
178 Kinesis::AABB2D bounds,
179 std::shared_ptr<Portal::Text::PressParams> params = nullptr,
180 std::string initial_text = {},
181 bool scrollable = false);
182
183/**
184 * @struct Tend
185 * @brief The fourth member of the Collapsible/Scrollable/TextField family,
186 * for a bare Form<T> control.
187 *
188 * Configure with form/initial, then place(Surface&) - same as all three
189 * siblings, Tend never builds a Surface itself, it only ever wraps one the
190 * caller already has. place() can only call Portal::Forma::create(), never
191 * create_element(), so the wire-dropping mistake create_element<T>'s
192 * bare-GeometryFn overload allows is structurally unreachable through Tend.
193 *
194 * place() retains a copy of the Surface it was given in the surface member
195 * (cheap - Surface's own three fields are shared_ptrs, so the copy shares
196 * the same Layer/Context/window as the original), so ctx()/layer()/window()
197 * stay reachable afterward without holding a separate reference of your own.
198 *
199 * Accepting a SurfaceConfig or a window description directly, so a caller
200 * with nothing yet does not need to call create_surface() themselves first,
201 * is deliberately not this struct's job - that escalation belongs to a
202 * higher-level composer (a future Creator-level tend_forma), which can
203 * resolve either shape into a Surface via Atelier::create_surface's own two
204 * overloads and hand the result to place() same as any other caller would.
205 *
206 * place() alone stops at construction. bridge() and track() are what keep
207 * going without falling out of the chain into a separate statement:
208 *
209 * @code
210 * auto fader = Tend<float>{
211 * .form = Geometry::horizontal_fader(track, 0.04F),
212 * .initial = 0.5F,
213 * }.place(surface)
214 * .bridge().write(vega.Constant(0.5) | Audio[0]);
215 * @endcode
216 *
217 * @tparam T MappedState value type.
218 */
219template <typename T>
220struct Tend {
221 /// @brief Geometry, topology, capacity, and interaction. Consumed by place().
223
224 /// @brief Starting value written into MappedState. Consumed by place().
226
227 /// @brief Optional shared buffer from create_buffer(). When set, place()
228 /// draws the element as a slice of it instead of a buffer of its own.
229 std::shared_ptr<Buffers::FormaBuffer> batch;
230
231 /// @brief Populated by place(). The real, fully wired element.
232 std::optional<Mapped<T>> result;
233
234 /// @brief Populated by place() with a copy of the Surface it was given,
235 /// so ctx()/layer()/window() stay reachable afterward.
236 std::optional<Surface> surface;
237
238 /**
239 * @brief Realize form on @p target.
240 * @param target Canvas to register on. A copy is retained in surface.
241 * @return *this, with surface and result populated.
242 */
244 {
245 surface = target;
246 result = batch
247 ? Portal::Forma::create<T>(target, batch, std::move(form), std::move(initial))
248 : Portal::Forma::create<T>(target, std::move(form), std::move(initial));
249 return *this;
250 }
251
252 /**
253 * @brief Bridge::Binding handle for result's element.
254 *
255 * Thin forwarder to Bridge::at(). It keeps the chain unbroken rather than
256 * requiring a separate Portal::Forma::bridge().at(...) statement after
257 * place(). Every real bind()/write()/unbind() overload lives on the
258 * returned Binding itself, unchanged. Nothing here duplicates them.
259 * Calls internal::atelier().bridge() rather than the public
260 * Portal::Forma::bridge() wrapper, since that free function is declared
261 * in Forma.hpp, which this file cannot include.
262 *
263 * @pre place() has been called.
264 * @return Binding handle, chainable into .bind(...)/.write(...).
265 */
266 [[nodiscard]] Bridge::Binding bridge()
267 {
268 return internal::atelier().bridge().at(result->state);
269 }
270
271 /**
272 * @brief Register result's element for scroll reflow in @p panel.
273 *
274 * Thin forwarder to Atelier::track<T>(). See its own doc for what the
275 * default (built when @p reposition is omitted) does and why it
276 * captures only independently-owned pieces of result, never a pointer
277 * back to Tend/result itself.
278 *
279 * @pre place() has been called.
280 * @param panel Scrollable to track into.
281 * @param layer Layer both panel and result's element were
282 * registered on.
283 * @param base_bounds result's current on-screen NDC bounds.
284 * @param reposition Called with the scrolled bounds on every scroll.
285 * Defaulted when empty; see Atelier::track<T>().
286 * @return *this.
287 */
289 Scrollable& panel,
290 Layer& layer,
291 Kinesis::AABB2D base_bounds,
292 std::function<void(Kinesis::AABB2D)> reposition = {})
293 {
294 internal::atelier().track(panel, layer, *result, base_bounds, std::move(reposition));
295 return *this;
296 }
297};
298
299} // namespace MayaFlux::Portal::Forma
Illustrative geometry functions for common Mapped use cases.
PrimitiveTopology topology
Non-owning handle for all bind/write/unbind operations on one element.
Definition Bridge.hpp:551
Binding at(ElementRef ref)
Return a Binding handle for the element registered under ref.
Definition Bridge.hpp:652
static FormaTarget slice_of(const std::shared_ptr< Buffers::FormaBuffer > &buffer, std::optional< Graphics::PrimitiveTopology > topology=std::nullopt)
A new empty slice of buffer, drawn after the others.
Definition Target.cpp:19
Where an element's geometry and texture are written.
Definition Target.hpp:22
Flat registry of Elements on a surface.
Definition Layer.hpp:24
Named owner of a (Window, Layer, Context) triple: the Forma canvas.
Definition Surface.hpp:121
Mapped< T > create_element(Surface &surface, GeometryFn< T > geom, T initial, Graphics::PrimitiveTopology topology=Graphics::PrimitiveTopology::TRIANGLE_STRIP, std::function< float(T)> project={})
create_element on a buffer of its own, as a slice of topology, followed by one sync so bounds_hint an...
Definition Atelier.hpp:165
void track(Scrollable &panel, Layer &layer, const Mapped< T > &mapped, Kinesis::AABB2D base_bounds, std::function< void(Kinesis::AABB2D)> reposition={})
Register mapped's element for scroll reflow in panel.
Definition Atelier.hpp:227
Bridge & bridge()
Bridge instance constructed during initialize.
Definition Atelier.cpp:80
Atelier & atelier()
Accessor for the process-wide Atelier.
Definition Atelier.hpp:304
Mapped< T > create(Surface &surface, const std::shared_ptr< Buffers::FormaBuffer > &batch, Geometry::Form< T > form, T initial, std::function< float(T)> project={})
Realize a Form on surface as a slice of batch.
Definition Tend.hpp:28
TextField create_text_field(Surface &surface, Kinesis::AABB2D bounds, std::shared_ptr< Portal::Text::PressParams > params, std::string initial_text, bool scrollable)
Build a text-capable FormaBuffer, register a TextField, and wire text editing onto it in one call.
Definition Tend.cpp:15
Mapped< T > create_element(Surface &surface, GeometryFn< T > geom, T initial, Graphics::PrimitiveTopology topology=Graphics::PrimitiveTopology::TRIANGLE_STRIP, std::function< float(T)> project={})
Build a FormaBuffer, register it, construct a Mapped<T>, add the element to surface's layer,...
Definition Tend.hpp:108
std::function< void(T value, std::vector< uint8_t > &out_bytes, Element &element)> GeometryFn
Geometry function signature.
Definition Mapped.hpp:71
PrimitiveTopology
Vertex assembly primitive topology.
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
GeometryFn< T > geometry
Writes vertex bytes for the current value.
Definition Geometry.hpp:85
std::function< void(Context &, uint32_t, std::shared_ptr< MappedState< T > >)> wire
Registers interaction against the realized element.
Definition Geometry.hpp:100
Graphics::PrimitiveTopology topology
Topology matching the vertex kind geometry writes.
Definition Geometry.hpp:88
A geometry function plus everything its realization requires.
Definition Geometry.hpp:83
Infrastructure for a continuously-mapped value whose GPU geometry tracks it.
Definition Mapped.hpp:96
A clipped, scroll-offset viewport over content taller than it.
T initial
Starting value written into MappedState. Consumed by place().
Definition Tend.hpp:225
Tend & track(Scrollable &panel, Layer &layer, Kinesis::AABB2D base_bounds, std::function< void(Kinesis::AABB2D)> reposition={})
Register result's element for scroll reflow in panel.
Definition Tend.hpp:288
Geometry::Form< T > form
Geometry, topology, capacity, and interaction. Consumed by place().
Definition Tend.hpp:222
std::optional< Surface > surface
Populated by place() with a copy of the Surface it was given, so ctx()/layer()/window() stay reachabl...
Definition Tend.hpp:236
std::shared_ptr< Buffers::FormaBuffer > batch
Optional shared buffer from create_buffer().
Definition Tend.hpp:229
Tend & place(Surface &target)
Realize form on target.
Definition Tend.hpp:243
Bridge::Binding bridge()
Bridge::Binding handle for result's element.
Definition Tend.hpp:266
std::optional< Mapped< T > > result
Populated by place(). The real, fully wired element.
Definition Tend.hpp:232
The fourth member of the Collapsible/Scrollable/TextField family, for a bare Form<T> control.
Definition Tend.hpp:220