floem/
id.rs

1#![deny(missing_docs)]
2//! # `ViewId`s
3//!
4//! [`ViewId`]s are unique identifiers for views.
5//! They're used to identify views in the view tree.
6
7use std::{any::Any, cell::RefCell, rc::Rc};
8
9use peniko::kurbo::{Insets, Point, Rect, Size};
10use slotmap::new_key_type;
11use taffy::{Display, Layout, NodeId, TaffyTree};
12use winit::window::WindowId;
13
14use crate::{
15    ScreenLayout,
16    animate::{AnimStateCommand, Animation},
17    context::{EventCallback, ResizeCallback},
18    event::{EventListener, EventPropagation},
19    menu::Menu,
20    style::{Disabled, DisplayProp, Draggable, Focusable, Hidden, Style, StyleClassRef},
21    unit::PxPct,
22    update::{CENTRAL_DEFERRED_UPDATE_MESSAGES, CENTRAL_UPDATE_MESSAGES, UpdateMessage},
23    view::{IntoView, View},
24    view_state::{ChangeFlags, StackOffset, ViewState},
25    view_storage::VIEW_STORAGE,
26    window_tracking::{is_known_root, window_id_for_root},
27};
28
29new_key_type! {
30    /// A small unique identifier for an instance of a [View](crate::View).
31    ///
32    /// This id is how you can access and modify a view, including accessing children views and updating state.
33   pub struct ViewId;
34}
35
36impl ViewId {
37    /// Create a new unique `Viewid`.
38    pub fn new() -> ViewId {
39        VIEW_STORAGE.with_borrow_mut(|s| s.view_ids.insert(()))
40    }
41
42    /// Remove this view id and all of it's children from the `VIEW_STORAGE`
43    pub fn remove(&self) {
44        VIEW_STORAGE.with_borrow_mut(|s| {
45            // Remove the cached root, in the (unlikely) case that this view is
46            // re-added to a different window
47            s.root.remove(*self);
48            if let Some(Some(parent)) = s.parent.get(*self) {
49                if let Some(children) = s.children.get_mut(*parent) {
50                    children.retain(|c| c != self);
51                }
52            }
53            s.view_ids.remove(*self);
54        });
55    }
56
57    /// Get access to the taffy tree
58    pub fn taffy(&self) -> Rc<RefCell<TaffyTree>> {
59        VIEW_STORAGE.with_borrow(|s| s.taffy.clone())
60    }
61
62    /// Create a new taffy layout node
63    pub fn new_taffy_node(&self) -> NodeId {
64        self.taffy()
65            .borrow_mut()
66            .new_leaf(taffy::style::Style::DEFAULT)
67            .unwrap()
68    }
69
70    /// Set the layout properties on a taffy node
71    pub fn set_taffy_style(&self, node: NodeId, style: taffy::Style) {
72        let _ = self.taffy().borrow_mut().set_style(node, style);
73    }
74
75    /// Get the layout for a taffy node relative to it's parent
76    pub fn taffy_layout(&self, node: NodeId) -> Option<taffy::Layout> {
77        self.taffy().borrow().layout(node).cloned().ok()
78    }
79
80    /// Get the taffy node associated with this Id
81    pub fn taffy_node(&self) -> NodeId {
82        self.state().borrow().node
83    }
84
85    pub(crate) fn state(&self) -> Rc<RefCell<ViewState>> {
86        VIEW_STORAGE.with_borrow_mut(|s| {
87            if !s.view_ids.contains_key(*self) {
88                // if view_ids doesn't have this view id, that means it's been cleaned up,
89                // so we shouldn't create a new ViewState for this Id.
90                s.stale_view_state.clone()
91            } else {
92                s.states
93                    .entry(*self)
94                    .unwrap()
95                    .or_insert_with(|| {
96                        Rc::new(RefCell::new(ViewState::new(&mut s.taffy.borrow_mut())))
97                    })
98                    .clone()
99            }
100        })
101    }
102
103    /// Get access to the View
104    pub(crate) fn view(&self) -> Rc<RefCell<Box<dyn View>>> {
105        VIEW_STORAGE.with_borrow(|s| {
106            s.views
107                .get(*self)
108                .cloned()
109                .unwrap_or_else(|| s.stale_view.clone())
110        })
111    }
112
113    /// Add a child View to this Id's list of children
114    pub fn add_child(&self, child: Box<dyn View>) {
115        VIEW_STORAGE.with_borrow_mut(|s| {
116            let child_id = child.id();
117            s.children.entry(*self).unwrap().or_default().push(child_id);
118            s.parent.insert(child_id, Some(*self));
119            s.views.insert(child_id, Rc::new(RefCell::new(child)));
120        });
121    }
122
123    /// Set the children views of this Id
124    /// See also [`Self::set_children_vec`]
125    pub fn set_children<const N: usize, V: IntoView>(&self, children: [V; N]) {
126        VIEW_STORAGE.with_borrow_mut(|s| {
127            let mut children_ids = Vec::new();
128            for child in children {
129                let child_view = child.into_view();
130                let child_view_id = child_view.id();
131                children_ids.push(child_view_id);
132                s.parent.insert(child_view_id, Some(*self));
133                s.views
134                    .insert(child_view_id, Rc::new(RefCell::new(child_view.into_any())));
135            }
136            s.children.insert(*self, children_ids);
137        });
138    }
139
140    /// Set the children views of this Id using a Vector
141    /// See also [`Self::set_children`]
142    pub fn set_children_vec(&self, children: Vec<impl IntoView>) {
143        VIEW_STORAGE.with_borrow_mut(|s| {
144            let mut children_ids = Vec::new();
145            for child in children {
146                let child_view = child.into_view();
147                let child_view_id = child_view.id();
148                children_ids.push(child_view_id);
149                s.parent.insert(child_view_id, Some(*self));
150                s.views
151                    .insert(child_view_id, Rc::new(RefCell::new(child_view.into_any())));
152            }
153            s.children.insert(*self, children_ids);
154        });
155    }
156
157    /// Set the view that should be associated with this Id
158    pub fn set_view(&self, view: Box<dyn View>) {
159        VIEW_STORAGE.with_borrow_mut(|s| {
160            if s.view_ids.contains_key(*self) {
161                s.views.insert(*self, Rc::new(RefCell::new(view)));
162            }
163        });
164    }
165
166    /// Set the Id that should be used as the parent of this Id
167    pub fn set_parent(&self, parent: ViewId) {
168        VIEW_STORAGE.with_borrow_mut(|s| {
169            if s.view_ids.contains_key(*self) {
170                s.parent.insert(*self, Some(parent));
171            }
172        });
173    }
174
175    /// Set the Ids that should be used as the children of this Id
176    pub fn set_children_ids(&self, children: Vec<ViewId>) {
177        VIEW_STORAGE.with_borrow_mut(|s| {
178            if s.view_ids.contains_key(*self) {
179                s.children.insert(*self, children);
180            }
181        });
182    }
183
184    /// Get the list of `ViewId`s that are associated with the children views of this `ViewId`
185    pub fn children(&self) -> Vec<ViewId> {
186        VIEW_STORAGE.with_borrow(|s| s.children.get(*self).cloned().unwrap_or_default())
187    }
188
189    /// Get access to the list of `ViewId`s that are associated with the children views of this `ViewId`
190    pub fn with_children<R>(&self, children: impl Fn(&[ViewId]) -> R) -> R {
191        VIEW_STORAGE.with_borrow(|s| children(s.children.get(*self).map_or(&[], |v| v)))
192    }
193
194    /// Get the `ViewId` that has been set as this `ViewId`'s parent
195    pub fn parent(&self) -> Option<ViewId> {
196        VIEW_STORAGE.with_borrow(|s| s.parent.get(*self).cloned().flatten())
197    }
198
199    /// Get the root view of the window that the given view is in
200    pub fn root(&self) -> Option<ViewId> {
201        VIEW_STORAGE.with_borrow_mut(|s| {
202            if let Some(root) = s.root.get(*self) {
203                // The cached value will be cleared on remove() above
204                return *root;
205            }
206            let root_view_id = s.root_view_id(*self);
207            // root_view_id() always returns SOMETHING.  If the view is not yet added
208            // to a window, it can be itself or its nearest ancestor, which means we
209            // will store garbage permanently.
210            if let Some(root) = root_view_id {
211                if is_known_root(&root) {
212                    s.root.insert(*self, root_view_id);
213                    return Some(root);
214                }
215            }
216            None
217        })
218    }
219
220    /// Get the computed rectangle that covers the area of this View
221    pub fn layout_rect(&self) -> Rect {
222        self.state().borrow().layout_rect
223    }
224
225    /// Get the size of this View
226    pub fn get_size(&self) -> Option<Size> {
227        self.get_layout()
228            .map(|l| Size::new(l.size.width as f64, l.size.height as f64))
229    }
230
231    /// Get the Size of the parent View
232    pub fn parent_size(&self) -> Option<Size> {
233        let parent_id = self.parent()?;
234        parent_id.get_size()
235    }
236
237    /// Returns the layout rect excluding borders, padding and position.
238    /// This is relative to the view.
239    pub fn get_content_rect(&self) -> Rect {
240        let size = self
241            .get_layout()
242            .map(|layout| layout.size)
243            .unwrap_or_default();
244        let rect = Size::new(size.width as f64, size.height as f64).to_rect();
245        let view_state = self.state();
246        let props = &view_state.borrow().layout_props;
247        let pixels = |px_pct, abs| match px_pct {
248            PxPct::Px(v) => v,
249            PxPct::Pct(pct) => pct * abs,
250        };
251        let border = props.border();
252        let padding = props.padding();
253        rect.inset(-Insets {
254            x0: border.left.map_or(0.0, |b| b.0.width)
255                + pixels(padding.left.unwrap_or(PxPct::Px(0.0)), rect.width()),
256            x1: border.right.map_or(0.0, |b| b.0.width)
257                + pixels(padding.right.unwrap_or(PxPct::Px(0.0)), rect.width()),
258            y0: border.top.map_or(0.0, |b| b.0.width)
259                + pixels(padding.top.unwrap_or(PxPct::Px(0.0)), rect.height()),
260            y1: border.bottom.map_or(0.0, |b| b.0.width)
261                + pixels(padding.bottom.unwrap_or(PxPct::Px(0.0)), rect.height()),
262        })
263    }
264
265    /// This gets the Taffy Layout and adjusts it to be relative to the parent `View`.
266    pub fn get_layout(&self) -> Option<Layout> {
267        let widget_parent = self.parent().map(|id| id.state().borrow().node);
268
269        let taffy = self.taffy();
270        let mut node = self.state().borrow().node;
271        let mut layout = *taffy.borrow().layout(node).ok()?;
272
273        loop {
274            let parent = taffy.borrow().parent(node);
275
276            if parent == widget_parent {
277                break;
278            }
279
280            node = parent?;
281
282            layout.location = layout.location + taffy.borrow().layout(node).ok()?.location;
283        }
284
285        Some(layout)
286    }
287
288    /// Get the taffy layout of this id relative to a parent/ancestor ID
289    pub fn get_layout_relative_to(&self, relative_to: ViewId) -> Option<Layout> {
290        let taffy = self.taffy();
291        let target_node = relative_to.state().borrow().node;
292        let mut node = self.state().borrow().node;
293        let mut layout = *taffy.borrow().layout(node).ok()?;
294
295        loop {
296            let parent = taffy.borrow().parent(node);
297            if parent == Some(target_node) {
298                break;
299            }
300
301            // If we've reached the root without finding the target, return None
302            node = parent?;
303            layout.location = layout.location + taffy.borrow().layout(node).ok()?.location;
304        }
305
306        Some(layout)
307    }
308
309    /// Get the taffy layout of this id relative to the root
310    pub fn get_layout_relative_to_root(&self) -> Option<Layout> {
311        let taffy = self.taffy();
312        let node = self.state().borrow().node;
313        let layout = *taffy.borrow().layout(node).ok()?;
314
315        Some(layout)
316    }
317
318    /// Returns true if the computed style for this view is marked as hidden by setting in this view, or any parent, `Hidden` to true. For hiding views, you should prefer to set `Hidden` to true rather than using `Display::None` as checking for `Hidden` is cheaper, more correct, and used for optimizations in Floem
319    pub fn is_hidden(&self) -> bool {
320        let state = self.state();
321        let state = state.borrow();
322        state.computed_style.get(Hidden) || state.computed_style.get(DisplayProp) == Display::None
323    }
324
325    /// Returns true if the view is disabled
326    ///
327    /// This is done by checking if the style for this view has `Disabled` set to true.
328    pub fn is_disabled(&self) -> bool {
329        let state = self.state();
330        let state = state.borrow();
331        state.computed_style.get(Disabled)
332    }
333
334    /// Returns true if the view is selected
335    ///
336    /// This is done by checking if the style for this view has `Selected` set to true.
337    pub fn is_selected(&self) -> bool {
338        let state = self.state();
339        let state = state.borrow();
340        state.computed_style.get(Disabled)
341    }
342
343    /// Check if this id can be focused.
344    ///
345    /// This is done by checking if the style for this view has `Focusable` set to true.
346    pub fn can_focus(&self) -> bool {
347        self.state().borrow().computed_style.get(Focusable)
348    }
349
350    /// Check if this id can be dragged.
351    ///
352    /// This is done by checking if the style for this view has `Draggable` set to true.
353    pub fn can_drag(&self) -> bool {
354        self.state().borrow().computed_style.get(Draggable)
355    }
356
357    /// Request that this the `id` view be styled, laid out and painted again.
358    /// This will recursively request this for all parents.
359    pub fn request_all(&self) {
360        self.request_changes(ChangeFlags::all());
361    }
362
363    /// Request that this view have it's layout pass run
364    pub fn request_layout(&self) {
365        self.request_changes(ChangeFlags::LAYOUT)
366    }
367
368    /// Get the window id of the window containing this view, if there is one.
369    pub fn window_id(&self) -> Option<WindowId> {
370        self.root().and_then(window_id_for_root)
371    }
372
373    /// Request that this view have it's paint pass run
374    pub fn request_paint(&self) {
375        self.add_update_message(UpdateMessage::RequestPaint);
376    }
377
378    /// request that this node be styled again
379    /// This will recursively request style for all parents.
380    pub fn request_style(&self) {
381        self.request_changes(ChangeFlags::STYLE)
382    }
383
384    pub(crate) fn request_changes(&self, flags: ChangeFlags) {
385        let state = self.state();
386        if state.borrow().requested_changes.contains(flags) {
387            return;
388        }
389        state.borrow_mut().requested_changes.insert(flags);
390        if let Some(parent) = self.parent() {
391            parent.request_changes(flags);
392        }
393    }
394
395    /// Requests style for this view and all direct and indirect children.
396    pub fn request_style_recursive(&self) {
397        let state = self.state();
398        state.borrow_mut().request_style_recursive = true;
399        self.request_style();
400    }
401
402    /// Request that this view gain the window focus
403    pub fn request_focus(&self) {
404        self.add_update_message(UpdateMessage::Focus(*self));
405    }
406
407    /// Clear the focus from this window
408    pub fn clear_focus(&self) {
409        self.add_update_message(UpdateMessage::ClearFocus(*self));
410    }
411
412    /// Set the system context menu that should be shown when this view is right-clicked
413    pub fn update_context_menu(&self, menu: impl Fn() -> Menu + 'static) {
414        self.state().borrow_mut().context_menu = Some(Rc::new(menu));
415    }
416
417    /// Set the system popout menu that should be shown when this view is clicked
418    ///
419    /// Adds a primary-click context menu, which opens below the view.
420    pub fn update_popout_menu(&self, menu: impl Fn() -> Menu + 'static) {
421        self.state().borrow_mut().popout_menu = Some(Rc::new(menu));
422    }
423
424    /// Request that this view receive the active state (mark that this element is currently being interacted with)
425    ///
426    /// When an View has Active, it will receive events such as mouse events, even if the mouse is not directly over this view.
427    /// This is usefor for views such as Sliders, where the mouse event should be sent to the slider view as long as the mouse is pressed down,
428    /// even if the mouse moves out of the view, or even out of the Window.
429    pub fn request_active(&self) {
430        self.add_update_message(UpdateMessage::Active(*self));
431    }
432
433    /// Request that the active state be removed from this View
434    pub fn clear_active(&self) {
435        self.add_update_message(UpdateMessage::ClearActive(*self));
436    }
437
438    /// Send a message to the application to open the Inspector for this Window
439    pub fn inspect(&self) {
440        self.add_update_message(UpdateMessage::Inspect);
441    }
442
443    /// Scrolls the view and all direct and indirect children to bring the view to be
444    /// visible. The optional rectangle can be used to add an additional offset and intersection.
445    pub fn scroll_to(&self, rect: Option<Rect>) {
446        self.add_update_message(UpdateMessage::ScrollTo { id: *self, rect });
447    }
448
449    pub(crate) fn transition_anim_complete(&self) {
450        self.add_update_message(UpdateMessage::ViewTransitionAnimComplete(*self));
451    }
452
453    pub(crate) fn update_animation(&self, offset: StackOffset<Animation>, animation: Animation) {
454        let state = self.state();
455        state.borrow_mut().animations.set(offset, animation);
456        self.request_style();
457    }
458
459    pub(crate) fn update_animation_state(
460        &self,
461        offset: StackOffset<Animation>,
462        command: AnimStateCommand,
463    ) {
464        let view_state = self.state();
465        view_state
466            .borrow_mut()
467            .animations
468            .update(offset, move |anim| anim.transition(command));
469        self.request_style();
470    }
471
472    /// Send a state update to the `update` method of the associated View
473    pub fn update_state(&self, state: impl Any) {
474        self.add_update_message(UpdateMessage::State {
475            id: *self,
476            state: Box::new(state),
477        });
478    }
479
480    /// `viewport` is relative to the `id` view.
481    pub(crate) fn set_viewport(&self, viewport: Rect) {
482        let state = self.state();
483        state.borrow_mut().viewport = Some(viewport);
484    }
485
486    /// Add an callback on an action for a given `EventListener`
487    pub fn add_event_listener(&self, listener: EventListener, action: Box<EventCallback>) {
488        let state = self.state();
489        state.borrow_mut().add_event_listener(listener, action);
490    }
491
492    /// Set a callback that should be run when the size of the view changes
493    pub fn add_resize_listener(&self, action: Rc<ResizeCallback>) {
494        let state = self.state();
495        state.borrow_mut().add_resize_listener(action);
496    }
497
498    /// Set a callback that should be run when the position of the view changes
499    pub fn add_move_listener(&self, action: Rc<dyn Fn(Point)>) {
500        let state = self.state();
501        state.borrow_mut().add_move_listener(action);
502    }
503
504    /// Set a callback that should be run when the view is removed from the view tree
505    pub fn add_cleanup_listener(&self, action: Rc<dyn Fn()>) {
506        let state = self.state();
507        state.borrow_mut().add_cleanup_listener(action);
508    }
509
510    /// Get the combined style that is associated with this View.
511    ///
512    /// This will have all of the style properties set in it that are relevant to this view, including all properties from relevant classes.
513    ///
514    /// ## Warning
515    /// The view styles do not store property transition states, only markers of which properties _should_ be transitioned over time on change.
516    ///
517    /// If you have a property that could be transitioned over time, make sure to use a [prop extractor](crate::prop_extractor) that is updated in a style method of the View to extract the property.
518    pub fn get_combined_style(&self) -> Style {
519        self.state().borrow().combined_style.clone()
520    }
521
522    /// Add a class to the list of style classes that are associated with this `ViewId`
523    pub fn add_class(&self, class: StyleClassRef) {
524        let state = self.state();
525        state.borrow_mut().classes.push(class);
526        self.request_style_recursive();
527    }
528
529    /// Remove a class from the list of style classes that are associated with this `ViewId`
530    pub fn remove_class(&self, class: StyleClassRef) {
531        let state = self.state();
532        state.borrow_mut().classes.retain_mut(|c| *c != class);
533        self.request_style_recursive();
534    }
535
536    pub(crate) fn update_style(&self, offset: StackOffset<Style>, style: Style) {
537        let state = self.state();
538        let old_any_inherited = state.borrow().style().any_inherited();
539        state.borrow_mut().style.set(offset, style);
540        if state.borrow().style().any_inherited() || old_any_inherited {
541            self.request_style_recursive();
542        } else {
543            self.request_style();
544        }
545    }
546
547    pub(crate) fn apply_event(
548        &self,
549        listener: &EventListener,
550        event: &crate::event::Event,
551    ) -> Option<EventPropagation> {
552        let mut handled = false;
553        let event_listeners = self.state().borrow().event_listeners.clone();
554        if let Some(handlers) = event_listeners.get(listener) {
555            for handler in handlers {
556                handled |= (handler.borrow_mut())(event).is_processed();
557            }
558        } else {
559            return None;
560        }
561        if handled {
562            Some(EventPropagation::Stop)
563        } else {
564            Some(EventPropagation::Continue)
565        }
566    }
567
568    /// Disables the default view behavior for the specified event.
569    ///
570    /// Children will still see the event, but the view event function will not be called nor the event listeners on the view
571    pub fn disable_default_event(&self, event: EventListener) {
572        self.state()
573            .borrow_mut()
574            .disable_default_events
575            .insert(event);
576    }
577
578    /// Re-enables the default view behavior for a previously disabled event.
579    pub fn remove_disable_default_event(&self, event: EventListener) {
580        self.state()
581            .borrow_mut()
582            .disable_default_events
583            .remove(&event);
584    }
585
586    /// Alter the visibility of the current window the view represented by this ID
587    /// is in.
588    pub fn window_visible(&self, visible: bool) {
589        self.add_update_message(UpdateMessage::WindowVisible(visible));
590    }
591
592    fn add_update_message(&self, msg: UpdateMessage) {
593        let _ = CENTRAL_UPDATE_MESSAGES.try_with(|msgs| {
594            let mut msgs = msgs.borrow_mut();
595            msgs.push((*self, msg));
596        });
597    }
598
599    /// Send a state update that will be placed in deferred messages
600    // TODO: what is the difference?
601    pub fn update_state_deferred(&self, state: impl Any) {
602        CENTRAL_DEFERRED_UPDATE_MESSAGES.with_borrow_mut(|msgs| {
603            msgs.push((*self, Box::new(state)));
604        });
605    }
606
607    /// Get a layout in screen-coordinates for this view, if possible.
608    pub fn screen_layout(&self) -> Option<ScreenLayout> {
609        crate::screen_layout::try_create_screen_layout(self)
610    }
611}