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