Make `Area` state public (#4576)

This commit is contained in:
Emil Ernerfeldt 2024-05-29 15:48:52 +02:00 committed by GitHub
parent 913cef3361
commit c7cb524bdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 43 additions and 29 deletions

View File

@ -4,25 +4,34 @@
use crate::*; use crate::*;
/// State that is persisted between frames. /// State of an [`Area`] that is persisted between frames.
// TODO(emilk): this is not currently stored in `Memory::data`, but maybe it should be? ///
/// Areas back [`crate::Window`]s and other floating containers,
/// like tooltips and the popups of [`crate::ComboBox`].
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State { pub struct AreaState {
/// Last known pos of the pivot /// Last known position of the pivot.
pub pivot_pos: Pos2, 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, pub pivot: Align2,
/// Last know size. Used for catching clicks. /// Last known size.
pub size: Vec2, pub size: Vec2,
/// If false, clicks goes straight through to what is behind us. /// If false, clicks goes straight through to what is behind us. Useful for tooltips etc.
/// Good for tooltips etc.
pub interactable: bool, pub interactable: bool,
} }
impl State { impl AreaState {
/// Load the state of an [`Area`] from memory.
pub fn load(ctx: &Context, id: Id) -> Option<Self> {
// 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 { pub fn left_top_pos(&self) -> Pos2 {
pos2( pos2(
self.pivot_pos.x - self.pivot.x().to_factor() * self.size.x, 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) { pub fn set_left_top_pos(&mut self, pos: Pos2) {
self.pivot_pos = pos2( self.pivot_pos = pos2(
pos.x + self.pivot.x().to_factor() * self.size.x, 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 { pub fn rect(&self) -> Rect {
Rect::from_min_size(self.left_top_pos(), self.size) Rect::from_min_size(self.left_top_pos(), self.size)
} }
@ -75,6 +86,10 @@ pub struct Area {
new_pos: Option<Pos2>, new_pos: Option<Pos2>,
} }
impl WidgetWithState for Area {
type State = AreaState;
}
impl Area { impl Area {
/// The `id` must be globally unique. /// The `id` must be globally unique.
pub fn new(id: Id) -> Self { pub fn new(id: Id) -> Self {
@ -267,7 +282,7 @@ impl Area {
pub(crate) struct Prepared { pub(crate) struct Prepared {
layer_id: LayerId, layer_id: LayerId,
state: State, state: AreaState,
move_response: Response, move_response: Response,
enabled: bool, enabled: bool,
constrain: bool, constrain: bool,
@ -313,13 +328,11 @@ impl Area {
let layer_id = LayerId::new(order, id); let layer_id = LayerId::new(order, id);
let state = ctx let state = AreaState::load(ctx, id).map(|mut state| {
.memory(|mem| mem.areas().get(id).copied()) // override the saved state with the correct value
.map(|mut state| { state.pivot = pivot;
// override the saved state with the correct value state
state.pivot = pivot; });
state
});
let is_new = state.is_none(); let is_new = state.is_none();
if is_new { if is_new {
ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place 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()); size = size.at_most(constrain_rect.size());
} }
State { AreaState {
pivot_pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)), pivot_pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
pivot, pivot,
size, size,
@ -433,7 +446,7 @@ impl Area {
} }
let layer_id = LayerId::new(self.order, self.id); 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 { if let Some(area_rect) = area_rect {
let clip_rect = Rect::EVERYTHING; let clip_rect = Rect::EVERYTHING;
let painter = Painter::new(ctx.clone(), layer_id, clip_rect); let painter = Painter::new(ctx.clone(), layer_id, clip_rect);
@ -449,11 +462,11 @@ impl Area {
} }
impl Prepared { impl Prepared {
pub(crate) fn state(&self) -> &State { pub(crate) fn state(&self) -> &AreaState {
&self.state &self.state
} }
pub(crate) fn state_mut(&mut self) -> &mut State { pub(crate) fn state_mut(&mut self) -> &mut AreaState {
&mut self.state &mut self.state
} }
@ -542,7 +555,7 @@ fn automatic_area_position(ctx: &Context) -> Pos2 {
mem.areas() mem.areas()
.visible_windows() .visible_windows()
.into_iter() .into_iter()
.map(State::rect) .map(AreaState::rect)
.collect() .collect()
}); });
existing.sort_by_key(|r| r.left().round() as i32); existing.sort_by_key(|r| r.left().round() as i32);

View File

@ -13,7 +13,7 @@ pub mod scroll_area;
pub(crate) mod window; pub(crate) mod window;
pub use { pub use {
area::Area, area::{Area, AreaState},
collapsing_header::{CollapsingHeader, CollapsingResponse}, collapsing_header::{CollapsingHeader, CollapsingResponse},
combo_box::*, combo_box::*,
frame::Frame, frame::Frame,

View File

@ -2,6 +2,7 @@
use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration}; use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration};
use containers::area::AreaState;
use epaint::{ use epaint::{
emath::TSTransform, mutex::*, stats::*, text::Fonts, util::OrderedFloat, TessellationOptions, *, 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: // Ensure we register the background area so panels and background ui can catch clicks:
self.memory.areas_mut().set_state( self.memory.areas_mut().set_state(
LayerId::background(), LayerId::background(),
containers::area::State { AreaState {
pivot_pos: screen_rect.left_top(), pivot_pos: screen_rect.left_top(),
pivot: Align2::LEFT_TOP, pivot: Align2::LEFT_TOP,
size: screen_rect.size(), size: screen_rect.size(),
@ -2702,7 +2703,7 @@ impl Context {
ui.label("Hover to highlight"); ui.label("Hover to highlight");
let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec()); let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
for layer_id in layers_ids { 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 { if let Some(area) = area {
let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id)); let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
if !is_visible { if !is_visible {

View File

@ -934,7 +934,7 @@ impl Memory {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(default))]
pub struct Areas { pub struct Areas {
areas: IdMap<area::State>, areas: IdMap<area::AreaState>,
/// Back-to-front. Top is last. /// Back-to-front. Top is last.
order: Vec<LayerId>, order: Vec<LayerId>,
@ -955,7 +955,7 @@ impl Areas {
self.areas.len() 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) self.areas.get(&id)
} }
@ -973,7 +973,7 @@ impl Areas {
.collect() .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.visible_current_frame.insert(layer_id);
self.areas.insert(layer_id.id, state); self.areas.insert(layer_id.id, state);
if !self.order.iter().any(|x| *x == layer_id) { if !self.order.iter().any(|x| *x == layer_id) {
@ -1022,7 +1022,7 @@ impl Areas {
.collect() .collect()
} }
pub(crate) fn visible_windows(&self) -> Vec<&area::State> { pub(crate) fn visible_windows(&self) -> Vec<&area::AreaState> {
self.visible_layer_ids() self.visible_layer_ids()
.iter() .iter()
.filter(|layer| layer.order == crate::Order::Middle) .filter(|layer| layer.order == crate::Order::Middle)

View File

@ -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 { pub trait WidgetWithState {
type State; type State;
} }