Compare commits

...

1 commit

Author SHA1 Message Date
Emil Ernerfeldt
2c76949b9b WIP: refactor Id system with StrongId 2020-11-03 22:03:19 +01:00
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. /// This forms the base of the `Window` container.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Area { pub struct Area {
id: Id, strong_id: StrongId,
movable: bool, movable: bool,
interactable: bool, interactable: bool,
order: Order, order: Order,
@ -41,9 +41,9 @@ pub struct Area {
} }
impl Area { impl Area {
pub fn new(id_source: impl Hash) -> Self { pub fn new(strong_id_source: impl Hash) -> Self {
Self { Self {
id: Id::new(id_source), strong_id: StrongId::new(strong_id_source),
movable: true, movable: true,
interactable: true, interactable: true,
order: Order::Middle, order: Order::Middle,
@ -53,7 +53,7 @@ impl Area {
} }
pub fn layer(&self) -> LayerId { pub fn layer(&self) -> LayerId {
LayerId::new(self.order, self.id) LayerId::new(self.order, self.strong_id)
} }
/// moveable by dragging the area? /// moveable by dragging the area?
@ -105,7 +105,7 @@ pub(crate) struct Prepared {
impl Area { impl Area {
pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared { pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared {
let Area { let Area {
id, strong_id,
movable, movable,
order, order,
interactable, interactable,
@ -113,9 +113,9 @@ impl Area {
fixed_pos, fixed_pos,
} = self; } = 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 { let mut state = state.unwrap_or_else(|| State {
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)), pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
size: Vec2::zero(), size: Vec2::zero(),

View file

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

View file

@ -40,14 +40,14 @@ impl Id {
Self(1) Self(1)
} }
pub fn new(source: impl Hash) -> Id { pub fn new(source: impl Hash) -> Self {
use std::hash::Hasher; use std::hash::Hasher;
let mut hasher = ahash::AHasher::default(); let mut hasher = ahash::AHasher::default();
source.hash(&mut hasher); source.hash(&mut hasher);
Id(hasher.finish()) Id(hasher.finish())
} }
pub fn with(self, child: impl Hash) -> Id { pub fn with(self, child: impl Hash) -> Self {
use std::hash::Hasher; use std::hash::Hasher;
let mut hasher = ahash::AHasher::default(); let mut hasher = ahash::AHasher::default();
hasher.write_u64(self.0); hasher.write_u64(self.0);
@ -59,3 +59,49 @@ impl Id {
format!("{:04X}", self.0 as u16) 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 std::collections::{HashMap, HashSet};
use crate::{ use crate::{cache::Cache, color::Hsva, *};
area,
cache::Cache,
collapsing_header, menu,
paint::color::{Hsva, Srgba},
resize, scroll_area,
widgets::text_edit,
window, Id, LayerId, Pos2, Rect,
};
/// The data that Egui persists between frames. /// The data that Egui persists between frames.
/// ///
@ -24,11 +16,11 @@ pub struct Memory {
pub(crate) interaction: Interaction, pub(crate) interaction: Interaction,
// states of various types of widgets // 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))] #[cfg_attr(feature = "serde", serde(skip))]
pub(crate) menu_bar: HashMap<Id, menu::BarState>, pub(crate) menu_bar: HashMap<StrongId, menu::BarState>,
pub(crate) resize: HashMap<Id, resize::State>, pub(crate) resize: HashMap<StrongId, resize::State>,
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>, pub(crate) scroll_areas: HashMap<StrongId, scroll_area::State>,
pub(crate) text_edit: HashMap<Id, text_edit::State>, pub(crate) text_edit: HashMap<Id, text_edit::State>,
#[cfg_attr(feature = "serde", serde(skip))] #[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", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(default))]
pub struct Areas { pub struct Areas {
areas: HashMap<Id, area::State>, areas: HashMap<StrongId, area::State>,
/// Top is last /// Top is last
order: Vec<LayerId>, order: Vec<LayerId>,
visible_last_frame: HashSet<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 /// Represents a region of the screen
/// with a type of layout (horizontal or vertical). /// with a type of layout (horizontal or vertical).
pub struct Ui { pub struct Ui {
/// ID of this ui. /// Used to seed more StrongId:s.
/// Generated based on id of parent ui together with strong_id: StrongId,
/// another source of child identity (e.g. window title).
/// Acts like a namespace for child uis. /// For automatic Id generation.
/// Hopefully unique. auto_id: Id,
id: Id,
painter: Painter, 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. /// 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. /// The cursor can thus be style.spacing.item_spacing pixels outside of the min_rect.
cursor: Pos2, // TODO: move into Layout? 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 { impl Ui {
@ -63,7 +57,7 @@ impl Ui {
pub fn new( pub fn new(
ctx: Arc<Context>, ctx: Arc<Context>,
layer_id: LayerId, layer_id: LayerId,
id: Id, strong_id: StrongId,
max_rect: Rect, max_rect: Rect,
clip_rect: Rect, clip_rect: Rect,
) -> Self { ) -> Self {
@ -73,14 +67,14 @@ impl Ui {
let min_size = Vec2::zero(); // TODO: From Style let min_size = Vec2::zero(); // TODO: From Style
let min_rect = layout.rect_from_cursor_size(cursor, min_size); let min_rect = layout.rect_from_cursor_size(cursor, min_size);
Ui { Ui {
id, strong_id,
auto_id: strong_id.with("auto"),
painter: Painter::new(ctx, layer_id, clip_rect), painter: Painter::new(ctx, layer_id, clip_rect),
min_rect, min_rect,
max_rect, max_rect,
style, style,
layout, layout,
cursor, cursor,
child_count: 0,
} }
} }
@ -366,7 +360,7 @@ impl Ui {
/// # `Id` creation /// # `Id` creation
impl Ui { impl Ui {
/// Use this to generate widget ids for widgets that have persistent state in `Memory`. /// 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 where
IdSource: Hash + std::fmt::Debug, IdSource: Hash + std::fmt::Debug,
{ {
@ -379,15 +373,22 @@ impl Ui {
/// Call AFTER allocating new space for your widget. /// Call AFTER allocating new space for your widget.
// TODO: return from `allocate_space` ? // TODO: return from `allocate_space` ?
pub fn make_position_id(&self) -> Id { 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 /// # Interaction
impl Ui { impl Ui {
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response { pub fn interact(&self, rect: Rect, id: impl Into<Id>, sense: Sense) -> Response {
self.ctx() self.ctx().interact(
.interact(self.layer_id(), self.clip_rect(), rect, Some(id), sense) self.layer_id(),
self.clip_rect(),
rect,
Some(id.into()),
sense,
)
} }
pub fn interact_hover(&self, rect: Rect) -> Response { pub fn interact_hover(&self, rect: Rect) -> Response {