1use std::f32::consts::FRAC_PI_2;
2
3use icons::*;
4use sge::prelude::*;
5use ui::prelude::*;
6
7struct State {
8 guy: TextureRef,
9 progress: f32,
10 clear_color: Color,
11 show_debug_info: bool,
12 slider_a_value: usize,
13 slider_b_value: f32,
14 slider_c_value: f32,
15 show_message: bool,
16}
17
18impl State {
19 fn update(&mut self) {
20 clear_screen(self.clear_color);
21
22 if key_pressed(KeyCode::KeyD) && held_control() {
23 toggle_wireframe();
24 } else if key_pressed(KeyCode::KeyD) {
25 self.show_debug_info = !self.show_debug_info;
26 }
27
28 if once_per_n_seconds(2.0 / 3.0) {
29 self.progress = rand();
30 }
31
32 let mut ui_parts = vec![];
33
34 ui_parts.push(self.scroll_section());
35 ui_parts.push(self.w95_section());
36 ui_parts.push(self.text_section());
37 ui_parts.push(self.button_section());
38 ui_parts.push(self.align_section());
39 ui_parts.push(self.flat_section());
40
41 let padding = media_query(5.0, 10.0, 20.0);
42 let ui = SizedBox::new(
43 window_size(),
44 Padding::all(padding, Grid::with_gap(2, 3, padding, ui_parts)),
45 );
46 draw_ui(ui, vec2(0.0, 0.0));
47
48 if self.show_debug_info {
49 draw_simple_debug_info();
50 }
51 }
52
53 fn scroll_section(&self) -> UiRef {
54 BoxFill::new(
55 Color::GRAY_900,
56 Scroll::new(
57 id!(),
58 Padding::all(
59 10.0,
60 Col::with_gap(
61 10.0,
62 (0..100)
63 .map(|n| {
64 SizedBox::height(
65 40.0,
66 Fill::rounded_hover(
67 Color::GRAY_800,
68 Color::GRAY_700,
69 7.0,
70 Center::new(Text::new(n)),
71 ),
72 )
73 })
74 .collect::<Vec<_>>(),
75 ),
76 ),
77 ),
78 )
79 }
80
81 fn w95_section(&mut self) -> UiRef {
82 let color_button = id!();
83 let wait_button = id!();
84
85 if button_clicked_last_frame(color_button) {
86 self.clear_color = rand_color();
87 }
88
89 if button_clicked_last_frame(wait_button) {
90 toggle_wait_for_events();
91 }
92
93 w95::Card::thick(
94 20.0,
95 Scroll::new(
96 id!(),
97 Col::with_gap(
98 10.0,
99 [
100 black_text(format!("{:.0}", avg_fps())),
101 black_text("Hello world!"),
102 black_text("This is UI."),
103 w95::Button::text(color_button, "Background"),
104 w95::Button::text(
105 wait_button,
106 format!("Toggle wait for events: {}", get_wait_for_events()),
107 ),
108 Text::new_with_size_color("Styled text", 30, Color::RED_600),
109 ConstrainedBox::max_size(
110 vec2(300.0, 200.0),
111 w95::Card::new(0.0, ImageNode::from_texture(self.guy)),
112 ),
113 w95::ProgressBar::new(vec2(200.0, 20.0), self.progress, 1.0, id!()),
114 Fill::gradient(
115 Color::NEUTRAL_100,
116 w95::PRIMARY,
117 FRAC_PI_2,
118 Center::new(AspectRatio::new(
119 4.0,
120 BoxFill::new(
121 Color::NEUTRAL_200,
122 CircleFill::new(Color::NEUTRAL_300).sized_wh(150.0, 150.0),
123 ),
124 )),
125 )
126 .sized_wh(200.0, 200.0),
127 ],
128 ),
129 ),
130 )
131 }
132
133 fn text_section(&self) -> UiRef {
134 BoxFill::new(
135 Color::NEUTRAL_950,
136 Padding::all(
137 20.0,
138 Scroll::new(
139 id!(),
140 Col::new([
141 Text::title("Title"),
142 Text::body(
143 "Lorem ipsum dolor sit amet, consectetur\n\n adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
144 ),
145 Text::h1("Heading 1"),
146 Text::body("Lorem ipsum dolor sit amet."),
147 Text::h2("Heading 1"),
148 Text::body("Lorem ipsum dolor sit amet."),
149 Text::mono_sized(
150 format!(
151 "{} {} {} {}",
152 ICON_PLUS_CIRCLE, ICON_BOMB, ICON_CARET_DOWN, ICON_CIRCLE_NOTCH
153 ),
154 40,
155 ),
156 Text::h3("Heading 1"),
157 Text::body("Lorem ipsum dolor sit amet."),
158 Text::italic("Lorem ipsum dolor sit amet."),
159 Text::bold("Lorem ipsum dolor sit amet."),
160 Text::bold_italic("Lorem ipsum dolor sit amet."),
161 Col::new(
162 Palette::NEUTRAL
163 .shades()
164 .map(|c| bold_italic_colored("Lorem ipsum dolor sit amet.", c)),
165 ),
166 ]),
167 ),
168 ),
169 )
170 }
171
172 fn button_section(&mut self) -> UiRef {
173 let button = id!();
174
175 if button_clicked_last_frame(button) {
176 self.show_message = !self.show_message;
177 }
178
179 library::flat::Card::bg0_expand(Col::with_gap(
180 30.0,
181 [
182 Center::new(flat::Button::primary_text(button, "Click me!")),
183 if self.show_message {
184 Center::new(Text::body("Thanks for clicking."))
185 } else {
186 EMPTY
187 },
188 ],
189 ))
190 .scissored()
191 }
192
193 fn align_section(&self) -> UiRef {
194 BoxFill::new(
195 Color::BLACK,
196 Stack::new([
197 Align::top_left(square(Color::RED_500)),
198 Align::top_center(square(Color::ORANGE_500)),
199 Align::top_right(square(Color::YELLOW_500)),
200 Align::center_right(square(Color::GREEN_500)),
201 Align::bottom_right(square(Color::SKY_500)),
202 Align::bottom_center(square(Color::BLUE_500)),
203 Align::bottom_left(square(Color::PURPLE_500)),
204 Align::center_left(square(Color::PINK_500)),
205 Align::center(square(Color::WHITE)),
206 ]),
207 )
208 .scissored()
209 }
210
211 fn flat_section(&mut self) -> UiRef {
212 use flat::*;
213
214 let bars: Vec<_> = Palette::PALETTES
215 .iter()
216 .enumerate()
217 .skip(5)
218 .map(|(i, p)| {
219 flat::LoadingBar::new_with_speed(p.v400, i as f32 * 10.0 + 10.0)
220 .height_infinite_width(30.0)
221 })
222 .collect();
223
224 let input_id = id!();
225
226 if text_input_changed(input_id) {
227 println!("{}", text_input_value(input_id));
228 }
229
230 Card::bg0_expand(FlexCol::with_gap(
231 10.0,
232 [
233 FlexBox::Flex(Col::with_gap(
234 20.0,
235 [
236 Drawer::new(
237 "Click me!",
238 BG1,
239 id!(),
240 Col::with_gap(10.0, bars).scroll(id!()),
241 )
242 .max_height(400.0),
243 Text::new(self.slider_a_value),
244 Slider::new(&mut self.slider_a_value, 0, 30, id!()),
245 Slider::alternate(&mut self.slider_b_value, 0.0, 30.0, id!()),
246 Slider::rounded(&mut self.slider_c_value, 0.0, 30.0, id!()),
247 self.window_button(),
248 ],
249 )),
250 FlexBox::Fixed(TextInput::with_prompt(BG2, "Start typing...", input_id)),
251 ],
253 ))
254 }
255
256 fn window_button(&self) -> UiRef {
257 let window_id = id!();
258 let window_button_id = id!();
259
260 let window = flat::FloatingWindow::new(
261 "Hello world",
262 window_id,
263 Col::with_gap(
264 10.0,
265 [
266 Text::body("This is a window"),
267 ImageNode::from_texture_with_scale(self.guy, 300.0),
268 Grid::with_gap(
269 4,
270 4,
271 5.0,
272 (0..16)
273 .map(|_| {
274 Border::all(
275 5.0,
276 Color::WHITE,
277 Fill::hover(Color::BLACK, Color::WHITE, EMPTY),
278 )
279 })
280 .collect::<Vec<_>>(),
281 )
282 .sized_wh(200., 200.),
283 ],
284 ),
285 );
286
287 if button_clicked_last_frame(window_button_id)
288 && let Some(state) = floating_window_state(window_id)
289 {
290 state.open = !state.open;
291 }
292
293 Stack::new([
294 flat::Button::text(flat::BG1, flat::BG2, window_button_id, "Toggle window"),
295 window,
296 ])
297 }
298}
299
300fn main() -> anyhow::Result<()> {
301 let opts = Opts::builder()
302 .swap_interval(SwapInterval::DontWait)
303 .title("UI".to_string())
304 .build();
305 init_custom(opts)?;
306
307 let mut state = State {
310 guy: include_texture!("../assets/textures/guy.jpg"),
311 progress: rand(),
312 clear_color: w95::PRIMARY,
313 show_debug_info: false,
314 slider_a_value: 10,
315 slider_b_value: 20.0,
316 slider_c_value: 15.0,
317 show_message: false,
318 };
319
320 run_async(async move {
321 loop {
322 state.update();
323
324 if should_quit() {
325 break;
326 }
327
328 next_frame().await;
329 }
330 });
331
332 Ok(())
333}
334
335fn square(color: Color) -> UiRef {
336 BoxFill::new(color, EmptyBox::new(50.0, 50.0))
337}
338
339fn black_text(text: impl ToString) -> UiRef {
342 Text::new_with_color(text, Color::BLACK)
343}
344
345fn bold_italic_colored(text: impl ToString, color: Color) -> UiRef {
346 Text::new_full(text, SANS_BOLD_ITALIC, 24, color, true, 1.0, false)
347}