mirror of
https://github.com/danbulant/cushy
synced 2026-06-20 23:11:12 +00:00
Refactored switcher to use Dynamic<WidgetInstance>
This commit is contained in:
parent
4a4bc5de1a
commit
89c8805924
3 changed files with 67 additions and 69 deletions
|
|
@ -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<ActiveContent>) -> WidgetInstance {
|
||||
|
|
|
|||
28
src/value.rs
28
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<T> Dynamic<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Dynamic<WidgetInstance> {
|
||||
/// Returns a new [`Switcher`] widget whose contents is the value of this
|
||||
/// dynamic.
|
||||
#[must_use]
|
||||
pub fn switcher(self) -> Switcher {
|
||||
Switcher::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Dynamic<T>
|
||||
where
|
||||
T: Default,
|
||||
|
|
@ -921,6 +930,21 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A type that can be the source of a [`Switcher`] widget.
|
||||
pub trait Switchable<T>: IntoDynamic<T> + Sized {
|
||||
/// Returns a new [`Switcher`] whose contents is the result of invoking
|
||||
/// `map` each time `self` is updated.
|
||||
fn switcher<F>(self, map: F) -> Switcher
|
||||
where
|
||||
F: FnMut(&T, &Dynamic<T>) -> WidgetInstance + Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
Switcher::mapping(self, map)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, W> Switchable<T> for W where W: IntoDynamic<T> {}
|
||||
|
||||
/// A value that may be either constant or dynamic.
|
||||
#[derive(Debug)]
|
||||
pub enum Value<T> {
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
||||
value: Value<T>,
|
||||
value_generation: Option<Generation>,
|
||||
factory: Box<dyn SwitchMap<T>>,
|
||||
#[derive(Debug)]
|
||||
pub struct Switcher {
|
||||
source: DynamicReader<WidgetInstance>,
|
||||
child: WidgetRef,
|
||||
}
|
||||
|
||||
impl<T> Switcher<T> {
|
||||
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<T, F>(source: impl IntoDynamic<T>, mut map: F) -> Self
|
||||
where
|
||||
F: FnMut(&T, &Dynamic<T>) -> 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<W, F>(value: impl IntoValue<T>, 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<WidgetInstance>) -> Self {
|
||||
let mut source = source.into_dynamic().into_reader();
|
||||
let child = WidgetRef::new(source.get());
|
||||
Self { source, child }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Switcher<T>
|
||||
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<T> WrapperWidget for Switcher<T>
|
||||
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<ConstraintLimit>,
|
||||
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
|
||||
) -> Size<ConstraintLimit> {
|
||||
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<T>: UnwindSafe + Send {
|
||||
fn invoke(&mut self, value: &T) -> WidgetInstance;
|
||||
}
|
||||
|
||||
impl<W, T, F> SwitchMap<T> 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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue