Skip to main content

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    #[widget]
42    #[layout(self.inner)]
43    pub struct Adapt<A, W: Widget> {
44        core: widget_core!(),
45        state: W::Data,
46        #[widget(&self.state)]
47        inner: W,
48        configure_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W, &mut W::Data)>>,
49        update_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W, &mut W::Data, &A)>>,
50        timer_handlers:
51            LinearMap<TimerHandle, Box<dyn Fn(&mut AdaptEventCx, &mut W, &mut W::Data, &A)>>,
52        message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &mut W, &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, &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, &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, &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, &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.inner, &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.inner, &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.inner, &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.inner, &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    #[derive_widget]
192    pub struct Map<A, W: Widget, F>
193    where
194        F: for<'a> Fn(&'a A) -> &'a W::Data,
195    {
196        /// The inner widget
197        #[widget = (self.map_fn)(data)]
198        pub inner: W,
199        map_fn: F,
200        _data: PhantomData<A>,
201    }
202
203    impl Widget for Self {
204        type Data = A;
205    }
206
207    impl Self {
208        /// Construct
209        ///
210        /// -   Over an `inner` widget
211        /// -   And `map_fn` mapping to the inner widget's data type
212        pub fn new(inner: W, map_fn: F) -> Self {
213            Map {
214                inner,
215                map_fn,
216                _data: PhantomData,
217            }
218        }
219    }
220}