[id] improve default Id generation

Base on child count instead of on screen position.
This is more robust against changing sizes of nearby widgets.
This commit is contained in:
Emil Ernerfeldt 2020-06-11 18:06:23 +02:00
parent 2dea2ee668
commit 94545409c6
2 changed files with 18 additions and 21 deletions

View file

@ -1,19 +1,19 @@
//! Egui tracks widgets frame-to-frame using `Id`s. //! Egui tracks widgets frame-to-frame using `Id`s.
//! //!
//! For instance, if you start dragging a slider one frame, egui stores //! For instance, if you start dragging a slider one frame, egui stores
//! the sldiers Id as the current `interact_id` so that next frame when //! the sliders `Id` as the current active id so that next frame when
//! you move the mouse the same slider changes, even if the mouse has //! you move the mouse the same slider changes, even if the mouse has
//! moved outside the slider. //! moved outside the slider.
//! //!
//! For some widgets `Id`s are also used to persist some state about the //! For some widgets `Id`s are also used to persist some state about the
//! widgets, such as Window position or wether not a collapsing header region is open. //! widgets, such as Window position or wether not a collapsing header region is open.
//! //!
//! This implicated that the `Id`s must be unqiue. //! This implicates that the `Id`s must be unqiue.
//! //!
//! For simple things like sliders and buttons that don't have any memory and //! For simple things like sliders and buttons that don't have any memory and
//! doesn't move we can use the location of the widget as a source of identity. //! doesn't move we can use the location of the widget as a source of identity.
//! For instance, a slider only needs a unique and persistent ID while you are //! For instance, a slider only needs a unique and persistent ID while you are
//! dragging the sldier. As long as it is still while moving, that is fine. //! dragging the slider. As long as it is still while moving, that is fine.
//! //!
//! For things that need to persist state even after moving (windows, collapsing headers) //! For things that need to persist state even after moving (windows, collapsing headers)
//! the location of the widgets is obviously not good enough. For instance, //! the location of the widgets is obviously not good enough. For instance,
@ -29,8 +29,6 @@
use std::hash::Hash; use std::hash::Hash;
use crate::math::Pos2;
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Id(u64); pub struct Id(u64);
@ -58,10 +56,4 @@ impl Id {
child.hash(&mut hasher); child.hash(&mut hasher);
Id(hasher.finish()) Id(hasher.finish())
} }
pub fn from_pos(p: Pos2) -> Id {
let x = p.x.round() as i32;
let y = p.y.round() as i32;
Id::new(&x).with(&y)
}
} }

View file

@ -49,6 +49,11 @@ pub struct Ui {
/// If something has already been added, this will point ot style.item_spacing beyond the latest child. /// If something has already been added, this will point ot style.item_spacing beyond the latest child.
/// The cursor can thus be style.item_spacing pixels outside of the child_bounds. /// The cursor can thus be style.item_spacing pixels outside of the child_bounds.
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 {
@ -67,17 +72,17 @@ impl Ui {
style, style,
layout: Default::default(), layout: Default::default(),
cursor: rect.min, cursor: rect.min,
child_count: 0,
} }
} }
pub fn child_ui(&self, child_rect: Rect) -> Self { pub fn child_ui(&mut self, child_rect: Rect) -> Self {
// let clip_rect = self let clip_rect = self.clip_rect(); // Keep it unless the child excplicitly desires differently
// .clip_rect let id = self.make_position_id(); // TODO: is this a good idea?
// .intersect(&child_rect.expand(self.style().clip_rect_margin)); self.child_count += 1;
let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently
Ui { Ui {
ctx: self.ctx.clone(), ctx: self.ctx.clone(),
id: self.id, id,
layer: self.layer, layer: self.layer,
clip_rect, clip_rect,
desired_rect: child_rect, desired_rect: child_rect,
@ -85,6 +90,7 @@ impl Ui {
style: self.style.clone(), style: self.style.clone(),
layout: self.layout, layout: self.layout,
cursor: child_rect.min, cursor: child_rect.min,
child_count: 0,
} }
} }
@ -293,7 +299,7 @@ impl Ui {
/// Can be used for widgets that do NOT persist state in Memory /// Can be used for widgets that do NOT persist state in Memory
/// but you still need to interact with (e.g. buttons, sliders). /// but you still need to interact with (e.g. buttons, sliders).
pub fn make_position_id(&self) -> Id { pub fn make_position_id(&self) -> Id {
self.id.with(&Id::from_pos(self.cursor)) self.id.with(self.child_count)
} }
pub fn make_child_id(&self, id_seed: impl Hash) -> Id { pub fn make_child_id(&self, id_seed: impl Hash) -> Id {
@ -393,6 +399,7 @@ impl Ui {
self.layout self.layout
.allocate_space(&mut self.cursor, &self.style, available_size, child_size); .allocate_space(&mut self.cursor, &self.style, available_size, child_size);
self.child_bounds = self.child_bounds.union(child_rect); self.child_bounds = self.child_bounds.union(child_rect);
self.child_count += 1;
child_rect child_rect
} }
@ -630,9 +637,7 @@ impl Ui {
add_contents: impl FnOnce(&mut Self) -> R, add_contents: impl FnOnce(&mut Self) -> R,
) -> (R, Rect) { ) -> (R, Rect) {
let child_rect = Rect::from_min_max(self.cursor, self.bottom_right()); let child_rect = Rect::from_min_max(self.cursor, self.bottom_right());
let mut child_ui = Self { let mut child_ui = self.child_ui(child_rect);
..self.child_ui(child_rect)
};
child_ui.set_layout(layout); // HACK: need a separate call right now child_ui.set_layout(layout); // HACK: need a separate call right now
let ret = add_contents(&mut child_ui); let ret = add_contents(&mut child_ui);
let size = child_ui.bounding_size(); let size = child_ui.bounding_size();