Skip to main content

ui/
ui.rs

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                // FlexBox::Fixed(Text::new("Hello world.")),
252            ],
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    // wait_for_events(); // can be useful for increasing performance in ui only apps
308
309    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
339// example simple widget. if you want to make something more complicated check out the UiNode trait
340// if you want to create a widget with retained mutable state, check out the source for ProgressBar or Slider
341fn 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}