pub struct PopupOverlayMounts<A> { /* private fields */ }Expand description
Persistent retained popup trees for one logical window presentation.
Closed registrations remain cached, so reopening the same registered popup reconciles into the same tree and preserves component state. A mount is destroyed only when its registration disappears or its owner no longer belongs to the synchronized presentation. Pointer capture/hover and focus are isolated per popup tree, while z-order follows the shared portal. This value is UI-thread-local through its runtime handle and component state.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
let mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
assert!(mounts.is_empty());Implementations§
Source§impl<A: 'static> PopupOverlayMounts<A>
Synchronization, frame stages, hit-testing, and event routing for overlays.
impl<A: 'static> PopupOverlayMounts<A>
Synchronization, frame stages, hit-testing, and event routing for overlays.
Sourcepub fn new(runtime: RuntimeHandle<A>) -> Self
pub fn new(runtime: RuntimeHandle<A>) -> Self
Creates an empty mount cache sharing runtime’s portal/application state.
No retained tree or presentation scope is allocated until synchronization observes a matching retained-overlay registration.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
let mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
assert_eq!((mounts.len(), mounts.open_len()), (0, 0));Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns cached persistent popup-tree count, including closed entries.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert_eq!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).len(), 0);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Reports whether no persistent popup tree is cached.
Closed cached mounts make this false even when Self::open_len is zero.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).is_empty());Sourcepub fn open_len(&self) -> usize
pub fn open_len(&self) -> usize
Returns the number of currently open matching retained overlays.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert_eq!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).open_len(), 0);Sourcepub fn contains(&self, popup_id: PopupId) -> bool
pub fn contains(&self, popup_id: PopupId) -> bool
Reports whether a persistent tree is cached for popup_id.
This is distinct from portal registration and open state.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup::PopupId, popup_mount::PopupOverlayMounts};
assert!(!PopupOverlayMounts::new(RuntimeHandle::<()>::new()).contains(PopupId::new(1)));Sourcepub fn element_tree_id(&self, popup_id: PopupId) -> Option<ElementTreeId>
pub fn element_tree_id(&self, popup_id: PopupId) -> Option<ElementTreeId>
Returns a cached popup mount’s allocated retained-tree namespace.
None means the popup has no retained mount, even if a procedural or
unsynchronized portal registration exists.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup::PopupId, popup_mount::PopupOverlayMounts};
assert_eq!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).element_tree_id(PopupId::new(1)), None);Sourcepub fn owner(&self, popup_id: PopupId) -> Option<&PopupOwner>
pub fn owner(&self, popup_id: PopupId) -> Option<&PopupOwner>
Borrows the last synchronized owner of a cached mount.
None means no retained mount is cached. Owner liveness can change in
the portal until the next synchronization.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup::PopupId, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).owner(PopupId::new(1)).is_none());Sourcepub fn focus_owner(&self) -> Option<PopupMountFocus>
pub fn focus_owner(&self) -> Option<PopupMountFocus>
Snapshots strict keyboard focus in the claimed popup tree.
None means no mounted popup currently owns a focused element or cached
focus state became stale.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).focus_owner().is_none());Sourcepub fn has_focus(&self) -> bool
pub fn has_focus(&self) -> bool
Whether keyboard focus currently belongs to a retained popup tree.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(!PopupOverlayMounts::new(RuntimeHandle::<()>::new()).has_focus());Sourcepub fn focused_input_role(&self) -> InputRole
pub fn focused_input_role(&self) -> InputRole
Input role of the focused retained-popup element.
Returns InputRole::None when no popup/element owns focus or the mount
disappeared.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, input::InputRole, popup_mount::PopupOverlayMounts};
assert_eq!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).focused_input_role(), InputRole::None);Sourcepub fn focused_ime_cursor_rect_global(&self) -> Option<Rect>
pub fn focused_ime_cursor_rect_global(&self) -> Option<Rect>
IME caret rectangle translated from popup-local to window coordinates.
Returns None without focused popup input, non-empty bounds, committed
layout, or a widget-provided caret. Dimensions remain logical pixels.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).focused_ime_cursor_rect_global().is_none());Sourcepub fn hovered_cursor_role_at_global(
&self,
point: Point,
) -> Option<HoverCursorRole>
pub fn hovered_cursor_role_at_global( &self, point: Point, ) -> Option<HoverCursorRole>
Cursor role under a window-global point when a retained popup owns it.
Some(Default) deliberately masks the owner-tree cursor while the
pointer rests on passive popup content. None means no retained popup
occupies the point and lets the host query the owner tree instead.
point and returned hit geometry use window-global logical pixels.
§Examples
use ailloli_ui_core::Point; use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).hovered_cursor_role_at_global(Point::new(1.0, 1.0)).is_none());Sourcepub fn apply_pending_popup_intents(&mut self) -> bool
pub fn apply_pending_popup_intents(&mut self) -> bool
Applies queued popup focus effects to every mounted tree in z-order.
Hosts call this before applying the owner-tree effects so a nested
popup can restore focus into its retained parent without leaking the
intent into a sibling tree namespace.
Returns whether any popup focus changed. Before the first sync there is
no presentation and the method is a no-op returning false.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
let mut mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
assert!(!mounts.apply_pending_popup_intents());Sourcepub fn sync(
&mut self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
) -> PopupMountSyncOutcome
pub fn sync( &mut self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, ) -> PopupMountSyncOutcome
Reconciles every open portal entry belonging to one exact presentation.
Calling this once per frame is intentional: retained reconciliation
refreshes declarative props while component slots remain attached to the
mount’s persistent element tree.
Only RetainedOverlay registrations with exact logical-window/generation
ownership participate. Existing closed matching registrations stay
cached; removed/mismatched mounts are blurred and destroyed. New mounts
are created only for open entries. The method also clears stale pointer
and focus ownership. Portal factories/component builds run synchronously.
§Panics
Propagates content-factory/component reconciliation panics and panics on
conflicting portal/runtime RefCell borrows.
§Examples
use ailloli_ui_core::LogicalWindowId; use ailloli_ui_runtime::{app::{PresentationGeneration, RuntimeHandle}, popup_mount::PopupOverlayMounts};
let mut mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
let outcome = mounts.sync(&LogicalWindowId::new("main"), PresentationGeneration::INITIAL);
assert_eq!((outcome.mounted(), outcome.removed(), outcome.open()), (0, 0, 0));Sourcepub fn resolve_and_sync(
&mut self,
logical_window_id: &LogicalWindowId,
presentation_generation: PresentationGeneration,
viewport: Rect,
capabilities: PopupBackendCapabilities,
) -> PopupMountSyncOutcome
pub fn resolve_and_sync( &mut self, logical_window_id: &LogicalWindowId, presentation_generation: PresentationGeneration, viewport: Rect, capabilities: PopupBackendCapabilities, ) -> PopupMountSyncOutcome
Resolves retained-overlay geometry for one host viewport, then syncs the persistent popup trees.
Requests that already carry backend-published bounds but omit the
declarative anchor or desired size keep those explicit bounds. This
preserves low-level hosts while allowing fully provider-neutral popup
requests to flip and clamp through the shared resolver.
Placement failures are intentionally ignored here; such entries retain
previous explicit bounds (if any) and still synchronize. Successfully
resolved bounds are recorded with viewport before Self::sync. All
geometry uses global logical pixels.
§Examples
use ailloli_ui_core::{LogicalWindowId, Rect}; use ailloli_ui_runtime::{app::{PresentationGeneration, RuntimeHandle}, popup::PopupBackendCapabilities, popup_mount::PopupOverlayMounts};
let mut mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
let outcome = mounts.resolve_and_sync(&LogicalWindowId::new("main"), PresentationGeneration::INITIAL, Rect::new(0.0, 0.0, 800.0, 600.0), PopupBackendCapabilities::overlay_only());
assert_eq!(outcome.open(), 0);Sourcepub fn layout(&mut self, scale: Scale, text_system: &mut TextSystem)
pub fn layout(&mut self, scale: Scale, text_system: &mut TextSystem)
Lays out all open popup trees in their non-empty portal bounds.
Width/height become tight logical-pixel constraints; scale is physical
pixels per logical pixel. Missing/zero-or-negative bounds are skipped.
Pending focus-on-open is applied after successful layout and focus is
made exclusive across mounts.
§Panics
Propagates widget layout/focus callback panics.
§Examples
use ailloli_ui_core::Scale; use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts}; use ailloli_ui_text::TextSystem;
let mut mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
mounts.layout(Scale::new(1.0), &mut TextSystem::new());
assert_eq!(mounts.open_len(), 0);Sourcepub fn paint(&self, text_system: &mut TextSystem, frame_time_ms: u128) -> Scene
pub fn paint(&self, text_system: &mut TextSystem, frame_time_ms: u128) -> Scene
Produces overlay layers in portal z-order, clipped to popup bounds.
Mounts without non-empty bounds/root are skipped. Painting translates
popup-local geometry by the window-global bounds origin, forces every
emitted layer to LayerKind::Overlay, and exposes frame_time_ms
verbatim to widget paint callbacks.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts}; use ailloli_ui_text::TextSystem;
let mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new());
assert!(mounts.paint(&mut TextSystem::new(), 16).layers.is_empty());Sourcepub fn append_to_scene(
&self,
scene: &mut Scene,
text_system: &mut TextSystem,
frame_time_ms: u128,
)
pub fn append_to_scene( &self, scene: &mut Scene, text_system: &mut TextSystem, frame_time_ms: u128, )
Appends retained popup layers after the owner scene.
Existing layers retain their order and popup layers append in portal bottom-to-top order. No sorting or deduplication occurs.
§Examples
use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts, scene::Scene}; use ailloli_ui_text::TextSystem;
let mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new()); let mut scene = Scene::default();
mounts.append_to_scene(&mut scene, &mut TextSystem::new(), 0);
assert!(scene.layers.is_empty());Sourcepub fn hit_test(&self, point: Point) -> Option<PopupMountHit>
pub fn hit_test(&self, point: Point) -> Option<PopupMountHit>
Hit-tests mounted content from topmost popup to bottommost.
point is window-global logical pixels. Portal bounds select a retained
mount, then the point is translated to popup-local coordinates for tree
hit-testing. Missing/non-positive bounds or non-widget hits yield None.
§Examples
use ailloli_ui_core::Point; use ailloli_ui_runtime::{app::RuntimeHandle, popup_mount::PopupOverlayMounts};
assert!(PopupOverlayMounts::new(RuntimeHandle::<()>::new()).hit_test(Point::new(1.0, 1.0)).is_none());Sourcepub fn route_envelope(
&mut self,
envelope: &EventEnvelope,
) -> PopupMountRouteOutcome
pub fn route_envelope( &mut self, envelope: &EventEnvelope, ) -> PopupMountRouteOutcome
Applies portal authority once, then routes into a popup-local tree.
The caller should skip owner-tree dispatch when PopupMountRouteOutcome::consumed
is true. Pointer coordinates are translated to the selected mount while
all event correlation metadata is preserved.
Envelopes outside the last synchronized logical window/generation are
ignored. Pointer gesture consumption persists through matching release;
Escape/outside press invoke portal authority before local dispatch;
keyboard/IME route to the focused popup; window focus loss cancels every
captured popup pointer before clearing interaction state.
§Examples
use std::time::Duration; use ailloli_ui_core::{event::{Event, FocusEvent}, LogicalWindowId};
use ailloli_ui_runtime::{app::{PresentationGeneration, RuntimeHandle}, input::{EventEnvelope, EventId, EventMeta, EventTimestamp}, popup_mount::PopupOverlayMounts};
let mut mounts = PopupOverlayMounts::new(RuntimeHandle::<()>::new()); let window = LogicalWindowId::new("main");
mounts.sync(&window, PresentationGeneration::INITIAL);
let envelope = EventEnvelope::new(EventMeta::new(EventId::new(1), EventTimestamp::new(Duration::ZERO), window, PresentationGeneration::INITIAL), Event::Focus(FocusEvent::new(true)));
assert!(!mounts.route_envelope(&envelope).consumed());