From 2c76949b9b41c45d815c9c946ee1583a9972dc7f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 2 Nov 2020 22:57:38 +0100 Subject: [PATCH] WIP: refactor Id system with StrongId --- egui/src/containers/area.rs | 14 +++++----- egui/src/containers/panel.rs | 34 +++++++++++++++--------- egui/src/id.rs | 50 ++++++++++++++++++++++++++++++++++-- egui/src/memory.rs | 20 +++++---------- egui/src/ui.rs | 39 ++++++++++++++-------------- 5 files changed, 103 insertions(+), 54 deletions(-) diff --git a/egui/src/containers/area.rs b/egui/src/containers/area.rs index b2500bbc..08b5cf7f 100644 --- a/egui/src/containers/area.rs +++ b/egui/src/containers/area.rs @@ -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) -> 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(), diff --git a/egui/src/containers/panel.rs b/egui/src/containers/panel.rs index 91141619..eb76c6c8 100644 --- a/egui/src/containers/panel.rs +++ b/egui/src/containers/panel.rs @@ -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, 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, } 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, 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)); diff --git a/egui/src/id.rs b/egui/src/id.rs index d4402ae1..adaa4dea 100644 --- a/egui/src/id.rs +++ b/egui/src/id.rs @@ -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 for Id { + fn from(strong: StrongId) -> Self { + Id(strong.0) + } +} diff --git a/egui/src/memory.rs b/egui/src/memory.rs index ef4ce820..f574a91d 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -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, + pub(crate) collapsing_headers: HashMap, #[cfg_attr(feature = "serde", serde(skip))] - pub(crate) menu_bar: HashMap, - pub(crate) resize: HashMap, - pub(crate) scroll_areas: HashMap, + pub(crate) menu_bar: HashMap, + pub(crate) resize: HashMap, + pub(crate) scroll_areas: HashMap, pub(crate) text_edit: HashMap, #[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, + areas: HashMap, /// Top is last order: Vec, visible_last_frame: HashSet, diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 28137223..cef18ba5 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -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, 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(&self, id_source: IdSource) -> Id + pub fn make_persistent_id(&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, 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 {