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 with_material_indication(|| scaffold_inner(content, config))
48}
49
50fn scaffold_inner(content: impl Fn(PaddingValues) -> View, config: ScaffoldConfig) -> View {
51 let insets = window_insets();
52 let itop = px_to_dp(insets.top);
53 let ibottom = px_to_dp(insets.bottom);
54 let iime = px_to_dp(insets.ime_bottom);
55 let ileft = px_to_dp(insets.left);
56 let iright = px_to_dp(insets.right);
57
58 let content_padding = PaddingValues {
59 top: if config.top_bar.is_some() {
60 ScaffoldDefaults::TOP_BAR_HEIGHT
61 } else {
62 itop
63 },
64 bottom: if config.bottom_bar.is_some() {
65 ScaffoldDefaults::BOTTOM_BAR_HEIGHT + ibottom + iime
66 } else {
67 ibottom + iime
68 },
69 left: ileft,
70 right: iright,
71 };
72
73 Column(
74 config
75 .modifier
76 .fill_max_size()
77 .background(config.container_color),
78 )
79 .child((
80 Box(Modifier::new()
81 .fill_max_size()
82 .padding_values(PaddingValues {
83 top: if config.top_bar.is_some() {
84 ScaffoldDefaults::TOP_BAR_HEIGHT + itop
85 } else {
86 0.0
87 },
88 bottom: if config.bottom_bar.is_some() {
89 ScaffoldDefaults::BOTTOM_BAR_HEIGHT + ibottom + iime
90 } else {
91 ibottom + iime
92 },
93 ..Default::default()
94 }))
95 .child(content(content_padding)),
96 if let Some(bar) = config.top_bar {
97 Box(Modifier::new()
98 .absolute()
99 .offset(Some(0.0), Some(itop), Some(0.0), None))
100 .child(bar)
101 } else {
102 Box(Modifier::new())
103 },
104 if let Some(bar) = config.bottom_bar {
105 Box(Modifier::new()
106 .absolute()
107 .offset(Some(0.0), None, Some(ibottom + iime), Some(0.0)))
108 .child(bar)
109 } else {
110 Box(Modifier::new())
111 },
112 if let Some(fab) = config.floating_action_button {
113 let mut fab_m = Modifier::new().absolute();
114 match config.fab_position {
115 FabPosition::End => {
116 fab_m = fab_m.offset(None, None, Some(16.0 + ibottom + iime), Some(16.0));
117 }
118 FabPosition::Center => {
119 fab_m = fab_m.fill_max_width().align_self(AlignSelf::CENTER).offset(
120 None,
121 None,
122 Some(16.0 + ibottom + iime),
123 None,
124 );
125 }
126 }
127 Box(fab_m).child(fab)
128 } else {
129 Box(Modifier::new())
130 },
131 config.snackbar_host.unwrap_or_else(|| Box(Modifier::new())),
132 ))
133}