diff --git a/CHANGELOG.md b/CHANGELOG.md index 45e7a72c..d04daae2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ * Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`). * You can no longer throw windows * `Context::begin_frame()` no longer returns anything. - * Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`. + * Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. ## 0.2.0 - 2020-10-10 diff --git a/egui/benches/benchmark.rs b/egui/benches/benchmark.rs index c396316b..e36ade34 100644 --- a/egui/benches/benchmark.rs +++ b/egui/benches/benchmark.rs @@ -36,11 +36,12 @@ pub fn criterion_benchmark(c: &mut Criterion) { { let mut ctx = egui::Context::new(); ctx.begin_frame(raw_input); - let mut ui = ctx.background_ui(); - c.bench_function("label", |b| { - b.iter(|| { - ui.label(egui::demos::LOREM_IPSUM_LONG); - }) + egui::CentralPanel::default().show(&ctx, |ui| { + c.bench_function("label", |b| { + b.iter(|| { + ui.label(egui::demos::LOREM_IPSUM_LONG); + }) + }); }); // let _ = ctx.end_frame(); // skip, because tessellating all that text is slow } diff --git a/egui/src/app.rs b/egui/src/app.rs index c399e812..4417180b 100644 --- a/egui/src/app.rs +++ b/egui/src/app.rs @@ -13,7 +13,7 @@ use crate::Context; /// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate. pub trait App { /// Called each time the UI needs repainting, which may be many times per second. - /// Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`. + /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. fn ui( &mut self, ctx: &std::sync::Arc, diff --git a/egui/src/containers/frame.rs b/egui/src/containers/frame.rs index 33338c04..f62527f1 100644 --- a/egui/src/containers/frame.rs +++ b/egui/src/containers/frame.rs @@ -31,6 +31,16 @@ impl Frame { } } + /// Suitable for a fullscreen app + pub fn background(style: &Style) -> Self { + Self { + margin: Vec2::new(8.0, 8.0), + corner_radius: 0.0, + fill: style.visuals.widgets.noninteractive.bg_fill, + stroke: Default::default(), + } + } + pub(crate) fn panel(style: &Style) -> Self { Self { margin: Vec2::new(8.0, 2.0), diff --git a/egui/src/containers/mod.rs b/egui/src/containers/mod.rs index 3e357311..1fd7ef80 100644 --- a/egui/src/containers/mod.rs +++ b/egui/src/containers/mod.rs @@ -17,7 +17,7 @@ pub use { collapsing_header::*, combo_box::*, frame::Frame, - panel::{SidePanel, TopPanel}, + panel::{CentralPanel, SidePanel, TopPanel}, popup::*, resize::Resize, scroll_area::ScrollArea, diff --git a/egui/src/containers/panel.rs b/egui/src/containers/panel.rs index 58e10d7b..91141619 100644 --- a/egui/src/containers/panel.rs +++ b/egui/src/containers/panel.rs @@ -1,4 +1,6 @@ -//! Panels +//! Panels are fixed `Ui` regions. +//! Together with `Window` and `Area`:s they are +//! the only places where you can put you widgets. use crate::*; use std::sync::Arc; @@ -103,3 +105,41 @@ impl TopPanel { (r, response) } } + +// ---------------------------------------------------------------------------- + +/// A panel that covers the remainder of the screen, +/// i.e. whatever area is left after adding other panels. +/// +/// `CentralPanel` should be added after all other panels. +/// Any `Window`s and `Area`s will cover the `CentralPanel`. +#[derive(Default)] +pub struct CentralPanel {} + +impl CentralPanel { + pub fn show( + self, + ctx: &Arc, + add_contents: impl FnOnce(&mut Ui) -> R, + ) -> (R, Response) { + let Self {} = self; + + let panel_rect = ctx.available_rect(); + + let layer_id = LayerId::background(); + let id = Id::new("central_panel"); + + 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::background(&ctx.style()); + let r = frame.show(&mut panel_ui, |ui| add_contents(ui)); + + let panel_rect = panel_ui.min_rect(); + let response = panel_ui.interact_hover(panel_rect); + + ctx.allocate_central_panel(panel_rect); + + (r, response) + } +} diff --git a/egui/src/context.rs b/egui/src/context.rs index d3200850..74a512c0 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -43,8 +43,10 @@ pub struct Context { input: InputState, /// Starts off as the screen_rect, shrinks as panels are added. - /// Becomes `Rect::nothing()` when `Context::background_ui` is called. + /// Becomes `Rect::nothing()` after a `CentralPanel` is finished. available_rect: Mutex>, + /// How much space is used by panels. + used_by_panels: Mutex>, // The output of a frame: graphics: Mutex, @@ -67,6 +69,7 @@ impl Clone for Context { animation_manager: self.animation_manager.clone(), input: self.input.clone(), available_rect: self.available_rect.clone(), + used_by_panels: self.used_by_panels.clone(), graphics: self.graphics.clone(), output: self.output.clone(), used_ids: self.used_ids.clone(), @@ -199,10 +202,7 @@ impl Context { // --------------------------------------------------------------------- /// Call at the start of every frame. - /// To get a `Ui` to place widgets into you one or more of: - /// * `SidePanel` or `TopPanel` - /// * `Window` - /// * `Context::background_ui()` + /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. pub fn begin_frame(self: &mut Arc, new_input: RawInput) { let mut self_: Self = (**self).clone(); self_.begin_frame_mut(new_input); @@ -216,6 +216,7 @@ impl Context { self.input = std::mem::take(&mut self.input).begin_frame(new_raw_input); *self.available_rect.lock() = Some(self.input.screen_rect()); + *self.used_by_panels.lock() = Some(Rect::nothing()); let mut font_definitions = self.options.lock().font_definitions.clone(); font_definitions.pixels_per_point = self.input.pixels_per_point(); @@ -278,31 +279,12 @@ impl Context { // --------------------------------------------------------------------- - /// A `Ui` that covers the whole background (not used by panels). - /// - /// This is the same area that `Window`s are put in to, - /// so either put your UI into `background_ui()` or into `Window`s. - /// - /// Call this at most once per frame. - pub fn background_ui(self: &Arc) -> Ui { - let rect = self.available_rect(); - debug_assert!( - rect != Rect::nothing(), - "You already called `background_ui()` once this frame!" - ); - *self.available_rect.lock() = Some(Rect::nothing()); // Nothing left after this - - let layer_id = LayerId::background(); - Ui::new(self.clone(), layer_id, layer_id.id, rect, rect) - } - - // --------------------------------------------------------------------- - /// Shrink `available_rect()`. pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) { let mut remainder = self.available_rect(); remainder.min.x = panel_rect.max.x; *self.available_rect.lock() = Some(remainder); + self.register_panel(panel_rect); } /// Shrink `available_rect()`. @@ -310,6 +292,38 @@ impl Context { let mut remainder = self.available_rect(); remainder.min.y = panel_rect.max.y; *self.available_rect.lock() = Some(remainder); + self.register_panel(panel_rect); + } + + /// Shrink `available_rect()`. + pub(crate) fn allocate_central_panel(&self, panel_rect: Rect) { + let mut available_rect = self.available_rect.lock(); + debug_assert!( + *available_rect != Some(Rect::nothing()), + "You already created a `CentralPanel` this frame!" + ); + *available_rect = Some(Rect::nothing()); // Nothing left after this + self.register_panel(panel_rect); + } + + fn register_panel(&self, panel_rect: Rect) { + let mut used = self.used_by_panels.lock(); + *used = Some(used.unwrap_or(Rect::nothing()).union(panel_rect)); + } + + /// How much space is used by panels and windows. + pub fn used_rect(&self) -> Rect { + let mut used = self.used_by_panels.lock().unwrap_or(Rect::nothing()); + for window in self.memory().areas.visible_windows() { + used = used.union(window.rect()); + } + used + } + + /// How much space is used by panels and windows. + /// You can shrink your Egui area to this size and still fit all Egui components. + pub fn used_size(&self) -> Vec2 { + self.used_rect().max - Pos2::new(0.0, 0.0) } // --------------------------------------------------------------------- diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index e335c9ff..cffb2daa 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -12,7 +12,7 @@ struct MyApp { impl egui::app::App for MyApp { /// Called each time the UI needs repainting, which may be many times per second. - /// Put your widgets into a `SidePanel`, `TopPanel`, `Window` or into `ctx.background_ui()`. + /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. fn ui( &mut self, ctx: &std::sync::Arc,