Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Use associated types in views
Erik Kundt committed 2 years ago
commit 166add7780136bcb0a1dc07f5bdbd4a2eb361075
parent 956ca70f4a43013e1556ac020a735b9c4fd016d4
6 files changed +60 -31
modified src/store.rs
@@ -13,13 +13,15 @@ const STORE_TICK_RATE: Duration = Duration::from_millis(1000);

/// The `State` known to the application store. It handles user-defined
/// application messages as well as ticks.
-
pub trait State<A, P>
+
pub trait State<P>
where
    P: Clone + Debug + Send + Sync,
{
+
    type Action;
+

    /// Handle a user-defined application message and return an `Exit` object
    /// in case the received message requested the application to also quit.
-
    fn handle_action(&mut self, action: A) -> Option<Exit<P>>;
+
    fn handle_action(&mut self, action: Self::Action) -> Option<Exit<P>>;

    /// Handle recurring tick.
    fn tick(&self);
@@ -29,7 +31,7 @@ where
/// messages coming from the frontend and updates the state accordingly.
pub struct Store<A, S, P>
where
-
    S: State<A, P> + Clone + Send + Sync,
+
    S: State<P> + Clone + Send + Sync,
    P: Clone + Debug + Send + Sync,
{
    state_tx: UnboundedSender<S>,
@@ -38,7 +40,7 @@ where

impl<A, S, P> Store<A, S, P>
where
-
    S: State<A, P> + Clone + Send + Sync,
+
    S: State<P> + Clone + Send + Sync,
    P: Clone + Debug + Send + Sync,
{
    pub fn new() -> (Self, UnboundedReceiver<S>) {
@@ -56,7 +58,7 @@ where

impl<A, S, P> Store<A, S, P>
where
-
    S: State<A, P> + Clone + Debug + Send + Sync + 'static,
+
    S: State<P, Action = A> + Clone + Debug + Send + Sync + 'static,
    P: Clone + Debug + Send + Sync + 'static,
{
    /// By calling `main_loop`, the store will wait for new messages coming
modified src/ui.rs
@@ -71,8 +71,8 @@ impl<A> Frontend<A> {
        mut interrupt_rx: broadcast::Receiver<Interrupted<P>>,
    ) -> anyhow::Result<Interrupted<P>>
    where
-
        S: State<A, P>,
-
        W: Widget<Backend, S, A>,
+
        S: State<P>,
+
        W: Widget<Backend, State = S, Action = A>,
        P: Clone + Send + Sync + Debug,
    {
        let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
modified src/ui/widget.rs
@@ -18,7 +18,7 @@ use ratatui::widgets::{Cell, Row, TableState};
use super::theme::style;
use super::{layout, span};

-
pub type BoxedWidget<B, S, A> = Box<dyn Widget<B, S, A>>;
+
pub type BoxedWidget<B, S, A> = Box<dyn Widget<B, State = S, Action = A>>;

pub type UpdateCallback<S> = fn(&S) -> Box<dyn Any>;
pub type EventCallback<A> = fn(&dyn Any, UnboundedSender<A>);
@@ -36,15 +36,18 @@ pub struct BaseView<S, A> {
/// Main trait defining a `View` behaviour.
///
/// This is the first trait that you should implement to define a custom `Widget`.
-
pub trait View<S, A> {
+
pub trait View {
+
    type State;
+
    type Action;
+

    /// Should return a new view with props build from state (if type is known) and a
    /// message sender set.
-
    fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
+
    fn new(state: &Self::State, action_tx: UnboundedSender<Self::Action>) -> Self
    where
        Self: Sized;

    /// Should set the optional custom event handler.
-
    fn on_event(mut self, callback: EventCallback<A>) -> Self
+
    fn on_event(mut self, callback: EventCallback<Self::Action>) -> Self
    where
        Self: Sized,
    {
@@ -53,7 +56,7 @@ pub trait View<S, A> {
    }

    /// Should set the optional update handler.
-
    fn on_update(mut self, callback: UpdateCallback<S>) -> Self
+
    fn on_update(mut self, callback: UpdateCallback<Self::State>) -> Self
    where
        Self: Sized,
    {
@@ -70,7 +73,7 @@ pub trait View<S, A> {
    }

    /// Return a mutable reference to this widgets' base view.
-
    fn base_mut(&mut self) -> &mut BaseView<S, A>;
+
    fn base_mut(&mut self) -> &mut BaseView<Self::State, Self::Action>;

    /// Should handle key events and call `handle_event` on all children.
    ///
@@ -88,13 +91,13 @@ pub trait View<S, A> {
    /// construct and update the internal props. If it is not set, app widgets can construct
    /// props directly via their state converters, whereas library widgets can just fallback
    /// to their current props.
-
    fn update(&mut self, state: &S);
+
    fn update(&mut self, state: &Self::State);
}

/// A `Widget` is a `View` that can be rendered using a specific backend.
///
/// This is the second trait that you should implement to define a custom `Widget`.
-
pub trait Widget<B, S, A>: View<S, A>
+
pub trait Widget<B>: View
where
    B: Backend,
{
@@ -179,11 +182,14 @@ where
    }
}

-
impl<'a: 'static, B, S, A, Id> View<S, A> for Window<B, S, A, Id>
+
impl<'a: 'static, B, S, A, Id> View for Window<B, S, A, Id>
where
    B: Backend + 'a,
    Id: Clone + Hash + Eq + PartialEq + 'a,
{
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self
    where
        Self: Sized,
@@ -230,7 +236,7 @@ where
    }
}

-
impl<'a: 'static, B, S, A, Id> Widget<B, S, A> for Window<B, S, A, Id>
+
impl<'a: 'static, B, S, A, Id> Widget<B> for Window<B, S, A, Id>
where
    B: Backend + 'a,
    Id: Clone + Hash + Eq + PartialEq + 'a,
@@ -310,7 +316,10 @@ impl<S, A> Shortcuts<S, A> {
    }
}

-
impl<S, A> View<S, A> for Shortcuts<S, A> {
+
impl<S, A> View for Shortcuts<S, A> {
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self {
        Self {
            base: BaseView {
@@ -334,7 +343,7 @@ impl<S, A> View<S, A> for Shortcuts<S, A> {
    }
}

-
impl<B, S, A> Widget<B, S, A> for Shortcuts<S, A>
+
impl<B, S, A> Widget<B> for Shortcuts<S, A>
where
    B: Backend,
{
@@ -540,10 +549,13 @@ where
    }
}

-
impl<'a: 'static, S, A, R> View<S, A> for Table<'a, S, A, R>
+
impl<'a: 'static, S, A, R> View for Table<'a, S, A, R>
where
    R: ToRow + Clone + 'static,
{
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self {
        Self {
            base: BaseView {
@@ -603,7 +615,7 @@ where
    }
}

-
impl<'a: 'static, B, S, A, R> Widget<B, S, A> for Table<'a, S, A, R>
+
impl<'a: 'static, B, S, A, R> Widget<B> for Table<'a, S, A, R>
where
    B: Backend,
    R: ToRow + Clone + Debug + 'static,
modified src/ui/widget/container.rs
@@ -77,7 +77,10 @@ impl<'a, S, A> Header<'a, S, A> {
    }
}

-
impl<'a: 'static, S, A> View<S, A> for Header<'a, S, A> {
+
impl<'a: 'static, S, A> View for Header<'a, S, A> {
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self {
        Self {
            base: BaseView {
@@ -108,7 +111,7 @@ impl<'a: 'static, S, A> View<S, A> for Header<'a, S, A> {
    }
}

-
impl<'a: 'static, B, S, A> Widget<B, S, A> for Header<'a, S, A>
+
impl<'a: 'static, B, S, A> Widget<B> for Header<'a, S, A>
where
    B: Backend,
{
@@ -234,7 +237,10 @@ impl<'a, S, A> Footer<'a, S, A> {
    }
}

-
impl<'a: 'static, S, A> View<S, A> for Footer<'a, S, A> {
+
impl<'a: 'static, S, A> View for Footer<'a, S, A> {
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self {
        Self {
            base: BaseView {
@@ -289,7 +295,7 @@ impl<'a, S, A> Footer<'a, S, A> {
    }
}

-
impl<'a: 'static, B, S, A> Widget<B, S, A> for Footer<'a, S, A>
+
impl<'a: 'static, B, S, A> Widget<B> for Footer<'a, S, A>
where
    B: Backend,
{
@@ -386,10 +392,13 @@ where
    }
}

-
impl<B, S, A> View<S, A> for Container<B, S, A>
+
impl<B, S, A> View for Container<B, S, A>
where
    B: Backend,
{
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self
    where
        Self: Sized,
@@ -436,7 +445,7 @@ where
    }
}

-
impl<'a: 'static, B, S, A> Widget<B, S, A> for Container<B, S, A>
+
impl<'a: 'static, B, S, A> Widget<B> for Container<B, S, A>
where
    B: Backend,
{
modified src/ui/widget/input.rs
@@ -126,7 +126,10 @@ impl<S, A> TextField<S, A> {
    }
}

-
impl<S, A> View<S, A> for TextField<S, A> {
+
impl<S, A> View for TextField<S, A> {
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self {
        Self {
            base: BaseView {
@@ -186,7 +189,7 @@ impl<S, A> View<S, A> for TextField<S, A> {
    }
}

-
impl<B, S, A> Widget<B, S, A> for TextField<S, A>
+
impl<B, S, A> Widget<B> for TextField<S, A>
where
    B: Backend,
{
modified src/ui/widget/text.rs
@@ -142,7 +142,10 @@ impl<'a, S, A> Paragraph<'a, S, A> {
    }
}

-
impl<'a: 'static, S, A> View<S, A> for Paragraph<'a, S, A> {
+
impl<'a: 'static, S, A> View for Paragraph<'a, S, A> {
+
    type Action = A;
+
    type State = S;
+

    fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self
    where
        Self: Sized,
@@ -202,7 +205,7 @@ impl<'a: 'static, S, A> View<S, A> for Paragraph<'a, S, A> {
    }
}

-
impl<'a: 'static, B, S, A> Widget<B, S, A> for Paragraph<'a, S, A>
+
impl<'a: 'static, B, S, A> Widget<B> for Paragraph<'a, S, A>
where
    B: Backend,
{