From 4848c171ebae1ab7d26899d4c26a539639441af1 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 1 Jan 2021 21:27:10 +0100 Subject: [PATCH] [demo] Move Fractal Clock to WrapApp --- egui/src/containers/panel.rs | 15 ++- egui_demo_lib/benches/benchmark.rs | 6 +- egui_demo_lib/src/apps/demo/app.rs | 20 +--- egui_demo_lib/src/apps/demo/demo_windows.rs | 75 +-------------- egui_demo_lib/src/apps/demo/mod.rs | 5 +- .../src/apps/{demo => }/fractal_clock.rs | 20 ++-- egui_demo_lib/src/apps/mod.rs | 2 + egui_demo_lib/src/lib.rs | 2 +- egui_demo_lib/src/wrap_app.rs | 94 +++++++++++++------ epi/src/lib.rs | 1 + 10 files changed, 100 insertions(+), 140 deletions(-) rename egui_demo_lib/src/apps/{demo => }/fractal_clock.rs (93%) diff --git a/egui/src/containers/panel.rs b/egui/src/containers/panel.rs index dcc02895..524f6b23 100644 --- a/egui/src/containers/panel.rs +++ b/egui/src/containers/panel.rs @@ -137,11 +137,20 @@ impl TopPanel { /// }); /// ``` #[derive(Default)] -pub struct CentralPanel {} +pub struct CentralPanel { + frame: Option, +} + +impl CentralPanel { + pub fn frame(mut self, frame: Frame) -> Self { + self.frame = Some(frame); + self + } +} impl CentralPanel { pub fn show(self, ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Response) { - let Self {} = self; + let Self { frame } = self; let panel_rect = ctx.available_rect(); @@ -151,7 +160,7 @@ impl CentralPanel { let clip_rect = ctx.input().screen_rect(); let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect); - let frame = Frame::central_panel(&ctx.style()); + let frame = frame.unwrap_or_else(|| Frame::central_panel(&ctx.style())); let r = frame.show(&mut panel_ui, |ui| { let r = add_contents(ui); ui.expand_to_include_rect(ui.max_rect()); // Use it all diff --git a/egui_demo_lib/benches/benchmark.rs b/egui_demo_lib/benches/benchmark.rs index 8d488a52..f2d0959d 100644 --- a/egui_demo_lib/benches/benchmark.rs +++ b/egui_demo_lib/benches/benchmark.rs @@ -10,7 +10,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { c.bench_function("demo_windows_minimal", |b| { b.iter(|| { ctx.begin_frame(raw_input.clone()); - demo_windows.ui(&ctx, &Default::default(), &mut None, |_ui| {}); + demo_windows.ui(&ctx, &mut None, |_ui| {}); ctx.end_frame() }) }); @@ -24,7 +24,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { c.bench_function("demo_windows_full", |b| { b.iter(|| { ctx.begin_frame(raw_input.clone()); - demo_windows.ui(&ctx, &Default::default(), &mut None, |_ui| {}); + demo_windows.ui(&ctx, &mut None, |_ui| {}); ctx.end_frame() }) }); @@ -35,7 +35,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { ctx.memory().all_collpasing_are_open = true; // expand the demo window with everything let mut demo_windows = egui_demo_lib::DemoWindows::default(); ctx.begin_frame(raw_input.clone()); - demo_windows.ui(&ctx, &Default::default(), &mut None, |_ui| {}); + demo_windows.ui(&ctx, &mut None, |_ui| {}); let (_, paint_commands) = ctx.end_frame(); c.bench_function("tessellate", |b| { diff --git a/egui_demo_lib/src/apps/demo/app.rs b/egui_demo_lib/src/apps/demo/app.rs index ec03cdb2..ade75e69 100644 --- a/egui_demo_lib/src/apps/demo/app.rs +++ b/egui_demo_lib/src/apps/demo/app.rs @@ -297,24 +297,6 @@ impl epi::App for DemoApp { self.frame_history .on_new_frame(ctx.input().time, frame.info().cpu_usage); - let web_location_hash = frame - .info() - .web_info - .as_ref() - .map(|info| info.web_location_hash.clone()) - .unwrap_or_default(); - - let link = if web_location_hash == "clock" { - Some(super::DemoLink::Clock) - } else { - None - }; - - let demo_environment = super::DemoEnvironment { - seconds_since_midnight: frame.info().seconds_since_midnight, - link, - }; - let mean_frame_time = self.frame_history.mean_frame_time(); let Self { @@ -323,7 +305,7 @@ impl epi::App for DemoApp { .. } = self; - demo_windows.ui(ctx, &demo_environment, frame.tex_allocator(), |ui| { + demo_windows.ui(ctx, frame.tex_allocator(), |ui| { ui.separator(); ui.checkbox(backend_window_open, "💻 Backend"); diff --git a/egui_demo_lib/src/apps/demo/demo_windows.rs b/egui_demo_lib/src/apps/demo/demo_windows.rs index 02b1b2e3..d463de35 100644 --- a/egui_demo_lib/src/apps/demo/demo_windows.rs +++ b/egui_demo_lib/src/apps/demo/demo_windows.rs @@ -2,24 +2,6 @@ use egui::{CtxRef, Resize, ScrollArea, Ui, Window}; // ---------------------------------------------------------------------------- -/// Link to show a specific part of the demo app. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum DemoLink { - Clock, -} - -/// Special input to the demo-app. -#[derive(Default)] -pub struct DemoEnvironment { - /// Local time. Used for the clock in the demo app. - pub seconds_since_midnight: Option, - - /// Set to `Some` to open a specific part of the demo app. - pub link: Option, -} - -// ---------------------------------------------------------------------------- - #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] struct Demos { @@ -67,13 +49,8 @@ pub struct DemoWindows { #[serde(skip)] color_test: super::ColorTest, - fractal_clock: super::FractalClock, - /// open, title, view demos: Demos, - - #[serde(skip)] - previous_link: Option, } impl DemoWindows { @@ -82,26 +59,11 @@ impl DemoWindows { pub fn ui( &mut self, ctx: &CtxRef, - env: &DemoEnvironment, tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>, sidebar_ui: impl FnOnce(&mut Ui), ) { - if self.previous_link != env.link { - match env.link { - None => {} - Some(DemoLink::Clock) => { - self.open_windows = OpenWindows { - fractal_clock: true, - ..OpenWindows::none() - }; - } - } - self.previous_link = env.link; - } - egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| { ui.heading("✒ Egui Demo"); - egui::warn_if_debug_build(ui); ui.separator(); @@ -132,24 +94,22 @@ impl DemoWindows { }); egui::TopPanel::top("menu_bar").show(ctx, |ui| { - show_menu_bar(ui, &mut self.open_windows, env.seconds_since_midnight); + show_menu_bar(ui); }); - self.windows(ctx, env, tex_allocator); + self.windows(ctx, tex_allocator); } /// Show the open windows. fn windows( &mut self, ctx: &CtxRef, - env: &DemoEnvironment, tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>, ) { let Self { open_windows, demo_window, color_test, - fractal_clock, demos, .. } = self; @@ -191,12 +151,6 @@ impl DemoWindows { demos.show(ctx); - fractal_clock.window( - ctx, - &mut open_windows.fractal_clock, - env.seconds_since_midnight, - ); - self.resize_windows(ctx); } @@ -259,7 +213,6 @@ impl DemoWindows { #[derive(serde::Deserialize, serde::Serialize)] struct OpenWindows { demo: bool, - fractal_clock: bool, // egui stuff: settings: bool, @@ -284,7 +237,6 @@ impl OpenWindows { fn none() -> Self { Self { demo: false, - fractal_clock: false, settings: false, inspection: false, @@ -298,7 +250,6 @@ impl OpenWindows { fn checkboxes(&mut self, ui: &mut Ui) { let Self { demo, - fractal_clock, settings, inspection, memory, @@ -317,11 +268,10 @@ impl OpenWindows { .on_hover_text("For testing the integrations painter"); ui.separator(); ui.label("Misc:"); - ui.checkbox(fractal_clock, "🕑 Fractal Clock"); } } -fn show_menu_bar(ui: &mut Ui, windows: &mut OpenWindows, seconds_since_midnight: Option) { +fn show_menu_bar(ui: &mut Ui) { use egui::*; menu::bar(ui, |ui| { @@ -337,24 +287,5 @@ fn show_menu_bar(ui: &mut Ui, windows: &mut OpenWindows, seconds_since_midnight: *ui.ctx().memory() = Default::default(); } }); - - if let Some(time) = seconds_since_midnight { - let time = format!( - "{:02}:{:02}:{:02}.{:02}", - (time % (24.0 * 60.0 * 60.0) / 3600.0).floor(), - (time % (60.0 * 60.0) / 60.0).floor(), - (time % 60.0).floor(), - (time % 1.0 * 100.0).floor() - ); - - ui.with_layout(Layout::right_to_left(), |ui| { - if ui - .add(Button::new(time).text_style(TextStyle::Monospace)) - .clicked - { - windows.fractal_clock = !windows.fractal_clock; - } - }); - } }); } diff --git a/egui_demo_lib/src/apps/demo/mod.rs b/egui_demo_lib/src/apps/demo/mod.rs index bdcb2367..9298aa42 100644 --- a/egui_demo_lib/src/apps/demo/mod.rs +++ b/egui_demo_lib/src/apps/demo/mod.rs @@ -13,7 +13,6 @@ mod drag_and_drop; mod font_book; pub mod font_contents_emoji; pub mod font_contents_ubuntu; -mod fractal_clock; mod painting; mod scrolls; mod sliders; @@ -23,8 +22,8 @@ mod widgets; pub use { app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow, - demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock, - painting::Painting, scrolls::Scrolls, sliders::Sliders, tests::Tests, widgets::Widgets, + demo_windows::*, drag_and_drop::*, font_book::FontBook, painting::Painting, scrolls::Scrolls, + sliders::Sliders, tests::Tests, widgets::Widgets, }; // ---------------------------------------------------------------------------- diff --git a/egui_demo_lib/src/apps/demo/fractal_clock.rs b/egui_demo_lib/src/apps/fractal_clock.rs similarity index 93% rename from egui_demo_lib/src/apps/demo/fractal_clock.rs rename to egui_demo_lib/src/apps/fractal_clock.rs index d1772501..79c6b6f8 100644 --- a/egui_demo_lib/src/apps/demo/fractal_clock.rs +++ b/egui_demo_lib/src/apps/fractal_clock.rs @@ -29,17 +29,19 @@ impl Default for FractalClock { } } -impl FractalClock { - pub fn window(&mut self, ctx: &CtxRef, open: &mut bool, seconds_since_midnight: Option) { - Window::new("🕑 Fractal Clock") - .open(open) - .default_size(vec2(512.0, 512.0)) - .scroll(false) - // Dark background frame to make it pop: - .frame(Frame::window(&ctx.style()).fill(Srgba::black_alpha(250))) - .show(ctx, |ui| self.ui(ui, seconds_since_midnight)); +impl epi::App for FractalClock { + fn name(&self) -> &str { + "🕑 Fractal Clock" } + fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) { + egui::CentralPanel::default() + .frame(Frame::dark_canvas(&ctx.style())) + .show(ctx, |ui| self.ui(ui, frame.info().seconds_since_midnight)); + } +} + +impl FractalClock { pub fn ui(&mut self, ui: &mut Ui, seconds_since_midnight: Option) { if !self.paused { self.time = seconds_since_midnight.unwrap_or_else(|| ui.input().time); diff --git a/egui_demo_lib/src/apps/mod.rs b/egui_demo_lib/src/apps/mod.rs index d11e7369..6be76f95 100644 --- a/egui_demo_lib/src/apps/mod.rs +++ b/egui_demo_lib/src/apps/mod.rs @@ -1,7 +1,9 @@ mod demo; +mod fractal_clock; mod http_app; pub use demo::DemoApp; +pub use fractal_clock::FractalClock; pub use http_app::HttpApp; pub use demo::DemoWindows; // used for tests diff --git a/egui_demo_lib/src/lib.rs b/egui_demo_lib/src/lib.rs index d59443a0..ea018ee3 100644 --- a/egui_demo_lib/src/lib.rs +++ b/egui_demo_lib/src/lib.rs @@ -96,7 +96,7 @@ fn test_egui_e2e() { const NUM_FRAMES: usize = 5; for _ in 0..NUM_FRAMES { ctx.begin_frame(raw_input.clone()); - demo_windows.ui(&ctx, &Default::default(), &mut None, |_ui| {}); + demo_windows.ui(&ctx, &mut None, |_ui| {}); let (_output, paint_commands) = ctx.end_frame(); let paint_jobs = ctx.tessellate(paint_commands); assert!(!paint_jobs.is_empty()); diff --git a/egui_demo_lib/src/wrap_app.rs b/egui_demo_lib/src/wrap_app.rs index b3e7fb13..fb4d216c 100644 --- a/egui_demo_lib/src/wrap_app.rs +++ b/egui_demo_lib/src/wrap_app.rs @@ -2,10 +2,27 @@ #[derive(Default, serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct WrapApp { - selectable_demo_name: String, + selected_anchor: String, + apps: Apps, +} +#[derive(Default, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct Apps { demo: crate::apps::DemoApp, http: crate::apps::HttpApp, + clock: crate::apps::FractalClock, +} + +impl Apps { + fn iter_mut(&mut self) -> impl Iterator { + vec![ + ("demo", &mut self.demo as &mut dyn epi::App), + ("http", &mut self.http as &mut dyn epi::App), + ("clock", &mut self.clock as &mut dyn epi::App), + ] + .into_iter() + } } impl epi::App for WrapApp { @@ -22,46 +39,63 @@ impl epi::App for WrapApp { } fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) { - let web_location_hash = frame - .info() - .web_info - .as_ref() - .map(|info| info.web_location_hash.clone()) - .unwrap_or_default(); + if let Some(web_info) = frame.info().web_info.as_ref() { + if let Some(anchor) = web_info.web_location_hash.strip_prefix("#") { + self.selected_anchor = anchor.to_owned(); + } + } - if web_location_hash == "#clock" { - // TODO - } else if web_location_hash == "#http" { - self.selectable_demo_name = self.http.name().to_owned(); + if self.selected_anchor.is_empty() { + self.selected_anchor = self.apps.iter_mut().next().unwrap().0.to_owned(); } egui::TopPanel::top("wrap_app").show(ctx, |ui| { - ui.horizontal(|ui| { - ui.label(format!("web_location_hash: {:?}", web_location_hash)); - ui.label("Demo Apps:"); - ui.selectable_value( - &mut self.selectable_demo_name, - self.demo.name().to_owned(), - self.demo.name(), - ); - ui.selectable_value( - &mut self.selectable_demo_name, - self.http.name().to_owned(), - self.http.name(), - ); + // A menu-bar is a horizontal layout with some special styles applied. + egui::menu::bar(ui, |ui| { + for (anchor, app) in self.apps.iter_mut() { + if ui + .selectable_label(self.selected_anchor == anchor, app.name()) + .clicked + { + self.selected_anchor = anchor.to_owned(); + if frame.is_web() { + ui.output().open_url = Some(format!("#{}", anchor)); + } + } + } ui.with_layout(egui::Layout::right_to_left(), |ui| { + if let Some(seconds_since_midnight) = frame.info().seconds_since_midnight { + if clock_button(ui, seconds_since_midnight).clicked { + self.selected_anchor = "clock".to_owned(); + if frame.is_web() { + ui.output().open_url = Some("#clock".to_owned()); + } + } + } + egui::warn_if_debug_build(ui); }); }); }); - if self.selectable_demo_name == self.demo.name() { - self.demo.update(ctx, frame); - } else if self.selectable_demo_name == self.http.name() { - self.http.update(ctx, frame); - } else { - self.selectable_demo_name = self.demo.name().to_owned(); + for (anchor, app) in self.apps.iter_mut() { + if anchor == self.selected_anchor { + app.update(ctx, frame); + } } } } + +fn clock_button(ui: &mut egui::Ui, seconds_since_midnight: f64) -> egui::Response { + let time = seconds_since_midnight; + let time = format!( + "{:02}:{:02}:{:02}.{:02}", + (time % (24.0 * 60.0 * 60.0) / 3600.0).floor(), + (time % (60.0 * 60.0) / 60.0).floor(), + (time % 60.0).floor(), + (time % 1.0 * 100.0).floor() + ); + + ui.add(egui::Button::new(time).text_style(egui::TextStyle::Monospace)) +} diff --git a/epi/src/lib.rs b/epi/src/lib.rs index ceed3eed..6d61ea8a 100644 --- a/epi/src/lib.rs +++ b/epi/src/lib.rs @@ -159,6 +159,7 @@ impl<'a> Frame<'a> { pub struct WebInfo { /// e.g. "#fragment" part of "www.example.com/index.html#fragment". /// Note that the leading `#` is included in the string. + /// Also known as "hash-link" or "anchor". pub web_location_hash: String, }