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`).
* 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

View file

@ -36,12 +36,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{
let mut ctx = egui::Context::new();
ctx.begin_frame(raw_input);
let mut ui = ctx.background_ui();
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
}
}

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.
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<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 {
Self {
margin: Vec2::new(8.0, 2.0),

View file

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

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 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<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,
/// 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>>,
/// How much space is used by panels.
used_by_panels: Mutex<Option<Rect>>,
// The output of a frame:
graphics: Mutex<GraphicLayers>,
@ -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<Self>, 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<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()`.
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)
}
// ---------------------------------------------------------------------

View file

@ -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<egui::Context>,