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