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-core/src/elements/stack/save_position.rs
StratoSDK / crates / strato-ui-core / src / elements / stack / save_position.rs
1use pathfinder_geometry::{rect::RectF, vector::Vector2F};
2 
3use crate::elements::Selection;
4use crate::EntityId;
5use crate::{
6 elements::{Point, SelectableElement, SelectionFragment},
7 event::DispatchedEvent,
8 text::{word_boundaries::WordBoundariesPolicy, IsRect, SelectionDirection, SelectionType},
9 AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext,
10 SizeConstraint,
11};
12 
13pub struct SavePosition {
14 child: Box<dyn Element>,
15 position_id: String,
16 for_single_frame: bool,
17}
18 
19impl SavePosition {
20 pub fn new(child: Box<dyn Element>, position_id: &str) -> Self {
21 Self {
22 child,
23 position_id: position_id.to_string(),
24 for_single_frame: false,
25 }
26 }
27 
28 /// Only saves the position for a single frame. At the start
29 /// of rendering the next frame the position is cleared.
30 pub fn for_single_frame(mut self) -> Self {
31 self.for_single_frame = true;
32 self
33 }
34}
35 
36impl Element for SavePosition {
37 fn layout(
38 &mut self,
39 constraint: SizeConstraint,
40 ctx: &mut LayoutContext,
41 app: &AppContext,
42 ) -> Vector2F {
43 self.child.layout(constraint, ctx, app)
44 }
45 
46 fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) {
47 self.child.after_layout(ctx, app);
48 }
49 
50 fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
51 if self.for_single_frame {
52 ctx.position_cache.cache_position_for_one_frame(
53 self.position_id.clone(),
54 RectF::new(
55 origin,
56 self.child.size().expect("size must be set before paint"),
57 ),
58 );
59 } else {
60 ctx.position_cache.cache_position_indefinitely(
61 self.position_id.clone(),
62 RectF::new(
63 origin,
64 self.child.size().expect("size must be set before paint"),
65 ),
66 );
67 }
68 
69 self.child.paint(origin, ctx, app);
70 }
71 
72 fn dispatch_event(
73 &mut self,
74 event: &DispatchedEvent,
75 ctx: &mut EventContext,
76 app: &AppContext,
77 ) -> bool {
78 self.child.dispatch_event(event, ctx, app)
79 }
80 
81 fn size(&self) -> Option<Vector2F> {
82 self.child.size()
83 }
84 
85 fn origin(&self) -> Option<Point> {
86 self.child.origin()
87 }
88 
89 fn as_selectable_element(&self) -> Option<&dyn SelectableElement> {
90 Some(self as &dyn SelectableElement)
91 }
92 
93 #[cfg(any(test, feature = "test-util"))]
94 fn debug_text_content(&self) -> Option<String> {
95 self.child.debug_text_content()
96 }
97}
98 
99impl SelectableElement for SavePosition {
100 fn get_selection(
101 &self,
102 selection_start: Vector2F,
103 selection_end: Vector2F,
104 is_rect: IsRect,
105 ) -> Option<Vec<SelectionFragment>> {
106 self.child
107 .as_selectable_element()
108 .and_then(|selectable_child| {
109 selectable_child.get_selection(selection_start, selection_end, is_rect)
110 })
111 }
112 
113 fn expand_selection(
114 &self,
115 point: Vector2F,
116 direction: SelectionDirection,
117 unit: SelectionType,
118 word_boundaries_policy: &WordBoundariesPolicy,
119 ) -> Option<Vector2F> {
120 self.child
121 .as_selectable_element()
122 .and_then(|selectable_child| {
123 selectable_child.expand_selection(point, direction, unit, word_boundaries_policy)
124 })
125 }
126 
127 fn is_point_semantically_before(
128 &self,
129 absolute_point: Vector2F,
130 absolute_point_other: Vector2F,
131 ) -> Option<bool> {
132 self.child
133 .as_selectable_element()
134 .and_then(|selectable_child| {
135 selectable_child.is_point_semantically_before(absolute_point, absolute_point_other)
136 })
137 }
138 
139 fn smart_select(
140 &self,
141 absolute_point: Vector2F,
142 smart_select_fn: crate::elements::SmartSelectFn,
143 ) -> Option<(Vector2F, Vector2F)> {
144 self.child
145 .as_selectable_element()
146 .and_then(|selectable_child| {
147 selectable_child.smart_select(absolute_point, smart_select_fn)
148 })
149 }
150 
151 fn calculate_clickable_bounds(&self, current_selection: Option<Selection>) -> Vec<RectF> {
152 self.child
153 .as_selectable_element()
154 .map(|selectable_child| selectable_child.calculate_clickable_bounds(current_selection))
155 .unwrap_or_default()
156 }
157}
158 
159pub fn get_rich_content_position_id(view_id: &EntityId) -> String {
160 format!("rich_content_position_{view_id}")
161}
162