druid/widget/
scope.rs

1// Copyright 2020 The Druid Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::marker::PhantomData;
16
17use crate::widget::prelude::*;
18use crate::widget::WidgetWrapper;
19use crate::{Data, Lens, Point, WidgetPod};
20use tracing::instrument;
21
22/// A policy that controls how a [`Scope`] will interact with its surrounding
23/// application data. Specifically, how to create an initial State from the
24/// input, and how to synchronise the two using a [`ScopeTransfer`].
25///
26/// [`Scope`]: struct.Scope.html
27/// [`ScopeTransfer`]: trait.ScopeTransfer.html
28pub trait ScopePolicy {
29    /// The type of data that comes in from the surrounding application or scope.
30    type In: Data;
31    /// The type of data that the `Scope` will maintain internally.
32    /// This will usually be larger than the input data, and will embed the input data.
33    type State: Data;
34    /// The type of transfer that will be used to synchronise internal and application state
35    type Transfer: ScopeTransfer<In = Self::In, State = Self::State>;
36    /// Make a new state and transfer from the input.
37    ///
38    /// This consumes the policy, so non-cloneable items can make their way
39    /// into the state this way.
40    fn create(self, inner: &Self::In) -> (Self::State, Self::Transfer);
41}
42
43/// A `ScopeTransfer` knows how to synchronise input data with its counterpart
44/// within a [`Scope`].
45///
46/// It is separate from the policy mainly to allow easy use of lenses to do
47/// synchronisation, with a custom [`ScopePolicy`].
48///
49/// [`Scope`]: struct.Scope.html
50/// [`ScopePolicy`]: trait.ScopePolicy.html
51pub trait ScopeTransfer {
52    /// The type of data that comes in from the surrounding application or scope.
53    type In: Data;
54    /// The type of data that the Scope will maintain internally.
55    type State: Data;
56
57    /// Replace the input we have within our State with a new one from outside
58    fn read_input(&self, state: &mut Self::State, input: &Self::In);
59    /// Take the modifications we have made and write them back
60    /// to our input.
61    fn write_back_input(&self, state: &Self::State, input: &mut Self::In);
62}
63
64/// A default implementation of [`ScopePolicy`] that takes a function and a transfer.
65///
66/// [`ScopePolicy`]: trait.ScopePolicy.html
67pub struct DefaultScopePolicy<F: FnOnce(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer> {
68    make_state: F,
69    transfer: Transfer,
70}
71
72impl<F: FnOnce(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer>
73    DefaultScopePolicy<F, Transfer>
74{
75    /// Create a `ScopePolicy` from a factory function and a `ScopeTransfer`.
76    pub fn new(make_state: F, transfer: Transfer) -> Self {
77        DefaultScopePolicy {
78            make_state,
79            transfer,
80        }
81    }
82}
83
84impl<F: FnOnce(In) -> State, L: Lens<State, In>, In: Data, State: Data>
85    DefaultScopePolicy<F, LensScopeTransfer<L, In, State>>
86{
87    /// Create a `ScopePolicy` from a factory function and a lens onto that
88    /// `Scope`'s state.
89    pub fn from_lens(make_state: F, lens: L) -> Self {
90        Self::new(make_state, LensScopeTransfer::new(lens))
91    }
92}
93
94impl<F: FnOnce(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer> ScopePolicy
95    for DefaultScopePolicy<F, Transfer>
96{
97    type In = Transfer::In;
98    type State = Transfer::State;
99    type Transfer = Transfer;
100
101    fn create(self, input: &Self::In) -> (Self::State, Self::Transfer) {
102        let state = (self.make_state)(input.clone());
103        (state, self.transfer)
104    }
105}
106
107/// A `ScopeTransfer` that uses a Lens to synchronise between a large internal
108/// state and a small input.
109pub struct LensScopeTransfer<L: Lens<State, In>, In, State> {
110    lens: L,
111    phantom_in: PhantomData<In>,
112    phantom_state: PhantomData<State>,
113}
114
115impl<L: Lens<State, In>, In, State> LensScopeTransfer<L, In, State> {
116    /// Create a `ScopeTransfer` from a Lens onto a portion of the `Scope`'s state.
117    pub fn new(lens: L) -> Self {
118        LensScopeTransfer {
119            lens,
120            phantom_in: PhantomData::default(),
121            phantom_state: PhantomData::default(),
122        }
123    }
124}
125
126impl<L: Lens<State, In>, In: Data, State: Data> ScopeTransfer for LensScopeTransfer<L, In, State> {
127    type In = In;
128    type State = State;
129
130    fn read_input(&self, state: &mut State, data: &In) {
131        self.lens.with_mut(state, |inner| {
132            if !inner.same(data) {
133                *inner = data.clone()
134            }
135        });
136    }
137
138    fn write_back_input(&self, state: &State, data: &mut In) {
139        self.lens.with(state, |inner| {
140            if !inner.same(data) {
141                *data = inner.clone();
142            }
143        });
144    }
145}
146
147enum ScopeContent<SP: ScopePolicy> {
148    Policy {
149        policy: Option<SP>,
150    },
151    Transfer {
152        state: SP::State,
153        transfer: SP::Transfer,
154    },
155}
156
157/// A widget that allows encapsulation of application state.
158///
159/// This is useful in circumstances where
160/// * A (potentially reusable) widget is composed of a tree of multiple cooperating child widgets
161/// * Those widgets communicate amongst themselves using Druid's reactive data mechanisms
162/// * It is undesirable to complicate the surrounding application state with the internal details
163///   of the widget.
164///
165///
166/// Examples include:
167/// * In a tabs widget composed of a tab bar, and a widget switching body, those widgets need to
168///   cooperate on which tab is selected. However not every user of a tabs widget wishes to
169///   encumber their application state with this internal detail - especially as many tabs widgets may
170///   reasonably exist in an involved application.
171/// * In a table/grid widget composed of various internal widgets, many things need to be synchronised.
172///   Scroll position, heading moves, drag operations, sort/filter operations. For many applications
173///   access to this internal data outside of the table widget isn't needed.
174///   For this reason it may be useful to use a Scope to establish private state.
175///
176/// A scope embeds some input state (from its surrounding application or parent scope)
177/// into a larger piece of internal state. This is controlled by a user provided policy.
178///
179/// The ScopePolicy needs to do two things
180/// a) Create a new scope from the initial value of its input,
181/// b) Provide two way synchronisation between the input and the state via a ScopeTransfer
182///
183/// Convenience methods are provided to make a policy from a function and a lens.
184/// It may sometimes be advisable to implement ScopePolicy directly if you need to
185/// mention the type of a Scope.
186///
187/// # Examples
188/// ```
189/// use druid::{Data, Lens, WidgetExt};
190/// use druid::widget::{TextBox, Scope};
191/// #[derive(Clone, Data, Lens)]
192/// struct AppState {
193///     name: String,
194/// }
195///
196/// #[derive(Clone, Data, Lens)]
197/// struct PrivateState {
198///     text: String,
199///     other: u32,
200/// }
201///
202/// impl PrivateState {
203///     pub fn new(text: String) -> Self {
204///         PrivateState { text, other: 0 }
205///     }
206/// }
207///
208/// fn main() {
209///     let scope = Scope::from_lens(
210///         PrivateState::new,
211///         PrivateState::text,
212///         TextBox::new().lens(PrivateState::text),
213///     );
214/// }
215/// ```
216pub struct Scope<SP: ScopePolicy, W: Widget<SP::State>> {
217    content: ScopeContent<SP>,
218    inner: WidgetPod<SP::State, W>,
219}
220
221impl<SP: ScopePolicy, W: Widget<SP::State>> Scope<SP, W> {
222    /// Create a new scope from a policy and an inner widget
223    pub fn new(policy: SP, inner: W) -> Self {
224        Scope {
225            content: ScopeContent::Policy {
226                policy: Some(policy),
227            },
228            inner: WidgetPod::new(inner),
229        }
230    }
231
232    /// A reference to the contents of the `Scope`'s state.
233    ///
234    /// This allows you to access the content from outside the widget.
235    pub fn state(&self) -> Option<&SP::State> {
236        if let ScopeContent::Transfer { ref state, .. } = &self.content {
237            Some(state)
238        } else {
239            None
240        }
241    }
242
243    /// A mutable reference to the contents of the [`Scope`]'s state.
244    ///
245    /// This allows you to mutably access the content of the `Scope`' s state from
246    /// outside the widget. Mainly useful for composite widgets.
247    ///
248    /// # Note:
249    ///
250    /// If you modify the state through this reference, the Scope will not
251    /// call update on its children until the next event it receives.
252    pub fn state_mut(&mut self) -> Option<&mut SP::State> {
253        if let ScopeContent::Transfer { ref mut state, .. } = &mut self.content {
254            Some(state)
255        } else {
256            None
257        }
258    }
259
260    fn with_state<V>(
261        &mut self,
262        data: &SP::In,
263        mut f: impl FnMut(&mut SP::State, &mut WidgetPod<SP::State, W>) -> V,
264    ) -> V {
265        match &mut self.content {
266            ScopeContent::Policy { policy } => {
267                // We know that the policy is a Some - it is an option to allow
268                // us to take ownership before replacing the content.
269                let (mut state, policy) = policy.take().unwrap().create(data);
270                let v = f(&mut state, &mut self.inner);
271                self.content = ScopeContent::Transfer {
272                    state,
273                    transfer: policy,
274                };
275                v
276            }
277            ScopeContent::Transfer {
278                ref mut state,
279                transfer,
280            } => {
281                transfer.read_input(state, data);
282                f(state, &mut self.inner)
283            }
284        }
285    }
286
287    fn write_back_input(&mut self, data: &mut SP::In) {
288        if let ScopeContent::Transfer { state, transfer } = &mut self.content {
289            transfer.write_back_input(state, data)
290        }
291    }
292}
293
294impl<
295        F: FnOnce(Transfer::In) -> Transfer::State,
296        Transfer: ScopeTransfer,
297        W: Widget<Transfer::State>,
298    > Scope<DefaultScopePolicy<F, Transfer>, W>
299{
300    /// Create a new policy from a function creating the state, and a ScopeTransfer synchronising it
301    pub fn from_function(make_state: F, transfer: Transfer, inner: W) -> Self {
302        Self::new(DefaultScopePolicy::new(make_state, transfer), inner)
303    }
304}
305
306impl<In: Data, State: Data, F: Fn(In) -> State, L: Lens<State, In>, W: Widget<State>>
307    Scope<DefaultScopePolicy<F, LensScopeTransfer<L, In, State>>, W>
308{
309    /// Create a new policy from a function creating the state, and a Lens synchronising it
310    pub fn from_lens(make_state: F, lens: L, inner: W) -> Self {
311        Self::new(DefaultScopePolicy::from_lens(make_state, lens), inner)
312    }
313}
314
315impl<SP: ScopePolicy, W: Widget<SP::State>> Widget<SP::In> for Scope<SP, W> {
316    #[instrument(name = "Scope", level = "trace", skip(self, ctx, event, data, env))]
317    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut SP::In, env: &Env) {
318        self.with_state(data, |state, inner| inner.event(ctx, event, state, env));
319        self.write_back_input(data);
320        ctx.request_update()
321    }
322
323    #[instrument(name = "Scope", level = "trace", skip(self, ctx, event, data, env))]
324    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &SP::In, env: &Env) {
325        self.with_state(data, |state, inner| inner.lifecycle(ctx, event, state, env));
326    }
327
328    #[instrument(name = "Scope", level = "trace", skip(self, ctx, _old_data, data, env))]
329    fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &SP::In, data: &SP::In, env: &Env) {
330        self.with_state(data, |state, inner| inner.update(ctx, state, env));
331    }
332
333    #[instrument(name = "Scope", level = "trace", skip(self, ctx, bc, data, env))]
334    fn layout(
335        &mut self,
336        ctx: &mut LayoutCtx,
337        bc: &BoxConstraints,
338        data: &SP::In,
339        env: &Env,
340    ) -> Size {
341        self.with_state(data, |state, inner| {
342            let size = inner.layout(ctx, bc, state, env);
343            inner.set_origin(ctx, Point::ORIGIN);
344            size
345        })
346    }
347
348    #[instrument(name = "Scope", level = "trace", skip(self, ctx, data, env))]
349    fn paint(&mut self, ctx: &mut PaintCtx, data: &SP::In, env: &Env) {
350        self.with_state(data, |state, inner| inner.paint_raw(ctx, state, env));
351    }
352
353    // TODO
354    // fn debug_state(&self, data: &SP::In) -> DebugState;
355}
356
357impl<SP: ScopePolicy, W: Widget<SP::State>> WidgetWrapper for Scope<SP, W> {
358    widget_wrapper_pod_body!(W, inner);
359}