Skip to main content

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