|
| 1 | +use crate::kurbo::{Point, Rect}; |
| 2 | +use crate::{ |
| 3 | + BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, Lens, LifeCycle, LifeCycleCtx, PaintCtx, |
| 4 | + Size, UpdateCtx, Widget, WidgetPod, |
| 5 | +}; |
| 6 | +use std::marker::PhantomData; |
| 7 | + |
| 8 | +pub trait ScopePolicy { |
| 9 | + type In: Data; |
| 10 | + type State: Data; |
| 11 | + type Transfer: ScopeTransfer<In = Self::In, State = Self::State>; |
| 12 | + // Make a new state and transfer from the input. |
| 13 | + // This consumes the policy, so non cloneable items can make their way into the state this way. |
| 14 | + fn create(self, inner: &Self::In) -> (Self::State, Self::Transfer); |
| 15 | +} |
| 16 | + |
| 17 | +pub trait ScopeTransfer { |
| 18 | + type In: Data; |
| 19 | + type State: Data; |
| 20 | + |
| 21 | + // Replace the input we have with a new one from outside |
| 22 | + fn read_input(&self, state: &mut Self::State, inner: &Self::In); |
| 23 | + // Take the modifications we have made and write them back |
| 24 | + // to our input. |
| 25 | + fn write_back_input(&self, state: &Self::State, inner: &mut Self::In); |
| 26 | +} |
| 27 | + |
| 28 | +pub struct DefaultScopePolicy<F: Fn(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer> { |
| 29 | + make_state: F, |
| 30 | + transfer: Transfer, |
| 31 | +} |
| 32 | + |
| 33 | +impl<F: Fn(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer> |
| 34 | + DefaultScopePolicy<F, Transfer> |
| 35 | +{ |
| 36 | + pub fn new(make_state: F, transfer: Transfer) -> Self { |
| 37 | + DefaultScopePolicy { |
| 38 | + make_state, |
| 39 | + transfer, |
| 40 | + } |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +impl<F: Fn(In) -> State, L: Lens<State, In>, In: Data, State: Data> |
| 45 | + DefaultScopePolicy<F, LensScopeTransfer<L, In, State>> |
| 46 | +{ |
| 47 | + pub fn for_lens(make_state: F, lens: L) -> Self { |
| 48 | + Self::new(make_state, LensScopeTransfer::new(lens)) |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +impl<F: Fn(Transfer::In) -> Transfer::State, Transfer: ScopeTransfer> ScopePolicy |
| 53 | + for DefaultScopePolicy<F, Transfer> |
| 54 | +{ |
| 55 | + type In = Transfer::In; |
| 56 | + type State = Transfer::State; |
| 57 | + type Transfer = Transfer; |
| 58 | + |
| 59 | + fn create(self, inner: &Self::In) -> (Self::State, Self::Transfer) { |
| 60 | + let state = (self.make_state)(inner.clone()); |
| 61 | + (state, self.transfer) |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +pub struct LensScopeTransfer<L: Lens<State, In>, In, State> { |
| 66 | + lens: L, |
| 67 | + phantom_in: PhantomData<In>, |
| 68 | + phantom_state: PhantomData<State>, |
| 69 | +} |
| 70 | + |
| 71 | +impl<L: Lens<State, In>, In, State> LensScopeTransfer<L, In, State> { |
| 72 | + pub fn new(lens: L) -> Self { |
| 73 | + LensScopeTransfer { |
| 74 | + lens, |
| 75 | + phantom_in: PhantomData::default(), |
| 76 | + phantom_state: PhantomData::default(), |
| 77 | + } |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +impl<L: Lens<State, In>, In: Data, State: Data> ScopeTransfer for LensScopeTransfer<L, In, State> { |
| 82 | + type In = In; |
| 83 | + type State = State; |
| 84 | + |
| 85 | + fn read_input(&self, state: &mut State, data: &In) { |
| 86 | + self.lens.with_mut(state, |inner| { |
| 87 | + if !inner.same(&data) { |
| 88 | + *inner = data.clone() |
| 89 | + } |
| 90 | + }); |
| 91 | + } |
| 92 | + |
| 93 | + fn write_back_input(&self, state: &State, data: &mut In) { |
| 94 | + self.lens.with(state, |inner| { |
| 95 | + if !inner.same(&data) { |
| 96 | + *data = inner.clone(); |
| 97 | + } |
| 98 | + }); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +enum ScopeContent<SP: ScopePolicy> { |
| 103 | + Policy { |
| 104 | + policy: Option<SP>, |
| 105 | + }, |
| 106 | + Transfer { |
| 107 | + state: SP::State, |
| 108 | + transfer: SP::Transfer, |
| 109 | + }, |
| 110 | +} |
| 111 | + |
| 112 | +pub struct Scope<SP: ScopePolicy, W: Widget<SP::State>> { |
| 113 | + content: ScopeContent<SP>, |
| 114 | + inner: WidgetPod<SP::State, W>, |
| 115 | + widget_added: bool, |
| 116 | +} |
| 117 | + |
| 118 | +impl<SP: ScopePolicy, W: Widget<SP::State>> Scope<SP, W> { |
| 119 | + pub fn new(policy: SP, inner: W) -> Self { |
| 120 | + Scope { |
| 121 | + content: ScopeContent::Policy { |
| 122 | + policy: Some(policy), |
| 123 | + }, |
| 124 | + inner: WidgetPod::new(inner), |
| 125 | + widget_added: false, |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + fn with_state<V>( |
| 130 | + &mut self, |
| 131 | + data: &SP::In, |
| 132 | + mut f: impl FnMut(&mut SP::State, &mut WidgetPod<SP::State, W>) -> V, |
| 133 | + ) -> V { |
| 134 | + match &mut self.content { |
| 135 | + ScopeContent::Policy { policy } => { |
| 136 | + // We know that the policy is a Some - it is an option to allow |
| 137 | + // us to take ownership before replacing the content. |
| 138 | + let (mut state, policy) = policy.take().unwrap().create(data); |
| 139 | + let v = f(&mut state, &mut self.inner); |
| 140 | + self.content = ScopeContent::Transfer { |
| 141 | + state, |
| 142 | + transfer: policy, |
| 143 | + }; |
| 144 | + v |
| 145 | + } |
| 146 | + ScopeContent::Transfer { |
| 147 | + ref mut state, |
| 148 | + transfer: policy, |
| 149 | + } => { |
| 150 | + policy.read_input(state, data); |
| 151 | + f(state, &mut self.inner) |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + fn write_back_input(&mut self, data: &mut SP::In) { |
| 157 | + if let ScopeContent::Transfer { state, transfer } = &mut self.content { |
| 158 | + transfer.write_back_input(state, data) |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +impl<SP: ScopePolicy, W: Widget<SP::State>> Widget<SP::In> for Scope<SP, W> { |
| 164 | + fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut SP::In, env: &Env) { |
| 165 | + if self.widget_added { |
| 166 | + self.with_state(data, |state, inner| inner.event(ctx, event, state, env)); |
| 167 | + self.write_back_input(data); |
| 168 | + ctx.request_update() |
| 169 | + } else { |
| 170 | + log::warn!("Scope dropping event (widget not added) {:?}", event); |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &SP::In, env: &Env) { |
| 175 | + self.with_state(data, |state, inner| inner.lifecycle(ctx, event, state, env)); |
| 176 | + if let LifeCycle::WidgetAdded = event { |
| 177 | + self.widget_added = true; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &SP::In, data: &SP::In, env: &Env) { |
| 182 | + self.with_state(data, |state, inner| inner.update(ctx, state, env)); |
| 183 | + } |
| 184 | + |
| 185 | + fn layout( |
| 186 | + &mut self, |
| 187 | + ctx: &mut LayoutCtx, |
| 188 | + bc: &BoxConstraints, |
| 189 | + data: &SP::In, |
| 190 | + env: &Env, |
| 191 | + ) -> Size { |
| 192 | + self.with_state(data, |state, inner| { |
| 193 | + let size = inner.layout(ctx, bc, state, env); |
| 194 | + inner.set_layout_rect(ctx, state, env, Rect::from_origin_size(Point::ORIGIN, size)); |
| 195 | + size |
| 196 | + }) |
| 197 | + } |
| 198 | + |
| 199 | + fn paint(&mut self, ctx: &mut PaintCtx, data: &SP::In, env: &Env) { |
| 200 | + self.with_state(data, |state, inner| inner.paint_raw(ctx, state, env)); |
| 201 | + } |
| 202 | +} |
0 commit comments