use std::convert::Infallible;
use rxrust::observable::boxed::LocalBoxedObservableClone;
use crate::prelude::*;
pub trait IntoVariant {
type Value: 'static;
fn into_variant(self) -> Variant<Self::Value>;
}
impl<V: 'static> IntoVariant for Variant<V> {
type Value = V;
fn into_variant(self) -> Variant<<Self as IntoVariant>::Value> { self }
}
impl<T> IntoVariant for T
where
T: StateWatcher + 'static,
T::Value: Sized + 'static,
{
type Value = T::Value;
fn into_variant(self) -> Variant<Self::Value> { Variant::from_watcher(self) }
}
pub trait VariantSource: Sized + 'static {
type Value: 'static;
#[doc(hidden)]
fn modifies(&self) -> Option<LocalBoxedObservableClone<'static, ModifyInfo, Infallible>>;
fn map<F, U>(self, map: F) -> VariantMap<Self, F>
where
F: Fn(&Self::Value) -> U,
{
VariantMap { source: self, map }
}
fn combine<R>(self, rhs: R) -> Combine<Self, R::Source>
where
R: VariantInput,
{
Combine { left: self, right: rhs.into_source() }
}
fn combine_with<R, F, U: 'static>(self, rhs: R, f: F) -> CombineWithMap<Self, R::Source, F>
where
R: VariantInput,
F: Fn(&(Self::Value, R::Value)) -> U + 'static,
{
self.combine(rhs).map(f)
}
}
pub trait VariantSnapshot: VariantSource
where
<Self as VariantSource>::Value: Clone,
{
fn snapshot(&self) -> <Self as VariantSource>::Value;
fn into_pipe_value(self) -> PipeValue<<Self as VariantSource>::Value>
where
Self: Sized,
{
let init_value = self.snapshot();
match self.modifies() {
Some(modifies) => {
let trigger = modifies.box_it();
let source = self;
let pipe = Pipe::new(trigger, move |_| source.snapshot());
PipeValue::Pipe { init_value, pipe }
}
None => PipeValue::Value(init_value),
}
}
fn freeze(self) -> Variant<<Self as VariantSource>::Value>
where
Self: Sized,
{
Variant::Value(self.snapshot())
}
}
pub type CombineWithMap<L, R, F> = VariantMap<Combine<L, R>, F>;
pub trait VariantInput {
type Value: 'static;
type Source: VariantSource<Value = Self::Value>;
fn into_source(self) -> Self::Source;
}
impl<T> VariantInput for T
where
T: IntoVariant,
{
type Value = T::Value;
type Source = Variant<Self::Value>;
fn into_source(self) -> Self::Source { self.into_variant() }
}
impl<S, F, U> VariantInput for VariantMap<S, F>
where
S: VariantSource,
U: 'static,
F: Fn(&S::Value) -> U + 'static,
{
type Value = U;
type Source = VariantMap<S, F>;
fn into_source(self) -> Self::Source { self }
}
impl<L, R> VariantInput for Combine<L, R>
where
L: VariantSource,
R: VariantSource,
{
type Value = (L::Value, R::Value);
type Source = Combine<L, R>;
fn into_source(self) -> Self::Source { self }
}
pub struct Combine<L, R> {
left: L,
right: R,
}
impl<L, R> Combine<L, R>
where
L: VariantSource,
R: VariantSource,
{
pub fn map<F, U>(self, map: F) -> VariantMap<Combine<L, R>, F>
where
F: Fn(&(L::Value, R::Value)) -> U,
{
VariantMap { source: self, map }
}
}
pub enum Variant<V> {
Watcher(Box<dyn StateWatcher<Value = V>>),
Value(V),
}
#[derive(Clone)]
pub struct VariantMap<S, F> {
source: S,
map: F,
}
impl<V: 'static> Variant<V> {
pub fn new(ctx: &impl AsRef<ProviderCtx>) -> Option<Self>
where
V: Clone,
{
if let Some(value) = Provider::state_of::<Box<dyn StateWatcher<Value = V>>>(ctx) {
Some(Variant::Watcher(value.clone_boxed_watcher()))
} else {
Provider::of::<V>(ctx).map(|v| Variant::Value(v.clone()))
}
}
pub fn new_or(ctx: &impl AsRef<ProviderCtx>, default: V) -> Self
where
V: Clone,
{
Self::new(ctx).unwrap_or(Variant::Value(default))
}
pub fn new_or_else(ctx: &impl AsRef<ProviderCtx>, f: impl FnOnce() -> V) -> Self
where
V: Clone,
{
Self::new(ctx).unwrap_or_else(|| Variant::Value(f()))
}
pub fn new_or_default(ctx: &impl AsRef<ProviderCtx>) -> Self
where
V: Default + Clone,
{
Self::new_or_else(ctx, V::default)
}
pub fn from_watcher(watcher: impl StateWatcher<Value = V>) -> Self {
match watcher.try_into_value() {
Ok(v) => Variant::Value(v),
Err(w) => Variant::Watcher(w.clone_boxed_watcher()),
}
}
pub fn into_provider(self) -> Provider
where
V: Sized + 'static,
{
match self {
Variant::Watcher(w) => Provider::watcher(w),
Variant::Value(v) => Provider::new(v),
}
}
pub fn map<F, U>(self, map: F) -> VariantMap<Variant<V>, F>
where
F: Fn(&V) -> U,
{
VariantMap { source: self, map }
}
pub fn snapshot(&self) -> V
where
V: Clone,
{
match self {
Variant::Value(v) => v.clone(),
Variant::Watcher(v) => v.read().clone(),
}
}
}
impl<V> VariantSource for Variant<V>
where
V: 'static,
{
type Value = V;
fn modifies(&self) -> Option<LocalBoxedObservableClone<'static, ModifyInfo, Infallible>> {
match self {
Variant::Watcher(watcher) => Some(watcher.modifies()),
Variant::Value(_) => None,
}
}
}
impl<V> VariantSnapshot for Variant<V>
where
V: Clone + 'static,
{
fn snapshot(&self) -> V {
match self {
Variant::Value(v) => v.clone(),
Variant::Watcher(v) => v.read().clone(),
}
}
}
type ColorMap<S> =
VariantMap<Combine<S, Variant<LightnessTone>>, fn(&(Color, LightnessTone)) -> Color>;
fn apply_lightness(input: &(Color, LightnessTone)) -> Color { input.0.with_lightness(input.1) }
pub trait VariantColorExt: VariantSource<Value = Color> + Sized {
fn into_base_color(self, ctx: &impl AsRef<ProviderCtx>) -> ColorMap<Self> {
let palette = Palette::of(ctx);
let lightness = palette.lightness_group().base;
self
.combine(Variant::Value(lightness))
.map(apply_lightness)
}
fn into_container_color(self, ctx: &impl AsRef<ProviderCtx>) -> ColorMap<Self> {
let palette = Palette::of(ctx);
let lightness = palette.lightness_group().container;
self
.combine(Variant::Value(lightness))
.map(apply_lightness)
}
fn on_this_color(self, ctx: &impl AsRef<ProviderCtx>) -> ColorMap<Self> {
let palette = Palette::of(ctx);
let lightness = palette.lightness_group().on;
self
.combine(Variant::Value(lightness))
.map(apply_lightness)
}
fn on_this_container_color(self, ctx: &impl AsRef<ProviderCtx>) -> ColorMap<Self> {
let palette = Palette::of(ctx);
let lightness = palette.lightness_group().on_container;
self
.combine(Variant::Value(lightness))
.map(apply_lightness)
}
}
impl<T> VariantColorExt for T where T: VariantSource<Value = Color> + Sized {}
impl<S, F, U> VariantMap<S, F>
where
S: VariantSource,
F: Fn(&S::Value) -> U,
{
pub fn map<F2, U2>(self, map: F2) -> VariantMap<S, impl Fn(&S::Value) -> U2>
where
F2: Fn(U) -> U2,
{
let VariantMap { source, map: previous_map } = self;
VariantMap { source, map: move |value: &S::Value| map(previous_map(value)) }
}
pub fn snapshot(&self) -> U
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
U: Clone,
{
(self.map)(&self.source.snapshot())
}
}
impl<V, F, U> VariantMap<Variant<V>, F>
where
V: 'static,
U: Sized + 'static,
F: Fn(&V) -> U + Clone + 'static,
{
pub fn into_provider(self) -> Provider {
let VariantMap { source, map } = self;
match source {
Variant::Value(v) => Provider::new(map(&v)),
Variant::Watcher(w) => {
Provider::watcher(w.part_watcher(move |v| PartRef::from_value(map(v))))
}
}
}
}
impl<S, F> crate::widget_children::sealed::IntoXChild<'static, SingleKind> for VariantMap<S, F>
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
F: Fn(&S::Value) -> XSingleChild<'static> + 'static,
{
fn into_x_child(self) -> XSingleChild<'static> {
let VariantMap { source, map } = self;
if let Some(modifies) = source.modifies() {
let trigger = modifies
.merge(Local::of(ModifyInfo::default()))
.box_it();
Pipe::new(trigger, move |_| map(&source.snapshot())).into_single_child()
} else {
map(&source.snapshot())
}
}
}
impl<S, F> crate::widget_children::sealed::IntoXChild<'static, MultiKind> for VariantMap<S, F>
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
F: Fn(&S::Value) -> XMultiChild<'static> + 'static,
{
fn into_x_child(self) -> XMultiChild<'static> {
let VariantMap { source, map } = self;
if let Some(modifies) = source.modifies() {
let trigger = modifies
.merge(Local::of(ModifyInfo::default()))
.box_it();
Pipe::new(trigger, move |_| map(&source.snapshot())).into_multi_child()
} else {
map(&source.snapshot())
}
}
}
impl<S, F, U> VariantSource for VariantMap<S, F>
where
S: VariantSource,
U: 'static,
F: Fn(&S::Value) -> U + 'static,
{
type Value = U;
fn modifies(&self) -> Option<LocalBoxedObservableClone<'static, ModifyInfo, Infallible>> {
self.source.modifies()
}
}
impl<S, F, U> VariantSnapshot for VariantMap<S, F>
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
U: Clone + 'static,
F: Fn(&S::Value) -> U + 'static,
{
fn snapshot(&self) -> Self::Value { (self.map)(&self.source.snapshot()) }
}
impl<L, R> VariantSource for Combine<L, R>
where
L: VariantSource,
R: VariantSource,
{
type Value = (L::Value, R::Value);
fn modifies(&self) -> Option<LocalBoxedObservableClone<'static, ModifyInfo, Infallible>> {
match (self.left.modifies(), self.right.modifies()) {
(Some(left), Some(right)) => Some(left.merge(right).box_it_clone()),
(Some(left), None) => Some(left),
(None, Some(right)) => Some(right),
(None, None) => None,
}
}
}
impl<L, R> VariantSnapshot for Combine<L, R>
where
L: VariantSnapshot,
<L as VariantSource>::Value: Clone,
R: VariantSnapshot,
<R as VariantSource>::Value: Clone,
{
fn snapshot(&self) -> Self::Value { (self.left.snapshot(), self.right.snapshot()) }
}
pub struct VariantKind<K: ?Sized>(PhantomData<fn() -> K>);
impl<S, U, K: ?Sized + 'static> RFrom<S, VariantKind<K>> for PipeValue<U>
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
U: RFrom<S::Value, K> + 'static,
{
fn r_from(value: S) -> Self { value.into_pipe_value().map(U::r_from) }
}
impl<V: Clone + 'static> Clone for Variant<V> {
fn clone(&self) -> Self {
match self {
Variant::Watcher(value) => Variant::Watcher(value.clone_boxed_watcher()),
Variant::Value(value) => Variant::Value(value.clone()),
}
}
}
impl<V, K: ?Sized> RFrom<Variant<V>, OtherWidget<K>> for Widget<'static>
where
V: RInto<Widget<'static>, K> + Clone + 'static,
{
fn r_from(value: Variant<V>) -> Self {
match value {
Variant::Watcher(w) => pipe!($read(w).clone().r_into()).into_widget(),
Variant::Value(v) => v.r_into(),
}
}
}
impl<S, F, U, K: ?Sized> RFrom<VariantMap<S, F>, OtherWidget<K>> for Widget<'static>
where
S: VariantSnapshot,
<S as VariantSource>::Value: Clone,
F: Fn(&S::Value) -> U + 'static,
U: RInto<Widget<'static>, K> + 'static,
{
fn r_from(value: VariantMap<S, F>) -> Self {
let VariantMap { source, map } = value;
if let Some(modifies) = source.modifies() {
let trigger = modifies
.merge(Local::of(ModifyInfo::default()))
.box_it();
let pipe = Pipe::new(trigger, move |_| map(&source.snapshot()));
pipe.build_single()
} else {
map(&source.snapshot()).r_into()
}
}
}