kas_core/core/
node.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Node API for widgets
7
8use super::Widget;
9use crate::event::{ConfigCx, Event, EventCx, IsUsed, NavAdvance};
10use crate::geom::{Coord, Rect};
11use crate::layout::{AlignHints, AxisInfo, SizeRules};
12use crate::theme::SizeCx;
13use crate::util::IdentifyWidget;
14use crate::{Id, Tile};
15
16#[cfg(not(feature = "unsafe_node"))]
17trait NodeT {
18    fn id_ref(&self) -> &Id;
19    fn rect(&self) -> Rect;
20
21    fn clone_node(&mut self) -> Node<'_>;
22    fn as_tile(&self) -> &dyn Tile;
23
24    fn find_child_index(&self, id: &Id) -> Option<usize>;
25    fn child_node(&mut self, index: usize) -> Option<Node<'_>>;
26
27    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;
28    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);
29
30    fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize>;
31
32    fn _configure(&mut self, cx: &mut ConfigCx, id: Id);
33    fn _update(&mut self, cx: &mut ConfigCx);
34
35    fn _send(&mut self, cx: &mut EventCx, id: Id, event: Event) -> IsUsed;
36    fn _replay(&mut self, cx: &mut EventCx, id: Id);
37    fn _nav_next(
38        &mut self,
39        cx: &mut ConfigCx,
40        focus: Option<&Id>,
41        advance: NavAdvance,
42    ) -> Option<Id>;
43}
44#[cfg(not(feature = "unsafe_node"))]
45impl<'a, T> NodeT for (&'a mut dyn Widget<Data = T>, &'a T) {
46    fn id_ref(&self) -> &Id {
47        self.0.id_ref()
48    }
49    fn rect(&self) -> Rect {
50        self.0.rect()
51    }
52
53    fn clone_node(&mut self) -> Node<'_> {
54        Node::new(self.0, self.1)
55    }
56    fn as_tile(&self) -> &dyn Tile {
57        self.0.as_tile()
58    }
59
60    fn find_child_index(&self, id: &Id) -> Option<usize> {
61        self.0.find_child_index(id)
62    }
63
64    fn child_node(&mut self, index: usize) -> Option<Node<'_>> {
65        self.0.child_node(self.1, index)
66    }
67
68    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
69        self.0.size_rules(sizer, axis)
70    }
71    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
72        self.0.set_rect(cx, rect, hints);
73    }
74
75    fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
76        self.0.nav_next(reverse, from)
77    }
78
79    fn _configure(&mut self, cx: &mut ConfigCx, id: Id) {
80        self.0._configure(cx, self.1, id);
81    }
82    fn _update(&mut self, cx: &mut ConfigCx) {
83        self.0._update(cx, self.1);
84    }
85
86    fn _send(&mut self, cx: &mut EventCx, id: Id, event: Event) -> IsUsed {
87        self.0._send(cx, self.1, id, event)
88    }
89    fn _replay(&mut self, cx: &mut EventCx, id: Id) {
90        self.0._replay(cx, self.1, id);
91    }
92    fn _nav_next(
93        &mut self,
94        cx: &mut ConfigCx,
95        focus: Option<&Id>,
96        advance: NavAdvance,
97    ) -> Option<Id> {
98        self.0._nav_next(cx, self.1, focus, advance)
99    }
100}
101
102/// Type-erased widget with input data
103///
104/// This type is a `&mut dyn Widget<Data = A>` paired with input data `&A`,
105/// where the type `A` is erased.
106///
107/// The default implementation of this type uses a boxed trait object.
108/// The `unsafe_node` feature enables a more efficient unboxed implementation
109/// (this must make assumptions about VTables beyond what Rust specifies, thus
110/// lacks even the usual programmer-provided verification of `unsafe` code).
111pub struct Node<'a>(
112    #[cfg(not(feature = "unsafe_node"))] Box<dyn NodeT + 'a>,
113    #[cfg(feature = "unsafe_node")] &'a mut dyn Widget<Data = ()>,
114    #[cfg(feature = "unsafe_node")] &'a (),
115);
116
117impl<'a> Node<'a> {
118    /// Construct
119    #[inline(always)]
120    pub fn new<T: 'a>(widget: &'a mut dyn Widget<Data = T>, data: &'a T) -> Self {
121        cfg_if::cfg_if! {
122            if #[cfg(feature = "unsafe_node")] {
123                // Safety: since the vtable for dyn Widget<Data = T> only uses T as &T
124                // and T: Sized, the vtable should be equivalent for all T.
125                // We ensure here that the type of `data` matches that used by `widget`.
126                // NOTE: This makes assumptions beyond Rust's specification.
127                use std::mem::transmute;
128                #[allow(clippy::missing_transmute_annotations)]
129                unsafe { Node(transmute(widget), transmute(data)) }
130            } else {
131                Node(Box::new((widget, data)))
132            }
133        }
134    }
135
136    /// Reborrow with a new lifetime
137    ///
138    /// Rust allows references like `&T` or `&mut T` to be "reborrowed" through
139    /// coercion: essentially, the pointer is copied under a new, shorter, lifetime.
140    /// Until rfcs#1403 lands, reborrows on user types require a method call.
141    #[inline(always)]
142    pub fn re<'b>(&'b mut self) -> Node<'b>
143    where
144        'a: 'b,
145    {
146        cfg_if::cfg_if! {
147            if #[cfg(feature = "unsafe_node")] {
148                Node(self.0, self.1)
149            } else {
150                self.0.clone_node()
151            }
152        }
153    }
154
155    /// Reborrow as a `dyn Tile`
156    pub fn as_tile(&self) -> &dyn Tile {
157        self.0.as_tile()
158    }
159
160    /// Get the widget's identifier
161    #[inline]
162    pub fn id_ref(&self) -> &Id {
163        self.0.id_ref()
164    }
165
166    /// Get the widget's identifier
167    #[inline]
168    pub fn id(&self) -> Id {
169        self.0.id_ref().clone()
170    }
171
172    /// Return a [`Display`]-able widget identifier
173    ///
174    /// [`Display`]: std::fmt::Display
175    #[inline]
176    pub fn identify(&self) -> IdentifyWidget<'_> {
177        self.0.as_tile().identify()
178    }
179
180    /// Test widget identifier for equality
181    ///
182    /// This method may be used to test against `Id`, `Option<Id>`
183    /// and `Option<&Id>`.
184    #[inline]
185    pub fn eq_id<T>(&self, rhs: T) -> bool
186    where
187        Id: PartialEq<T>,
188    {
189        *self.id_ref() == rhs
190    }
191
192    /// Check whether `id` is self or a descendant
193    ///
194    /// This function assumes that `id` is a valid widget.
195    #[inline]
196    pub fn is_ancestor_of(&self, id: &Id) -> bool {
197        self.id().is_ancestor_of(id)
198    }
199
200    /// Check whether `id` is not self and is a descendant
201    ///
202    /// This function assumes that `id` is a valid widget.
203    #[inline]
204    pub fn is_strict_ancestor_of(&self, id: &Id) -> bool {
205        !self.eq_id(id) && self.id().is_ancestor_of(id)
206    }
207
208    /// Get the widget's region, relative to its parent.
209    #[inline]
210    pub fn rect(&self) -> Rect {
211        self.0.rect()
212    }
213
214    /// Probe a coordinate for a widget's [`Id`]
215    #[inline]
216    pub fn try_probe(&self, coord: Coord) -> Option<Id> {
217        self.0.as_tile().try_probe(coord)
218    }
219
220    /// Access a child as a [`Node`], if available
221    #[inline(always)]
222    pub fn get_child(&mut self, index: usize) -> Option<Node<'_>> {
223        cfg_if::cfg_if! {
224            if #[cfg(feature = "unsafe_node")] {
225                self.0.child_node(self.1, index)
226            } else {
227                self.0.child_node(index)
228            }
229        }
230    }
231
232    /// Find the child which is an ancestor of this `id`, if any
233    ///
234    /// If `Some(index)` is returned, this is *probably* but not guaranteed
235    /// to be a valid child index.
236    #[inline]
237    pub fn find_child_index(&self, id: &Id) -> Option<usize> {
238        self.0.find_child_index(id)
239    }
240
241    /// Find the descendant with this `id`, if any, and call `cb` on it
242    ///
243    /// Returns `Some(result)` if and only if node `id` was found.
244    #[inline(always)]
245    pub fn find_node<F: FnOnce(Node<'_>) -> T, T>(&mut self, id: &Id, cb: F) -> Option<T> {
246        if let Some(index) = self.find_child_index(id) {
247            if let Some(mut node) = self.get_child(index) {
248                node.find_node(id, cb)
249            } else {
250                debug_assert!(false);
251                None
252            }
253        } else if self.eq_id(id) {
254            Some(cb(self.re()))
255        } else {
256            None
257        }
258    }
259}
260
261#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
262#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
263impl<'a> Node<'a> {
264    /// Get size rules for the given axis
265    pub(crate) fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
266        self.0.size_rules(sizer, axis)
267    }
268
269    /// Set size and position
270    pub(crate) fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
271        self.0.set_rect(cx, rect, hints);
272    }
273
274    /// Navigation in spatial order
275    pub(crate) fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
276        self.0.nav_next(reverse, from)
277    }
278
279    /// Internal method: configure recursively
280    pub(crate) fn _configure(&mut self, cx: &mut ConfigCx, id: Id) {
281        cfg_if::cfg_if! {
282            if #[cfg(feature = "unsafe_node")] {
283                self.0._configure(cx, self.1, id);
284            } else {
285                self.0._configure(cx, id);
286            }
287        }
288    }
289
290    /// Internal method: update recursively
291    pub(crate) fn _update(&mut self, cx: &mut ConfigCx) {
292        cfg_if::cfg_if! {
293            if #[cfg(feature = "unsafe_node")] {
294                self.0._update(cx, self.1);
295            } else {
296                self.0._update(cx);
297            }
298        }
299    }
300
301    /// Internal method: send recursively
302    pub(crate) fn _send(&mut self, cx: &mut EventCx, id: Id, event: Event) -> IsUsed {
303        cfg_if::cfg_if! {
304            if #[cfg(feature = "unsafe_node")] {
305                self.0._send(cx, self.1, id, event)
306            } else {
307                self.0._send(cx, id, event)
308            }
309        }
310    }
311
312    /// Internal method: replay recursively
313    pub(crate) fn _replay(&mut self, cx: &mut EventCx, id: Id) {
314        cfg_if::cfg_if! {
315            if #[cfg(feature = "unsafe_node")] {
316                self.0._replay(cx, self.1, id);
317            } else {
318                self.0._replay(cx, id);
319            }
320        }
321    }
322
323    /// Internal method: search for the previous/next navigation target
324    // NOTE: public on account of ListView
325    pub fn _nav_next(
326        &mut self,
327        cx: &mut ConfigCx,
328        focus: Option<&Id>,
329        advance: NavAdvance,
330    ) -> Option<Id> {
331        cfg_if::cfg_if! {
332            if #[cfg(feature = "unsafe_node")] {
333                self.0._nav_next(cx, self.1, focus, advance)
334            } else {
335                self.0._nav_next(cx, focus, advance)
336            }
337        }
338    }
339}