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}