raui_material/component/interactive/
button_paper.rs

1use crate::{
2    component::containers::paper::PaperProps,
3    theme::{ThemeColor, ThemeProps, ThemeVariant, ThemedImageMaterial, ThemedWidgetProps},
4};
5use raui_core::{
6    PropsData, Scalar, make_widget,
7    props::Props,
8    unpack_named_slots,
9    widget::{
10        component::{
11            WidgetComponent,
12            containers::content_box::content_box,
13            image_box::{ImageBoxProps, image_box},
14            interactive::button::{ButtonProps, button},
15        },
16        context::WidgetContext,
17        node::WidgetNode,
18        unit::{
19            content::ContentBoxItemLayout,
20            image::{ImageBoxColor, ImageBoxImageScaling, ImageBoxMaterial},
21        },
22    },
23};
24use serde::{Deserialize, Serialize};
25
26#[derive(PropsData, Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
27#[props_data(raui_core::props::PropsData)]
28#[prefab(raui_core::Prefab)]
29pub enum ButtonPaperOverrideStyle {
30    #[default]
31    None,
32    Default,
33    Selected,
34    Triggered,
35}
36
37fn button_paper_content(context: WidgetContext) -> WidgetNode {
38    let WidgetContext {
39        key,
40        props,
41        shared_props,
42        named_slots,
43        ..
44    } = context;
45    unpack_named_slots!(named_slots => content);
46
47    let mut button_props = props.read_cloned_or_default::<ButtonProps>();
48    let paper_props = props.read_cloned_or_default::<PaperProps>();
49    let themed_props = props.read_cloned_or_default::<ThemedWidgetProps>();
50    let override_style = props.read_cloned_or_default::<ButtonPaperOverrideStyle>();
51
52    if override_style != ButtonPaperOverrideStyle::None {
53        button_props.selected = override_style == ButtonPaperOverrideStyle::Selected;
54        button_props.trigger = override_style == ButtonPaperOverrideStyle::Triggered;
55        button_props.context = false;
56    }
57
58    let items = match themed_props.variant {
59        ThemeVariant::ContentOnly => vec![content],
60        ThemeVariant::Filled => {
61            let button_background = shared_props.map_or_default::<ThemeProps, _, _>(|props| {
62                if button_props.trigger || button_props.context {
63                    props
64                        .button_backgrounds
65                        .get(&paper_props.variant)
66                        .cloned()
67                        .unwrap_or_default()
68                        .trigger
69                } else if button_props.selected {
70                    props
71                        .button_backgrounds
72                        .get(&paper_props.variant)
73                        .cloned()
74                        .unwrap_or_default()
75                        .selected
76                } else {
77                    props
78                        .button_backgrounds
79                        .get(&paper_props.variant)
80                        .cloned()
81                        .unwrap_or_default()
82                        .default
83                }
84            });
85            let button_colors = shared_props
86                .map_or_default::<ThemeProps, _, _>(|props| props.active_colors.clone());
87            let image = match button_background {
88                ThemedImageMaterial::Color => {
89                    let color = match themed_props.color {
90                        ThemeColor::Default => button_colors.main.default.main,
91                        ThemeColor::Primary => button_colors.main.primary.main,
92                        ThemeColor::Secondary => button_colors.main.secondary.main,
93                    };
94                    ImageBoxProps {
95                        material: ImageBoxMaterial::Color(ImageBoxColor {
96                            color,
97                            ..Default::default()
98                        }),
99                        ..Default::default()
100                    }
101                }
102                ThemedImageMaterial::Image(material) => ImageBoxProps {
103                    material: ImageBoxMaterial::Image(material),
104                    ..Default::default()
105                },
106                ThemedImageMaterial::Procedural(material) => ImageBoxProps {
107                    material: ImageBoxMaterial::Procedural(material),
108                    ..Default::default()
109                },
110            };
111            let props = Props::new(ContentBoxItemLayout {
112                depth: Scalar::NEG_INFINITY,
113                ..Default::default()
114            })
115            .with(image);
116            let background = make_widget!(image_box)
117                .key("background")
118                .merge_props(props)
119                .into();
120            if let Some(frame) = paper_props.frame {
121                let color = match themed_props.color {
122                    ThemeColor::Default => button_colors.main.default.dark,
123                    ThemeColor::Primary => button_colors.main.primary.dark,
124                    ThemeColor::Secondary => button_colors.main.secondary.dark,
125                };
126                let props = Props::new(ContentBoxItemLayout {
127                    depth: Scalar::NEG_INFINITY,
128                    ..Default::default()
129                })
130                .with(ImageBoxProps {
131                    material: ImageBoxMaterial::Color(ImageBoxColor {
132                        color,
133                        scaling: ImageBoxImageScaling::Frame(frame),
134                    }),
135                    ..Default::default()
136                });
137                let frame = make_widget!(image_box)
138                    .key("frame")
139                    .merge_props(props)
140                    .into();
141                vec![background, frame, content]
142            } else {
143                vec![background, content]
144            }
145        }
146        ThemeVariant::Outline => {
147            if let Some(frame) = paper_props.frame {
148                let button_colors = shared_props
149                    .map_or_default::<ThemeProps, _, _>(|props| props.active_colors.clone());
150                let color = match themed_props.color {
151                    ThemeColor::Default => button_colors.main.default.dark,
152                    ThemeColor::Primary => button_colors.main.primary.dark,
153                    ThemeColor::Secondary => button_colors.main.secondary.dark,
154                };
155                let props = Props::new(ContentBoxItemLayout {
156                    depth: Scalar::NEG_INFINITY,
157                    ..Default::default()
158                })
159                .with(ImageBoxProps {
160                    material: ImageBoxMaterial::Color(ImageBoxColor {
161                        color,
162                        scaling: ImageBoxImageScaling::Frame(frame),
163                    }),
164                    ..Default::default()
165                });
166                let frame = make_widget!(image_box)
167                    .key("frame")
168                    .merge_props(props)
169                    .into();
170                vec![frame, content]
171            } else {
172                vec![content]
173            }
174        }
175    };
176
177    make_widget!(content_box)
178        .key(key)
179        .merge_props(props.clone())
180        .listed_slots(items)
181        .into()
182}
183
184pub fn button_paper(context: WidgetContext) -> WidgetNode {
185    button_paper_impl(make_widget!(button), context)
186}
187
188pub fn button_paper_impl(component: WidgetComponent, context: WidgetContext) -> WidgetNode {
189    let WidgetContext {
190        idref,
191        key,
192        props,
193        named_slots,
194        ..
195    } = context;
196    unpack_named_slots!(named_slots => content);
197
198    component
199        .key(key)
200        .maybe_idref(idref.cloned())
201        .merge_props(props.clone())
202        .named_slot(
203            "content",
204            make_widget!(button_paper_content)
205                .key("content")
206                .merge_props(props.clone())
207                .named_slot("content", content),
208        )
209        .into()
210}