[demo] Move Fractal Clock to WrapApp

This commit is contained in:
Emil Ernerfeldt 2021-01-01 21:27:10 +01:00
parent b1022d01c1
commit 4848c171eb
10 changed files with 100 additions and 140 deletions

View file

@ -137,11 +137,20 @@ impl TopPanel {
/// }); /// });
/// ``` /// ```
#[derive(Default)] #[derive(Default)]
pub struct CentralPanel {} pub struct CentralPanel {
frame: Option<Frame>,
}
impl CentralPanel {
pub fn frame(mut self, frame: Frame) -> Self {
self.frame = Some(frame);
self
}
}
impl CentralPanel { impl CentralPanel {
pub fn show<R>(self, ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Response) { pub fn show<R>(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(); let panel_rect = ctx.available_rect();
@ -151,7 +160,7 @@ impl CentralPanel {
let clip_rect = ctx.input().screen_rect(); let clip_rect = ctx.input().screen_rect();
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_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 = frame.show(&mut panel_ui, |ui| {
let r = add_contents(ui); let r = add_contents(ui);
ui.expand_to_include_rect(ui.max_rect()); // Use it all ui.expand_to_include_rect(ui.max_rect()); // Use it all

View file

@ -10,7 +10,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_minimal", |b| { c.bench_function("demo_windows_minimal", |b| {
b.iter(|| { b.iter(|| {
ctx.begin_frame(raw_input.clone()); 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() ctx.end_frame()
}) })
}); });
@ -24,7 +24,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_windows_full", |b| { c.bench_function("demo_windows_full", |b| {
b.iter(|| { b.iter(|| {
ctx.begin_frame(raw_input.clone()); 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() 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 ctx.memory().all_collpasing_are_open = true; // expand the demo window with everything
let mut demo_windows = egui_demo_lib::DemoWindows::default(); let mut demo_windows = egui_demo_lib::DemoWindows::default();
ctx.begin_frame(raw_input.clone()); 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(); let (_, paint_commands) = ctx.end_frame();
c.bench_function("tessellate", |b| { c.bench_function("tessellate", |b| {

View file

@ -297,24 +297,6 @@ impl epi::App for DemoApp {
self.frame_history self.frame_history
.on_new_frame(ctx.input().time, frame.info().cpu_usage); .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 mean_frame_time = self.frame_history.mean_frame_time();
let Self { let Self {
@ -323,7 +305,7 @@ impl epi::App for DemoApp {
.. ..
} = self; } = self;
demo_windows.ui(ctx, &demo_environment, frame.tex_allocator(), |ui| { demo_windows.ui(ctx, frame.tex_allocator(), |ui| {
ui.separator(); ui.separator();
ui.checkbox(backend_window_open, "💻 Backend"); ui.checkbox(backend_window_open, "💻 Backend");

View file

@ -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<f64>,
/// Set to `Some` to open a specific part of the demo app.
pub link: Option<DemoLink>,
}
// ----------------------------------------------------------------------------
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] #[serde(default)]
struct Demos { struct Demos {
@ -67,13 +49,8 @@ pub struct DemoWindows {
#[serde(skip)] #[serde(skip)]
color_test: super::ColorTest, color_test: super::ColorTest,
fractal_clock: super::FractalClock,
/// open, title, view /// open, title, view
demos: Demos, demos: Demos,
#[serde(skip)]
previous_link: Option<DemoLink>,
} }
impl DemoWindows { impl DemoWindows {
@ -82,26 +59,11 @@ impl DemoWindows {
pub fn ui( pub fn ui(
&mut self, &mut self,
ctx: &CtxRef, ctx: &CtxRef,
env: &DemoEnvironment,
tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>, tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>,
sidebar_ui: impl FnOnce(&mut Ui), 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| { egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| {
ui.heading("✒ Egui Demo"); ui.heading("✒ Egui Demo");
egui::warn_if_debug_build(ui);
ui.separator(); ui.separator();
@ -132,24 +94,22 @@ impl DemoWindows {
}); });
egui::TopPanel::top("menu_bar").show(ctx, |ui| { 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. /// Show the open windows.
fn windows( fn windows(
&mut self, &mut self,
ctx: &CtxRef, ctx: &CtxRef,
env: &DemoEnvironment,
tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>, tex_allocator: &mut Option<&mut dyn epi::TextureAllocator>,
) { ) {
let Self { let Self {
open_windows, open_windows,
demo_window, demo_window,
color_test, color_test,
fractal_clock,
demos, demos,
.. ..
} = self; } = self;
@ -191,12 +151,6 @@ impl DemoWindows {
demos.show(ctx); demos.show(ctx);
fractal_clock.window(
ctx,
&mut open_windows.fractal_clock,
env.seconds_since_midnight,
);
self.resize_windows(ctx); self.resize_windows(ctx);
} }
@ -259,7 +213,6 @@ impl DemoWindows {
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
struct OpenWindows { struct OpenWindows {
demo: bool, demo: bool,
fractal_clock: bool,
// egui stuff: // egui stuff:
settings: bool, settings: bool,
@ -284,7 +237,6 @@ impl OpenWindows {
fn none() -> Self { fn none() -> Self {
Self { Self {
demo: false, demo: false,
fractal_clock: false,
settings: false, settings: false,
inspection: false, inspection: false,
@ -298,7 +250,6 @@ impl OpenWindows {
fn checkboxes(&mut self, ui: &mut Ui) { fn checkboxes(&mut self, ui: &mut Ui) {
let Self { let Self {
demo, demo,
fractal_clock,
settings, settings,
inspection, inspection,
memory, memory,
@ -317,11 +268,10 @@ impl OpenWindows {
.on_hover_text("For testing the integrations painter"); .on_hover_text("For testing the integrations painter");
ui.separator(); ui.separator();
ui.label("Misc:"); ui.label("Misc:");
ui.checkbox(fractal_clock, "🕑 Fractal Clock");
} }
} }
fn show_menu_bar(ui: &mut Ui, windows: &mut OpenWindows, seconds_since_midnight: Option<f64>) { fn show_menu_bar(ui: &mut Ui) {
use egui::*; use egui::*;
menu::bar(ui, |ui| { 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(); *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;
}
});
}
}); });
} }

View file

@ -13,7 +13,6 @@ mod drag_and_drop;
mod font_book; mod font_book;
pub mod font_contents_emoji; pub mod font_contents_emoji;
pub mod font_contents_ubuntu; pub mod font_contents_ubuntu;
mod fractal_clock;
mod painting; mod painting;
mod scrolls; mod scrolls;
mod sliders; mod sliders;
@ -23,8 +22,8 @@ mod widgets;
pub use { pub use {
app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow, app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow,
demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock, demo_windows::*, drag_and_drop::*, font_book::FontBook, painting::Painting, scrolls::Scrolls,
painting::Painting, scrolls::Scrolls, sliders::Sliders, tests::Tests, widgets::Widgets, sliders::Sliders, tests::Tests, widgets::Widgets,
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View file

@ -29,17 +29,19 @@ impl Default for FractalClock {
} }
} }
impl FractalClock { impl epi::App for FractalClock {
pub fn window(&mut self, ctx: &CtxRef, open: &mut bool, seconds_since_midnight: Option<f64>) { fn name(&self) -> &str {
Window::new("🕑 Fractal Clock") "🕑 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));
} }
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<f64>) { pub fn ui(&mut self, ui: &mut Ui, seconds_since_midnight: Option<f64>) {
if !self.paused { if !self.paused {
self.time = seconds_since_midnight.unwrap_or_else(|| ui.input().time); self.time = seconds_since_midnight.unwrap_or_else(|| ui.input().time);

View file

@ -1,7 +1,9 @@
mod demo; mod demo;
mod fractal_clock;
mod http_app; mod http_app;
pub use demo::DemoApp; pub use demo::DemoApp;
pub use fractal_clock::FractalClock;
pub use http_app::HttpApp; pub use http_app::HttpApp;
pub use demo::DemoWindows; // used for tests pub use demo::DemoWindows; // used for tests

View file

@ -96,7 +96,7 @@ fn test_egui_e2e() {
const NUM_FRAMES: usize = 5; const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone()); 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 (_output, paint_commands) = ctx.end_frame();
let paint_jobs = ctx.tessellate(paint_commands); let paint_jobs = ctx.tessellate(paint_commands);
assert!(!paint_jobs.is_empty()); assert!(!paint_jobs.is_empty());

View file

@ -2,10 +2,27 @@
#[derive(Default, serde::Deserialize, serde::Serialize)] #[derive(Default, serde::Deserialize, serde::Serialize)]
#[serde(default)] #[serde(default)]
pub struct WrapApp { 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, demo: crate::apps::DemoApp,
http: crate::apps::HttpApp, http: crate::apps::HttpApp,
clock: crate::apps::FractalClock,
}
impl Apps {
fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut dyn epi::App)> {
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 { 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<'_>) { fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
let web_location_hash = frame if let Some(web_info) = frame.info().web_info.as_ref() {
.info() if let Some(anchor) = web_info.web_location_hash.strip_prefix("#") {
.web_info self.selected_anchor = anchor.to_owned();
.as_ref() }
.map(|info| info.web_location_hash.clone()) }
.unwrap_or_default();
if web_location_hash == "#clock" { if self.selected_anchor.is_empty() {
// TODO self.selected_anchor = self.apps.iter_mut().next().unwrap().0.to_owned();
} else if web_location_hash == "#http" {
self.selectable_demo_name = self.http.name().to_owned();
} }
egui::TopPanel::top("wrap_app").show(ctx, |ui| { egui::TopPanel::top("wrap_app").show(ctx, |ui| {
ui.horizontal(|ui| { // A menu-bar is a horizontal layout with some special styles applied.
ui.label(format!("web_location_hash: {:?}", web_location_hash)); egui::menu::bar(ui, |ui| {
ui.label("Demo Apps:"); for (anchor, app) in self.apps.iter_mut() {
ui.selectable_value( if ui
&mut self.selectable_demo_name, .selectable_label(self.selected_anchor == anchor, app.name())
self.demo.name().to_owned(), .clicked
self.demo.name(), {
); self.selected_anchor = anchor.to_owned();
ui.selectable_value( if frame.is_web() {
&mut self.selectable_demo_name, ui.output().open_url = Some(format!("#{}", anchor));
self.http.name().to_owned(), }
self.http.name(), }
); }
ui.with_layout(egui::Layout::right_to_left(), |ui| { 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); egui::warn_if_debug_build(ui);
}); });
}); });
}); });
if self.selectable_demo_name == self.demo.name() { for (anchor, app) in self.apps.iter_mut() {
self.demo.update(ctx, frame); if anchor == self.selected_anchor {
} else if self.selectable_demo_name == self.http.name() { app.update(ctx, frame);
self.http.update(ctx, frame); }
} else {
self.selectable_demo_name = self.demo.name().to_owned();
} }
} }
} }
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))
}

View file

@ -159,6 +159,7 @@ impl<'a> Frame<'a> {
pub struct WebInfo { pub struct WebInfo {
/// e.g. "#fragment" part of "www.example.com/index.html#fragment". /// e.g. "#fragment" part of "www.example.com/index.html#fragment".
/// Note that the leading `#` is included in the string. /// Note that the leading `#` is included in the string.
/// Also known as "hash-link" or "anchor".
pub web_location_hash: String, pub web_location_hash: String,
} }