diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 5586734b..1371f1a7 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -124,6 +124,7 @@ impl Ui { pub fn new(ctx: Context, id: Id, ui_builder: UiBuilder) -> Self { let UiBuilder { id_salt, + global_scope: _, ui_stack_info, layer_id, max_rect, @@ -250,6 +251,7 @@ impl Ui { pub fn new_child(&mut self, ui_builder: UiBuilder) -> Self { let UiBuilder { id_salt, + global_scope, ui_stack_info, layer_id, max_rect, @@ -287,8 +289,14 @@ impl Ui { } debug_assert!(!max_rect.any_nan(), "max_rect is NaN: {max_rect:?}"); - let stable_id = self.id.with(id_salt); - let unique_id = stable_id.with(self.next_auto_id_salt); + let (stable_id, unique_id) = if global_scope { + (id_salt, id_salt) + } else { + let stable_id = self.id.with(id_salt); + let unique_id = stable_id.with(self.next_auto_id_salt); + + (stable_id, unique_id) + }; let next_auto_id_salt = unique_id.value().wrapping_add(1); self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1); diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 54917018..e83148da 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -14,6 +14,7 @@ use crate::{Id, LayerId, Layout, Rect, Sense, Style, UiStackInfo}; #[derive(Clone, Default)] pub struct UiBuilder { pub id_salt: Option, + pub global_scope: bool, pub ui_stack_info: UiStackInfo, pub layer_id: Option, pub max_rect: Option, @@ -42,6 +43,34 @@ impl UiBuilder { self } + /// Set an id of the new `Ui` that is independent of the parent `Ui`. + /// This way child widgets can be moved in the ui tree without losing state. + /// You have to ensure that in a frame the child widgets do not get rendered in multiple places. + /// + /// You should set the same unique `id` at every place in the ui tree where you want the + /// child widgets to share state. + /// If the child widgets are not moved in the ui tree, use [`UiBuilder::id_salt`] instead. + /// + /// This is a shortcut for `.id_salt(my_id).global_scope(true)`. + #[inline] + pub fn id(mut self, id: impl Hash) -> Self { + self.id_salt = Some(Id::new(id)); + self.global_scope = true; + self + } + + /// Make the new `Ui` child ids independent of the parent `Ui`. + /// This way child widgets can be moved in the ui tree without losing state. + /// You have to ensure that in a frame the child widgets do not get rendered in multiple places. + /// + /// You should set the same globally unique `id_salt` at every place in the ui tree where you want the + /// child widgets to share state. + #[inline] + pub fn global_scope(mut self, global_scope: bool) -> Self { + self.global_scope = global_scope; + self + } + /// Provide some information about the new `Ui` being built. #[inline] pub fn ui_stack_info(mut self, ui_stack_info: UiStackInfo) -> Self {