mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-10 05:30:43 +08:00
lib: Use associated types in views
This commit is contained in:
+7
-5
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+26
-14
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user