raui_material/component/interactive/
slider_paper.rs1use crate::{
2 component::text_paper::{TextPaperProps, text_paper},
3 theme::{ThemeColor, ThemeProps, ThemedImageMaterial, ThemedSliderMaterial},
4};
5use raui_core::{
6 PropsData, make_widget, unpack_named_slots,
7 widget::{
8 component::{
9 WidgetComponent,
10 containers::content_box::content_box,
11 image_box::{ImageBoxProps, image_box},
12 interactive::slider_view::{SliderViewDirection, SliderViewProps, slider_view},
13 },
14 context::WidgetContext,
15 node::WidgetNode,
16 unit::{
17 content::ContentBoxItemLayout,
18 image::{ImageBoxColor, ImageBoxMaterial},
19 text::TextBoxSizeValue,
20 },
21 utils::Rect,
22 },
23};
24use serde::{Deserialize, Serialize};
25
26#[derive(PropsData, Debug, Clone, Serialize, Deserialize)]
27#[props_data(raui_core::props::PropsData)]
28#[prefab(raui_core::Prefab)]
29pub struct SliderPaperProps {
30 #[serde(default)]
31 pub variant: String,
32 #[serde(default = "SliderPaperProps::default_background_color")]
33 pub background_color: ThemeColor,
34 #[serde(default = "SliderPaperProps::default_filling_color")]
35 pub filling_color: ThemeColor,
36}
37
38impl Default for SliderPaperProps {
39 fn default() -> Self {
40 Self {
41 variant: Default::default(),
42 background_color: Self::default_background_color(),
43 filling_color: Self::default_filling_color(),
44 }
45 }
46}
47
48impl SliderPaperProps {
49 fn default_background_color() -> ThemeColor {
50 ThemeColor::Secondary
51 }
52
53 fn default_filling_color() -> ThemeColor {
54 ThemeColor::Primary
55 }
56}
57
58#[derive(PropsData, Debug, Default, Clone, Copy, Serialize, Deserialize)]
59#[props_data(raui_core::props::PropsData)]
60#[prefab(raui_core::Prefab)]
61pub struct NumericSliderPaperProps {
62 #[serde(default)]
63 pub fractional_digits_count: Option<usize>,
64}
65
66pub fn slider_paper(context: WidgetContext) -> WidgetNode {
67 slider_paper_impl(make_widget!(slider_view), context)
68}
69
70pub fn slider_paper_impl(component: WidgetComponent, context: WidgetContext) -> WidgetNode {
71 let WidgetContext {
72 idref,
73 key,
74 props,
75 shared_props,
76 named_slots,
77 ..
78 } = context;
79 unpack_named_slots!(named_slots => content);
80
81 let SliderPaperProps {
82 variant,
83 background_color,
84 filling_color,
85 } = props.read_cloned_or_default();
86 let anchors = props
87 .read::<SliderViewProps>()
88 .ok()
89 .map(|props| {
90 let percentage = props.get_percentage();
91 match props.direction {
92 SliderViewDirection::LeftToRight => Rect {
93 left: 0.0,
94 right: percentage,
95 top: 0.0,
96 bottom: 1.0,
97 },
98 SliderViewDirection::RightToLeft => Rect {
99 left: 1.0 - percentage,
100 right: 1.0,
101 top: 0.0,
102 bottom: 1.0,
103 },
104 SliderViewDirection::TopToBottom => Rect {
105 left: 0.0,
106 right: 1.0,
107 top: 0.0,
108 bottom: percentage,
109 },
110 SliderViewDirection::BottomToTop => Rect {
111 left: 0.0,
112 right: 1.0,
113 top: 1.0 - percentage,
114 bottom: 1.0,
115 },
116 }
117 })
118 .unwrap_or_default();
119 let (background, filling) = match shared_props.read::<ThemeProps>() {
120 Ok(props) => {
121 if let Some(material) = props.slider_variants.get(&variant).cloned() {
122 let background_color = match background_color {
123 ThemeColor::Default => props.active_colors.main.default.main,
124 ThemeColor::Primary => props.active_colors.main.primary.main,
125 ThemeColor::Secondary => props.active_colors.main.secondary.main,
126 };
127 let filling_color = match filling_color {
128 ThemeColor::Default => props.active_colors.main.default.main,
129 ThemeColor::Primary => props.active_colors.main.primary.main,
130 ThemeColor::Secondary => props.active_colors.main.secondary.main,
131 };
132 let ThemedSliderMaterial {
133 background,
134 filling,
135 } = material;
136 let background = match background {
137 ThemedImageMaterial::Color => ImageBoxProps {
138 material: ImageBoxMaterial::Color(ImageBoxColor {
139 color: background_color,
140 ..Default::default()
141 }),
142 ..Default::default()
143 },
144 ThemedImageMaterial::Image(mut data) => {
145 data.tint = filling_color;
146 ImageBoxProps {
147 material: ImageBoxMaterial::Image(data),
148 ..Default::default()
149 }
150 }
151 ThemedImageMaterial::Procedural(data) => ImageBoxProps {
152 material: ImageBoxMaterial::Procedural(data),
153 ..Default::default()
154 },
155 };
156 let filling = match filling {
157 ThemedImageMaterial::Color => ImageBoxProps {
158 material: ImageBoxMaterial::Color(ImageBoxColor {
159 color: filling_color,
160 ..Default::default()
161 }),
162 ..Default::default()
163 },
164 ThemedImageMaterial::Image(mut data) => {
165 data.tint = filling_color;
166 ImageBoxProps {
167 material: ImageBoxMaterial::Image(data),
168 ..Default::default()
169 }
170 }
171 ThemedImageMaterial::Procedural(data) => ImageBoxProps {
172 material: ImageBoxMaterial::Procedural(data),
173 ..Default::default()
174 },
175 };
176 (background, filling)
177 } else {
178 Default::default()
179 }
180 }
181 Err(_) => Default::default(),
182 };
183
184 component
185 .key(key)
186 .maybe_idref(idref.cloned())
187 .merge_props(props.clone())
188 .named_slot(
189 "content",
190 make_widget!(content_box)
191 .key("content")
192 .merge_props(props.clone())
193 .listed_slot(
194 make_widget!(image_box)
195 .key("background")
196 .with_props(background),
197 )
198 .listed_slot(
199 make_widget!(image_box)
200 .key("filling")
201 .with_props(ContentBoxItemLayout {
202 anchors,
203 ..Default::default()
204 })
205 .with_props(filling),
206 )
207 .listed_slot(content),
208 )
209 .into()
210}
211
212pub fn numeric_slider_paper(context: WidgetContext) -> WidgetNode {
213 numeric_slider_paper_impl(make_widget!(slider_paper), context)
214}
215
216pub fn numeric_slider_paper_impl(component: WidgetComponent, context: WidgetContext) -> WidgetNode {
217 let WidgetContext {
218 idref, key, props, ..
219 } = context;
220
221 let mut text = props.read_cloned_or_default::<TextPaperProps>();
222 text.width = TextBoxSizeValue::Fill;
223 text.height = TextBoxSizeValue::Fill;
224 let value = props
225 .read::<SliderViewProps>()
226 .ok()
227 .map(|props| props.get_value())
228 .unwrap_or_default();
229 text.text = if let Some(count) = props
230 .read_cloned_or_default::<NumericSliderPaperProps>()
231 .fractional_digits_count
232 {
233 format!("{value:.count$}")
234 } else {
235 value.to_string()
236 };
237
238 component
239 .key(key)
240 .maybe_idref(idref.cloned())
241 .merge_props(props.clone())
242 .named_slot(
243 "content",
244 make_widget!(text_paper)
245 .merge_props(props.clone())
246 .with_props(text),
247 )
248 .into()
249}