From 89c8805924185e3e9e331e8e0ad736ee102c240a Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Tue, 14 Nov 2023 13:03:09 -0800 Subject: [PATCH] Refactored switcher to use Dynamic --- examples/switcher.rs | 20 +++++----- src/value.rs | 28 ++++++++++++- src/widgets/switcher.rs | 88 +++++++++++++++-------------------------- 3 files changed, 67 insertions(+), 69 deletions(-) diff --git a/examples/switcher.rs b/examples/switcher.rs index f0e970b..0d35052 100644 --- a/examples/switcher.rs +++ b/examples/switcher.rs @@ -1,6 +1,5 @@ -use gooey::value::Dynamic; +use gooey::value::{Dynamic, Switchable}; use gooey::widget::{MakeWidget, WidgetInstance}; -use gooey::widgets::Switcher; use gooey::Run; #[derive(Debug)] @@ -12,14 +11,15 @@ enum ActiveContent { fn main() -> gooey::Result { let active = Dynamic::new(ActiveContent::Intro); - Switcher::new(active.clone(), move |content| match content { - ActiveContent::Intro => intro(active.clone()), - ActiveContent::Success => success(active.clone()), - }) - .contain() - .centered() - .expand() - .run() + active + .switcher(|current, active| match current { + ActiveContent::Intro => intro(active.clone()), + ActiveContent::Success => success(active.clone()), + }) + .contain() + .centered() + .expand() + .run() } fn intro(active: Dynamic) -> WidgetInstance { diff --git a/src/value.rs b/src/value.rs index 930ee32..a19dacc 100644 --- a/src/value.rs +++ b/src/value.rs @@ -15,8 +15,8 @@ use intentional::Assert; use crate::animation::{DynamicTransition, LinearInterpolate}; use crate::context::{WidgetContext, WindowHandle}; use crate::utils::{IgnorePoison, WithClone}; -use crate::widget::WidgetId; -use crate::widgets::Input; +use crate::widget::{WidgetId, WidgetInstance}; +use crate::widgets::{Input, Switcher}; /// An instance of a value that provides APIs to observe and react to its /// contents. @@ -395,6 +395,15 @@ impl Dynamic { } } +impl Dynamic { + /// Returns a new [`Switcher`] widget whose contents is the value of this + /// dynamic. + #[must_use] + pub fn switcher(self) -> Switcher { + Switcher::new(self) + } +} + impl Default for Dynamic where T: Default, @@ -921,6 +930,21 @@ where } } +/// A type that can be the source of a [`Switcher`] widget. +pub trait Switchable: IntoDynamic + Sized { + /// Returns a new [`Switcher`] whose contents is the result of invoking + /// `map` each time `self` is updated. + fn switcher(self, map: F) -> Switcher + where + F: FnMut(&T, &Dynamic) -> WidgetInstance + Send + 'static, + T: Send + 'static, + { + Switcher::mapping(self, map) + } +} + +impl Switchable for W where W: IntoDynamic {} + /// A value that may be either constant or dynamic. #[derive(Debug)] pub enum Value { diff --git a/src/widgets/switcher.rs b/src/widgets/switcher.rs index fd9cd18..d4b9ee9 100644 --- a/src/widgets/switcher.rs +++ b/src/widgets/switcher.rs @@ -1,58 +1,49 @@ use std::fmt::Debug; -use std::panic::UnwindSafe; use kludgine::figures::Size; use crate::context::{AsEventContext, LayoutContext}; -use crate::value::{Generation, IntoValue, Value}; -use crate::widget::{MakeWidget, WidgetInstance, WidgetRef, WrapperWidget}; +use crate::value::{Dynamic, DynamicReader, IntoDynamic}; +use crate::widget::{WidgetInstance, WidgetRef, WrapperWidget}; use crate::ConstraintLimit; /// A widget that switches its contents based on a value of `T`. -pub struct Switcher { - value: Value, - value_generation: Option, - factory: Box>, +#[derive(Debug)] +pub struct Switcher { + source: DynamicReader, child: WidgetRef, } -impl Switcher { +impl Switcher { + /// Returns a new widget that replaces its contents with the results of + /// calling `map` each time `source` is updated. + /// + /// This function is equivalent to calling + /// `Self::new(source.into_dynamic().map_each(map))`, but this function's + /// signature helps the compiler's type inference work correctly. When using + /// new directly, the compiler often requires annotating the closure's + /// argument type. + pub fn mapping(source: impl IntoDynamic, mut map: F) -> Self + where + F: FnMut(&T, &Dynamic) -> WidgetInstance + Send + 'static, + T: Send + 'static, + { + let source = source.into_dynamic(); + + Self::new(source.clone().map_each(move |value| map(value, &source))) + } + /// Returns a new widget that replaces its contents with the result of /// `widget_factory` each time `value` changes. #[must_use] - pub fn new(value: impl IntoValue, mut widget_factory: F) -> Self - where - F: for<'a> FnMut(&'a T) -> W + Send + UnwindSafe + 'static, - W: MakeWidget, - { - let value = value.into_value(); - let value_generation = value.generation(); - let child = WidgetRef::new(value.map(|value| widget_factory(value))); - Self { - value, - value_generation, - factory: Box::new(widget_factory), - child, - } + pub fn new(source: impl IntoDynamic) -> Self { + let mut source = source.into_dynamic().into_reader(); + let child = WidgetRef::new(source.get()); + Self { source, child } } } -impl Debug for Switcher -where - T: Debug, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Switcher") - .field("value", &self.value) - .field("child", &self.child) - .finish_non_exhaustive() - } -} - -impl WrapperWidget for Switcher -where - T: Debug + Send + UnwindSafe + 'static, -{ +impl WrapperWidget for Switcher { fn child_mut(&mut self) -> &mut WidgetRef { &mut self.child } @@ -63,11 +54,8 @@ where available_space: Size, context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> Size { - let current_generation = self.value.generation(); - if self.value_generation != current_generation { - self.value_generation = current_generation; - let new_child = WidgetRef::new(self.value.map(|value| self.factory.invoke(value))); - let removed = std::mem::replace(&mut self.child, new_child); + if self.source.has_updated() { + let removed = std::mem::replace(&mut self.child, WidgetRef::new(self.source.get())); if let WidgetRef::Mounted(removed) = removed { context.remove_child(&removed); } @@ -75,17 +63,3 @@ where available_space } } - -trait SwitchMap: UnwindSafe + Send { - fn invoke(&mut self, value: &T) -> WidgetInstance; -} - -impl SwitchMap for F -where - F: for<'a> FnMut(&'a T) -> W + Send + UnwindSafe, - W: MakeWidget, -{ - fn invoke(&mut self, value: &T) -> WidgetInstance { - self(value).make_widget() - } -}