MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
LayoutCursor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Mapped.hpp"
4
6
7/**
8 * @brief Reactive Y-position accumulator for vertical primitive stacking.
9 *
10 * Holds a shared MappedState<float> carrying the current NDC Y baseline.
11 * Any primitive whose GeometryFn closes over the state pointer will reflow
12 * automatically when the cursor advances, via the normal version-bump path.
13 *
14 * NDC Y runs +1 (top) to -1 (bottom). advance() subtracts height.
15 *
16 * Default construction places the cursor at the top of the screen (y=1).
17 * This matches the most common starting position and allows LayoutCursor
18 * to be a plain member of structs that use aggregate or value initialization.
19 */
21public:
23 : LayoutCursor(1.F)
24 {
25 }
26
27 /**
28 * @brief Construct a cursor at @p y_start scoped to a horizontal column.
29 *
30 * Bounds returned by advance() span [@p x_min, @p x_max] rather than the
31 * full NDC width. The default extents reproduce the previous behaviour,
32 * so existing call sites are unaffected.
33 *
34 * @param y_start Starting NDC Y baseline.
35 * @param x_min Left edge of the column in NDC.
36 * @param x_max Right edge of the column in NDC.
37 */
38 explicit LayoutCursor(float y_start, float x_min = -1.F, float x_max = 1.F)
39 : m_x_min(x_min)
40 , m_x_max(x_max)
41 {
42 m_state = std::make_shared<MappedState<float>>();
43 m_state->write(y_start);
44 }
45
46 /**
47 * @brief Shared baseline state. Close over this in GeometryFn to reflow
48 * when any preceding primitive changes height.
49 */
50 [[nodiscard]] std::shared_ptr<MappedState<float>> state() const { return m_state; }
51
52 [[nodiscard]] float y() const { return m_state->value; }
53
54 [[nodiscard]] float x_min() const noexcept { return m_x_min; }
55 [[nodiscard]] float x_max() const noexcept { return m_x_max; }
56
57 /**
58 * @brief Bind a live scroll offset. advance() folds its current value
59 * into every bounds it returns from this point on.
60 *
61 * The offset itself is Kinesis::ScrollState's concern (content bounds,
62 * clamping, wheel/drag input); this only consumes the resulting value.
63 * Binding does not retroactively shift bounds already handed out by
64 * earlier advance() calls - those were snapshots, same as advance()
65 * has always returned. A geometry function that needs those existing
66 * placements to reflow live as the user scrolls (not just new ones
67 * placed after a scroll already happened) should close over
68 * scroll_offset() itself and add it to its own captured bounds inside
69 * the GeometryFn body, the same reflow-via-shared-state idiom every
70 * other Mapped<T> primitive already uses for its own value.
71 *
72 * @param offset Shared scroll offset, written elsewhere (typically from
73 * Kinesis::apply_scroll/apply_wheel_scroll via an
74 * on_scroll callback).
75 */
77 {
78 m_scroll_offset = std::move(offset);
79 }
80
81 /**
82 * @brief The bound scroll offset, or nullptr if none was bound.
83 *
84 * Exposed so a geometry function can close over the same live value
85 * advance() itself reads, for reflowing bounds placed before scrolling
86 * began.
87 */
88 [[nodiscard]] const std::shared_ptr<MappedState<glm::vec2>>& scroll_offset() const
89 {
90 return m_scroll_offset;
91 }
92
93 /**
94 * @brief Reset both cursors to the lower of their two current Y values.
95 *
96 * Used after a row spanning several columns so that independent column
97 * cursors resume from a common baseline.
98 *
99 * @note LayoutCursor copies share one MappedState. Calling sync_to on two
100 * cursors copied from one another is a no-op, since both already
101 * reference the same baseline.
102 */
104 {
105 const float y = std::min(m_state->value, other.m_state->value);
106 m_state->write(y);
107 other.m_state->write(y);
108 }
109
110 /**
111 * @brief Advance the cursor downward by @p height and return the NDC
112 * AABB occupied by the primitive just placed, scoped to the
113 * cursor's column extents.
114 */
116 {
117 const float top = m_state->value;
118 const float bot = top - height;
119 m_state->write(bot);
120
121 Kinesis::AABB2D bounds { .min = { m_x_min, bot }, .max = { m_x_max, top } };
122 if (m_scroll_offset)
123 bounds = bounds.translated(m_scroll_offset->value);
124 return bounds;
125 }
126
127 /**
128 * @brief Advance without returning bounds. Use for padding between primitives.
129 */
130 void skip(float height) { m_state->write(m_state->value - height); }
131
132 /**
133 * @brief Reset to @p y_start. Existing closures over state() will reflow
134 * on their next sync() tick.
135 */
136 void reset(float y_start = 1.F) { m_state->write(y_start); }
137
138private:
139 std::shared_ptr<MappedState<float>> m_state;
140 std::shared_ptr<MappedState<glm::vec2>> m_scroll_offset;
141 float m_x_min { -1.F };
142 float m_x_max { 1.F };
143};
144
145} // namespace MayaFlux::Portal::Forma
uint64_t offset
Definition Composite.cpp:10
uint32_t height
void bind_scroll(std::shared_ptr< MappedState< glm::vec2 > > offset)
Bind a live scroll offset.
std::shared_ptr< MappedState< float > > state() const
Shared baseline state.
void sync_to(LayoutCursor &other)
Reset both cursors to the lower of their two current Y values.
void reset(float y_start=1.F)
Reset to y_start.
LayoutCursor(float y_start, float x_min=-1.F, float x_max=1.F)
Construct a cursor at y_start scoped to a horizontal column.
const std::shared_ptr< MappedState< glm::vec2 > > & scroll_offset() const
The bound scroll offset, or nullptr if none was bound.
Kinesis::AABB2D advance(float height)
Advance the cursor downward by height and return the NDC AABB occupied by the primitive just placed,...
std::shared_ptr< MappedState< glm::vec2 > > m_scroll_offset
std::shared_ptr< MappedState< float > > m_state
void skip(float height)
Advance without returning bounds.
Reactive Y-position accumulator for vertical primitive stacking.
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
Value carrier for a Mapped primitive.
Definition Mapped.hpp:40