pub struct PopupPortal<A> { /* private fields */ }Expand description
UI-local popup registry and z-order authority.
The portal is deliberately Rc-backed through PopupContent, matching
the runtime’s retained UI ownership. It must be driven on the UI thread.
Registration storage is unbounded. Open z-order is bottom-to-top with a
fixed procedural-fallback stratum below retained overlays; opening raises
only within the popup’s stratum.
§Examples
use ailloli_ui_runtime::popup::PopupPortal;
let portal = PopupPortal::<()>::new();
assert!(portal.topmost().is_none());Implementations§
Source§impl<A> PopupPortal<A>
Registration, geometry, lifecycle, z-order, and input authority operations.
impl<A> PopupPortal<A>
Registration, geometry, lifecycle, z-order, and input authority operations.
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an empty portal equivalent to Default::default.
§Examples
use ailloli_ui_runtime::popup::PopupPortal;
assert_eq!(PopupPortal::<()>::new().open_ids().count(), 0);Sourcepub fn allocate_id(&mut self) -> Result<PopupId, PopupPortalError>
pub fn allocate_id(&mut self) -> Result<PopupId, PopupPortalError>
Allocates a portal-local popup ID suitable for registration.
IDs start at one, skip currently registered collisions, and are not reused merely because an entry is unregistered. Allocation reserves no entry, so callers must still register the returned ID.
§Errors
Returns IdExhausted when checked increment cannot advance beyond
u64::MAX; that terminal candidate is not returned.
§Examples
use ailloli_ui_runtime::popup::{PopupId, PopupPortal};
let mut portal = PopupPortal::<()>::new();
assert_eq!(portal.allocate_id()?, PopupId::new(1));
assert_eq!(portal.allocate_id()?, PopupId::new(2));Sourcepub fn register(
&mut self,
request: PopupRequest<A>,
) -> Result<(), PopupPortalError>
pub fn register( &mut self, request: PopupRequest<A>, ) -> Result<(), PopupPortalError>
Registers a closed popup without presenting or building it.
Explicit IDs advance subsequent automatic allocation. A parent must already be registered and share its logical window/presentation generation; tree namespace may differ. Geometry is not validated here.
§Errors
Returns DuplicateId, UnknownParent, ParentPresentationMismatch, or
IdExhausted. Errors leave registration storage unchanged, though a
terminal explicit u64::MAX cannot advance the allocator.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(4);
portal.register(PopupRequest::new(id, PopupOwner::new("main", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
assert!(portal.contains(id) && !portal.is_open(id));Sourcepub fn contains(&self, popup_id: PopupId) -> bool
pub fn contains(&self, popup_id: PopupId) -> bool
Reports whether an ID is registered, regardless of visibility.
§Examples
use ailloli_ui_runtime::popup::{PopupId, PopupPortal};
assert!(!PopupPortal::<()>::new().contains(PopupId::new(1)));Sourcepub fn is_open(&self, popup_id: PopupId) -> bool
pub fn is_open(&self, popup_id: PopupId) -> bool
Reports whether a registered popup is semantically open.
Unknown and closed IDs both return false.
§Examples
use ailloli_ui_runtime::popup::{PopupId, PopupPortal};
assert!(!PopupPortal::<()>::new().is_open(PopupId::new(1)));Sourcepub fn request(&self, popup_id: PopupId) -> Option<&PopupRequest<A>>
pub fn request(&self, popup_id: PopupId) -> Option<&PopupRequest<A>>
Borrows a registered semantic request.
None means unknown ID. The returned request may describe either a
closed or open popup; resolved bounds live separately in the portal.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(2)), PopupContent::new(View::empty)))?;
assert_eq!(portal.request(id).unwrap().owner().element_id(), ElementId(2));Sourcepub fn build_content(&self, popup_id: PopupId) -> Option<View<A>>
pub fn build_content(&self, popup_id: PopupId) -> Option<View<A>>
Builds a fresh view from a registered popup’s retained factory.
Closed registrations can be built. None means unknown ID; a factory
panic propagates.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(|| View::empty().key("built"))))?;
assert_eq!(portal.build_content(id).unwrap().key_ref(), Some("built"));Sourcepub fn set_content(
&mut self,
popup_id: PopupId,
content: PopupContent<A>,
) -> Result<(), PopupPortalError>
pub fn set_content( &mut self, popup_id: PopupId, content: PopupContent<A>, ) -> Result<(), PopupPortalError>
Replaces the declarative content factory without changing visibility, z-order, ownership, or backend-resolved geometry.
Component reconciliation uses this when a stable popup owner rebuilds with new options, bindings, callbacks, or disabled state. The factory is moved but not invoked.
§Errors
Returns UnknownPopup without retaining content when the ID is absent.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
portal.set_content(id, PopupContent::new(|| View::empty().key("new")))?;
assert_eq!(portal.build_content(id).unwrap().key_ref(), Some("new"));Sourcepub fn bounds(&self, popup_id: PopupId) -> Option<Rect>
pub fn bounds(&self, popup_id: PopupId) -> Option<Rect>
Returns backend-resolved global logical-pixel bounds.
None covers unknown IDs and registered popups with no current bounds;
closing or changed semantic placement clears bounds.
§Examples
use ailloli_ui_runtime::popup::{PopupId, PopupPortal};
assert_eq!(PopupPortal::<()>::new().bounds(PopupId::new(1)), None);Sourcepub fn resolved_viewport(&self, popup_id: PopupId) -> Option<Rect>
pub fn resolved_viewport(&self, popup_id: PopupId) -> Option<Rect>
Returns the host viewport that produced the current resolved bounds.
Explicit backend bounds installed through Self::set_bounds do not
imply a viewport and therefore return None here.
Unknown IDs and cleared/closed geometry also return None.
§Examples
use ailloli_ui_runtime::popup::{PopupId, PopupPortal};
assert_eq!(PopupPortal::<()>::new().resolved_viewport(PopupId::new(1)), None);Sourcepub fn set_anchor(
&mut self,
popup_id: PopupId,
anchor: Option<Rect>,
) -> Result<(), PopupPortalError>
pub fn set_anchor( &mut self, popup_id: PopupId, anchor: Option<Rect>, ) -> Result<(), PopupPortalError>
Updates the retained anchor used by the selected presentation backend.
This does not change popup bounds: the anchor belongs to the semantic
request while Self::set_bounds records the rectangle produced by the
active backend.
None explicitly clears the semantic anchor. Validation occurs before
ID lookup, so an invalid anchor returns InvalidBounds even for an
unknown ID.
§Errors
Returns InvalidBounds for non-finite edges/negative dimensions, or
UnknownPopup after successful validation.
§Examples
use ailloli_ui_core::{ElementId, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
let anchor = Rect::new(1.0, 2.0, 3.0, 4.0); portal.set_anchor(id, Some(anchor))?;
assert_eq!(portal.request(id).unwrap().anchor(), Some(anchor));Sourcepub fn set_placement_request(
&mut self,
popup_id: PopupId,
placement: PopupPlacementSpec,
) -> Result<(), PopupPortalError>
pub fn set_placement_request( &mut self, popup_id: PopupId, placement: PopupPlacementSpec, ) -> Result<(), PopupPortalError>
Publishes provider-neutral placement inputs for a registered popup.
The update is atomic: every geometry value is validated before the
retained request is changed. Previously resolved backend bounds are
cleared only when a placement field changes, so an idempotent repaint
cannot discard geometry that the host already resolved.
Backend and mount policy are not part of PopupPlacementSpec and stay
unchanged.
§Errors
All geometry validation failures map to InvalidBounds; a valid update
for an absent ID returns UnknownPopup.
§Examples
use ailloli_ui_core::{ElementId, Rect, Size}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
let spec = PopupPlacementSpec::new(Rect::new(1.0, 2.0, 3.0, 4.0), Size::new(20.0, 10.0)).with_gap(2.0);
portal.set_placement_request(id, spec)?; assert_eq!(portal.request(id).unwrap().gap(), 2.0);Sourcepub fn set_bounds(
&mut self,
popup_id: PopupId,
bounds: Rect,
) -> Result<(), PopupPortalError>
pub fn set_bounds( &mut self, popup_id: PopupId, bounds: Rect, ) -> Result<(), PopupPortalError>
Records bounds produced by the chosen popup backend for hit-testing.
Bounds are global logical pixels and must have finite edges/non-negative dimensions. Any previously associated resolved viewport is cleared. Validation precedes ID lookup.
§Errors
Returns InvalidBounds or UnknownPopup.
§Examples
use ailloli_ui_core::{ElementId, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
let bounds = Rect::new(5.0, 6.0, 20.0, 10.0); portal.set_bounds(id, bounds)?;
assert_eq!(portal.bounds(id), Some(bounds));Sourcepub fn set_resolved_bounds(
&mut self,
popup_id: PopupId,
viewport: Rect,
bounds: Rect,
) -> Result<(), PopupPortalError>
pub fn set_resolved_bounds( &mut self, popup_id: PopupId, viewport: Rect, bounds: Rect, ) -> Result<(), PopupPortalError>
Records bounds resolved by a host together with its complete viewport.
Both rectangles are validated before either value is committed, so a rejected update cannot separate bounds from the viewport that produced them. Geometry is global logical pixels. Viewport must have strictly positive dimensions, but bounds may be zero-sized and need not lie inside it.
§Errors
Any invalid/empty viewport or invalid bounds maps to InvalidBounds;
valid geometry for an absent ID returns UnknownPopup.
§Examples
use ailloli_ui_core::{ElementId, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
let viewport = Rect::new(0.0, 0.0, 100.0, 80.0); let bounds = Rect::new(5.0, 6.0, 20.0, 10.0);
portal.set_resolved_bounds(id, viewport, bounds)?; assert_eq!(portal.resolved_viewport(id), Some(viewport));Sourcepub fn clear_bounds(
&mut self,
popup_id: PopupId,
) -> Result<(), PopupPortalError>
pub fn clear_bounds( &mut self, popup_id: PopupId, ) -> Result<(), PopupPortalError>
Clears both backend bounds and their optional resolved viewport.
Visibility, semantic anchor/request, and z-order remain unchanged.
§Errors
Returns UnknownPopup for an absent ID.
§Examples
use ailloli_ui_core::{ElementId, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
portal.set_bounds(id, Rect::new(0.0, 0.0, 1.0, 1.0))?; portal.clear_bounds(id)?;
assert_eq!(portal.bounds(id), None);Sourcepub fn open(
&mut self,
popup_id: PopupId,
) -> Result<PopupPortalOutcome, PopupPortalError>
pub fn open( &mut self, popup_id: PopupId, ) -> Result<PopupPortalOutcome, PopupPortalError>
Opens or raises a popup and emits presentation/focus intents.
A closed popup emits Present, followed by MoveFocusInto when
requested. Reopening an open popup raises it within its mount stratum but
emits no duplicate intents. Geometry is optional. A child requires its
parent to be open.
§Errors
Returns UnknownPopup or ParentNotOpen without changing visibility.
§Panics
Panics only if the internal popup map loses the entry between the initial existence check and its mutation.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
let outcome = portal.open(id)?; assert!(outcome.handled());
assert!(matches!(outcome.intents(), [PopupIntent::Present { popup_id }] if *popup_id == id));Sourcepub fn close(&mut self, popup_id: PopupId) -> PopupPortalOutcome
pub fn close(&mut self, popup_id: PopupId) -> PopupPortalOutcome
Programmatically closes an open popup subtree.
This is shorthand for close_with_reason(id, Programmatic). Unknown IDs
return an unhandled empty outcome; a registered but already-closed ID is
handled with no intents.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.open(id)?;
assert!(portal.close(id).handled()); assert!(!portal.is_open(id));Sourcepub fn close_with_reason(
&mut self,
popup_id: PopupId,
reason: PopupDismissReason,
) -> PopupPortalOutcome
pub fn close_with_reason( &mut self, popup_id: PopupId, reason: PopupDismissReason, ) -> PopupPortalOutcome
Closes a popup and every registered descendant with explicit root reason.
Open descendants dismiss topmost-first with ParentClosed; the root uses
reason. Bounds are cleared, registrations remain, and optional owner
focus restoration is emitted last. Unknown IDs are unhandled.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.open(id)?;
let outcome = portal.close_with_reason(id, PopupDismissReason::Escape);
assert!(matches!(outcome.intents()[0], PopupIntent::Dismiss { reason: PopupDismissReason::Escape, .. }));Sourcepub fn unregister(&mut self, popup_id: PopupId) -> PopupPortalOutcome
pub fn unregister(&mut self, popup_id: PopupId) -> PopupPortalOutcome
Removes a registration and all registered descendants.
Open entries first emit Unregistered/ParentClosed dismissals without
focus restoration, then all subtree entries/z-order records are removed.
A registered closed entry still yields handled == true; unknown IDs do
not.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?;
assert!(portal.unregister(id).handled()); assert!(!portal.contains(id));Sourcepub fn open_ids(&self) -> impl DoubleEndedIterator<Item = PopupId> + '_
pub fn open_ids(&self) -> impl DoubleEndedIterator<Item = PopupId> + '_
Iterates open popups from bottom to top across the fixed mount strata.
The iterator borrows the portal, is double-ended, and yields IDs by value. Procedural fallback entries always precede retained overlays.
§Examples
use ailloli_ui_runtime::popup::PopupPortal;
let portal = PopupPortal::<()> ::new();
assert_eq!(portal.open_ids().next(), None);Sourcepub fn topmost(&self) -> Option<PopupId>
pub fn topmost(&self) -> Option<PopupId>
Returns the effective topmost open popup across mount strata.
None means no popup is open.
§Examples
use ailloli_ui_runtime::popup::PopupPortal;
assert_eq!(PopupPortal::<()>::new().topmost(), None);Sourcepub fn topmost_for_owner(
&self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
element_tree_id: ElementTreeId,
element_id: ElementId,
) -> Option<PopupId>
pub fn topmost_for_owner( &self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, element_tree_id: ElementTreeId, element_id: ElementId, ) -> Option<PopupId>
Returns the topmost open popup owned by an exact retained element in one presentation.
Procedural overlay backends use this to translate their authoritative tree hit-test into a portal popup id before first paint has committed global popup bounds. Exact matching includes logical window, presentation generation, retained-tree namespace, and element ID. Geometry is irrelevant.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::INITIAL, ElementTreeId::new(3), ElementId(4)), PopupContent::new(View::empty)))?; portal.open(id)?;
assert_eq!(portal.topmost_for_owner(&window, PresentationGeneration::INITIAL, ElementTreeId::new(3), ElementId(4)), Some(id));Sourcepub fn hit_test(
&self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
point: Point,
) -> Option<PopupId>
pub fn hit_test( &self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, point: Point, ) -> Option<PopupId>
Returns the topmost bounded popup containing point for one presentation.
Point/bounds use global logical pixels and [Rect::contains] edge
semantics. Open unpositioned popups cannot hit. Exact logical-window and
generation matching prevents stale surfaces from participating.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId, Point, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.set_bounds(id, Rect::new(0.0, 0.0, 10.0, 10.0))?; portal.open(id)?;
assert_eq!(portal.hit_test(&window, PresentationGeneration::INITIAL, Point::new(5.0, 5.0)), Some(id));Sourcepub fn handle_pointer_press(
&mut self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
point: Point,
) -> PopupPortalOutcome
pub fn handle_pointer_press( &mut self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, point: Point, ) -> PopupPortalOutcome
Routes a pointer press through popup z-order.
Popups above the hit popup see an outside press and may close. A hit on
an interactive popup is consumed so underlying content is not activated.
Routing is restricted to the exact logical window/generation and uses
global logical-pixel geometry. handled can be true with no dismissal
when an inside popup consumes pointer input.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId, Point, Rect}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.set_bounds(id, Rect::new(0.0, 0.0, 10.0, 10.0))?; portal.open(id)?;
assert!(portal.handle_pointer_press(&window, PresentationGeneration::INITIAL, Point::new(20.0, 20.0)).handled());
assert!(!portal.is_open(id));Sourcepub fn handle_pointer_press_with_backend_hit(
&mut self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
point: Point,
backend_hit: Option<PopupId>,
) -> PopupPortalOutcome
pub fn handle_pointer_press_with_backend_hit( &mut self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, point: Point, backend_hit: Option<PopupId>, ) -> PopupPortalOutcome
Routes a pointer press using an optional hit confirmed by the selected presentation backend.
The explicit hit is useful before a procedural overlay’s first paint,
when retained overlay hit regions exist but global portal bounds have
not yet been committed. It is accepted only when the popup is open and
belongs to the routed presentation, then arbitrated with any bounds hit
according to the portal’s effective z-order.
point remains relevant for competing committed-bounds hits. A backend
hit does not require portal bounds.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId, Point}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.open(id)?;
let outcome = portal.handle_pointer_press_with_backend_hit(&window, PresentationGeneration::INITIAL, Point::new(500.0, 500.0), Some(id));
assert!(outcome.handled() && portal.is_open(id));Sourcepub fn handle_escape(
&mut self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
) -> PopupPortalOutcome
pub fn handle_escape( &mut self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, ) -> PopupPortalOutcome
Dismisses the topmost popup in a presentation when it allows Escape.
A topmost popup that disables Escape blocks lower popups and returns an
unhandled outcome. Successful close uses reason Escape and may restore
owner focus.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.open(id)?;
assert!(portal.handle_escape(&window, PresentationGeneration::INITIAL).handled());
assert!(!portal.is_open(id));Sourcepub fn prune_stale_owners(
&mut self,
owner_is_alive: impl FnMut(&PopupOwner) -> bool,
) -> PopupPortalOutcome
pub fn prune_stale_owners( &mut self, owner_is_alive: impl FnMut(&PopupOwner) -> bool, ) -> PopupPortalOutcome
Removes every registration whose owner callback reports missing.
The callback sees all registrations in unspecified hash-map order. Stale roots are closed/removed with descendants; duplicate descendant matches are collapsed. Open roots can emit dismissal intents; focus is not restored during stale-owner pruning.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(0), ElementId(9)), PopupContent::new(View::empty)))?;
assert!(portal.prune_stale_owners(|owner| owner.element_id() != ElementId(9)).handled());
assert!(!portal.contains(id));Sourcepub fn prune_stale_owners_in_tree(
&mut self,
element_tree_id: ElementTreeId,
element_is_alive: impl FnMut(ElementId) -> bool,
) -> PopupPortalOutcome
pub fn prune_stale_owners_in_tree( &mut self, element_tree_id: ElementTreeId, element_is_alive: impl FnMut(ElementId) -> bool, ) -> PopupPortalOutcome
Removes registrations owned by one retained tree whose element no longer exists, without inspecting registrations from sibling trees.
A crate::app::RuntimeHandle can be shared by multiple windows. A
tree-local reconcile must therefore never decide that owners belonging
to another tree are stale.
Callback order is unspecified. Removal semantics match
Self::prune_stale_owners and do not restore focus.
§Examples
use ailloli_ui_core::ElementId; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1);
portal.register(PopupRequest::new(id, PopupOwner::new("w", PresentationGeneration::INITIAL, ElementTreeId::new(3), ElementId(9)), PopupContent::new(View::empty)))?;
portal.prune_stale_owners_in_tree(ElementTreeId::new(3), |_| false);
assert!(!portal.contains(id));Sourcepub fn close_stale_presentations(
&mut self,
logical_window_id: &LogicalWindowId,
current_generation: PresentationGeneration,
) -> PopupPortalOutcome
pub fn close_stale_presentations( &mut self, logical_window_id: &LogicalWindowId, current_generation: PresentationGeneration, ) -> PopupPortalOutcome
Removes popups attached to obsolete generations of a logical window.
Registrations for other logical windows and for current_generation
remain. Stale roots and descendants are closed/removed with
PresentationStale/ParentClosed; focus restoration is suppressed.
§Examples
use ailloli_ui_core::{ElementId, LogicalWindowId}; use ailloli_ui_runtime::{app::PresentationGeneration, component::View, popup::*};
let mut portal = PopupPortal::<()>::new(); let id = PopupId::new(1); let window = LogicalWindowId::new("w");
portal.register(PopupRequest::new(id, PopupOwner::new(window.clone(), PresentationGeneration::new(1), ElementTreeId::new(0), ElementId(1)), PopupContent::new(View::empty)))?; portal.open(id)?;
assert!(portal.close_stale_presentations(&window, PresentationGeneration::new(2)).handled());
assert!(!portal.contains(id));