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,
}