diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index 985d3af8c16..488adcfa647 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -4,25 +4,34 @@ use crate::*; -/// State that is persisted between frames. -// TODO(emilk): this is not currently stored in `Memory::data`, but maybe it should be? +/// State of an [`Area`] that is persisted between frames. +/// +/// Areas back [`crate::Window`]s and other floating containers, +/// like tooltips and the popups of [`crate::ComboBox`]. #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub(crate) struct State { - /// Last known pos of the pivot +pub struct AreaState { + /// Last known position of the pivot. pub pivot_pos: Pos2, + /// The anchor point of the area, i.e. where on the area the [`Self::pivot_pos`] refers to. pub pivot: Align2, - /// Last know size. Used for catching clicks. + /// Last known size. pub size: Vec2, - /// If false, clicks goes straight through to what is behind us. - /// Good for tooltips etc. + /// If false, clicks goes straight through to what is behind us. Useful for tooltips etc. pub interactable: bool, } -impl State { +impl AreaState { + /// Load the state of an [`Area`] from memory. + pub fn load(ctx: &Context, id: Id) -> Option { + // TODO(emilk): Area state is not currently stored in `Memory::data`, but maybe it should be? + ctx.memory(|mem| mem.areas().get(id).copied()) + } + + /// The left top positions of the area. pub fn left_top_pos(&self) -> Pos2 { pos2( self.pivot_pos.x - self.pivot.x().to_factor() * self.size.x, @@ -30,6 +39,7 @@ impl State { ) } + /// Move the left top positions of the area. pub fn set_left_top_pos(&mut self, pos: Pos2) { self.pivot_pos = pos2( pos.x + self.pivot.x().to_factor() * self.size.x, @@ -37,6 +47,7 @@ impl State { ); } + /// Where the area is on screen. pub fn rect(&self) -> Rect { Rect::from_min_size(self.left_top_pos(), self.size) } @@ -75,6 +86,10 @@ pub struct Area { new_pos: Option, } +impl WidgetWithState for Area { + type State = AreaState; +} + impl Area { /// The `id` must be globally unique. pub fn new(id: Id) -> Self { @@ -267,7 +282,7 @@ impl Area { pub(crate) struct Prepared { layer_id: LayerId, - state: State, + state: AreaState, move_response: Response, enabled: bool, constrain: bool, @@ -313,13 +328,11 @@ impl Area { let layer_id = LayerId::new(order, id); - let state = ctx - .memory(|mem| mem.areas().get(id).copied()) - .map(|mut state| { - // override the saved state with the correct value - state.pivot = pivot; - state - }); + let state = AreaState::load(ctx, id).map(|mut state| { + // override the saved state with the correct value + state.pivot = pivot; + state + }); let is_new = state.is_none(); if is_new { ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place @@ -341,7 +354,7 @@ impl Area { size = size.at_most(constrain_rect.size()); } - State { + AreaState { pivot_pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)), pivot, size, @@ -433,7 +446,7 @@ impl Area { } let layer_id = LayerId::new(self.order, self.id); - let area_rect = ctx.memory(|mem| mem.areas().get(self.id).map(|area| area.rect())); + let area_rect = AreaState::load(ctx, self.id).map(|state| state.rect()); if let Some(area_rect) = area_rect { let clip_rect = Rect::EVERYTHING; let painter = Painter::new(ctx.clone(), layer_id, clip_rect); @@ -449,11 +462,11 @@ impl Area { } impl Prepared { - pub(crate) fn state(&self) -> &State { + pub(crate) fn state(&self) -> &AreaState { &self.state } - pub(crate) fn state_mut(&mut self) -> &mut State { + pub(crate) fn state_mut(&mut self) -> &mut AreaState { &mut self.state } @@ -542,7 +555,7 @@ fn automatic_area_position(ctx: &Context) -> Pos2 { mem.areas() .visible_windows() .into_iter() - .map(State::rect) + .map(AreaState::rect) .collect() }); existing.sort_by_key(|r| r.left().round() as i32); diff --git a/crates/egui/src/containers/mod.rs b/crates/egui/src/containers/mod.rs index 53e8e7e2ce0..2e5903239d6 100644 --- a/crates/egui/src/containers/mod.rs +++ b/crates/egui/src/containers/mod.rs @@ -13,7 +13,7 @@ pub mod scroll_area; pub(crate) mod window; pub use { - area::Area, + area::{Area, AreaState}, collapsing_header::{CollapsingHeader, CollapsingResponse}, combo_box::*, frame::Frame, diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index d0dedaa1ac1..54cdae84de9 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration}; +use containers::area::AreaState; use epaint::{ emath::TSTransform, mutex::*, stats::*, text::Fonts, util::OrderedFloat, TessellationOptions, *, }; @@ -490,7 +491,7 @@ impl ContextImpl { // Ensure we register the background area so panels and background ui can catch clicks: self.memory.areas_mut().set_state( LayerId::background(), - containers::area::State { + AreaState { pivot_pos: screen_rect.left_top(), pivot: Align2::LEFT_TOP, size: screen_rect.size(), @@ -2702,7 +2703,7 @@ impl Context { ui.label("Hover to highlight"); let layers_ids: Vec = self.memory(|mem| mem.areas().order().to_vec()); for layer_id in layers_ids { - let area = self.memory(|mem| mem.areas().get(layer_id.id).copied()); + let area = AreaState::load(self, layer_id.id); if let Some(area) = area { let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id)); if !is_visible { diff --git a/crates/egui/src/memory.rs b/crates/egui/src/memory.rs index ed2a7024ba1..949f3f47749 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -934,7 +934,7 @@ impl Memory { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct Areas { - areas: IdMap, + areas: IdMap, /// Back-to-front. Top is last. order: Vec, @@ -955,7 +955,7 @@ impl Areas { self.areas.len() } - pub(crate) fn get(&self, id: Id) -> Option<&area::State> { + pub(crate) fn get(&self, id: Id) -> Option<&area::AreaState> { self.areas.get(&id) } @@ -973,7 +973,7 @@ impl Areas { .collect() } - pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::State) { + pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::AreaState) { self.visible_current_frame.insert(layer_id); self.areas.insert(layer_id.id, state); if !self.order.iter().any(|x| *x == layer_id) { @@ -1022,7 +1022,7 @@ impl Areas { .collect() } - pub(crate) fn visible_windows(&self) -> Vec<&area::State> { + pub(crate) fn visible_windows(&self) -> Vec<&area::AreaState> { self.visible_layer_ids() .iter() .filter(|layer| layer.order == crate::Order::Middle) diff --git a/crates/egui/src/widgets/mod.rs b/crates/egui/src/widgets/mod.rs index 0ab9273c7a5..86bfbc7c213 100644 --- a/crates/egui/src/widgets/mod.rs +++ b/crates/egui/src/widgets/mod.rs @@ -91,7 +91,7 @@ where } } -/// Helper so that you can do `TextEdit::State::read…` +/// Helper so that you can do e.g. `TextEdit::State::load`. pub trait WidgetWithState { type State; }