MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Scrollable.cpp
Go to the documentation of this file.
1#include "Scrollable.hpp"
2
4
7
9
10namespace {
11
12 /// @brief (position_frac, extent_frac) pair an indicator's geometry function reacts to.
13 glm::vec2 scroll_extent(const Kinesis::ScrollState& scroll) noexcept
14 {
15 const float content_h = scroll.content_bounds.height();
16 const float viewport_h = scroll.viewport_bounds.height();
17 const float max_offset_y = std::max(0.F, content_h - viewport_h);
18
19 const float extent_frac = content_h > 0.F ? std::clamp(viewport_h / content_h, 0.F, 1.F) : 1.F;
20 const float position_frac = max_offset_y > 0.F ? std::clamp(scroll.offset.y / max_offset_y, 0.F, 1.F) : 0.F;
21
22 return { position_frac, extent_frac };
23 }
24
25 void reflow(
26 Kinesis::ScrollState& scroll,
27 MappedState<glm::vec2>& offset,
28 std::vector<Tracked>& tracked,
29 Layer& layer,
30 Mapped<glm::vec2>* indicator)
31 {
32 offset.write(scroll.offset);
33 for (auto& t : tracked) {
34 const Kinesis::AABB2D shifted = t.base_bounds.translated(scroll.offset);
35 layer.set_bounds(t.id, shifted);
36 t.reposition(shifted);
37 }
38
39 if (indicator && indicator->state) {
40 indicator->state->write(scroll_extent(scroll));
41 indicator->sync(&layer);
42 }
43 }
44
45} // namespace
46
48 FormaTarget in_buf,
49 Surface& surface,
50 Kinesis::AABB2D viewport)
51{
52 buf = std::move(in_buf);
53 viewport_bounds = viewport;
54
56
57 if (buf) {
58 if (auto render = buf->get_render_processor())
59 render->set_scissor(Kinesis::Scissor::from(viewport));
60 }
61
62 Element el = Element {}
63 .with_name("scrollable_viewport")
64 .with_bounds(viewport)
66
67 viewport_id = surface.layer().add(el);
68
69 scroll = std::make_shared<Kinesis::ScrollState>();
70 scroll->viewport_bounds = viewport;
71 scroll->content_bounds = viewport;
72
73 offset = std::make_shared<MappedState<glm::vec2>>();
74
75 m_tracked = std::make_shared<std::vector<Tracked>>();
76 m_indicator = std::make_shared<Mapped<glm::vec2>>();
77
78 cursor_out = LayoutCursor(viewport.max.y, viewport.min.x, viewport.max.x);
80
81 surface.ctx().on_scroll(viewport_id, wheel_handler(surface));
82
83 return *this;
84}
85
86std::function<void(uint32_t, glm::vec2, double, double)>
88{
89 return [scroll_state = scroll, scroll_offset = offset, tracked = m_tracked,
90 indicator = m_indicator, surface, speed = m_wheel_speed](
91 uint32_t, glm::vec2, double dx, double dy) mutable {
92 Kinesis::apply_scroll(*scroll_state,
93 glm::vec2(static_cast<float>(dx), static_cast<float>(dy)) * speed);
94 reflow(*scroll_state, *scroll_offset, *tracked, surface.layer(), indicator.get());
95 };
96}
97
99 Layer& layer,
100 uint32_t id,
101 Kinesis::AABB2D base_bounds,
102 std::function<void(Kinesis::AABB2D)> reposition,
103 const FormaTarget& clip_buf)
104{
105 if (!scroll || !m_tracked)
106 return *this;
107
108 base_bounds = base_bounds.translated(-scroll->offset);
109
110 scroll->content_bounds.min = glm::min(scroll->content_bounds.min, base_bounds.min);
111 scroll->content_bounds.max = glm::max(scroll->content_bounds.max, base_bounds.max);
112
113 if (clip_buf) {
114 if (auto render = clip_buf->get_render_processor())
115 render->set_scissor(Kinesis::Scissor::from(viewport_bounds));
116 }
117
118 layer.relate(viewport_id, id);
119
120 m_tracked->push_back({ .id = id, .base_bounds = base_bounds, .reposition = std::move(reposition) });
121 layer.set_clip(id, viewport_bounds);
122
123 if (m_indicator && m_indicator->state) {
124 m_indicator->state->write(scroll_extent(*scroll));
125 m_indicator->sync();
126 }
127
128 return *this;
129}
130
131void Scrollable::scroll_by(Layer& layer, glm::vec2 delta)
132{
133 if (!scroll || !offset || !m_tracked)
134 return;
135
137 reflow(*scroll, *offset, *m_tracked, layer, m_indicator.get());
138}
139
141{
142 if (!scroll || !offset || !m_tracked || !m_indicator)
143 return *this;
144
145 *m_indicator = std::move(el);
146 m_indicator->state->write(scroll_extent(*scroll));
147 m_indicator->sync(&surface.layer());
148
149 auto scroll_state = scroll;
150 auto scroll_offset = offset;
151 auto tracked = m_tracked;
152 auto ind = m_indicator;
153
154 surface.ctx().on_drag(ind->element.id, IO::MouseButtons::Left,
155 [scroll_state, scroll_offset, tracked, ind, surface, track](uint32_t, glm::vec2 ndc) mutable {
156 const float max_offset_y = std::max(0.F,
157 scroll_state->content_bounds.height() - scroll_state->viewport_bounds.height());
158 if (max_offset_y <= 0.F)
159 return;
160
161 const float frac = std::clamp((track.max.y - ndc.y) / track.height(), 0.F, 1.F);
162 const float target = frac * max_offset_y;
163
164 Kinesis::apply_scroll(*scroll_state, { 0.F, target - scroll_state->offset.y });
165 reflow(*scroll_state, *scroll_offset, *tracked, surface.layer(), ind.get());
166 });
167
168 return *this;
169}
170
172 FormaTarget buf,
173 Surface& surface,
174 Kinesis::AABB2D viewport,
175 glm::vec3 background_color,
176 float wheel_speed)
177{
178 return Scrollable {}
179 .background_color(background_color)
180 .wheel_speed(wheel_speed)
181 .place(std::move(buf), surface, viewport);
182}
183
184} // namespace MayaFlux::Portal::Forma
uint64_t offset
Definition Composite.cpp:10
virtual std::shared_ptr< RenderProcessor > get_render_processor() const
Get a RenderProcessor suitable for rendering this buffer.
Definition VKBuffer.hpp:605
void on_scroll(uint32_t id, ScrollFn fn)
Called on scroll while the cursor is over an element.
Definition Context.cpp:68
void on_drag(uint32_t id, IO::MouseButtons btn, MoveFn fn)
Called on each mouse-move event while btn is held, tracking the element where the drag began even whe...
Definition Context.cpp:53
void submit(const std::vector< uint8_t > &bytes) const
Replace this target's geometry.
Definition Target.cpp:51
Where an element's geometry and texture are written.
Definition Target.hpp:22
bool relate(uint32_t primary_id, uint32_t related_id)
Record that related_id belongs with primary_id.
Definition Layer.cpp:205
const Element * get(uint32_t id) const
Definition Layer.cpp:272
Slot add(Element element)
Add an element to the layer.
Definition Layer.cpp:9
bool set_clip(uint32_t id, std::optional< Kinesis::AABB2D > clip)
Confine hit testing of an element to a region, or lift the confinement with std::nullopt.
Definition Layer.cpp:73
Flat registry of Elements on a surface.
Definition Layer.hpp:24
void bind_scroll(std::shared_ptr< MappedState< glm::vec2 > > offset)
Bind a live scroll offset.
Reactive Y-position accumulator for vertical primitive stacking.
Context & ctx() noexcept
Access the event router.
Definition Surface.hpp:170
Layer & layer() noexcept
Access the spatial registry.
Definition Surface.hpp:159
Named owner of a (Window, Layer, Context) triple: the Forma canvas.
Definition Surface.hpp:121
void apply_scroll(NavigationState &st, float ticks)
Dolly eye along the current forward vector.
std::array< Kakshya::Vertex, 4 > filled_rect(Kinesis::AABB2D region, glm::vec3 color)
Generate a filled TRIANGLE_STRIP quad from an AABB2D.
Scrollable make_scrollable(FormaTarget buf, Surface &surface, Kinesis::AABB2D viewport, glm::vec3 background_color, float wheel_speed)
Construct a scrollable viewport.
AABB2D translated(glm::vec2 offset) const noexcept
Definition Bounds.hpp:84
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
static Scissor from(AABB2D ndc_bounds) noexcept
Construct directly from an NDC AABB2D.
Definition Scissor.hpp:37
Element & with_bounds(Kinesis::AABB2D b)
Set the fast-reject AABB.
Definition Element.hpp:109
Element & with_buffer(FormaTarget target)
Attach a FormaBuffer, or a slice of one, as the rendered output for this region.
Definition Element.hpp:195
Element & with_name(std::string n)
Set the human-readable name used in Lila introspection and debug output.
Definition Element.hpp:304
A bounded, renderable region on a window surface.
Definition Element.hpp:61
Infrastructure for a continuously-mapped value whose GPU geometry tracks it.
Definition Mapped.hpp:96
MAYAFLUX_API std::function< void(uint32_t, glm::vec2, double, double)> wheel_handler(Surface &surface) const
A self-contained wheel-scroll handler bound to this Scrollable's own state.
Kinesis::AABB2D viewport_bounds
NDC region occupied by the viewport. Valid after place().
std::shared_ptr< MappedState< glm::vec2 > > offset
Reactive mirror of scroll->offset, bound into cursor_out. Valid after place().
std::shared_ptr< Mapped< glm::vec2 > > m_indicator
uint32_t viewport_id
Element id of the viewport background. Valid after place().
FormaTarget buf
FormaBuffer backing the viewport background. Valid after place().
MAYAFLUX_API Scrollable & indicator(Surface &surface, Mapped< glm::vec2 > el, Kinesis::AABB2D track)
Attach a caller-built element as a live, draggable scroll indicator.
MAYAFLUX_API void scroll_by(Layer &layer, glm::vec2 delta)
Apply a scroll delta and reflow every tracked element.
MAYAFLUX_API Scrollable & track(Layer &layer, uint32_t id, Kinesis::AABB2D base_bounds, std::function< void(Kinesis::AABB2D)> reposition, const FormaTarget &clip_buf={})
Register an element for scroll reflow.
MAYAFLUX_API Scrollable & place(FormaTarget in_buf, Surface &surface, Kinesis::AABB2D viewport)
Register the viewport element, wire wheel scrolling, and populate scroll, offset, buf,...
glm::vec3 m_background
Background fill color. Default: glm::vec3(0.08F, 0.08F, 0.1F).
std::shared_ptr< std::vector< Tracked > > m_tracked
float m_wheel_speed
Content-space units scrolled per wheel tick. Default: 0.05F.
Scrollable & wheel_speed(float s)
std::shared_ptr< Kinesis::ScrollState > scroll
Clamped scroll offset and content/viewport extents. Valid after place().
LayoutCursor cursor_out
Cursor scoped to the viewport, bound to offset. Valid after place().
Scrollable & background_color(glm::vec3 c)
A clipped, scroll-offset viewport over content taller than it.