2022-06-22 11:19:13 +00:00
use egui ::Widget ;
2021-05-27 22:40:22 +00:00
/// How often we repaint the demo app by default
#[ derive(Clone, Copy, Debug, Eq, PartialEq) ]
enum RunMode {
/// This is the default for the demo.
///
/// If this is selected, egui is only updated if are input events
/// (like mouse movements) or there are some animations in the GUI.
///
/// Reactive mode saves CPU.
///
/// The downside is that the UI can become out-of-date if something it is supposed to monitor changes.
/// For instance, a GUI for a thermostat need to repaint each time the temperature changes.
/// To ensure the UI is up to date you need to call `egui::Context::request_repaint()` each
/// time such an event happens. You can also chose to call `request_repaint()` once every second
2022-04-03 16:18:35 +00:00
/// or after every single frame - this is called [`Continuous`](RunMode::Continuous) mode,
2021-05-27 22:40:22 +00:00
/// and for games and interactive tools that need repainting every frame anyway, this should be the default.
Reactive ,
/// This will call `egui::Context::request_repaint()` at the end of each frame
/// to request the backend to repaint as soon as possible.
///
/// On most platforms this will mean that egui will run at the display refresh rate of e.g. 60 Hz.
///
/// For this demo it is not any reason to do so except to
/// demonstrate how quickly egui runs.
///
/// For games or other interactive apps, this is probably what you want to do.
/// It will guarantee that egui is always up-to-date.
Continuous ,
}
/// Default for demo is Reactive since
/// 1) We want to use minimal CPU
/// 2) There are no external events that could invalidate the UI
/// so there are no events to miss.
impl Default for RunMode {
fn default ( ) -> Self {
RunMode ::Reactive
}
}
// ----------------------------------------------------------------------------
2021-09-29 06:45:13 +00:00
#[ cfg_attr(feature = " serde " , derive(serde::Deserialize, serde::Serialize)) ]
#[ cfg_attr(feature = " serde " , serde(default)) ]
2021-05-27 22:40:22 +00:00
pub struct BackendPanel {
pub open : bool ,
2021-09-29 06:45:13 +00:00
#[ cfg_attr(feature = " serde " , serde(skip)) ]
2022-06-22 12:25:42 +00:00
// go back to [`RunMode::Reactive`] mode each time we start
2021-05-27 22:40:22 +00:00
run_mode : RunMode ,
2022-06-22 11:19:13 +00:00
#[ cfg_attr(feature = " serde " , serde(skip)) ]
2022-07-29 13:56:04 +00:00
repaint_after_seconds : f32 ,
2022-06-22 11:19:13 +00:00
2021-05-27 22:40:22 +00:00
/// current slider value for current gui scale
2022-02-05 22:54:44 +00:00
#[ cfg_attr(feature = " serde " , serde(skip)) ]
2021-05-27 22:40:22 +00:00
pixels_per_point : Option < f32 > ,
2021-09-29 06:45:13 +00:00
#[ cfg_attr(feature = " serde " , serde(skip)) ]
2021-05-27 22:40:22 +00:00
frame_history : crate ::frame_history ::FrameHistory ,
egui_windows : EguiWindows ,
}
2022-06-22 11:19:13 +00:00
impl Default for BackendPanel {
fn default ( ) -> Self {
Self {
open : false ,
run_mode : Default ::default ( ) ,
2022-07-29 13:56:04 +00:00
repaint_after_seconds : 1.0 ,
2022-06-22 11:19:13 +00:00
pixels_per_point : None ,
frame_history : Default ::default ( ) ,
egui_windows : Default ::default ( ) ,
}
}
}
2021-05-27 22:40:22 +00:00
impl BackendPanel {
2022-04-28 09:23:34 +00:00
pub fn update ( & mut self , ctx : & egui ::Context , frame : & mut eframe ::Frame ) {
2021-05-27 22:40:22 +00:00
self . frame_history
2023-01-25 09:24:23 +00:00
. on_new_frame ( ctx . input ( | i | i . time ) , frame . info ( ) . cpu_usage ) ;
2021-05-27 22:40:22 +00:00
2022-06-22 11:19:13 +00:00
match self . run_mode {
RunMode ::Continuous = > {
// Tell the backend to repaint as soon as possible
ctx . request_repaint ( ) ;
}
2022-06-22 12:25:42 +00:00
RunMode ::Reactive = > {
// let the computer rest for a bit
ctx . request_repaint_after ( std ::time ::Duration ::from_secs_f32 (
2022-07-29 13:56:04 +00:00
self . repaint_after_seconds ,
2022-06-22 12:25:42 +00:00
) ) ;
}
2021-05-27 22:40:22 +00:00
}
}
2022-01-10 22:13:10 +00:00
pub fn end_of_frame ( & mut self , ctx : & egui ::Context ) {
2021-05-27 22:40:22 +00:00
self . egui_windows . windows ( ctx ) ;
}
2022-04-28 09:23:34 +00:00
pub fn ui ( & mut self , ui : & mut egui ::Ui , frame : & mut eframe ::Frame ) {
2021-05-27 22:40:22 +00:00
egui ::trace! ( ui ) ;
2021-10-20 11:54:56 +00:00
self . integration_ui ( ui , frame ) ;
2021-05-27 22:40:22 +00:00
ui . separator ( ) ;
self . run_mode_ui ( ui ) ;
2021-10-20 11:54:56 +00:00
ui . separator ( ) ;
self . frame_history . ui ( ui ) ;
ui . separator ( ) ;
ui . label ( " egui windows: " ) ;
self . egui_windows . checkboxes ( ui ) ;
2021-05-27 22:40:22 +00:00
ui . separator ( ) ;
{
2021-10-20 11:54:56 +00:00
let mut debug_on_hover = ui . ctx ( ) . debug_on_hover ( ) ;
ui . checkbox ( & mut debug_on_hover , " 🐛 Debug on hover " )
. on_hover_text ( " Show structure of the ui when you hover with the mouse " ) ;
ui . ctx ( ) . set_debug_on_hover ( debug_on_hover ) ;
2021-05-27 22:40:22 +00:00
}
ui . separator ( ) ;
2021-10-20 11:54:56 +00:00
{
2023-01-25 09:24:23 +00:00
let mut screen_reader = ui . ctx ( ) . options ( | o | o . screen_reader ) ;
2021-10-20 11:54:56 +00:00
ui . checkbox ( & mut screen_reader , " 🔈 Screen reader " ) . on_hover_text ( " Experimental feature: checking this will turn on the screen reader on supported platforms " ) ;
2023-01-25 09:24:23 +00:00
ui . ctx ( ) . options_mut ( | o | o . screen_reader = screen_reader ) ;
2021-10-20 11:54:56 +00:00
}
2021-05-27 22:40:22 +00:00
2022-07-29 12:37:23 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
{
2021-10-20 11:54:56 +00:00
ui . separator ( ) ;
if ui . button ( " Quit " ) . clicked ( ) {
2022-08-20 14:08:59 +00:00
frame . close ( ) ;
2021-10-20 11:54:56 +00:00
}
}
}
2022-04-28 09:23:34 +00:00
fn integration_ui ( & mut self , ui : & mut egui ::Ui , frame : & mut eframe ::Frame ) {
2022-04-30 08:44:35 +00:00
ui . horizontal ( | ui | {
ui . spacing_mut ( ) . item_spacing . x = 0.0 ;
ui . label ( " egui running inside " ) ;
2022-08-20 13:18:02 +00:00
ui . hyperlink_to (
" eframe " ,
" https://github.com/emilk/egui/tree/master/crates/eframe " ,
) ;
2022-05-02 11:13:35 +00:00
ui . label ( " . " ) ;
2022-04-30 08:44:35 +00:00
} ) ;
2021-05-27 22:40:22 +00:00
2022-07-29 12:37:23 +00:00
#[ cfg(target_arch = " wasm32 " ) ]
ui . collapsing ( " Web info (location) " , | ui | {
ui . monospace ( format! ( " {:#?} " , frame . info ( ) . web_info . location ) ) ;
} ) ;
2022-02-17 15:46:43 +00:00
2022-11-05 10:18:13 +00:00
// On web, the browser controls `pixels_per_point`.
let integration_controls_pixels_per_point = frame . is_web ( ) ;
2021-10-20 11:54:56 +00:00
if ! integration_controls_pixels_per_point {
2022-11-05 10:18:13 +00:00
self . pixels_per_point_ui ( ui , & frame . info ( ) ) ;
2021-10-20 11:54:56 +00:00
}
2021-09-28 15:34:58 +00:00
2022-07-29 12:37:23 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
{
2022-07-29 12:21:23 +00:00
ui . horizontal ( | ui | {
2022-07-29 12:37:23 +00:00
{
let mut fullscreen = frame . info ( ) . window_info . fullscreen ;
2022-12-14 13:13:52 +00:00
if ui
. checkbox ( & mut fullscreen , " 🗖 Fullscreen (F11) " )
. on_hover_text ( " Fullscreen the window " )
. changed ( )
{
frame . set_fullscreen ( fullscreen ) ;
}
2022-07-29 12:21:23 +00:00
}
if ui
. button ( " 📱 Phone Size " )
. on_hover_text ( " Resize the window to be small like a phone. " )
. clicked ( )
{
2022-07-29 12:34:26 +00:00
// frame.set_window_size(egui::vec2(375.0, 812.0)); // iPhone 12 mini
frame . set_window_size ( egui ::vec2 ( 375.0 , 667.0 ) ) ; // iPhone SE 2nd gen
2022-07-29 14:07:26 +00:00
frame . set_fullscreen ( false ) ;
2022-07-29 12:21:23 +00:00
ui . close_menu ( ) ;
}
} ) ;
2022-07-29 14:07:26 +00:00
if ! frame . info ( ) . window_info . fullscreen
& & ui
. button ( " Drag me to drag window " )
. is_pointer_button_down_on ( )
{
frame . drag_window ( ) ;
}
2021-05-27 22:40:22 +00:00
}
}
2022-11-05 10:18:13 +00:00
fn pixels_per_point_ui ( & mut self , ui : & mut egui ::Ui , info : & eframe ::IntegrationInfo ) {
let pixels_per_point = self
. pixels_per_point
. get_or_insert_with ( | | ui . ctx ( ) . pixels_per_point ( ) ) ;
let mut reset = false ;
2021-05-27 22:40:22 +00:00
ui . horizontal ( | ui | {
ui . spacing_mut ( ) . slider_width = 90.0 ;
2022-11-05 10:18:13 +00:00
let response = ui
. add (
egui ::Slider ::new ( pixels_per_point , 0. 5 ..= 5.0 )
. logarithmic ( true )
. clamp_to_range ( true )
. text ( " Scale " ) ,
)
. on_hover_text ( " Physical pixels per point. " ) ;
if response . drag_released ( ) {
// We wait until mouse release to activate:
ui . ctx ( ) . set_pixels_per_point ( * pixels_per_point ) ;
reset = true ;
} else if ! response . is_pointer_button_down_on ( ) {
// When not dragging, show the current pixels_per_point so others can change it.
reset = true ;
}
2021-05-27 22:40:22 +00:00
if let Some ( native_pixels_per_point ) = info . native_pixels_per_point {
2022-11-05 10:18:13 +00:00
let enabled = ui . ctx ( ) . pixels_per_point ( ) ! = native_pixels_per_point ;
2021-05-27 22:40:22 +00:00
if ui
2021-10-17 20:17:50 +00:00
. add_enabled ( enabled , egui ::Button ::new ( " Reset " ) )
2021-05-27 22:40:22 +00:00
. on_hover_text ( format! (
" Reset scale to native value ({:.1}) " ,
native_pixels_per_point
) )
. clicked ( )
{
2022-11-05 10:18:13 +00:00
ui . ctx ( ) . set_pixels_per_point ( native_pixels_per_point ) ;
2021-05-27 22:40:22 +00:00
}
}
} ) ;
2022-11-05 10:18:13 +00:00
if reset {
self . pixels_per_point = None ;
2021-05-27 22:40:22 +00:00
}
}
fn run_mode_ui ( & mut self , ui : & mut egui ::Ui ) {
ui . horizontal ( | ui | {
let run_mode = & mut self . run_mode ;
ui . label ( " Mode: " ) ;
ui . radio_value ( run_mode , RunMode ::Reactive , " Reactive " )
. on_hover_text ( " Repaint when there are animations or input (e.g. mouse movement) " ) ;
2021-10-20 11:54:56 +00:00
ui . radio_value ( run_mode , RunMode ::Continuous , " Continuous " )
. on_hover_text ( " Repaint everything each frame " ) ;
2021-05-27 22:40:22 +00:00
} ) ;
if self . run_mode = = RunMode ::Continuous {
ui . label ( format! (
" Repainting the UI each frame. FPS: {:.1} " ,
self . frame_history . fps ( )
) ) ;
} else {
2022-04-30 15:50:39 +00:00
ui . label ( " Only running UI code when there are animations or input. " ) ;
2022-06-22 12:25:42 +00:00
ui . horizontal ( | ui | {
ui . spacing_mut ( ) . item_spacing . x = 0.0 ;
ui . label ( " (but at least every " ) ;
2022-07-29 13:56:04 +00:00
egui ::DragValue ::new ( & mut self . repaint_after_seconds )
2022-06-22 12:25:42 +00:00
. clamp_range ( 0. 1 ..= 10.0 )
. speed ( 0.1 )
. suffix ( " s " )
. ui ( ui )
. on_hover_text ( " Repaint this often, even if there is no input. " ) ;
ui . label ( " ) " ) ;
} ) ;
2021-05-27 22:40:22 +00:00
}
}
}
// ----------------------------------------------------------------------------
2021-09-29 06:45:13 +00:00
#[ cfg_attr(feature = " serde " , derive(serde::Deserialize, serde::Serialize)) ]
2021-05-27 22:40:22 +00:00
struct EguiWindows {
// egui stuff:
settings : bool ,
inspection : bool ,
memory : bool ,
2021-10-20 11:54:56 +00:00
output_events : bool ,
#[ cfg_attr(feature = " serde " , serde(skip)) ]
output_event_history : std ::collections ::VecDeque < egui ::output ::OutputEvent > ,
2021-05-27 22:40:22 +00:00
}
impl Default for EguiWindows {
fn default ( ) -> Self {
EguiWindows ::none ( )
}
}
impl EguiWindows {
fn none ( ) -> Self {
Self {
settings : false ,
inspection : false ,
memory : false ,
2021-10-20 11:54:56 +00:00
output_events : false ,
output_event_history : Default ::default ( ) ,
2021-05-27 22:40:22 +00:00
}
}
fn checkboxes ( & mut self , ui : & mut egui ::Ui ) {
let Self {
settings ,
inspection ,
memory ,
2021-10-20 11:54:56 +00:00
output_events ,
output_event_history : _ ,
2021-05-27 22:40:22 +00:00
} = self ;
ui . checkbox ( settings , " 🔧 Settings " ) ;
ui . checkbox ( inspection , " 🔍 Inspection " ) ;
ui . checkbox ( memory , " 📝 Memory " ) ;
2021-10-20 11:54:56 +00:00
ui . checkbox ( output_events , " 📤 Output Events " ) ;
2021-05-27 22:40:22 +00:00
}
2022-01-10 22:13:10 +00:00
fn windows ( & mut self , ctx : & egui ::Context ) {
2021-05-27 22:40:22 +00:00
let Self {
settings ,
inspection ,
memory ,
2021-10-20 11:54:56 +00:00
output_events ,
output_event_history ,
2021-05-27 22:40:22 +00:00
} = self ;
2023-01-25 09:24:23 +00:00
ctx . output ( | o | {
for event in & o . events {
output_event_history . push_back ( event . clone ( ) ) ;
}
} ) ;
2021-10-20 11:54:56 +00:00
while output_event_history . len ( ) > 1000 {
output_event_history . pop_front ( ) ;
}
2021-05-27 22:40:22 +00:00
egui ::Window ::new ( " 🔧 Settings " )
. open ( settings )
2021-08-28 11:18:21 +00:00
. vscroll ( true )
2021-05-27 22:40:22 +00:00
. show ( ctx , | ui | {
ctx . settings_ui ( ui ) ;
} ) ;
egui ::Window ::new ( " 🔍 Inspection " )
. open ( inspection )
2021-08-28 11:18:21 +00:00
. vscroll ( true )
2021-05-27 22:40:22 +00:00
. show ( ctx , | ui | {
ctx . inspection_ui ( ui ) ;
} ) ;
egui ::Window ::new ( " 📝 Memory " )
. open ( memory )
. resizable ( false )
. show ( ctx , | ui | {
ctx . memory_ui ( ui ) ;
} ) ;
2021-10-20 11:54:56 +00:00
egui ::Window ::new ( " 📤 Output Events " )
. open ( output_events )
. resizable ( true )
. default_width ( 520.0 )
. show ( ctx , | ui | {
ui . label (
" Recent output events from egui. \
These are emitted when you interact with widgets , or move focus between them with TAB . \
They can be hooked up to a screen reader on supported platforms . " ,
) ;
ui . separator ( ) ;
egui ::ScrollArea ::vertical ( )
2022-08-02 18:34:50 +00:00
. stick_to_bottom ( true )
2021-10-20 11:54:56 +00:00
. show ( ui , | ui | {
for event in output_event_history {
ui . label ( format! ( " {:?} " , event ) ) ;
}
} ) ;
} ) ;
2021-05-27 22:40:22 +00:00
}
}