Skip to main content

lgui_core/core/component/runtime/
state.rs

1use super::*;
2
3pub struct RuntimeOutput {
4    pub events: Vec<UiEvent>,
5    pub handler_events: Vec<UiHandlerEvent>,
6    pub action_events: Vec<UiActionEvent>,
7    pub default_actions: Vec<UiDefaultAction>,
8    pub dirty_bounds: Option<UiRect>,
9    pub animation_changed: bool,
10    pub route_changed: bool,
11}
12
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
14pub struct PendingUpdateOutput {
15    pub dirty_ids: Vec<UiId>,
16    pub focus_changed: bool,
17    pub frame_requested: bool,
18}
19
20#[derive(Clone, Debug)]
21pub struct UiDefaultAction {
22    pub event_target: UiId,
23    pub action_target: UiId,
24    pub action: UiAction,
25}
26
27#[derive(Default)]
28pub struct UiRuntime {
29    pub(super) events: UiEventDispatcher,
30    pub(super) animations: AnimationRegistry,
31    pub(super) component_states: ComponentStateStore,
32    pub(super) component_tree: ComponentTree,
33    pub(super) contexts: ContextRegistry,
34    pub(super) hook_states: HookStateStore,
35    pub(super) hook_updates: Arc<UiUpdateQueue>,
36    pub(super) task_spawner: Option<UiTaskSpawner>,
37    pub(super) effects: EffectRegistry,
38    pub(super) dirty: DirtyTracker,
39    pub(super) frame_interval_ms: Option<u64>,
40}
41
42impl UiRuntime {
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    pub fn reset(&mut self) {
48        let hook_updates = Arc::clone(&self.hook_updates);
49        let task_spawner = self.task_spawner.clone();
50        hook_updates.clear();
51        *self = Self {
52            hook_updates,
53            task_spawner,
54            ..Self::default()
55        };
56    }
57
58    pub fn animations(&self) -> &AnimationRegistry {
59        &self.animations
60    }
61
62    pub fn animations_mut(&mut self) -> &mut AnimationRegistry {
63        &mut self.animations
64    }
65
66    pub fn component_states(&self) -> &ComponentStateStore {
67        &self.component_states
68    }
69
70    pub fn component_tree(&self) -> &ComponentTree {
71        &self.component_tree
72    }
73
74    pub fn frame_interval_ms(&self) -> Option<u64> {
75        [
76            self.frame_interval_ms,
77            self.animations.is_running().then_some(16),
78            self.component_states.requested_frame_interval_ms(),
79        ]
80        .into_iter()
81        .flatten()
82        .min()
83    }
84
85    pub fn invalidate_all_components(&self) {
86        self.component_tree.mark_all_dirty();
87    }
88
89    pub fn contexts(&self) -> &ContextRegistry {
90        &self.contexts
91    }
92
93    pub fn hook_states(&self) -> &HookStateStore {
94        &self.hook_states
95    }
96
97    pub fn hook_updates(&self) -> &Arc<UiUpdateQueue> {
98        &self.hook_updates
99    }
100
101    pub fn set_wake(&self, wake: UiWake) {
102        self.hook_updates.set_wake(wake);
103    }
104
105    pub fn set_task_spawner(&mut self, spawner: UiTaskSpawner) {
106        self.task_spawner = Some(spawner);
107    }
108
109    pub fn task_spawner(&self) -> Option<&UiTaskSpawner> {
110        self.task_spawner.as_ref()
111    }
112
113    pub fn apply_pending_updates(&mut self, tree: &HostTree) -> PendingUpdateOutput {
114        let dirty = self
115            .hook_updates
116            .apply(&self.hook_states, &self.component_tree);
117        let focus_changed = self
118            .hook_updates
119            .take_focus_request()
120            .is_some_and(|target| self.focus_node(tree, &target));
121        if focus_changed {
122            // The caller schedules focus damage from PendingUpdateOutput. Do not leak the same
123            // dirty event into the next unrelated native input pass.
124            let _ = self.dirty.take();
125        }
126        let frame_requested = self.hook_updates.take_frame_request();
127        PendingUpdateOutput {
128            dirty_ids: dirty,
129            focus_changed,
130            frame_requested,
131        }
132    }
133
134    pub fn effects(&self) -> &EffectRegistry {
135        &self.effects
136    }
137
138    pub fn run_effects(&self) {
139        self.effects.run_pending();
140    }
141
142    pub fn interaction_state(&self) -> super::UiInteractionState {
143        self.events.state()
144    }
145
146    pub fn clear_interaction_state(&mut self) -> bool {
147        let state_changed = self.events.clear_interaction_state();
148        let animation_changed = self.animations.clear_targets(&[
149            AnimProperty::Hover,
150            AnimProperty::Pressed,
151            AnimProperty::Focus,
152        ]);
153        state_changed || animation_changed
154    }
155
156    pub fn clear_effects(&mut self) {
157        self.effects.clear();
158    }
159
160    pub fn clear_hook_state(&mut self) {
161        self.hook_updates.clear();
162        self.hook_states.clear();
163    }
164
165    pub(crate) fn suspend_rendering(&mut self) {
166        self.clear_interaction_state();
167        self.dirty = DirtyTracker::default();
168    }
169
170    pub(super) fn mark_component_owners<'a>(
171        &self,
172        tree: &HostTree,
173        ids: impl IntoIterator<Item = &'a UiId>,
174    ) {
175        for id in ids {
176            if let Some(owner) = tree.node(id).and_then(|node| node.component_owner) {
177                self.component_tree.mark_dirty(owner);
178            }
179        }
180    }
181}