telar_ui_core/input_region.rs
1use std::collections::HashMap;
2
3use geometry_core::Rect;
4use layout_core::NodeId;
5use reactive_core::ReadSignal;
6
7reactive_core::surface_local! {
8 /// A per-surface set of interactive (press/drag) targets and their laid-out rect signals. The runner
9 /// activates each surface's [`InputRegionContext`] around its build/event/frame.
10 slot INTERACTIVE: HashMap<NodeId, ReadSignal<Rect>> = HashMap::new();
11 access with_interactive, with_interactive_ref;
12 context InputRegionContext, InputRegionGuard;
13}
14
15/// Registers `node` as an interactive (press/drag) target, tracking its laid-out `rect` signal. A surface that
16/// carves its input region from its content — a click-through overlay such as the notification popups — reads
17/// [`interactive_rects`] to receive pointer input only where widgets actually respond. Idempotent per node.
18pub(crate) fn register_interactive(node: NodeId, rect: ReadSignal<Rect>) {
19 with_interactive(|m| {
20 m.insert(node, rect);
21 });
22}
23
24/// Drops `node` from the interactive set — called when its widget is dropped, so a dismissed card stops
25/// contributing to the input region.
26pub(crate) fn unregister_interactive(node: NodeId) {
27 with_interactive(|m| {
28 m.remove(&node);
29 });
30}
31
32/// The current laid-out rects of every interactive widget on the active surface, dropping any not yet laid
33/// out (zero-sized). Read without subscribing (`peek`), so the platform's frame loop can call it outside a
34/// reactive scope without accidentally tracking the layout signals.
35pub fn interactive_rects() -> Vec<Rect> {
36 with_interactive_ref(|m| {
37 m.values()
38 .map(ReadSignal::peek)
39 .filter(|r| r.width > 0.0 && r.height > 0.0)
40 .collect()
41 })
42}