raui_material/component/interactive/
slider_paper.rs

1use 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}