virtual_desktop_manager/
dynamic_gui.rs

1//! Compose multiple [`nwg::PartialUi`] types in a convenient way.
2
3/// A macro that generates a method that calls a method with the same name on
4/// the dynamic UI, provided a [`DynamicUiRef`] is stored inside the current
5/// type.
6macro_rules! _forward_to_dynamic_ui {
7    ($dynamic_ui:ident => $($method_name:ident),* $(,)?) => {
8        $(
9            fn $method_name(&self) {
10                let Some($dynamic_ui) = self.$dynamic_ui.get() else { return };
11                $dynamic_ui.$method_name();
12            }
13        )*
14    }
15}
16
17#[allow(unused_imports)]
18pub(crate) use _forward_to_dynamic_ui as forward_to_dynamic_ui;
19
20use std::{
21    any::{self, TypeId},
22    cell::{Cell, OnceCell, Ref, RefCell},
23    collections::VecDeque,
24    fmt,
25    marker::PhantomData,
26    ops::Deref,
27    rc::{Rc, Weak},
28};
29
30use crate::nwg_ext::enum_child_windows;
31
32/// A trait object safe version of [`nwg::PartialUi`].
33pub trait PartialUiDyn {
34    /// Forwards calls to [`nwg::PartialUi::build_partial`].
35    fn build_partial_dyn(
36        &mut self,
37        parent: Option<nwg::ControlHandle>,
38    ) -> Result<(), nwg::NwgError>;
39
40    /// Forwards calls to [`nwg::PartialUi::process_event`].
41    fn process_event_dyn(
42        &self,
43        _evt: nwg::Event,
44        _evt_data: &nwg::EventData,
45        _handle: nwg::ControlHandle,
46    ) {
47    }
48
49    /// Forwards calls to [`nwg::PartialUi::handles`].
50    fn handles_dyn(&self) -> Vec<&'_ nwg::ControlHandle> {
51        vec![]
52    }
53}
54impl<T> PartialUiDyn for T
55where
56    T: nwg::PartialUi,
57{
58    fn build_partial_dyn(
59        &mut self,
60        parent: Option<nwg::ControlHandle>,
61    ) -> Result<(), nwg::NwgError> {
62        <T as nwg::PartialUi>::build_partial(self, parent)
63    }
64    fn process_event_dyn(
65        &self,
66        evt: nwg::Event,
67        evt_data: &nwg::EventData,
68        handle: nwg::ControlHandle,
69    ) {
70        <T as nwg::PartialUi>::process_event(self, evt, evt_data, handle)
71    }
72    fn handles_dyn(&self) -> Vec<&'_ nwg::ControlHandle> {
73        <T as nwg::PartialUi>::handles(self)
74    }
75}
76
77/// Allow downcast for trait object.
78pub trait AsAny {
79    fn as_any(&self) -> &dyn any::Any;
80    fn type_name(&self) -> &'static str;
81    /// Swap 2 values. Returns `true` if both values had the same type and so
82    /// the swap was successful.
83    fn swap_dyn(&mut self, other: &mut dyn any::Any) -> bool;
84}
85impl<T> AsAny for T
86where
87    T: Sized + 'static,
88{
89    fn as_any(&self) -> &dyn any::Any {
90        self
91    }
92    #[cfg(debug_assertions)]
93    fn type_name(&self) -> &'static str {
94        any::type_name::<T>()
95    }
96    #[cfg(not(debug_assertions))]
97    fn type_name(&self) -> &'static str {
98        any::type_name::<dyn AsAny>()
99    }
100    fn swap_dyn(&mut self, other: &mut dyn any::Any) -> bool {
101        if let Some(other) = <dyn any::Any>::downcast_mut::<T>(other) {
102            core::mem::swap(self, other);
103            true
104        } else {
105            false
106        }
107    }
108}
109
110pub trait DynWithDefault: AsAny {
111    /// Create a temporary default value of the current type and provide it in a
112    /// closure. The callback's first argument is `self` and the second argument
113    /// is the new temporary default value. The callback can then modify the
114    /// value as needed.
115    fn with_default_mut(&mut self, f: &mut dyn FnMut(&mut dyn DynWithDefault, &mut dyn any::Any));
116
117    /// Set `self` to a new default value and inspect the previous value as the
118    /// second argument to the callback.
119    fn clear_and_inspect_old(
120        &mut self,
121        f: &mut dyn FnMut(&mut dyn DynWithDefault, &mut dyn any::Any),
122    ) {
123        self.with_default_mut(&mut |current, new| {
124            current.swap_dyn(new);
125            let old = new;
126            f(current, old);
127        });
128    }
129
130    fn clear(&mut self) {
131        self.with_default_mut(&mut |current, new| {
132            current.swap_dyn(new);
133        });
134    }
135}
136impl<T> DynWithDefault for T
137where
138    T: Default + AsAny + 'static,
139{
140    fn with_default_mut(&mut self, f: &mut dyn FnMut(&mut dyn DynWithDefault, &mut dyn any::Any)) {
141        f(self, &mut T::default())
142    }
143    fn clear_and_inspect_old(
144        &mut self,
145        f: &mut dyn FnMut(&mut dyn DynWithDefault, &mut dyn any::Any),
146    ) {
147        let mut old = core::mem::take(self);
148        f(self, &mut old);
149    }
150    fn clear(&mut self) {
151        *self = T::default();
152    }
153}
154
155/// A trait for [`nwg::PartialUi`] types that wants to be managed by
156/// [`DynamicUi`].
157///
158/// # Lifecycle
159///
160/// ## Initial build
161///
162/// For each item (in their specified order) these functions are called:
163///
164/// 1. [`DynamicUiHooks::before_partial_build`].
165///    - If the `should_build: &mut bool` argument is set to `false` then all
166///      steps until the `Rebuild` section is skipped. So no events will be
167///      delivered to items that haven't been built. Such items will also be
168///      skipped by helper methods like [`DynamicUi::for_each_ui`].
169/// 2. [`PartialUiDyn::build_partial_dyn`].
170/// 3. [`DynamicUiHooks::after_partial_build`].
171/// 4. [`PartialUiDyn::handles_dyn`].
172/// 5. [`DynamicUiHooks::after_handles`].
173///    - The handles are used to bind event handlers.
174/// 6. [`DynamicUiHooks::need_raw_events_for_children`].
175///
176/// ## Process events
177///
178/// 7. [`DynamicUiHooks::process_raw_event`].
179///    - This might be called after [`DynamicUiHooks::after_process_events`]
180///      depending on if the last registered event handler gets events first.
181///      (The raw event handler is registered last.)
182/// 8. [`PartialUiDyn::process_event_dyn`].
183/// 9. [`DynamicUiHooks::after_process_events`].
184///
185/// ## Rebuild
186///
187/// After processing events the [`DynamicUi`] checks if there are items that
188/// need to be rebuilt:
189///
190/// 10. [`DynamicUiHooks::need_rebuild`].
191/// 11. [`DynamicUiHooks::is_ordered_in_parent`] if not rebuild was needed but a
192///     previous sibling was rebuilt.
193///
194/// If one of the previous predicate functions returned `true` or the item's
195/// parent was rebuilt then the item will be rebuilt:
196///
197/// 12. [`PartialUiDyn::handles_dyn`].
198/// 13. [`DynamicUiHooks::after_handles`].
199///     - The handles are used to unbind event handlers.
200/// 15. [`DynamicUiHooks::before_rebuild`]
201///
202/// After that the same functions as the initial build is used.
203pub trait DynamicUiHooks<T: ?Sized>: PartialUiDyn + DynWithDefault + 'static {
204    /// Called before the item has been built. The returned parent will be
205    /// passed to [`nwg::PartialUi::build_partial`] and used by controls in
206    /// structs that make use of the [`nwd::NwgPartial`] derive macro.
207    ///
208    /// This function should also return the type ID of the dynamic ui that owns
209    /// the control handle so that this ui is rebuilt in case its parent is
210    /// rebuilt.
211    ///
212    /// The `&mut bool` argument can be set to `false` in order to not build
213    /// this item.
214    fn before_partial_build(
215        &mut self,
216        _dynamic_ui: &Rc<T>,
217        _should_build: &mut bool,
218    ) -> Option<(nwg::ControlHandle, TypeId)>;
219
220    /// Called right after the ui has finished building.
221    ///
222    /// Note: this will also be called after the ui has been rebuilt.
223    fn after_partial_build(&mut self, _dynamic_ui: &Rc<T>) {}
224
225    /// Run right after [`nwg::PartialUi::handles`] and allows modifying its
226    /// result.
227    fn after_handles<'a>(
228        &'a self,
229        _dynamic_ui: &Rc<T>,
230        _handles: &mut Vec<&'a nwg::ControlHandle>,
231    ) {
232    }
233
234    /// Called after [`DynamicUiHooks::after_handles`] to check if we should
235    /// bind **raw** event handlers for child controls as well.
236    fn need_raw_events_for_children(&self) -> bool {
237        false
238    }
239
240    /// Run right after [`nwg::PartialUi::process_event`] and allows easily
241    /// doing some extra processing. Useful since the original method might be
242    /// implemented by a derive macro which would make it difficult to modify.
243    fn after_process_events(
244        &self,
245        _dynamic_ui: &Rc<T>,
246        _evt: nwg::Event,
247        _evt_data: &nwg::EventData,
248        _handle: nwg::ControlHandle,
249        _window: nwg::ControlHandle,
250    ) {
251    }
252    /// Listen to raw window events (not filtered or processed by
253    /// [`native_windows_gui`]). The first result that returns `Some` will be
254    /// used as the actual return value for the event.
255    ///
256    /// Note that [`nwg::bind_raw_event_handler`] only listens for events on the
257    /// top most control and not its children which differs from how
258    /// [`nwg::full_bind_event_handler`] works and therefore you might see less
259    /// events in this hook than in [`DynamicUiHooks::after_process_events`].
260    fn process_raw_event(
261        &self,
262        _dynamic_ui: &Rc<T>,
263        _hwnd: isize,
264        _msg: u32,
265        _w: usize,
266        _l: isize,
267        _window: nwg::ControlHandle,
268    ) -> Option<isize> {
269        None
270    }
271
272    /// Indicate that this item needs to be rebuilt. Maybe because its part of a
273    /// context menu and its items need to be changed.
274    ///
275    /// This method is called automatically after
276    /// [`DynamicUiHooks::after_process_events`] to check if processing the
277    /// events changed the UI so that it needs to be rebuilt.
278    fn need_rebuild(&self, _dynamic_ui: &Rc<T>) -> bool {
279        false
280    }
281    /// Indicates if this item has a specific position relative to other items
282    /// in its parent. If this returns `true` then the item will be rebuilt
283    /// after previous siblings (items that share the same parent) are rebuilt.
284    ///
285    /// Defaults to `true` since its usually safer to rebuild more often.
286    fn is_ordered_in_parent(&self) -> bool {
287        true
288    }
289    /// Do some cleanup before the plugin is built again. By default this resets
290    /// the state to its default value.
291    fn before_rebuild(&mut self, _dynamic_ui: &Rc<T>) {
292        self.clear();
293    }
294}
295
296pub trait DynamicUiWrapper: Sized + 'static {
297    type Hooks: ?Sized + DynamicUiHooks<Self>;
298
299    fn get_dynamic_ui(&self) -> &DynamicUi<Self>;
300    fn get_dynamic_ui_mut(&mut self) -> &mut DynamicUi<Self>;
301}
302
303/// A weak reference to the system tray. Equivalent to
304/// `OnceCell<Weak<SystemTray>>`.
305pub struct DynamicUiRef<T>(OnceCell<Weak<T>>);
306impl<T> DynamicUiRef<T> {
307    pub const fn new() -> Self {
308        Self(OnceCell::new())
309    }
310    pub fn set(&self, dynamic_ui: &Rc<T>) {
311        let _ = self.0.set(Rc::downgrade(dynamic_ui));
312    }
313    pub fn is_set(&self) -> bool {
314        self.0.get().map_or(false, |tray| tray.strong_count() > 0)
315    }
316    pub fn get(&self) -> Option<Rc<T>> {
317        self.0.get().and_then(Weak::upgrade)
318    }
319}
320impl<T> fmt::Debug for DynamicUiRef<T> {
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        f.debug_tuple("DynamicUiRef").field(&self.0).finish()
323    }
324}
325impl<T> Clone for DynamicUiRef<T> {
326    fn clone(&self) -> Self {
327        Self(self.0.clone())
328    }
329}
330impl<T> Default for DynamicUiRef<T> {
331    fn default() -> Self {
332        Self(OnceCell::new())
333    }
334}
335impl<T> From<&'_ Rc<T>> for DynamicUiRef<T> {
336    fn from(dynamic_ui: &Rc<T>) -> Self {
337        let this = Self::new();
338        this.set(dynamic_ui);
339        this
340    }
341}
342
343/// This is mostly used to ensure no action is being executed while some partial
344/// ui is being borrowed mutably.
345struct DelayEventsGuard<'a>(&'a Cell<bool>);
346impl<'a> DelayEventsGuard<'a> {
347    fn new(delay_events: &'a Cell<bool>) -> Self {
348        delay_events.set(true);
349        Self(delay_events)
350    }
351}
352impl Drop for DelayEventsGuard<'_> {
353    fn drop(&mut self) {
354        self.0.set(false);
355    }
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
359enum PluginState {
360    Destroyed,
361    Built,
362}
363
364/// Data about a plugin kept by [`DynamicUi`]
365struct PluginData<T: DynamicUiWrapper> {
366    ui: Box<T::Hooks>,
367    /// `None` if root item.
368    parent_id: Option<TypeId>,
369    /// Tracks if the item is destroyed or built.
370    state: PluginState,
371}
372impl<T: DynamicUiWrapper> PluginData<T> {
373    fn new(ui: Box<T::Hooks>) -> Self {
374        Self {
375            ui,
376            parent_id: None,
377            state: PluginState::Destroyed,
378        }
379    }
380    fn after_build(ui: Box<T::Hooks>, parent_id: Option<TypeId>) -> Self {
381        Self {
382            ui,
383            parent_id,
384            state: PluginState::Built,
385        }
386    }
387    fn id(&self) -> TypeId {
388        <T::Hooks as AsAny>::as_any(&*self.ui).type_id()
389    }
390    fn plugin_type_name(&self) -> &'static str {
391        <T::Hooks as AsAny>::type_name(&*self.ui)
392    }
393}
394enum RawEventHandlerData {
395    WithChildren(Vec<nwg::RawEventHandler>),
396    ParentOnly(nwg::RawEventHandler),
397    FailedToBind,
398}
399impl RawEventHandlerData {
400    fn as_slice(&self) -> &[nwg::RawEventHandler] {
401        match self {
402            RawEventHandlerData::WithChildren(handlers) => handlers.as_slice(),
403            RawEventHandlerData::ParentOnly(parent) => core::array::from_ref(parent),
404            RawEventHandlerData::FailedToBind => &[],
405        }
406    }
407}
408struct EventHandlerData {
409    plugin_id: TypeId,
410    window: nwg::ControlHandle,
411    handler: nwg::EventHandler,
412    raw_handler: RawEventHandlerData,
413}
414impl Drop for EventHandlerData {
415    fn drop(&mut self) {
416        nwg::unbind_event_handler(&self.handler);
417        for raw_handler in self.raw_handler.as_slice() {
418            let _ = nwg::unbind_raw_event_handler(raw_handler);
419        }
420    }
421}
422
423/// Stores many [`nwg::PartialUi`] type. Usually behind an [`Rc`] pointer stored
424/// inside [`DynamicUiRef`].
425pub struct DynamicUi<T: DynamicUiWrapper> {
426    /// Can be mutably borrowed when rebuilding. The type id indicates which
427    /// plugin owns the parent control.
428    ///
429    /// # Lock pattern
430    ///
431    /// For write locks:
432    ///
433    /// 1. Check if [`Self::delay_events`] is set and if so don't acquire a
434    ///    lock.
435    /// 2. Create a [`DelayEventsGuard`] that sets the [`Self::delay_events`]
436    ///    field to `true` (and then to `false` when dropped)
437    /// 3. Acquiring the [`std::cell::RefMut`] guard.
438    /// 4. Don't call into plugin hooks while holding the guard.
439    ///
440    /// For read locks:
441    ///
442    /// These should be okay everywhere except in functions that use
443    /// [`DelayEventsGuard`].
444    ui_list: RefCell<Vec<PluginData<T>>>,
445
446    /// Used to store events that should be handled after rebuilding some plugins.
447    #[allow(clippy::type_complexity)]
448    event_queue: RefCell<VecDeque<Box<dyn FnOnce(&Rc<T>)>>>,
449
450    /// Used to delay events while rebuilding. We take mutable references to
451    /// plugins while rebuilding so this prevents issues where events are
452    /// handled recursively while building new UI elements.
453    delay_events: Cell<bool>,
454
455    /// `true` if the UI should be destroyed.
456    should_destroy: Cell<bool>,
457
458    /// Event handlers that are bound to plugin windows.
459    ///
460    /// Raw event handlers can fail to be registered in which case we ignore the
461    /// error (this only happens if raw event handler was registered previously
462    /// with the same id for the same window).
463    event_handlers: RefCell<Vec<EventHandlerData>>,
464
465    /// Prevent recursive event handling.
466    prevent_recursive_events: Cell<bool>,
467
468    self_wrapper_ty: PhantomData<T>,
469}
470impl<T: DynamicUiWrapper> Default for DynamicUi<T> {
471    fn default() -> Self {
472        Self {
473            ui_list: Default::default(),
474            event_queue: Default::default(),
475            delay_events: Default::default(),
476            should_destroy: Default::default(),
477            event_handlers: Default::default(),
478            prevent_recursive_events: Default::default(),
479            self_wrapper_ty: Default::default(),
480        }
481    }
482}
483impl<T> fmt::Debug for DynamicUi<T>
484where
485    T: DynamicUiWrapper,
486{
487    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488        f.debug_struct("DynamicUi")
489            .field(
490                "plugins",
491                &self.ui_list.try_borrow().map_or_else(
492                    |e| vec![e.to_string()],
493                    |plugins| {
494                        plugins
495                            .iter()
496                            .map(|p| <T::Hooks as AsAny>::type_name(&*p.ui).to_string())
497                            .collect::<Vec<_>>()
498                    },
499                ),
500            )
501            .field(
502                "event_queue_len",
503                &self.event_queue.try_borrow().map(|q| q.len()).ok(),
504            )
505            .field("delay_events", &self.delay_events)
506            .finish()
507    }
508}
509impl<T> DynamicUi<T>
510where
511    T: DynamicUiWrapper,
512{
513    pub fn new(ui_list: Vec<Box<T::Hooks>>) -> Self {
514        let mut ui_list: Vec<_> = ui_list.into_iter().map(|ui| PluginData::new(ui)).collect();
515        ui_list.shrink_to_fit();
516        Self {
517            ui_list: RefCell::new(ui_list),
518            event_queue: Default::default(),
519            delay_events: Default::default(),
520            should_destroy: Default::default(),
521            event_handlers: Default::default(),
522            prevent_recursive_events: Default::default(),
523            self_wrapper_ty: Default::default(),
524        }
525    }
526
527    pub fn set_prevent_recursive_events(&self, value: bool) {
528        self.prevent_recursive_events.set(value);
529    }
530
531    /// Run some code while delaying other event handlers.
532    pub fn with_paused_events<R>(&self, f: impl FnOnce() -> R) -> R {
533        let _prevent_other_actions = DelayEventsGuard::new(&self.delay_events);
534        f()
535    }
536
537    /// Get a reference to a ui item managed by this dynamic ui.
538    ///
539    /// Warning: the returned ui item might not have been built.
540    pub fn get_ui<U: DynamicUiHooks<T>>(&self) -> Option<Ref<'_, U>> {
541        // TODO: prevent getting item that will be rebuilt (maybe only get items
542        // that are earlier in the list than the item that is currently being
543        // built).
544
545        let guard = self.ui_list.borrow();
546        Ref::filter_map(guard, |guard| {
547            guard
548                .iter()
549                .find_map(|p| <T::Hooks as AsAny>::as_any(&*p.ui).downcast_ref::<U>())
550        })
551        .ok()
552    }
553    pub fn with_all_ui<R>(&self, f: impl FnOnce(&mut dyn Iterator<Item = &T::Hooks>) -> R) -> R {
554        // TODO: don't allow this call while rebuilding.
555        f(&mut self
556            .ui_list
557            .borrow()
558            .iter()
559            .filter(|item| item.state == PluginState::Built)
560            .map(|p| &*p.ui))
561    }
562    pub fn for_each_ui(&self, f: impl FnMut(&T::Hooks)) {
563        // TODO: don't allow this call while rebuilding.
564        self.ui_list
565            .borrow()
566            .iter()
567            .filter(|item| item.state == PluginState::Built)
568            .map(|p| &*p.ui)
569            .for_each(f);
570    }
571    /// Preforms an action and rebuilds the UI if needed. The action will be
572    /// skipped if the UI is currently being rebuilt.
573    pub fn maybe_preform_action<R>(wrapper: &Rc<T>, action: impl FnOnce(&Rc<T>) -> R) -> Option<R> {
574        let this = wrapper.get_dynamic_ui();
575        if this.delay_events.get() {
576            tracing::warn!("A UI action was not performed because the UI was being rebuilt");
577            None
578        } else {
579            let mut state = Some(Err(action));
580            Self::preform_action_and_maybe_rebuild(
581                wrapper,
582                Some(&mut |wrapper| {
583                    if let Some(Err(action)) = state.take() {
584                        state = Some(Ok(action(wrapper)));
585                    }
586                }),
587            );
588            Some(
589                state
590                    .expect("callback panicked")
591                    .ok()
592                    .expect("callback never called"),
593            )
594        }
595    }
596    /// Preforms an action and then rebuilds the UI if needed. The action will
597    /// be queued if UI is currently being rebuilt.
598    ///
599    /// The action will be called with the `wrapper` and a `bool` that is `true`
600    /// if the action was delayed.
601    pub fn preform_action<R>(
602        wrapper: &Rc<T>,
603        action: impl FnOnce(&Rc<T>, bool) -> R + 'static,
604    ) -> Option<R> {
605        let this = wrapper.get_dynamic_ui();
606        if this.delay_events.get() {
607            this.event_queue
608                .borrow_mut()
609                .push_back(Box::new(move |wrapper| drop(action(wrapper, true))));
610            None
611        } else {
612            let mut state = Some(Err(action));
613            Self::preform_action_and_maybe_rebuild(
614                wrapper,
615                Some(&mut |wrapper| {
616                    if let Some(Err(action)) = state.take() {
617                        state = Some(Ok(action(wrapper, false)));
618                    }
619                }),
620            );
621            Some(
622                state
623                    .expect("callback panicked")
624                    .ok()
625                    .expect("callback never called"),
626            )
627        }
628    }
629    pub fn preform_action_adv<S>(
630        wrapper: &Rc<T>,
631        state: S,
632        delay_action: impl FnOnce(S) -> Option<Box<dyn FnOnce(&Rc<T>) + 'static>>,
633        preform_action: impl FnOnce(S),
634    ) {
635        let this = wrapper.get_dynamic_ui();
636        if this.delay_events.get() {
637            if let Some(delayed) = delay_action(state) {
638                this.event_queue.borrow_mut().push_back(delayed);
639            }
640        } else {
641            let mut state = Some((state, preform_action));
642            Self::preform_action_and_maybe_rebuild(
643                wrapper,
644                Some(&mut |_wrapper| {
645                    if let Some((state, preform_action)) = state.take() {
646                        preform_action(state);
647                    }
648                }),
649            );
650        }
651    }
652    #[allow(clippy::type_complexity)]
653    fn preform_action_and_maybe_rebuild(
654        wrapper: &Rc<T>,
655        mut action: Option<&mut dyn FnMut(&Rc<T>)>,
656    ) {
657        let this = wrapper.get_dynamic_ui();
658        if this.delay_events.get() {
659            return;
660        }
661
662        loop {
663            if this.should_destroy.get() {
664                Self::destroy_ui(wrapper);
665                return;
666            }
667
668            let first_queued = this.event_queue.borrow_mut().pop_front();
669            if let Some(queued) = first_queued {
670                queued(wrapper);
671                continue;
672            } else if let Some(action) = action.take() {
673                action(wrapper);
674            } else {
675                // No action => handling events after rebuild => ensure no
676                // plugin is invalidated
677            }
678
679            // Check for invalidated plugins:
680            let mut rebuild_ids = VecDeque::new();
681            for item in &*this.ui_list.borrow() {
682                if item.ui.need_rebuild(wrapper) {
683                    rebuild_ids.push_back(item.id());
684                    tracing::info!("Dynamic ui required rebuild: {}", item.plugin_type_name());
685                }
686            }
687            if rebuild_ids.is_empty() {
688                return;
689            }
690
691            // Rebuild partial UIs:
692            let _prevent_other_actions = DelayEventsGuard::new(&this.delay_events);
693            {
694                let Ok(mut guard) = this.ui_list.try_borrow_mut() else {
695                    // Someone else must already be rebuilding. (Should not
696                    // happen since we always have a DelayEventsGuard while
697                    // taking a RefMut lock.)
698                    tracing::warn!(
699                        "Failed to lock plugin list in DynamicUi, this should never happen"
700                    );
701                    return;
702                };
703                let len = guard.len();
704                let mut affected_parents = Vec::new();
705                while let Some(rebuild_id) = rebuild_ids.pop_front() {
706                    affected_parents.clear();
707                    for ix in 0..len {
708                        let item = &guard[ix];
709                        let plugin = &item.ui;
710                        let parent_id = item.parent_id;
711                        let plugin_id = item.id();
712
713                        if !affected_parents.is_empty() {
714                            // Already rebuilt the requested plugin, now
715                            // checking for later siblings (items that share the
716                            // same parent) which need to be rebuilt in order
717                            // for the UI elements to remain in the same order.
718                            if let Some(parent_id) = parent_id {
719                                if !affected_parents.contains(&parent_id) {
720                                    continue; // item's parent isn't affected
721                                }
722                            } else {
723                                continue; // items without parents aren't affected
724                            }
725                            // Is sibling to the rebuilt item!
726                            if !plugin.is_ordered_in_parent() {
727                                // This item doesn't depend on its build order
728                                // relative to other items in its parent.
729                                continue;
730                            }
731                            // Affected by the rebuilt item => rebuild it now!
732                            rebuild_ids.retain(|&id| id != plugin_id);
733                        } else if plugin_id != rebuild_id {
734                            continue; // Keep looking for first item to rebuild!
735                        }
736                        let prev_state = item.state;
737
738                        // Temporarily remove the partial ui:
739                        let mut plugin = guard.swap_remove(ix).ui;
740
741                        // Build the partial ui:
742                        drop(guard);
743                        tracing::info!(
744                            "Rebuilding dynamic ui: {}",
745                            <T::Hooks as AsAny>::type_name(&*plugin)
746                        );
747
748                        if prev_state == PluginState::Built {
749                            // Unbind any event handlers associated with
750                            // top-level windows in this partial ui:
751                            let mut handles = plugin.handles_dyn();
752                            plugin.after_handles(wrapper, &mut handles);
753                            Self::unbind_specific_event_handlers(wrapper, &handles);
754
755                            plugin.before_rebuild(wrapper);
756                        }
757                        let mut should_build = true;
758                        let parent = plugin.before_partial_build(wrapper, &mut should_build);
759                        let (plugin_data, res) = if should_build {
760                            let res = plugin.build_partial_dyn(parent.map(|p| p.0));
761                            <T::Hooks as DynamicUiHooks<T>>::after_partial_build(
762                                &mut plugin,
763                                wrapper,
764                            );
765
766                            let parent_id = parent.map(|(_, id)| id);
767                            // might need to rebuild later items in the new parent:
768                            if let Some(parent_id) = parent_id {
769                                if !affected_parents.contains(&parent_id) {
770                                    affected_parents.push(parent_id);
771                                }
772                            }
773
774                            (PluginData::after_build(plugin, parent_id), res)
775                        } else {
776                            (PluginData::new(plugin), Ok(()))
777                        };
778
779                        // Put partial ui back in list:
780                        guard = this.ui_list.borrow_mut();
781                        guard.push(plugin_data);
782                        guard.swap(ix, len - 1);
783
784                        // Log build errors:
785                        if let Err(e) = res {
786                            tracing::error!(
787                                "Rebuild of dynamic ui {} failed: {e:?}",
788                                guard[ix].plugin_type_name()
789                            );
790                        }
791
792                        // Queue children for rebuild:
793                        for child in &*guard {
794                            // TODO: detect cycles (we could enforce that
795                            // children are always after their parents in the
796                            // plugin list)
797                            if child.parent_id == Some(plugin_id) {
798                                let id = child.id();
799                                if !rebuild_ids.contains(&id) {
800                                    rebuild_ids.push_back(id);
801                                }
802                            }
803                        }
804
805                        if affected_parents.is_empty() {
806                            break; // Don't need to check if siblings need to be rebuilt
807                        }
808                    }
809                }
810            }
811
812            // Rebind event handlers if window handles changed.
813            Self::bind_event_handlers(wrapper);
814
815            // Continue main loop since there might be new queued events.
816        }
817    }
818
819    fn initial_build(wrapper: &Rc<T>) -> Result<(), nwg::NwgError> {
820        {
821            let this = wrapper.get_dynamic_ui();
822            let ui_list = &this.ui_list;
823            let mut guard = ui_list.borrow_mut();
824
825            // Prevent rebuilds during initial build (this should not happen
826            // anyway since no event handlers are registered until we return):
827            let _prevent_other_actions = DelayEventsGuard::new(&this.delay_events);
828
829            // Build plugins:
830            let mut plugin_ix = 0;
831            loop {
832                let len = guard.len();
833                if plugin_ix >= len {
834                    break;
835                }
836                // Remove the partial ui from the list:
837                let mut plugin = guard.swap_remove(plugin_ix).ui;
838
839                // Build the partial ui:
840                drop(guard);
841                let mut should_build = true;
842                let parent = plugin.before_partial_build(wrapper, &mut should_build);
843                let (plugin, res) = if should_build {
844                    let res = plugin.build_partial_dyn(parent.map(|p| p.0));
845                    DynamicUiHooks::after_partial_build(&mut *plugin, wrapper);
846
847                    let parent_id = parent.map(|(_, id)| id);
848                    (PluginData::after_build(plugin, parent_id), res)
849                } else {
850                    (PluginData::new(plugin), Ok(()))
851                };
852
853                // Put the partial ui back where we took it:
854                guard = ui_list.borrow_mut();
855                guard.insert(len - 1, plugin);
856                guard.swap(plugin_ix, len - 1);
857
858                res?;
859
860                plugin_ix += 1;
861            }
862        }
863
864        Ok(())
865    }
866    fn all_handles(wrapper: &Rc<T>) -> Vec<(TypeId, bool, nwg::ControlHandle)> {
867        wrapper
868            .get_dynamic_ui()
869            .ui_list
870            .borrow()
871            .iter()
872            .filter(|item| item.state == PluginState::Built)
873            .flat_map(|item| {
874                // The derive macro `NwgPartial` always emits Vec::new(), so don't
875                // expect anything here:
876                let mut item_handles = item.ui.handles_dyn();
877                // But plugins can easily add more handles:
878                item.ui.after_handles(wrapper, &mut item_handles);
879
880                let raw_child_handlers = item.ui.need_raw_events_for_children();
881
882                // Remember what plugin a window is associated with:
883                let id = item.id();
884                item_handles
885                    .into_iter()
886                    .copied()
887                    .map(move |handle| (id, raw_child_handlers, handle))
888            })
889            .collect()
890    }
891    fn process_events_for_plugin_and_children(
892        wrapper: &Rc<T>,
893        plugin_id: TypeId,
894        mut f: impl FnMut(&PluginData<T>),
895    ) {
896        let this = wrapper.get_dynamic_ui();
897        let _event_guard = this
898            .prevent_recursive_events
899            .get()
900            .then(|| DelayEventsGuard::new(&this.delay_events));
901        let guard = this.ui_list.borrow();
902        let mut parent_ids = Vec::<TypeId>::with_capacity(guard.len());
903
904        for item in &*guard {
905            if item.state != PluginState::Built {
906                continue;
907            }
908            if parent_ids.is_empty() {
909                let id = item.id();
910                if id != plugin_id {
911                    continue; // Not the plugin that owns the window
912                }
913                parent_ids.push(id);
914            } else if let Some(parent_id) = item.parent_id {
915                if !parent_ids.contains(&parent_id) {
916                    continue; // Doesn't have an affected parent
917                } else {
918                    parent_ids.push(item.id());
919                }
920            } else {
921                continue; // No parent
922            }
923
924            f(item);
925        }
926    }
927    fn process_event(
928        wrapper: &Rc<T>,
929        evt: nwg::Event,
930        evt_data: &nwg::EventData,
931        handle: nwg::ControlHandle,
932        window: nwg::ControlHandle,
933        plugin_id: TypeId,
934    ) {
935        if !matches!(
936            evt,
937            nwg::Event::OnNotice | nwg::Event::OnMouseMove | nwg::Event::Unknown
938        ) {
939            // Note: We use nwg::Notice for timers so they are triggered a lot.
940            tracing::trace!(
941                event = ?evt,
942                event_data = ?evt_data,
943                handle = ?handle,
944                window = ?window,
945                "SystemTrayUiAdaptor::process_event()"
946            );
947        }
948        Self::process_events_for_plugin_and_children(wrapper, plugin_id, move |item| {
949            item.ui.process_event_dyn(evt, evt_data, handle);
950            item.ui
951                .after_process_events(wrapper, evt, evt_data, handle, window);
952        });
953    }
954    fn process_raw_event(
955        wrapper: &Rc<T>,
956        hwnd: isize,
957        msg: u32,
958        w: usize,
959        l: isize,
960        window: nwg::ControlHandle,
961        plugin_id: TypeId,
962    ) -> Option<isize> {
963        let mut first = None;
964        Self::process_events_for_plugin_and_children(wrapper, plugin_id, |item| {
965            if let Some(result) = item.ui.process_raw_event(wrapper, hwnd, msg, w, l, window) {
966                if let Some(first_result) = first {
967                    tracing::warn!(
968                        ?first_result,
969                        ?result,
970                        "Multiple raw event handlers returned a result, the first result will be used"
971                    )
972                } else {
973                    first = Some(result);
974                }
975            }
976        });
977        first
978    }
979    fn unbind_specific_event_handlers(wrapper: &Rc<T>, window_handles: &[&nwg::ControlHandle]) {
980        let this = wrapper.get_dynamic_ui();
981        this.event_handlers
982            .borrow_mut()
983            .retain(|data| !window_handles.contains(&&data.window));
984    }
985    fn unbind_event_handlers(wrapper: &Rc<T>) {
986        let this = wrapper.get_dynamic_ui();
987        this.event_handlers.take();
988    }
989    fn bind_event_handlers(wrapper: &Rc<T>) {
990        let this = wrapper.get_dynamic_ui();
991        let window_handles = Self::all_handles(wrapper);
992        let current_handles = this
993            .event_handlers
994            .borrow()
995            .iter()
996            .map(|data| {
997                (
998                    data.plugin_id,
999                    matches!(data.raw_handler, RawEventHandlerData::WithChildren(_)),
1000                    data.window,
1001                )
1002            })
1003            .collect::<Vec<_>>();
1004        if window_handles == current_handles {
1005            return;
1006        }
1007        tracing::debug!(
1008            ?window_handles,
1009            previous_handles = ?current_handles,
1010            "Binding event handlers to windows"
1011        );
1012
1013        Self::unbind_event_handlers(wrapper);
1014
1015        let mut handlers = Vec::with_capacity(window_handles.len());
1016        for &(plugin_id, raw_child_events, window) in window_handles.iter() {
1017            // Note: bind raw event handler first so that nwg's event handler
1018            // doesn't suppress an event before we see it.
1019            let evt_ui = Rc::downgrade(wrapper);
1020            let handle_raw_events = move |hwnd, msg, l, w| {
1021                if let Some(ui) = evt_ui.upgrade() {
1022                    // !!! Partials Event Dispatch !!!
1023                    Self::preform_action(&ui, {
1024                        move |ui, delayed| {
1025                            let res = DynamicUi::process_raw_event(
1026                                ui,
1027                                hwnd as isize,
1028                                msg,
1029                                l,
1030                                w,
1031                                window,
1032                                plugin_id,
1033                            );
1034                            if let (Some(res), true) = (res, delayed) {
1035                                tracing::warn!(
1036                                    return_value = ?res,
1037                                    "Delayed handling of raw event and now can't handle return value"
1038                                );
1039                            }
1040                            res
1041                        }
1042                    })
1043                    .flatten()
1044                    .inspect(|val| {
1045                        tracing::debug!(
1046                            return_value = ?val,
1047                            "Returned custom value from raw event handle"
1048                        );
1049                    })
1050                } else {
1051                    None
1052                }
1053            };
1054            let raw_event_handler = match nwg::bind_raw_event_handler(
1055                &window,
1056                // This argument has to be > 0xFFFF, but otherwise can be anything:
1057                0x85dead,
1058                handle_raw_events.clone(),
1059            ) {
1060                Ok(event_handler) => {
1061                    if raw_child_events {
1062                        if let Some(hwnd) = window.hwnd() {
1063                            let mut handlers = vec![event_handler];
1064                            enum_child_windows(
1065                                Some(windows::Win32::Foundation::HWND(hwnd.cast())),
1066                                |child| {
1067                                    match nwg::bind_raw_event_handler(
1068                                        &window,
1069                                        // This argument has to be > 0xFFFF, but otherwise can be anything:
1070                                        0xc85dead,
1071                                        handle_raw_events.clone(),
1072                                    ) {
1073                                        Ok(handler) => handlers.push(handler),
1074                                        Err(e) => {
1075                                            tracing::warn!(?window, ?child, "Failed to register raw event handler for child window: {e}");
1076                                        }
1077                                    }
1078                                    std::ops::ControlFlow::Continue(())
1079                                },
1080                            );
1081                            RawEventHandlerData::WithChildren(handlers)
1082                        } else {
1083                            tracing::warn!("Could not find child windows since parent handle wasn't for a window");
1084                            RawEventHandlerData::ParentOnly(event_handler)
1085                        }
1086                    } else {
1087                        RawEventHandlerData::ParentOnly(event_handler)
1088                    }
1089                }
1090                Err(e) => {
1091                    tracing::warn!(?window, "Failed to register raw event handler: {e}");
1092                    RawEventHandlerData::FailedToBind
1093                }
1094            };
1095
1096            let evt_ui = Rc::downgrade(wrapper);
1097            let handle_events = move |evt, evt_data, handle| {
1098                if let Some(ui) = evt_ui.upgrade() {
1099                    // !!! Partials Event Dispatch !!!
1100                    Self::preform_action(&ui, {
1101                        move |ui, _| {
1102                            DynamicUi::process_event(ui, evt, &evt_data, handle, window, plugin_id);
1103                        }
1104                    });
1105                }
1106            };
1107            let event_handler = nwg::full_bind_event_handler(&window, handle_events);
1108
1109            handlers.push(EventHandlerData {
1110                plugin_id,
1111                window,
1112                handler: event_handler,
1113                raw_handler: raw_event_handler,
1114            });
1115        }
1116
1117        *this.event_handlers.borrow_mut() = handlers;
1118    }
1119    /// To make sure that everything is freed without issues, the default
1120    /// handler must be unbound.
1121    fn destroy_ui(wrapper: &Rc<T>) {
1122        let this = wrapper.get_dynamic_ui();
1123        this.should_destroy.set(true);
1124
1125        Self::unbind_event_handlers(wrapper);
1126
1127        if this.delay_events.get() {
1128            return;
1129        }
1130        let _prevent_other_actions = DelayEventsGuard::new(&this.delay_events);
1131        for item in &mut *this.ui_list.borrow_mut() {
1132            if item.state == PluginState::Destroyed {
1133                continue;
1134            }
1135            item.ui.before_rebuild(wrapper);
1136            item.state = PluginState::Destroyed;
1137        }
1138    }
1139}
1140
1141/// When this goes out of scope the [`DynamicUi`] will be destroyed.
1142pub struct DynamicUiOwner<T>(Rc<T>)
1143where
1144    T: DynamicUiWrapper;
1145impl<T> Deref for DynamicUiOwner<T>
1146where
1147    T: DynamicUiWrapper,
1148{
1149    type Target = Rc<T>;
1150
1151    fn deref(&self) -> &Self::Target {
1152        &self.0
1153    }
1154}
1155impl<T> Drop for DynamicUiOwner<T>
1156where
1157    T: DynamicUiWrapper,
1158{
1159    fn drop(&mut self) {
1160        DynamicUi::destroy_ui(&self.0);
1161    }
1162}
1163
1164/// Manual implementation of [`nwg::NativeUi`] for [`DynamicUi`] mostly because
1165/// the [`nwd::NwgUi`] derive macro ignores handles from
1166/// [`nwg::PartialUi::handles`].
1167///
1168/// # References
1169///
1170/// - [Native Windows GUI guide -
1171///   Partials](https://gabdube.github.io/native-windows-gui/native-windows-docs/partial.html)
1172/// - [native-windows-gui/native-windows-gui/examples/partials.rs at
1173///   a6c96e8de5d01fe7bb566d737622dfead3cd1aed ยท
1174///   gabdube/native-windows-gui](https://github.com/gabdube/native-windows-gui/blob/a6c96e8de5d01fe7bb566d737622dfead3cd1aed/native-windows-gui/examples/partials.rs)
1175impl<T> nwg::NativeUi<DynamicUiOwner<T>> for Rc<T>
1176where
1177    T: DynamicUiWrapper,
1178{
1179    fn build_ui(data: Self) -> Result<DynamicUiOwner<T>, nwg::NwgError> {
1180        let data = DynamicUiOwner(data);
1181        DynamicUi::initial_build(&data.0)?;
1182        DynamicUi::bind_event_handlers(&data.0);
1183        Ok(data)
1184    }
1185}