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/align.rs
1use crate::{
2 event::DispatchedEvent,
3 text::{word_boundaries::WordBoundariesPolicy, IsRect, SelectionDirection, SelectionType},
4};
5use pathfinder_geometry::rect::RectF;
6 
7use super::{
8 AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext, Point,
9 SelectableElement, Selection, SelectionFragment, SizeConstraint,
10};
11use pathfinder_geometry::vector::{vec2f, Vector2F};
12 
13pub struct Align {
14 child: Box<dyn Element>,
15 alignment: Vector2F,
16 size: Option<Vector2F>,
17}
18 
19/// By default, Align centers a child element
20impl Align {
21 pub fn new(child: Box<dyn Element>) -> Self {
22 Self {
23 child,
24 alignment: Vector2F::zero(),
25 size: None,
26 }
27 }
28 
29 pub fn top_center(mut self) -> Self {
30 self.alignment = vec2f(0.0, -1.0);
31 self
32 }
33 
34 pub fn top_right(mut self) -> Self {
35 self.alignment = vec2f(1.0, -1.0);
36 self
37 }
38 
39 pub fn top_left(mut self) -> Self {
40 self.alignment = vec2f(-1., -1.);
41 self
42 }
43 
44 pub fn bottom_center(mut self) -> Self {
45 self.alignment = vec2f(0.0, 1.0);
46 self
47 }
48 
49 pub fn bottom_right(mut self) -> Self {
50 self.alignment = vec2f(1.0, 1.0);
51 self
52 }
53 
54 pub fn bottom_left(mut self) -> Self {
55 self.alignment = vec2f(-1., 1.0);
56 self
57 }
58 
59 pub fn right(mut self) -> Self {
60 self.alignment = vec2f(1.0, 0.);
61 self
62 }
63 
64 pub fn left(mut self) -> Self {
65 self.alignment = vec2f(-1., 0.);
66 self
67 }
68}
69 
70impl Element for Align {
71 fn layout(
72 &mut self,
73 constraint: SizeConstraint,
74 ctx: &mut LayoutContext,
75 app: &AppContext,
76 ) -> Vector2F {
77 let mut size = constraint.max;
78 
79 let child_constraint = SizeConstraint::new(Vector2F::zero(), constraint.max);
80 let child_size = self.child.layout(child_constraint, ctx, app);
81 
82 if size.x().is_infinite() {
83 size.set_x(child_size.x().max(constraint.min.x()));
84 }
85 if size.y().is_infinite() {
86 size.set_y(child_size.y().max(constraint.min.y()));
87 }
88 self.size = Some(size);
89 size
90 }
91 
92 fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) {
93 self.child.after_layout(ctx, app);
94 }
95 
96 fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
97 let self_center = self.size.unwrap() / 2.0;
98 let self_target = self_center + self_center * self.alignment;
99 let child_center = self.child.size().unwrap() / 2.0;
100 let mut child_target = child_center + child_center * self.alignment;
101 // Make sure the child_target cannot extend past self which may happen if child size is
102 // larger than self size.
103 child_target = child_target.min(self_target);
104 let child_origin = origin - (child_target - self_target);
105 self.child.paint(child_origin, ctx, app);
106 }
107 
108 fn dispatch_event(
109 &mut self,
110 event: &DispatchedEvent,
111 ctx: &mut EventContext,
112 app: &AppContext,
113 ) -> bool {
114 self.child.dispatch_event(event, ctx, app)
115 }
116 
117 fn size(&self) -> Option<Vector2F> {
118 self.size
119 }
120 
121 fn origin(&self) -> Option<Point> {
122 self.child.origin()
123 }
124 
125 fn as_selectable_element(&self) -> Option<&dyn SelectableElement> {
126 Some(self as &dyn SelectableElement)
127 }
128 
129 #[cfg(any(test, feature = "test-util"))]
130 fn debug_text_content(&self) -> Option<String> {
131 self.child.debug_text_content()
132 }
133}
134 
135impl SelectableElement for Align {
136 fn get_selection(
137 &self,
138 selection_start: Vector2F,
139 selection_end: Vector2F,
140 is_rect: IsRect,
141 ) -> Option<Vec<SelectionFragment>> {
142 self.child
143 .as_selectable_element()
144 .and_then(|selectable_child| {
145 selectable_child.get_selection(selection_start, selection_end, is_rect)
146 })
147 }
148 
149 fn expand_selection(
150 &self,
151 point: Vector2F,
152 direction: SelectionDirection,
153 unit: SelectionType,
154 word_boundaries_policy: &WordBoundariesPolicy,
155 ) -> Option<Vector2F> {
156 self.child
157 .as_selectable_element()
158 .and_then(|selectable_child| {
159 selectable_child.expand_selection(point, direction, unit, word_boundaries_policy)
160 })
161 }
162 
163 fn is_point_semantically_before(
164 &self,
165 absolute_point: Vector2F,
166 absolute_point_other: Vector2F,
167 ) -> Option<bool> {
168 self.child
169 .as_selectable_element()
170 .and_then(|selectable_child| {
171 selectable_child.is_point_semantically_before(absolute_point, absolute_point_other)
172 })
173 }
174 
175 fn smart_select(
176 &self,
177 absolute_point: Vector2F,
178 smart_select_fn: crate::elements::SmartSelectFn,
179 ) -> Option<(Vector2F, Vector2F)> {
180 self.child
181 .as_selectable_element()
182 .and_then(|selectable_child| {
183 selectable_child.smart_select(absolute_point, smart_select_fn)
184 })
185 }
186 
187 fn calculate_clickable_bounds(&self, current_selection: Option<Selection>) -> Vec<RectF> {
188 self.child
189 .as_selectable_element()
190 .map(|selectable_child| selectable_child.calculate_clickable_bounds(current_selection))
191 .unwrap_or_default()
192 }
193}
194