kas_widgets/adapt/
adapt.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//! Adapt widget
7
8use super::{AdaptConfigCx, AdaptEventCx};
9use kas::event::TimerHandle;
10use kas::prelude::*;
11use linear_map::LinearMap;
12use std::fmt::Debug;
13use std::marker::PhantomData;
14
15#[impl_self]
16mod Adapt {
17    /// Data adaption node
18    ///
19    /// Where [`Map`] allows mapping to a sub-set of input data, `Adapt` allows
20    /// mapping to a super-set (including internal storage). Further, `Adapt`
21    /// supports message handlers which mutate internal storage.
22    ///
23    /// # Inner data type
24    ///
25    /// Note that, at least for now, the type of state stored by `Adapt` must
26    /// equal the data type of the inner widget: `state: <W as Widget>::Data`.
27    /// Since `W::Data` must outlive `W` (for our purposes this is not much
28    /// different than if `Widget::Data: 'static`), we cannot support `W::Data`
29    /// like `(&A, &S)` where `state: S`, so we might as well simply pass `&S`
30    /// to the inner widget `W`. This implies that any state from `A` which
31    /// needs to be passed into `W` must be *copied* into `state: W::Data` by
32    /// [`Adapt::on_update`].
33    ///
34    /// (It is possible that the above restrictions will change in the future,
35    /// but they would require Rust to support generic associated types in
36    /// dyn-safe traits (also known as object safe GATs), at least for lifetime
37    /// parameters. There *was* an unstable feature for this,
38    /// `generic_associated_types_extended`, but it was removed due to being
39    /// stale, experimental and unsound. But even if Rust did gain this feature,
40    /// it is not clear that [`Widget::Data`] should be generic.)
41    #[autoimpl(Scrollable using self.inner where W: trait)]
42    #[widget]
43    #[layout(self.inner)]
44    pub struct Adapt<A, W: Widget> {
45        core: widget_core!(),
46        state: W::Data,
47        #[widget(&self.state)]
48        inner: W,
49        configure_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W::Data)>>,
50        update_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W::Data, &A)>>,
51        timer_handlers: LinearMap<TimerHandle, Box<dyn Fn(&mut AdaptEventCx, &mut W::Data, &A)>>,
52        message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &mut W::Data, &A)>>,
53    }
54
55    impl Self {
56        /// Construct over `inner` with additional `state`
57        #[inline]
58        pub fn new(inner: W, state: W::Data) -> Self {
59            Adapt {
60                core: Default::default(),
61                state,
62                inner,
63                configure_handler: None,
64                update_handler: None,
65                timer_handlers: LinearMap::new(),
66                message_handlers: vec![],
67            }
68        }
69
70        /// Add a handler to be called on configuration
71        pub fn on_configure<F>(mut self, handler: F) -> Self
72        where
73            F: Fn(&mut AdaptConfigCx, &mut W::Data) + 'static,
74        {
75            debug_assert!(self.configure_handler.is_none());
76            self.configure_handler = Some(Box::new(handler));
77            self
78        }
79
80        /// Add a handler to be called on update of input data
81        ///
82        /// Children will be updated after the handler is called.
83        pub fn on_update<F>(mut self, handler: F) -> Self
84        where
85            F: Fn(&mut AdaptConfigCx, &mut W::Data, &A) + 'static,
86        {
87            debug_assert!(self.update_handler.is_none());
88            self.update_handler = Some(Box::new(handler));
89            self
90        }
91
92        /// Set a timer handler
93        ///
94        /// It is assumed that state is modified by this timer. Frequent usage
95        /// of timers which don't do anything may be inefficient; prefer usage
96        /// of [`EventState::send_async`](kas::event::EventState::send_async).
97        pub fn on_timer<H>(mut self, timer_id: TimerHandle, handler: H) -> Self
98        where
99            H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static,
100        {
101            debug_assert!(self.timer_handlers.get(&timer_id).is_none());
102            self.timer_handlers.insert(timer_id, Box::new(handler));
103            self
104        }
105
106        /// Add a handler on message of type `M`
107        ///
108        /// Children will be updated whenever this handler is invoked.
109        ///
110        /// Where access to input data (from parent widgets) is required,
111        /// use [`Self::on_messages`] instead.
112        pub fn on_message<M, H>(self, handler: H) -> Self
113        where
114            M: Debug + 'static,
115            H: Fn(&mut AdaptEventCx, &mut W::Data, M) + 'static,
116        {
117            self.on_messages(move |cx, state, _data| {
118                if let Some(m) = cx.try_pop() {
119                    handler(cx, state, m);
120                }
121            })
122        }
123
124        /// Add a generic message handler
125        pub fn on_messages<H>(mut self, handler: H) -> Self
126        where
127            H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static,
128        {
129            self.message_handlers.push(Box::new(handler));
130            self
131        }
132    }
133
134    impl Events for Self {
135        type Data = A;
136
137        fn configure(&mut self, cx: &mut ConfigCx) {
138            if let Some(handler) = self.configure_handler.as_ref() {
139                let mut cx = AdaptConfigCx::new(cx, self.id());
140                handler(&mut cx, &mut self.state);
141            }
142        }
143
144        fn update(&mut self, cx: &mut ConfigCx, data: &A) {
145            if let Some(handler) = self.update_handler.as_ref() {
146                let mut cx = AdaptConfigCx::new(cx, self.id());
147                handler(&mut cx, &mut self.state, data);
148            }
149        }
150
151        fn handle_event(&mut self, cx: &mut EventCx, data: &Self::Data, event: Event) -> IsUsed {
152            match event {
153                Event::Timer(timer_id) => {
154                    if let Some(handler) = self.timer_handlers.get(&timer_id) {
155                        let mut cx = AdaptEventCx::new(cx, self.id());
156                        handler(&mut cx, &mut self.state, data);
157                        cx.update(self.as_node(data));
158                        Used
159                    } else {
160                        Unused
161                    }
162                }
163                _ => Unused,
164            }
165        }
166
167        fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
168            let count = cx.msg_op_count();
169            let mut cx = AdaptEventCx::new(cx, self.id());
170            for handler in self.message_handlers.iter() {
171                handler(&mut cx, &mut self.state, data);
172            }
173            if cx.msg_op_count() != count {
174                cx.update(self.as_node(data));
175            }
176        }
177    }
178}
179
180#[impl_self]
181mod Map {
182    /// Data mapping
183    ///
184    /// This is a generic data-mapping widget-wrapper.
185    /// See also [`Adapt`], [`MapAny`](super::MapAny).
186    ///
187    /// This struct is a thin wrapper around the inner widget without its own
188    /// [`Id`]. It supports [`Deref`](std::ops::Deref) and
189    /// [`DerefMut`](std::ops::DerefMut) to the inner widget.
190    #[autoimpl(Deref, DerefMut using self.inner)]
191    #[autoimpl(Scrollable using self.inner where W: trait)]
192    #[derive_widget(type Data = A)]
193    pub struct Map<A, W: Widget, F>
194    where
195        F: for<'a> Fn(&'a A) -> &'a W::Data,
196    {
197        /// The inner widget
198        #[widget((self.map_fn)(data))]
199        pub inner: W,
200        map_fn: F,
201        _data: PhantomData<A>,
202    }
203
204    impl Self {
205        /// Construct
206        ///
207        /// -   Over an `inner` widget
208        /// -   And `map_fn` mapping to the inner widget's data type
209        pub fn new(inner: W, map_fn: F) -> Self {
210            Map {
211                inner,
212                map_fn,
213                _data: PhantomData,
214            }
215        }
216    }
217}