repose_material/material3/
scaffold.rs1#![allow(non_snake_case)]
2
3use repose_core::*;
4use repose_ui::{Box, Column, ViewExt};
5
6use super::*;
7
8#[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}