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/ui_components/toggle_button.rs
StratoSDK / crates / strato-ui-core / src / ui_components / toggle_button.rs
1use pathfinder_geometry::vector::vec2f;
2 
3use crate::{
4 elements::{
5 ChildAnchor, ConstrainedBox, Container, Empty, Hoverable, MouseState, MouseStateHandle,
6 OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Stack,
7 },
8 scene::Border,
9 Element,
10};
11 
12use super::{
13 components::{UiComponent, UiComponentStyles},
14 text::Span,
15};
16 
17/// A button element used to toggle a single value on or off.
18pub struct ToggleButton {
19 label: ToggleButtonLabel,
20 tooltip: Option<Box<dyn Element>>,
21 toggled_on: bool,
22 mouse_state: MouseStateHandle,
23 styles: UiComponentStyles,
24 hovered_styles: Option<UiComponentStyles>,
25 toggled_on_styles: Option<UiComponentStyles>,
26}
27 
28pub enum ToggleButtonLabel {
29 None,
30 Text(String),
31}
32 
33impl<S> From<S> for ToggleButtonLabel
34where
35 S: Into<String>,
36{
37 fn from(label: S) -> Self {
38 Self::Text(label.into())
39 }
40}
41 
42impl ToggleButton {
43 pub fn new(mouse_state: MouseStateHandle, styles: UiComponentStyles) -> Self {
44 Self {
45 label: ToggleButtonLabel::None,
46 toggled_on: false,
47 tooltip: None,
48 mouse_state,
49 styles,
50 hovered_styles: None,
51 toggled_on_styles: None,
52 }
53 }
54 
55 pub fn with_label(mut self, label: impl Into<ToggleButtonLabel>) -> Self {
56 self.label = label.into();
57 self
58 }
59 
60 pub fn with_tooltip(mut self, tooltip: Box<dyn Element>) -> Self {
61 self.tooltip = Some(tooltip);
62 self
63 }
64 
65 pub fn with_toggled_on(mut self, toggled_on: bool) -> Self {
66 self.toggled_on = toggled_on;
67 self
68 }
69 
70 pub fn with_hovered_styles(mut self, styles: UiComponentStyles) -> Self {
71 self.hovered_styles = Some(styles);
72 self
73 }
74 
75 pub fn with_toggled_on_styles(mut self, styles: UiComponentStyles) -> Self {
76 self.toggled_on_styles = Some(styles);
77 self
78 }
79 
80 fn styles(&self, state: &MouseState) -> UiComponentStyles {
81 let mut styles = self.styles;
82 if self.toggled_on {
83 if let Some(overlay) = self.toggled_on_styles {
84 styles = styles.merge(overlay);
85 }
86 }
87 
88 if state.is_mouse_over_element() {
89 if let Some(overlay) = self.hovered_styles {
90 styles = styles.merge(overlay);
91 }
92 }
93 styles
94 }
95 
96 fn render_button(&self, styles: &UiComponentStyles) -> Box<dyn Element> {
97 let label = match &self.label {
98 ToggleButtonLabel::Text(text) => Span::new(text.clone(), *styles).build().finish(),
99 ToggleButtonLabel::None => Empty::new().finish(),
100 };
101 
102 let mut constrained_box = ConstrainedBox::new(label);
103 if let Some(width) = styles.width {
104 constrained_box = constrained_box.with_width(width);
105 }
106 if let Some(height) = styles.height {
107 constrained_box = constrained_box.with_height(height);
108 };
109 
110 let mut button = Container::new(constrained_box.finish());
111 
112 if let Some(background) = styles.background {
113 button = button.with_background(background);
114 }
115 
116 if let Some(corner_radius) = styles.border_radius {
117 button = button.with_corner_radius(corner_radius);
118 }
119 
120 if let Some(padding) = styles.padding {
121 button = button
122 .with_padding_top(padding.top)
123 .with_padding_bottom(padding.bottom)
124 .with_padding_left(padding.left)
125 .with_padding_right(padding.right);
126 }
127 
128 let mut border = Border::all(styles.border_width.unwrap_or_default());
129 if let Some(border_fill) = styles.border_color {
130 border = border.with_border_fill(border_fill);
131 }
132 button = button.with_border(border);
133 
134 button.finish()
135 }
136}
137 
138impl UiComponent for ToggleButton {
139 type ElementType = Hoverable;
140 
141 fn build(mut self) -> Hoverable {
142 Hoverable::new(self.mouse_state.clone(), |state| {
143 let styles = self.styles(state);
144 let button = self.render_button(&styles);
145 let mut stack = Stack::new().with_child(button);
146 
147 if state.is_hovered() {
148 if let Some(tooltip) = self.tooltip.take() {
149 stack.add_positioned_overlay_child(
150 tooltip,
151 OffsetPositioning::offset_from_parent(
152 vec2f(0., 10.),
153 ParentOffsetBounds::Unbounded,
154 ParentAnchor::BottomRight,
155 ChildAnchor::TopRight,
156 ),
157 )
158 }
159 }
160 
161 stack.finish()
162 })
163 }
164 
165 fn with_style(mut self, style: UiComponentStyles) -> Self {
166 self.styles = style;
167 self
168 }
169}
170