From f11f1ac9a01ae82664b23fc6a6132d1c858036b9 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Sun, 10 Mar 2024 20:15:03 +0100 Subject: [PATCH] Add common traits to views and related (e.g. Animatable) Also remove `WithState` trait as it's unnecessary and can be added in `ViewExt` --- src/geometry.rs | 2 +- src/view.rs | 44 ++++++++++++++++++++++++++++++- src/view/animatables.rs | 7 +++-- src/view/border.rs | 2 +- src/view/events.rs | 7 +++++ src/view/fill_max_size.rs | 2 ++ src/view/linear_layout.rs | 1 + src/view/margin.rs | 2 ++ src/view/scroll_view.rs | 1 + src/view/use_state.rs | 55 ++++++--------------------------------- 10 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/geometry.rs b/src/geometry.rs index 666874d..4177862 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -7,7 +7,7 @@ use std::ops::Range; /// Most often used by widgets to describe /// the direction in which they grow as their number of children increases. /// Has some methods for manipulating geometry with respect to the axis. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Axis { /// The x axis Horizontal, diff --git a/src/view.rs b/src/view.rs index c0ddb5d..b2c1f4e 100644 --- a/src/view.rs +++ b/src/view.rs @@ -12,7 +12,7 @@ mod text; mod use_state; mod weighted_linear_layout; -use std::marker::PhantomData; +use std::{marker::PhantomData, sync::Arc}; use ratatui::style::{Color, Style}; pub use xilem_core::{Id, IdPath, VecSplice}; @@ -61,6 +61,48 @@ pub trait ViewExt: View + Sized { } } + /// Compose a view with added local state. + /// The local state is added within a closure additional to the app state via a tuple. + /// It's initialized with the first closure. + /// + /// # Examples + /// + /// ```ignore + /// fn with_counter + Clickable>(view: V) -> impl View { + /// view.with_state( + /// || 42, + /// |view, local_state| { + /// v_stack(( + /// format!("Click the view below to increment this: {local_state}"), + /// view.on_click(|(_app_state, local_state): &mut (Handle, i32)| { + /// *local_state += 1; + /// }), + /// )) + /// }, + /// ) + /// } + /// ``` + fn with_state( + self, + init: Finit, + view_factory: F, + ) -> WithLocalState + where + Vi: View, + Self: Into>, + S: Send, + Finit: Fn() -> S + Send + Sync, + Vo: View<(Handle, S), A>, + F: Fn(HandleState, &mut S) -> Vo + Send + Sync, + { + WithLocalState { + init, + view: self.into(), + view_factory, + phantom: PhantomData, + } + } + /// # Examples /// ``` /// # use trui::*; diff --git a/src/view/animatables.rs b/src/view/animatables.rs index 7bbc57a..a164365 100644 --- a/src/view/animatables.rs +++ b/src/view/animatables.rs @@ -42,7 +42,7 @@ pub trait Animatable: Send + Sync { ) -> MessageResult<()>; // TODO different type (AnimationMessage?) } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Lerp { tweenable: T, ratio: R, @@ -108,7 +108,7 @@ impl, R: Animatable> Animatable for Lerp { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub struct LowPassIIR { decay: f64, target: AT, @@ -463,6 +463,7 @@ impl_tweenable_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7; 0, 1, 2, 3, 4, 5, 6, 7 impl_tweenable_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8; 0, 1, 2, 3, 4, 5, 6, 7, 8); impl_tweenable_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Map { input: T, f: fn(&V) -> VO, @@ -505,6 +506,7 @@ impl> Tween } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct MapEase { input: T, f: fn(f64) -> f64, @@ -548,6 +550,7 @@ impl> Tweenable for MapEase { // TODO should this also be used within other animatables directly (not just Tweenable)? // TODO Duration could be animated too /// Overrides the duration of any tweenable it composes +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct WithDuration { pub(crate) tweenable: T, pub(crate) duration: Duration, diff --git a/src/view/border.rs b/src/view/border.rs index 528d2d9..c0c9856 100644 --- a/src/view/border.rs +++ b/src/view/border.rs @@ -9,7 +9,7 @@ use super::{BorderKind, Borders, Cx, Styleable, View, ViewMarker}; use ratatui::style::{Color, Style}; use xilem_core::MessageResult; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Border { pub(crate) content: V, pub(crate) borders: Borders, diff --git a/src/view/events.rs b/src/view/events.rs index 854c6af..5eed343 100644 --- a/src/view/events.rs +++ b/src/view/events.rs @@ -130,6 +130,7 @@ impl, E2: EventHandler { Begin(E), Update(E), @@ -200,6 +201,7 @@ impl StreamEventHandlerState { } } +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct StreamEventHandler { #[allow(clippy::complexity)] phantom: PhantomData (T, A, E, S)>, @@ -281,6 +283,7 @@ where } } +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DeferEventHandler { #[allow(clippy::complexity)] phantom: PhantomData (T, A, FO, F)>, @@ -376,6 +379,7 @@ impl_callback_event_handler!(widget::MouseEvent); // TODO some description // TODO Is this view useful at all? Should this be already abstracted (e.g. via the other views such as Hoverable, or Clickable) +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct OnMouse { pub(crate) view: V, pub(crate) catch_event: CatchMouseButton, @@ -462,6 +466,7 @@ where macro_rules! styled_event_views { ($($name:ident),*) => { $( + #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct $name { pub(crate) view: V, pub(crate) style: Style, @@ -630,6 +635,7 @@ styled_event_views!(StyleOnHover, StyleOnPressed); macro_rules! event_views { ($($name:ident),*) => { $( + #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct $name { pub(crate) view: V, pub(crate) event_handler: EH, @@ -742,6 +748,7 @@ macro_rules! event_views { event_views!(OnHover, OnHoverLost); // TODO this should probably be generated by the macro above (but for better IDE experience and easier prototyping this not yet) +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct OnClick { pub(crate) view: V, pub(crate) event_handler: EH, diff --git a/src/view/fill_max_size.rs b/src/view/fill_max_size.rs index d666daa..381134f 100644 --- a/src/view/fill_max_size.rs +++ b/src/view/fill_max_size.rs @@ -7,6 +7,7 @@ use crate::{ Animatable, Cx, Fill, View, ViewMarker, }; +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FillMaxSize { pub(crate) content: V, // TODO making this animatable would be great too @@ -113,6 +114,7 @@ impl, V: View> View for FillMaxSize { pub fill: Fill, pub percent: P, diff --git a/src/view/linear_layout.rs b/src/view/linear_layout.rs index 15038b7..48a85f4 100644 --- a/src/view/linear_layout.rs +++ b/src/view/linear_layout.rs @@ -6,6 +6,7 @@ use crate::{ use std::{any::Any, marker::PhantomData}; use xilem_core::{Id, VecSplice}; +#[derive(Clone, Copy, Debug, PartialEq)] pub struct LinearLayout { children: VT, axis: Axis, diff --git a/src/view/margin.rs b/src/view/margin.rs index 67b5c70..729706a 100644 --- a/src/view/margin.rs +++ b/src/view/margin.rs @@ -7,6 +7,7 @@ use crate::{ Cx, Position, View, ViewMarker, }; +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Margin { pub(crate) content: V, pub(crate) amount: u16, @@ -61,6 +62,7 @@ impl> View for Margin { } } +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct MarginStyle { pub amount: u16, pub position: Position, diff --git a/src/view/scroll_view.rs b/src/view/scroll_view.rs index 0e21e2e..3d3bbf6 100644 --- a/src/view/scroll_view.rs +++ b/src/view/scroll_view.rs @@ -6,6 +6,7 @@ use xilem_core::{Id, MessageResult}; use super::{Cx, ViewMarker, ViewSequence}; +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ScrollView { child: C, phantom: PhantomData (T, A)>, diff --git a/src/view/use_state.rs b/src/view/use_state.rs index 6ed0757..96581f6 100644 --- a/src/view/use_state.rs +++ b/src/view/use_state.rs @@ -12,6 +12,7 @@ use crate::{widget::ChangeFlags, Cx, View, ViewMarker}; /// This Handle is a workaround to erase the lifetime of &mut T, /// it can only be constructed in contexts, /// where it is actually safe to use (such as UseState) +#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct Handle(*mut T); impl Deref for Handle { @@ -37,6 +38,7 @@ impl DerefMut for Handle { /// not rebuild). The second callback takes that state as an argument. It /// is not passed the app state, but since that state is `Rc`, it would be /// natural to clone it and capture it in a `move` closure. +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct UseState { f_init: FInit, f: F, @@ -126,49 +128,7 @@ where UseState::new(f_init, f) } -pub trait WithState>: Into> { - /// Compose a view with added local state. - /// The local state is added within a closure additional to the app state via a tuple. - /// It's initialized with the first closure. - /// - /// # Examples - /// - /// ```ignore - /// fn with_counter + Clickable>(view: V) -> impl View { - /// view.with_state( - /// || 42, - /// |view, local_state| { - /// v_stack(( - /// format!("Click the view below to increment this: {local_state}"), - /// view.on_click(|(_app_state, local_state): &mut (Handle, i32)| { - /// *local_state += 1; - /// }), - /// )) - /// }, - /// ) - /// } - /// ``` - fn with_state< - S: Send, - Finit: Fn() -> S + Send + Sync, - Vo: View<(Handle, S), A>, - F: Fn(HandleState, &mut S) -> Vo + Send + Sync, - >( - self, - init: Finit, - view_factory: F, - ) -> WithLocalState { - WithLocalState { - init, - view: self.into(), - view_factory, - phantom: PhantomData, - } - } -} - -impl, V: Into>> WithState for V {} - +#[derive(Default, Clone, Debug, PartialEq, Eq, Hash)] pub struct HandleState(Arc); impl ViewMarker for HandleState {} @@ -204,11 +164,12 @@ impl, S> View<(Handle, S), A> for HandleState { } } +#[derive(Default, Clone, Debug, PartialEq, Eq, Hash)] pub struct WithLocalState { - init: Finit, - view: Arc, - view_factory: F, - phantom: PhantomData (Vi, Vo)>, + pub(crate) init: Finit, + pub(crate) view: Arc, + pub(crate) view_factory: F, + pub(crate) phantom: PhantomData (Vi, Vo)>, } impl ViewMarker for WithLocalState {}