Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/examples/manual-scrolling/root_view.rs
1use pathfinder_geometry::rect::RectF;
2use pathfinder_geometry::vector::vec2f;
3use pathfinder_geometry::vector::Vector2F;
4use strato_ui::elements::new_scrollable::AxisConfiguration;
5use strato_ui::elements::new_scrollable::ClippedAxisConfiguration;
6use strato_ui::elements::new_scrollable::DualAxisConfig;
7use strato_ui::elements::new_scrollable::NewScrollableElement;
8use strato_ui::elements::new_scrollable::ScrollableAppearance;
9use strato_ui::elements::new_scrollable::ScrollableAxis;
10use strato_ui::elements::Axis;
11 
12use strato_ui::elements::ChildView;
13use strato_ui::elements::ClippedScrollStateHandle;
14use strato_ui::elements::NewScrollable;
15use strato_ui::elements::Point;
16use strato_ui::elements::ScrollData;
17use strato_ui::elements::ScrollStateHandle;
18use strato_ui::keymap::FixedBinding;
19use strato_ui::units::Pixels;
20use strato_ui::AppContext;
21use strato_ui::TypedActionView;
22use strato_ui::ViewHandle;
23use strato_ui::{
24 elements::{ConstrainedBox, ParentElement, Rect, ScrollbarWidth, Stack},
25 Element, Entity, View, ViewContext,
26};
27 
28use strato_ui::color::ColorU;
29 
30pub fn init(ctx: &mut AppContext) {
31 use strato_ui::keymap::macros::*;
32 
33 // Add bindings to trigger actions in the subview.
34 ctx.register_fixed_bindings([
35 FixedBinding::new("up", SubViewAction::ScrollVertical(50.), id!("SubView")),
36 FixedBinding::new("down", SubViewAction::ScrollVertical(-50.), id!("SubView")),
37 ]);
38}
39 
40pub struct RootView {
41 // RootView "owns" a viewhandle to the subview.
42 sub_view: ViewHandle<SubView>,
43}
44 
45impl RootView {
46 pub fn new(ctx: &mut ViewContext<Self>) -> Self {
47 // Adding typed action view allows the view to receive keydown events.
48 let sub_view = ctx.add_typed_action_view(|ctx| {
49 let view = SubView::default();
50 // Need the view to be focused for keydown actions to be dispatched to it.
51 ctx.focus_self();
52 view
53 });
54 Self { sub_view }
55 }
56}
57 
58// Implement the entity trait.
59impl Entity for RootView {
60 type Event = ();
61}
62 
63// Implement the view trait so RootView could be considered as a view.
64impl View for RootView {
65 fn ui_name() -> &'static str {
66 "RootView"
67 }
68 
69 // Renders the child view of sub_view.
70 fn render(&self, _: &AppContext) -> Box<dyn Element> {
71 ChildView::new(&self.sub_view).finish()
72 }
73}
74 
75#[derive(Debug, Clone)]
76pub enum SubViewAction {
77 ScrollVertical(f32),
78}
79 
80#[derive(Default)]
81pub struct SubView {
82 pub scroll_state_horizontal: ClippedScrollStateHandle,
83 pub scroll_state_vertical: ScrollStateHandle,
84 pub scroll_top: f32,
85}
86 
87impl SubView {
88 pub fn new(_ctx: &mut ViewContext<Self>) -> Self {
89 SubView::default()
90 }
91}
92 
93impl Entity for SubView {
94 type Event = ();
95}
96impl View for SubView {
97 fn ui_name() -> &'static str {
98 "SubView"
99 }
100 
101 fn render(&self, _: &AppContext) -> Box<dyn Element> {
102 let axis_config = DualAxisConfig::Manual {
103 horizontal: AxisConfiguration::Clipped(ClippedAxisConfiguration {
104 handle: self.scroll_state_horizontal.clone(),
105 max_size: None,
106 stretch_child: false,
107 }),
108 vertical: AxisConfiguration::Manual(self.scroll_state_vertical.clone()),
109 child: ScrollableElement::new(self.scroll_top).finish_scrollable(),
110 };
111 let horizontally_scrollable = NewScrollable::horizontal_and_vertical(
112 axis_config,
113 ColorU::new(255, 255, 255, 150).into(),
114 ColorU::white().into(),
115 ColorU::new(100, 100, 100, 255).into(),
116 )
117 .with_horizontal_scrollbar(ScrollableAppearance::new(ScrollbarWidth::Auto, true))
118 .with_vertical_scrollbar(ScrollableAppearance::new(ScrollbarWidth::Auto, false));
119 
120 let constrained = ConstrainedBox::new(horizontally_scrollable.finish())
121 .with_height(250.)
122 .with_width(250.);
123 
124 Stack::new()
125 .with_child(Rect::new().with_background_color(ColorU::black()).finish())
126 .with_child(constrained.finish())
127 .finish()
128 }
129}
130impl TypedActionView for SubView {
131 type Action = SubViewAction;
132 
133 fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
134 match action {
135 SubViewAction::ScrollVertical(scroll_top) => {
136 // 250. viewport + 7. scrollbar width.
137 self.scroll_top = (self.scroll_top - *scroll_top).clamp(0., 257.);
138 }
139 };
140 ctx.notify();
141 }
142}
143 
144struct ScrollableElement {
145 size: Option<Vector2F>,
146 origin: Option<Point>,
147 scroll_top: f32,
148}
149 
150impl ScrollableElement {
151 fn new(scroll_top: f32) -> Self {
152 Self {
153 scroll_top,
154 size: None,
155 origin: None,
156 }
157 }
158}
159 
160impl Element for ScrollableElement {
161 fn layout(
162 &mut self,
163 constraint: strato_ui::SizeConstraint,
164 _: &mut strato_ui::LayoutContext,
165 _: &AppContext,
166 ) -> Vector2F {
167 let size = vec2f(
168 constraint.max_along(Axis::Horizontal).min(500.),
169 constraint.max_along(Axis::Vertical).min(500.),
170 );
171 self.size = Some(size);
172 size
173 }
174 
175 fn after_layout(&mut self, _: &mut strato_ui::AfterLayoutContext, _: &AppContext) {}
176 
177 fn paint(&mut self, origin: Vector2F, ctx: &mut strato_ui::PaintContext, _app: &AppContext) {
178 self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index()));
179 let adjusted_origin = origin - vec2f(0., self.scroll_top);
180 for i in 0..10 {
181 for j in 0..10 {
182 let color = (i + j) % 3;
183 let color = if color == 0 {
184 ColorU::new(255, 0, 0, 255)
185 } else if color == 1 {
186 ColorU::new(0, 255, 0, 255)
187 } else {
188 ColorU::new(0, 0, 255, 255)
189 };
190 
191 let cell_origin = adjusted_origin + vec2f(i as f32 * 50., j as f32 * 50.);
192 ctx.scene
193 .draw_rect_with_hit_recording(RectF::new(cell_origin, vec2f(50., 50.)))
194 .with_background(color);
195 }
196 }
197 }
198 
199 fn size(&self) -> Option<Vector2F> {
200 self.size
201 }
202 
203 fn origin(&self) -> Option<Point> {
204 self.origin
205 }
206 
207 fn dispatch_event(
208 &mut self,
209 _: &strato_ui::event::DispatchedEvent,
210 _: &mut strato_ui::EventContext,
211 _: &AppContext,
212 ) -> bool {
213 false
214 }
215}
216 
217impl NewScrollableElement for ScrollableElement {
218 fn axis(&self) -> ScrollableAxis {
219 ScrollableAxis::Vertical
220 }
221 
222 fn scroll_data(&self, axis: Axis, _app: &AppContext) -> Option<ScrollData> {
223 match axis {
224 Axis::Horizontal => None,
225 Axis::Vertical => Some(ScrollData {
226 scroll_start: Pixels::new(self.scroll_top),
227 visible_px: Pixels::new(self.size.unwrap().y()),
228 total_size: Pixels::new(500.),
229 }),
230 }
231 }
232 
233 fn scroll(
234 &mut self,
235 delta: strato_ui::units::Pixels,
236 axis: Axis,
237 ctx: &mut strato_ui::EventContext,
238 ) {
239 match axis {
240 Axis::Horizontal => (),
241 Axis::Vertical => {
242 ctx.dispatch_typed_action(SubViewAction::ScrollVertical(delta.as_f32()))
243 }
244 }
245 }
246 
247 fn axis_should_handle_scroll_wheel(&self, _axis: Axis) -> bool {
248 true
249 }
250}
251 
252impl TypedActionView for RootView {
253 type Action = ();
254}
255