Skip to main content

repose_material/material3/
scaffold.rs

1#![allow(non_snake_case)]
2
3use repose_core::*;
4use repose_ui::{Box, Column, ViewExt};
5
6use super::*;
7
8/// Position of the floating action button within a Scaffold.
9#[derive(Clone, Copy, Debug, PartialEq, Default)]
10pub enum FabPosition {
11    #[default]
12    End,
13    Center,
14}
15
16#[derive(Clone)]
17pub struct ScaffoldConfig {
18    pub modifier: Modifier,
19    pub top_bar: Option<View>,
20    pub bottom_bar: Option<View>,
21    pub floating_action_button: Option<View>,
22    pub snackbar_host: Option<View>,
23    pub container_color: Color,
24    pub content_color: Color,
25    pub fab_position: FabPosition,
26}
27
28impl Default for ScaffoldConfig {
29    fn default() -> Self {
30        Self {
31            modifier: Modifier::new(),
32            top_bar: None,
33            bottom_bar: None,
34            floating_action_button: None,
35            snackbar_host: None,
36            container_color: ScaffoldDefaults::container_color(),
37            content_color: ScaffoldDefaults::content_color(),
38            fab_position: FabPosition::End,
39        }
40    }
41}
42
43pub fn Scaffold(content: impl Fn(PaddingValues) -> View, config: ScaffoldConfig) -> View {
44    let insets = window_insets();
45    let itop = px_to_dp(insets.top);
46    let ibottom = px_to_dp(insets.bottom);
47    let iime = px_to_dp(insets.ime_bottom);
48    let ileft = px_to_dp(insets.left);
49    let iright = px_to_dp(insets.right);
50
51    let content_padding = PaddingValues {
52        top: if config.top_bar.is_some() {
53            ScaffoldDefaults::TOP_BAR_HEIGHT
54        } else {
55            itop
56        },
57        bottom: if config.bottom_bar.is_some() {
58            ScaffoldDefaults::BOTTOM_BAR_HEIGHT + ibottom + iime
59        } else {
60            ibottom + iime
61        },
62        left: ileft,
63        right: iright,
64    };
65
66    Column(
67        config
68            .modifier
69            .fill_max_size()
70            .background(config.container_color),
71    )
72    .child((
73        Box(Modifier::new()
74            .fill_max_size()
75            .padding_values(PaddingValues {
76                top: if config.top_bar.is_some() {
77                    ScaffoldDefaults::TOP_BAR_HEIGHT + itop
78                } else {
79                    0.0
80                },
81                bottom: if config.bottom_bar.is_some() {
82                    ScaffoldDefaults::BOTTOM_BAR_HEIGHT + ibottom + iime
83                } else {
84                    ibottom + iime
85                },
86                ..Default::default()
87            }))
88        .child(content(content_padding)),
89        if let Some(bar) = config.top_bar {
90            Box(Modifier::new()
91                .absolute()
92                .offset(Some(0.0), Some(itop), Some(0.0), None))
93            .child(bar)
94        } else {
95            Box(Modifier::new())
96        },
97        if let Some(bar) = config.bottom_bar {
98            Box(Modifier::new()
99                .absolute()
100                .offset(Some(0.0), None, Some(ibottom + iime), Some(0.0)))
101            .child(bar)
102        } else {
103            Box(Modifier::new())
104        },
105        if let Some(fab) = config.floating_action_button {
106            let mut fab_m = Modifier::new().absolute();
107            match config.fab_position {
108                FabPosition::End => {
109                    fab_m = fab_m.offset(None, None, Some(16.0 + ibottom + iime), Some(16.0));
110                }
111                FabPosition::Center => {
112                    fab_m = fab_m.fill_max_width().align_self(AlignSelf::CENTER).offset(
113                        None,
114                        None,
115                        Some(16.0 + ibottom + iime),
116                        None,
117                    );
118                }
119            }
120            Box(fab_m).child(fab)
121        } else {
122            Box(Modifier::new())
123        },
124        config.snackbar_host.unwrap_or_else(|| Box(Modifier::new())),
125    ))
126}