WIP: refactor Id system with StrongId

This commit is contained in:
Emil Ernerfeldt 2020-11-02 22:57:38 +01:00
parent 7abb9a2814
commit 2c76949b9b
5 changed files with 103 additions and 54 deletions

View file

@ -32,7 +32,7 @@ impl State {
/// This forms the base of the `Window` container.
#[derive(Clone, Copy, Debug)]
pub struct Area {
id: Id,
strong_id: StrongId,
movable: bool,
interactable: bool,
order: Order,
@ -41,9 +41,9 @@ pub struct Area {
}
impl Area {
pub fn new(id_source: impl Hash) -> Self {
pub fn new(strong_id_source: impl Hash) -> Self {
Self {
id: Id::new(id_source),
strong_id: StrongId::new(strong_id_source),
movable: true,
interactable: true,
order: Order::Middle,
@ -53,7 +53,7 @@ impl Area {
}
pub fn layer(&self) -> LayerId {
LayerId::new(self.order, self.id)
LayerId::new(self.order, self.strong_id)
}
/// moveable by dragging the area?
@ -105,7 +105,7 @@ pub(crate) struct Prepared {
impl Area {
pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared {
let Area {
id,
strong_id,
movable,
order,
interactable,
@ -113,9 +113,9 @@ impl Area {
fixed_pos,
} = self;
let layer_id = LayerId::new(order, id);
let layer_id = LayerId::new(order, strong_id);
let state = ctx.memory().areas.get(id).cloned();
let state = ctx.memory().areas.get(strong_id).cloned();
let mut state = state.unwrap_or_else(|| State {
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
size: Vec2::zero(),

View file

@ -3,6 +3,7 @@
//! the only places where you can put you widgets.
use crate::*;
use std::hash::Hash;
use std::sync::Arc;
// ----------------------------------------------------------------------------
@ -11,14 +12,17 @@ use std::sync::Arc;
///
/// Panels should be added before adding any `Window`s.
pub struct SidePanel {
id: Id,
strong_id: Id,
max_width: f32,
}
impl SidePanel {
/// The given `max_width` is a soft maximum (as always), and the actual panel may be smaller or larger.
pub fn left(id: Id, max_width: f32) -> Self {
Self { id, max_width }
pub fn left(strong_id_source: impl Hash, max_width: f32) -> Self {
Self {
strong_id: StrongId::new(strong_id_source),
max_width,
}
}
}
@ -28,7 +32,10 @@ impl SidePanel {
ctx: &Arc<Context>,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> (R, Response) {
let Self { id, max_width } = self;
let Self {
strong_id,
max_width,
} = self;
let mut panel_rect = ctx.available_rect();
panel_rect.max.x = panel_rect.max.x.at_most(panel_rect.min.x + max_width);
@ -36,7 +43,7 @@ impl SidePanel {
let layer_id = LayerId::background();
let clip_rect = ctx.input().screen_rect();
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect);
let mut panel_ui = Ui::new(ctx.clone(), layer_id, strong_id, panel_rect, clip_rect);
let frame = Frame::panel(&ctx.style());
let r = frame.show(&mut panel_ui, |ui| {
@ -59,16 +66,16 @@ impl SidePanel {
///
/// Panels should be added before adding any `Window`s.
pub struct TopPanel {
id: Id,
strong_id: StrongId,
max_height: Option<f32>,
}
impl TopPanel {
/// Default height is that of `interact_size.y` (i.e. a button),
/// but the panel will expand as needed.
pub fn top(id: Id) -> Self {
pub fn top(strong_id_source: impl Hash) -> Self {
Self {
id,
strong_id: StrongId::new(strong_id_source),
max_height: None,
}
}
@ -80,7 +87,10 @@ impl TopPanel {
ctx: &Arc<Context>,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> (R, Response) {
let Self { id, max_height } = self;
let Self {
strong_id,
max_height,
} = self;
let max_height = max_height.unwrap_or_else(|| ctx.style().spacing.interact_size.y);
let mut panel_rect = ctx.available_rect();
@ -89,7 +99,7 @@ impl TopPanel {
let layer_id = LayerId::background();
let clip_rect = ctx.input().screen_rect();
let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, panel_rect, clip_rect);
let mut panel_ui = Ui::new(ctx.clone(), layer_id, strong_id, panel_rect, clip_rect);
let frame = Frame::panel(&ctx.style());
let r = frame.show(&mut panel_ui, |ui| {
@ -127,10 +137,10 @@ impl CentralPanel {
let panel_rect = ctx.available_rect();
let layer_id = LayerId::background();
let id = Id::new("central_panel");
let strong_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 mut panel_ui = Ui::new(ctx.clone(), layer_id, strong_id, panel_rect, clip_rect);
let frame = Frame::background(&ctx.style());
let r = frame.show(&mut panel_ui, |ui| add_contents(ui));

View file

@ -40,14 +40,14 @@ impl Id {
Self(1)
}
pub fn new(source: impl Hash) -> Id {
pub fn new(source: impl Hash) -> Self {
use std::hash::Hasher;
let mut hasher = ahash::AHasher::default();
source.hash(&mut hasher);
Id(hasher.finish())
}
pub fn with(self, child: impl Hash) -> Id {
pub fn with(self, child: impl Hash) -> Self {
use std::hash::Hasher;
let mut hasher = ahash::AHasher::default();
hasher.write_u64(self.0);
@ -59,3 +59,49 @@ impl Id {
format!("{:04X}", self.0 as u16)
}
}
// ----------------------------------------------------------------------------
/// This is an identifier that must be unique over long time.
/// Used for storing state, like window position, scroll amount, etc.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct StrongId(u64);
impl StrongId {
pub fn background() -> Self {
Self(0)
}
pub fn tooltip() -> Self {
Self(1)
}
pub fn new(source: impl Hash) -> Self {
use std::hash::Hasher;
let mut hasher = ahash::AHasher::default();
source.hash(&mut hasher);
Id(hasher.finish())
}
pub fn with(self, child: impl Hash) -> Self {
use std::hash::Hasher;
let mut hasher = ahash::AHasher::default();
hasher.write_u64(self.0);
child.hash(&mut hasher);
Id(hasher.finish())
}
pub(crate) fn short_debug_format(&self) -> String {
format!("{:04X}", self.0 as u16)
}
}
// ----------------------------------------------------------------------------
/// Ok to weaken a `StrongId`
impl From<StrongId> for Id {
fn from(strong: StrongId) -> Self {
Id(strong.0)
}
}

View file

@ -1,14 +1,6 @@
use std::collections::{HashMap, HashSet};
use crate::{
area,
cache::Cache,
collapsing_header, menu,
paint::color::{Hsva, Srgba},
resize, scroll_area,
widgets::text_edit,
window, Id, LayerId, Pos2, Rect,
};
use crate::{cache::Cache, color::Hsva, *};
/// The data that Egui persists between frames.
///
@ -24,11 +16,11 @@ pub struct Memory {
pub(crate) interaction: Interaction,
// states of various types of widgets
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
pub(crate) collapsing_headers: HashMap<StrongId, collapsing_header::State>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) menu_bar: HashMap<Id, menu::BarState>,
pub(crate) resize: HashMap<Id, resize::State>,
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
pub(crate) menu_bar: HashMap<StrongId, menu::BarState>,
pub(crate) resize: HashMap<StrongId, resize::State>,
pub(crate) scroll_areas: HashMap<StrongId, scroll_area::State>,
pub(crate) text_edit: HashMap<Id, text_edit::State>,
#[cfg_attr(feature = "serde", serde(skip))]
@ -197,7 +189,7 @@ impl Memory {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct Areas {
areas: HashMap<Id, area::State>,
areas: HashMap<StrongId, area::State>,
/// Top is last
order: Vec<LayerId>,
visible_last_frame: HashSet<LayerId>,

View file

@ -7,12 +7,11 @@ use crate::{color::*, containers::*, layout::*, mutex::MutexGuard, paint::*, wid
/// Represents a region of the screen
/// with a type of layout (horizontal or vertical).
pub struct Ui {
/// ID of this ui.
/// Generated based on id of parent ui together with
/// another source of child identity (e.g. window title).
/// Acts like a namespace for child uis.
/// Hopefully unique.
id: Id,
/// Used to seed more StrongId:s.
strong_id: StrongId,
/// For automatic Id generation.
auto_id: Id,
painter: Painter,
@ -49,11 +48,6 @@ pub struct Ui {
/// If something has already been added, this will point ot style.spacing.item_spacing beyond the latest child.
/// The cursor can thus be style.spacing.item_spacing pixels outside of the min_rect.
cursor: Pos2, // TODO: move into Layout?
/// How many children has been added to us?
/// This is only used to create a unique interact ID for some widgets
/// that work as long as no other widgets are added/removed while interacting.
child_count: usize,
}
impl Ui {
@ -63,7 +57,7 @@ impl Ui {
pub fn new(
ctx: Arc<Context>,
layer_id: LayerId,
id: Id,
strong_id: StrongId,
max_rect: Rect,
clip_rect: Rect,
) -> Self {
@ -73,14 +67,14 @@ impl Ui {
let min_size = Vec2::zero(); // TODO: From Style
let min_rect = layout.rect_from_cursor_size(cursor, min_size);
Ui {
id,
strong_id,
auto_id: strong_id.with("auto"),
painter: Painter::new(ctx, layer_id, clip_rect),
min_rect,
max_rect,
style,
layout,
cursor,
child_count: 0,
}
}
@ -366,7 +360,7 @@ impl Ui {
/// # `Id` creation
impl Ui {
/// Use this to generate widget ids for widgets that have persistent state in `Memory`.
pub fn make_persistent_id<IdSource>(&self, id_source: IdSource) -> Id
pub fn make_persistent_id<IdSource>(&self, id_source: IdSource) -> StrongId
where
IdSource: Hash + std::fmt::Debug,
{
@ -379,15 +373,22 @@ impl Ui {
/// Call AFTER allocating new space for your widget.
// TODO: return from `allocate_space` ?
pub fn make_position_id(&self) -> Id {
self.id.with(self.child_count)
let id = self.id.with(self.child_count);
// self.ctx().register_unique_id(id, self.cursor, "position"); // NO: position may change until we interact
id
}
}
/// # Interaction
impl Ui {
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
self.ctx()
.interact(self.layer_id(), self.clip_rect(), rect, Some(id), sense)
pub fn interact(&self, rect: Rect, id: impl Into<Id>, sense: Sense) -> Response {
self.ctx().interact(
self.layer_id(),
self.clip_rect(),
rect,
Some(id.into()),
sense,
)
}
pub fn interact_hover(&self, rect: Rect) -> Response {