irox_egui_extras/
toolframe.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: MIT
// Copyright 2023 IROX Contributors
//

//!
//! Adds a helper App Wrapper called 'ToolFrame' that provides a boilerplate tool app for quick bootstrapping of new apps.

use eframe::{App, CreationContext, Frame};
use egui::{menu, Context, Id, TopBottomPanel, Ui, ViewportCommand, Window};

use crate::frame_history::FrameHistory;

///
/// A 'ToolFrame' is a egui App that provides a basic Menu bar, Bottom Status Bar, and pre-fills it with some utilities
/// like the Style UI, and rendering statistics.
///
/// Purpose is to reduce the amount of duplicated code across multiple tools.
pub struct ToolFrame {
    style_ui: bool,
    full_speed: bool,
    show_rendering_stats: bool,
    frame_history: FrameHistory,
    child: Box<dyn ToolApp>,
}

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct ToolFrameOptions {
    pub full_speed: bool,
    pub show_rendering_stats: bool,
}

impl ToolFrame {
    #[must_use]
    pub fn new(_cc: &CreationContext, child: Box<dyn ToolApp>) -> Self {
        Self::new_opts(_cc, child, ToolFrameOptions::default())
    }
    #[must_use]
    pub fn new_opts(
        _cc: &CreationContext,
        child: Box<dyn ToolApp>,
        opts: ToolFrameOptions,
    ) -> Self {
        let ToolFrameOptions {
            full_speed,
            show_rendering_stats,
        } = opts;
        Self {
            style_ui: false,
            full_speed,
            show_rendering_stats,
            frame_history: FrameHistory::default(),
            child,
        }
    }

    pub fn show_rendering_stats(&mut self, show: bool) {
        self.show_rendering_stats = show;
    }
    pub fn full_speed(&mut self, full_speed: bool) {
        self.full_speed = full_speed;
    }
}

impl App for ToolFrame {
    fn update(&mut self, ctx: &Context, frame: &mut Frame) {
        self.frame_history
            .on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage);

        TopBottomPanel::top(Id::new("top_panel")).show(ctx, |ui| {
            menu::bar(ui, |ui| {
                ui.menu_button("File", |ui| {
                    self.child.file_menu(ui);

                    if ui.button("Exit").clicked() {
                        ctx.send_viewport_cmd(ViewportCommand::Close);
                    }
                });

                ui.menu_button("Settings", |ui| {
                    self.child.settings_menu(ui);
                    ui.checkbox(&mut self.show_rendering_stats, "Show Rendering Metrics");
                    ui.checkbox(&mut self.full_speed, "Continuous Render");

                    if ui.button("Style").clicked() {
                        self.style_ui = true;
                        ui.close_menu();
                    }
                });

                self.child.menu(ui);
            });
        });

        if self.style_ui {
            Window::new("style")
                .open(&mut self.style_ui)
                .show(ctx, |ui| {
                    let mut theme = ctx.options(|o| o.theme_preference);
                    theme.radio_buttons(ui);
                    if theme != ctx.options(|o| o.theme_preference) {
                        ctx.set_theme(theme);
                    }
                    ctx.style_ui(ui, ctx.theme());
                });
        }

        TopBottomPanel::bottom(Id::new("bottom_panel")).show(ctx, |ui| {
            ui.horizontal(|ui| {
                if self.show_rendering_stats {
                    self.frame_history.ui(ui);
                }

                self.child.bottom_bar(ui);
            });
        });

        self.child.update(ctx, frame);

        if self.full_speed {
            ctx.request_repaint();
        }
    }
}

///
/// Downstream users should implement this trait.
///
/// * Implement `menu` to append new menu items to the menu bar
/// * Implement `file_menu` to append new items to the file menu in the menu bar
/// * Implement `settings_menu` to append new items to the settings menu in the menu bar
/// * Implement `bottom_bar` to append new items to the bottom status bar
///
/// [`egui::App`] is a required trait.  `update` will be called LAST, and is appropriate for adding a [`egui::CentralPanel`]
pub trait ToolApp: App {
    /// Appends stuff to the menu
    fn menu(&mut self, _ui: &mut Ui) {}

    /// Appends bits to the file menu, 'Exit' is always last.
    fn file_menu(&mut self, _ui: &mut Ui) {}

    /// Appends bits to the settings menu.
    fn settings_menu(&mut self, _ui: &mut Ui) {}

    /// Appends bits to the bottom bar/panel
    fn bottom_bar(&mut self, _ui: &mut Ui) {}
}