Skip to main content

kas_core/core/
data.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//! Core data types
7
8use super::Id;
9use crate::geom::Rect;
10use crate::layout::AxisInfo;
11#[allow(unused)] use crate::{Events, Layout, Widget};
12use std::ops::{Range, RangeInclusive};
13
14/// An opaque type representing a set of `usize` indices
15///
16/// The only representation currently supported is a range.
17//
18// NOTE: this API is extensible to other representations like an enum over
19// Range or Box<[usize]> (or Vec<usize>).
20#[derive(Clone, Debug)]
21pub struct ChildIndices(usize, usize);
22
23impl ChildIndices {
24    /// Construct: no indices
25    #[inline]
26    pub fn none() -> Self {
27        ChildIndices(0, 0)
28    }
29
30    /// Construct: one index
31    #[inline]
32    pub fn one(index: usize) -> Self {
33        ChildIndices(index, index + 1)
34    }
35
36    /// Construct: a range
37    #[inline]
38    pub fn range(range: impl Into<Self>) -> Self {
39        range.into()
40    }
41
42    // pub fn iter(&self) -> ChildIndicesRefIter<'_> { .. }
43
44    /// Convert to a Range
45    #[inline]
46    pub(crate) fn as_range(&self) -> Range<usize> {
47        self.0..self.1
48    }
49}
50
51impl IntoIterator for ChildIndices {
52    type Item = usize;
53    type IntoIter = ChildIndicesIter;
54
55    #[inline]
56    fn into_iter(self) -> ChildIndicesIter {
57        ChildIndicesIter(self.0..self.1)
58    }
59}
60
61impl From<Range<usize>> for ChildIndices {
62    #[inline]
63    fn from(range: Range<usize>) -> Self {
64        ChildIndices(range.start, range.end)
65    }
66}
67
68impl From<RangeInclusive<usize>> for ChildIndices {
69    #[inline]
70    fn from(range: RangeInclusive<usize>) -> Self {
71        ChildIndices(*range.start(), *range.end() + 1)
72    }
73}
74
75/// Owning iterator over [`ChildIndices`]
76#[derive(Clone, Debug)]
77pub struct ChildIndicesIter(Range<usize>);
78
79impl Iterator for ChildIndicesIter {
80    type Item = usize;
81
82    #[inline]
83    fn next(&mut self) -> Option<usize> {
84        self.0.next()
85    }
86
87    #[inline]
88    fn size_hint(&self) -> (usize, Option<usize>) {
89        self.0.size_hint()
90    }
91}
92impl ExactSizeIterator for ChildIndicesIter {}
93impl DoubleEndedIterator for ChildIndicesIter {
94    #[inline]
95    fn next_back(&mut self) -> Option<usize> {
96        self.0.next_back()
97    }
98}
99
100/// Type of the widget's core
101///
102/// This is a special placeholder macro for usage only with the [`widget`](crate::widget) macro.
103/// It expands to a type, dependant on the current widget.
104///
105/// This type always implements the [`WidgetCore`] trait.
106///
107/// This type *may* implement the [`WidgetCoreRect`] trait.
108///
109/// # Example
110///
111/// ```rust
112/// # extern crate kas_core as kas;
113/// use kas::{impl_self, Events};
114///
115/// #[impl_self]
116/// mod MyHelloWidget {
117///     /// A simple greeting
118///     #[widget]
119///     #[layout("Hello!")]
120///     struct MyHelloWidget(widget_core!());
121///
122///     impl Events for Self {
123///         type Data = ();
124///     }
125/// }
126/// ```
127#[macro_export]
128macro_rules! widget_core {
129    () => {
130        compile_error!(
131            "This macro may only be used in a struct affected by the `#[widget]` attribute"
132        );
133    };
134}
135
136/// Operations supported by a widget core
137pub trait WidgetCore: Default {
138    /// Get a reference to the widget's identifier
139    ///
140    /// The widget identifier is assigned when the widget is configured (see
141    /// [`Events#configuration`]). In case the
142    /// [`Id`] is accessed before this, it will be [invalid](Id#invalid-state).
143    /// The identifier *may* change when widgets which are descendants of some
144    /// dynamic layout are reconfigured.
145    fn id_ref(&self) -> &Id;
146
147    /// Get the widget's identifier
148    ///
149    /// This method returns a [`Clone`] of [`Self::id_ref`]. Since cloning an
150    /// `Id` is [very cheap](Id#representation), this can mostly be ignored.
151    ///
152    /// The widget identifier is assigned when the widget is configured (see
153    /// [`Events#configuration`]). In case the
154    /// [`Id`] is accessed before this, it will be [invalid](Id#invalid-state).
155    /// The identifier *may* change when widgets which are descendants of some
156    /// dynamic layout are reconfigured.
157    #[inline]
158    fn id(&self) -> Id {
159        self.id_ref().clone()
160    }
161
162    /// Get the widget configuration status
163    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
164    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
165    fn status(&self) -> WidgetStatus;
166
167    /// Require configuration status of at least `status`
168    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
169    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
170    #[inline]
171    fn require_status(&self, status: WidgetStatus) {
172        self.status().require(self.id_ref(), status);
173    }
174
175    /// Require that [`Layout::size_rules`] has been called for both axes
176    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
177    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
178    #[inline]
179    fn require_status_size_rules(&self) {
180        self.require_status(WidgetStatus::SizeRulesY);
181    }
182
183    /// Require that [`Layout::set_rect`] has been called
184    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
185    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
186    #[inline]
187    fn require_status_set_rect(&self) {
188        self.require_status(WidgetStatus::SetRect);
189    }
190
191    /// Set the widget configuration status
192    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
193    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
194    fn set_status(&mut self, status: WidgetStatus);
195
196    /// Update status for [`Events::configure`]
197    ///
198    /// Requires no prior state. Does not imply further actions.
199    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
200    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
201    fn update_status_configured(&mut self) {
202        // re-configure does not require repeating other actions
203        let status = self.status().max(WidgetStatus::Configured);
204        self.set_status(status);
205    }
206
207    /// Update status for [`Layout::size_rules`]
208    ///
209    /// Requires at least [`WidgetStatus::Configured`]. When
210    /// `axis.is_vertical()`, requires at least [`WidgetStatus::SizeRulesX`].
211    ///
212    /// Re-calling `size_rules` does not require additional actions.
213    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
214    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
215    fn update_status_size_rules(&mut self, axis: AxisInfo) {
216        let id = self.id_ref();
217        let mut status = self.status();
218        if axis.is_horizontal() {
219            status.require(id, WidgetStatus::Configured);
220            status = status.max(WidgetStatus::SizeRulesX);
221        } else {
222            status.require(id, WidgetStatus::SizeRulesX);
223            status = status.max(WidgetStatus::SizeRulesY);
224        }
225        self.set_status(status);
226    }
227
228    /// Set status signifying that [`Layout::set_rect`] has been called
229    ///
230    /// This does not check the prior status; the caller should call
231    /// [`Self::require_status_size_rules`] before this method.
232    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
233    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
234    #[inline]
235    fn set_status_set_rect(&mut self) {
236        self.set_status(WidgetStatus::SetRect);
237    }
238}
239
240/// Extension for a widget core with a [`Rect`]
241///
242/// This is only implemented on the core when there is not an explicit
243/// definition of [`Layout::rect`].
244pub trait WidgetCoreRect: WidgetCore {
245    /// Get the stored [`Rect`]
246    ///
247    /// This should be equivalent to [`Layout::rect`].
248    fn rect(&self) -> Rect;
249
250    /// Set the stored [`Rect`]
251    fn set_rect(&mut self, rect: Rect);
252}
253
254/// Common widget data
255///
256/// This type may be used for a [`Widget`]'s `core: widget_core!()` field.
257#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
258#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
259#[derive(Default, Debug)]
260pub struct DefaultCoreType {
261    pub _id: Id,
262    pub status: WidgetStatus,
263}
264
265impl WidgetCore for DefaultCoreType {
266    #[inline]
267    fn id_ref(&self) -> &Id {
268        &self._id
269    }
270
271    #[inline]
272    fn status(&self) -> WidgetStatus {
273        self.status
274    }
275
276    #[inline]
277    fn set_status(&mut self, status: WidgetStatus) {
278        self.status = status;
279    }
280}
281
282/// Common widget data
283///
284/// This type may be used for a [`Widget`]'s `core: widget_core!()` field.
285#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
286#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
287#[derive(Default, Debug)]
288pub struct DefaultCoreRectType {
289    pub _rect: Rect,
290    pub _id: Id,
291    pub status: WidgetStatus,
292}
293
294impl WidgetCore for DefaultCoreRectType {
295    #[inline]
296    fn id_ref(&self) -> &Id {
297        &self._id
298    }
299
300    #[inline]
301    fn status(&self) -> WidgetStatus {
302        self.status
303    }
304
305    #[inline]
306    fn set_status(&mut self, status: WidgetStatus) {
307        self.status = status;
308    }
309}
310
311impl WidgetCoreRect for DefaultCoreRectType {
312    #[inline]
313    fn rect(&self) -> Rect {
314        self._rect
315    }
316
317    #[inline]
318    fn set_rect(&mut self, rect: Rect) {
319        self._rect = rect;
320    }
321}
322
323/// Widget state tracker
324///
325/// This struct is used to track status of widget operations and panic in case
326/// of inappropriate call order (such cases are memory safe but may cause
327/// incorrect widget behaviour).
328///
329/// It is not used in release builds.
330#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
331#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
332#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
333pub enum WidgetStatus {
334    #[default]
335    New,
336    Configured,
337    SizeRulesX,
338    SizeRulesY,
339    SetRect,
340}
341
342impl WidgetStatus {
343    fn require(self, id: &Id, expected: Self) {
344        if self < expected {
345            panic!("WidgetStatus of {id}: require {expected:?}, found {self:?}");
346        }
347    }
348
349    /// Get whether the widget is configured
350    #[inline]
351    pub fn is_configured(self) -> bool {
352        self >= WidgetStatus::Configured
353    }
354
355    /// Get whether the widget is sized
356    #[inline]
357    pub fn is_sized(self) -> bool {
358        self >= WidgetStatus::SetRect
359    }
360}