Replace ctx.background_ui with CentralPanel

This commit is contained in:
Emil Ernerfeldt 2020-10-24 18:37:20 +02:00
parent 44a7cac046
commit 4b549a773e
8 changed files with 100 additions and 35 deletions

View file

@ -11,7 +11,7 @@
* Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`). * Fix a bug where some regions would slowly grow for non-integral scales (`pixels_per_point`).
* You can no longer throw windows * You can no longer throw windows
* `Context::begin_frame()` no longer returns anything. * `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 ## 0.2.0 - 2020-10-10

View file

@ -36,11 +36,12 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{ {
let mut ctx = egui::Context::new(); let mut ctx = egui::Context::new();
ctx.begin_frame(raw_input); ctx.begin_frame(raw_input);
let mut ui = ctx.background_ui(); egui::CentralPanel::default().show(&ctx, |ui| {
c.bench_function("label", |b| { c.bench_function("label", |b| {
b.iter(|| { b.iter(|| {
ui.label(egui::demos::LOREM_IPSUM_LONG); ui.label(egui::demos::LOREM_IPSUM_LONG);
}) })
});
}); });
// let _ = ctx.end_frame(); // skip, because tessellating all that text is slow // let _ = ctx.end_frame(); // skip, because tessellating all that text is slow
} }

View file

@ -13,7 +13,7 @@ use crate::Context;
/// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate. /// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate.
pub trait App { pub trait App {
/// Called each time the UI needs repainting, which may be many times per second. /// 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( fn ui(
&mut self, &mut self,
ctx: &std::sync::Arc<Context>, ctx: &std::sync::Arc<Context>,

View file

@ -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 { pub(crate) fn panel(style: &Style) -> Self {
Self { Self {
margin: Vec2::new(8.0, 2.0), margin: Vec2::new(8.0, 2.0),

View file

@ -17,7 +17,7 @@ pub use {
collapsing_header::*, collapsing_header::*,
combo_box::*, combo_box::*,
frame::Frame, frame::Frame,
panel::{SidePanel, TopPanel}, panel::{CentralPanel, SidePanel, TopPanel},
popup::*, popup::*,
resize::Resize, resize::Resize,
scroll_area::ScrollArea, scroll_area::ScrollArea,

View file

@ -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 crate::*;
use std::sync::Arc; use std::sync::Arc;
@ -103,3 +105,41 @@ impl TopPanel {
(r, response) (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<R>(
self,
ctx: &Arc<Context>,
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)
}
}

View file

@ -43,8 +43,10 @@ pub struct Context {
input: InputState, input: InputState,
/// Starts off as the screen_rect, shrinks as panels are added. /// 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<Option<Rect>>, available_rect: Mutex<Option<Rect>>,
/// How much space is used by panels.
used_by_panels: Mutex<Option<Rect>>,
// The output of a frame: // The output of a frame:
graphics: Mutex<GraphicLayers>, graphics: Mutex<GraphicLayers>,
@ -67,6 +69,7 @@ impl Clone for Context {
animation_manager: self.animation_manager.clone(), animation_manager: self.animation_manager.clone(),
input: self.input.clone(), input: self.input.clone(),
available_rect: self.available_rect.clone(), available_rect: self.available_rect.clone(),
used_by_panels: self.used_by_panels.clone(),
graphics: self.graphics.clone(), graphics: self.graphics.clone(),
output: self.output.clone(), output: self.output.clone(),
used_ids: self.used_ids.clone(), used_ids: self.used_ids.clone(),
@ -199,10 +202,7 @@ impl Context {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/// Call at the start of every frame. /// Call at the start of every frame.
/// To get a `Ui` to place widgets into you one or more of: /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
/// * `SidePanel` or `TopPanel`
/// * `Window`
/// * `Context::background_ui()`
pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) { pub fn begin_frame(self: &mut Arc<Self>, new_input: RawInput) {
let mut self_: Self = (**self).clone(); let mut self_: Self = (**self).clone();
self_.begin_frame_mut(new_input); 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.input = std::mem::take(&mut self.input).begin_frame(new_raw_input);
*self.available_rect.lock() = Some(self.input.screen_rect()); *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(); let mut font_definitions = self.options.lock().font_definitions.clone();
font_definitions.pixels_per_point = self.input.pixels_per_point(); 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<Self>) -> 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()`. /// Shrink `available_rect()`.
pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) { pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) {
let mut remainder = self.available_rect(); let mut remainder = self.available_rect();
remainder.min.x = panel_rect.max.x; remainder.min.x = panel_rect.max.x;
*self.available_rect.lock() = Some(remainder); *self.available_rect.lock() = Some(remainder);
self.register_panel(panel_rect);
} }
/// Shrink `available_rect()`. /// Shrink `available_rect()`.
@ -310,6 +292,38 @@ impl Context {
let mut remainder = self.available_rect(); let mut remainder = self.available_rect();
remainder.min.y = panel_rect.max.y; remainder.min.y = panel_rect.max.y;
*self.available_rect.lock() = Some(remainder); *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)
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------

View file

@ -12,7 +12,7 @@ struct MyApp {
impl egui::app::App for MyApp { impl egui::app::App for MyApp {
/// Called each time the UI needs repainting, which may be many times per second. /// 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( fn ui(
&mut self, &mut self,
ctx: &std::sync::Arc<egui::Context>, ctx: &std::sync::Arc<egui::Context>,