floem/
id.rs

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