A Window may now cover an existing CentralPanel

This commit is contained in:
Emil Ernerfeldt 2020-12-18 23:30:48 +01:00
parent 82a3997188
commit 58f36eb6ef
3 changed files with 96 additions and 65 deletions

View file

@ -22,10 +22,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Slight tweak of the default `Style` and font sizes.
* `SidePanel::left` and `TopPanel::top` now takes `impl Hash` as first argument.
* A `Window` may now cover an existing `CentralPanel`.
* `ui.image` now takes `impl Into<Vec2>` as a `size` argument.
* Made some more fields of `RawInput` optional.
* `Slider` and `DragValue` uses fewer decimals by default. See the full precision by hovering over the value.
* `egui::App`: added `fn name(&self)` and `fn clear_color(&self)`
* `egui::App`: added `fn name(&self)` and `fn clear_color(&self)`.
### Deprecated
* Deprecated `RawInput::screen_size` - use `RawInput::screen_rect` instead.

View file

@ -51,7 +51,7 @@ impl SidePanel {
let panel_rect = panel_ui.min_rect();
let response = panel_ui.interact_hover(panel_rect);
ctx.allocate_left_panel(panel_rect);
ctx.frame_state().allocate_left_panel(panel_rect);
(r, response)
}
@ -105,7 +105,7 @@ impl TopPanel {
let panel_rect = panel_ui.min_rect();
let response = panel_ui.interact_hover(panel_rect);
ctx.allocate_top_panel(panel_rect);
ctx.frame_state().allocate_top_panel(panel_rect);
(r, response)
}
@ -143,7 +143,7 @@ impl CentralPanel {
let panel_rect = panel_ui.min_rect();
let response = panel_ui.interact_hover(panel_rect);
ctx.allocate_central_panel(panel_rect);
ctx.frame_state().allocate_central_panel(panel_rect);
(r, response)
}

View file

@ -10,8 +10,7 @@ use crate::{
*,
};
#[derive(Clone, Copy, Default)]
struct SliceStats<T>(usize, std::marker::PhantomData<T>);
// ----------------------------------------------------------------------------
#[derive(Clone, Debug, Default)]
struct Options {
@ -23,6 +22,85 @@ struct Options {
font_definitions: FontDefinitions,
}
// ----------------------------------------------------------------------------
/// State that is collected during a frame and then cleared
#[derive(Clone)]
pub(crate) struct FrameState {
/// Starts off as the screen_rect, shrinks as panels are added.
/// The `CentralPanel` does not change this.
/// This is the area avilable to Window's.
available_rect: Rect,
/// Starts off as the screen_rect, shrinks as panels are added.
/// The `CentralPanel` retracts from this.
unused_rect: Rect,
/// How much space is used by panels.
used_by_panels: Rect,
// TODO: move some things from `Memory` to here
}
impl Default for FrameState {
fn default() -> Self {
Self {
available_rect: Rect::invalid(),
unused_rect: Rect::invalid(),
used_by_panels: Rect::invalid(),
}
}
}
impl FrameState {
pub fn begin_frame(&mut self, input: &InputState) {
self.available_rect = input.screen_rect();
self.unused_rect = input.screen_rect();
self.used_by_panels = Rect::nothing();
}
/// How much space is still available after panels has been added.
/// This is the "background" area, what Egui doesn't cover with panels (but may cover with windows).
/// This is also the area to which windows are constrained.
pub fn available_rect(&self) -> Rect {
debug_assert!(
self.available_rect.is_finite(),
"Called `available_rect()` before `begin_frame()`"
);
self.available_rect
}
/// Shrink `available_rect`.
pub(crate) fn allocate_left_panel(&mut self, panel_rect: Rect) {
debug_assert!(
panel_rect.min == self.available_rect.min,
"Mismatching panels. You must not create a panel from within another panel."
);
self.available_rect.min.x = panel_rect.max.x;
self.unused_rect.min.x = panel_rect.max.x;
self.used_by_panels = self.used_by_panels.union(panel_rect);
}
/// Shrink `available_rect`.
pub(crate) fn allocate_top_panel(&mut self, panel_rect: Rect) {
debug_assert!(
panel_rect.min == self.available_rect.min,
"Mismatching panels. You must not create a panel from within another panel."
);
self.available_rect.min.y = panel_rect.max.y;
self.unused_rect.min.y = panel_rect.max.y;
self.used_by_panels = self.used_by_panels.union(panel_rect);
}
pub(crate) fn allocate_central_panel(&mut self, panel_rect: Rect) {
// Note: we do not shrink `available_rect`, because
// we alllow windows to cover the CentralPanel.
self.unused_rect = Rect::nothing(); // Nothing left unused after this
self.used_by_panels = self.used_by_panels.union(panel_rect);
}
}
// ----------------------------------------------------------------------------
/// Thi is the first thing you need when working with Egui.
///
/// Contains the input state, memory, options and output.
@ -40,11 +118,8 @@ pub struct Context {
input: InputState,
/// Starts off as the screen_rect, shrinks as panels are added.
/// 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>>,
/// State that is collected during a frame and then cleared
frame_state: Mutex<FrameState>,
// The output of a frame:
graphics: Mutex<GraphicLayers>,
@ -64,8 +139,7 @@ impl Clone for Context {
memory: self.memory.clone(),
animation_manager: self.animation_manager.clone(),
input: self.input.clone(),
available_rect: self.available_rect.clone(),
used_by_panels: self.used_by_panels.clone(),
frame_state: self.frame_state.clone(),
graphics: self.graphics.clone(),
output: self.output.clone(),
paint_stats: self.paint_stats.clone(),
@ -88,9 +162,7 @@ impl Context {
/// This is the "background" area, what Egui doesn't cover with panels (but may cover with windows).
/// This is also the area to which windows are constrained.
pub fn available_rect(&self) -> Rect {
self.available_rect
.lock()
.expect("Called `available_rect()` before `begin_frame()`")
self.frame_state.lock().available_rect()
}
pub fn memory(&self) -> MutexGuard<'_, Memory> {
@ -105,6 +177,10 @@ impl Context {
self.output.lock()
}
pub(crate) fn frame_state(&self) -> MutexGuard<'_, FrameState> {
self.frame_state.lock()
}
/// Call this if there is need to repaint the UI, i.e. if you are showing an animation.
/// If this is called at least once in a frame, then there will be another frame right after this.
/// Call as many times as you wish, only one repaint will be issued.
@ -225,8 +301,7 @@ impl Context {
self.memory().begin_frame(&self.input, &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.used_by_panels.lock() = Some(Rect::nothing());
self.frame_state.lock().begin_frame(&self.input);
let mut font_definitions = self.options.lock().font_definitions.clone();
font_definitions.pixels_per_point = self.input.pixels_per_point();
@ -293,49 +368,9 @@ impl Context {
// ---------------------------------------------------------------------
/// Shrink `available_rect()`.
pub(crate) fn allocate_left_panel(&self, panel_rect: Rect) {
debug_assert!(
panel_rect.min == self.available_rect().min,
"Mismatching panels. You must not create a panel from within another panel."
);
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()`.
pub(crate) fn allocate_top_panel(&self, panel_rect: Rect) {
debug_assert!(
panel_rect.min == self.available_rect().min,
"Mismatching panels. You must not create a panel from within another panel."
);
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());
let mut used = self.frame_state().used_by_panels;
for window in self.memory().areas.visible_windows() {
used = used.union(window.rect());
}
@ -394,12 +429,7 @@ impl Context {
if let Some(mouse_pos) = self.input.mouse.pos {
if let Some(layer) = self.layer_id_at(mouse_pos) {
if layer.order == Order::Background {
if let Some(available_rect) = *self.available_rect.lock() {
// "available_rect" is the area that Egui is NOT using.
!available_rect.contains(mouse_pos)
} else {
false
}
!self.frame_state().unused_rect.contains(mouse_pos)
} else {
true
}