Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Restructure and rename rmUI / imUI modules
Erik Kundt committed 1 year ago
commit 4b48599019639d4123276a479adb6f8a9bc30236
parent 0e45703fa767de3c21612390a636451d715a6a0d
8 files changed +116 -111
modified examples/basic.rs
@@ -104,7 +104,7 @@ pub async fn main() -> Result<()> {
        })
        .on_update(|_| WindowProps::default().current_page(0).to_boxed_any().into());

-
    tui::run(channel, state, window).await?;
+
    tui::rm(channel, state, window).await?;

    Ok(())
}
modified examples/hello.rs
@@ -68,7 +68,7 @@ pub async fn main() -> Result<()> {
                .into()
        });

-
    tui::run(channel, state, scene).await?;
+
    tui::rm(channel, state, scene).await?;

    Ok(())
}
modified src/lib.rs
@@ -16,8 +16,7 @@ use anyhow::Result;
use store::State;
use task::Interrupted;
use ui::im;
-
use ui::rm::widget::Widget;
-
use ui::Frontend;
+
use ui::rm;

/// An optional return value.
#[derive(Clone, Debug)]
@@ -151,7 +150,11 @@ impl<A> Default for Channel<A> {
/// Initialize a `Store` with the `State` given and a `Frontend` with the `Widget` given,
/// and run their main loops concurrently. Connect them to the `Channel` and also to
/// an interrupt broadcast channel also initialized in this function.
-
pub async fn run<S, M, P>(channel: Channel<M>, state: S, root: Widget<S, M>) -> Result<Option<P>>
+
pub async fn rm<S, M, P>(
+
    channel: Channel<M>,
+
    state: S,
+
    root: rm::widget::Widget<S, M>,
+
) -> Result<Option<P>>
where
    S: State<P, Message = M> + Clone + Debug + Send + Sync + 'static,
    M: 'static,
@@ -160,11 +163,11 @@ where
    let (terminator, mut interrupt_rx) = task::create_termination();

    let (store, state_rx) = store::Store::<S, M, P>::new();
-
    let frontend = Frontend::default();
+
    let frontend = rm::Frontend::default();

    tokio::try_join!(
-
        store.main_loop(state, terminator, channel.rx, interrupt_rx.resubscribe()),
-
        frontend.main_loop(root, state_rx, interrupt_rx.resubscribe()),
+
        store.run(state, terminator, channel.rx, interrupt_rx.resubscribe()),
+
        frontend.run(root, state_rx, interrupt_rx.resubscribe()),
    )?;

    if let Ok(reason) = interrupt_rx.recv().await {
@@ -177,7 +180,10 @@ where
    }
}

-
pub async fn run_im<S, M, P>(
+
/// Initialize a `Store` with the `State` given and a `Frontend` with the `App` given,
+
/// and run their main loops concurrently. Connect them to the `Channel` and also to
+
/// an interrupt broadcast channel also initialized in this function.
+
pub async fn im<S, M, P>(
    channel: Channel<M>,
    state: S,
    app: impl im::App<State = S, Message = M>,
@@ -193,8 +199,8 @@ where
    let frontend = im::Frontend::default();

    tokio::try_join!(
-
        store.main_loop(state, terminator, channel.rx, interrupt_rx.resubscribe()),
-
        frontend.main_loop(app, state_rx, interrupt_rx.resubscribe()),
+
        store.run(state, terminator, channel.rx, interrupt_rx.resubscribe()),
+
        frontend.run(app, state_rx, interrupt_rx.resubscribe()),
    )?;

    if let Ok(reason) = interrupt_rx.recv().await {
modified src/store.rs
@@ -65,7 +65,7 @@ where
    /// from the frontend and update the applications' state accordingly. It will
    /// also tick with the defined `STORE_TICK_RATE`.
    /// Updated states are then being send to the state message channel.
-
    pub async fn main_loop(
+
    pub async fn run(
        self,
        mut state: S,
        mut terminator: Terminator<P>,
modified src/ui.rs
@@ -4,100 +4,3 @@ pub mod layout;
pub mod rm;
pub mod span;
pub mod theme;
-

-
use std::fmt::Debug;
-
use std::time::Duration;
-

-
use tokio::sync::broadcast;
-
use tokio::sync::mpsc::UnboundedReceiver;
-

-
use self::rm::widget::RenderProps;
-
use self::rm::widget::Widget;
-

-
use super::event::Event;
-
use super::store::State;
-
use super::task::Interrupted;
-
use super::terminal;
-

-
const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
-
const INLINE_HEIGHT: usize = 20;
-

-
pub const RENDER_WIDTH_XSMALL: usize = 50;
-
pub const RENDER_WIDTH_SMALL: usize = 70;
-
pub const RENDER_WIDTH_MEDIUM: usize = 150;
-
pub const RENDER_WIDTH_LARGE: usize = usize::MAX;
-

-
/// The `Frontend` runs an applications' view concurrently. It handles
-
/// terminal events as well as state updates and renders the view accordingly.
-
///
-
/// Once created and run with `main_loop`, the `Frontend` will wait for new messages
-
/// being sent on either the terminal event, the state or the interrupt message channel.
-
#[derive(Default)]
-
pub struct Frontend {}
-

-
impl Frontend {
-
    /// By calling `main_loop`, the `Frontend` will wait for new messages being sent
-
    /// on either the terminal event, the state or the interrupt message channel.
-
    /// After all, it will draw the (potentially) updated root widget.
-
    ///
-
    /// Terminal event messages are being sent by a thread polling `stdin` for new user input
-
    /// and another thread polling UNIX signals, e.g. `SIGWINCH` when the terminal
-
    /// window size is being changed. Terminal events are then passed to the root widget
-
    /// of the application.
-
    ///
-
    /// State messages are being sent by the applications' `Store`. Received state updates
-
    /// will be passed to the root widget as well.
-
    ///
-
    /// Interrupt messages are being sent to broadcast channel for retrieving the
-
    /// application kill signal.
-
    pub async fn main_loop<S, M, P>(
-
        self,
-
        mut root: Widget<S, M>,
-
        mut state_rx: UnboundedReceiver<S>,
-
        mut interrupt_rx: broadcast::Receiver<Interrupted<P>>,
-
    ) -> anyhow::Result<Interrupted<P>>
-
    where
-
        S: State<P> + 'static,
-
        M: 'static,
-
        P: Clone + Send + Sync + Debug,
-
    {
-
        let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
-

-
        let mut terminal = terminal::setup(INLINE_HEIGHT)?;
-
        let mut events_rx = terminal::events();
-

-
        let mut root = {
-
            let state = state_rx.recv().await.unwrap();
-

-
            root.update(&state);
-
            root
-
        };
-

-
        let result: anyhow::Result<Interrupted<P>> = loop {
-
            tokio::select! {
-
                // Tick to terminate the select every N milliseconds
-
                _ = ticker.tick() => (),
-
                Some(event) = events_rx.recv() => match event {
-
                    Event::Key(key) => root.handle_event(key),
-
                    Event::Resize => (),
-
                },
-
                // Handle state updates
-
                Some(state) = state_rx.recv() => {
-
                    root.update(&state);
-
                },
-
                // Catch and handle interrupt signal to gracefully shutdown
-
                Ok(interrupted) = interrupt_rx.recv() => {
-
                    let size = terminal.get_frame().size();
-
                    let _ = terminal.set_cursor(size.x, size.y);
-

-
                    break Ok(interrupted);
-
                }
-
            }
-
            terminal.draw(|frame| root.render(RenderProps::from(frame.size()), frame))?;
-
        };
-

-
        terminal::restore(&mut terminal)?;
-

-
        result
-
    }
-
}
modified src/ui/im.rs
@@ -40,7 +40,7 @@ pub trait App {
pub struct Frontend {}

impl Frontend {
-
    pub async fn main_loop<S, M, P>(
+
    pub async fn run<S, M, P>(
        self,
        app: impl App<State = S, Message = M>,
        mut state_rx: UnboundedReceiver<S>,
modified src/ui/rm.rs
@@ -1 +1,97 @@
pub mod widget;
+

+
use std::fmt::Debug;
+
use std::time::Duration;
+

+
use tokio::sync::broadcast;
+
use tokio::sync::mpsc::UnboundedReceiver;
+

+
use crate::event::Event;
+
use crate::store::State;
+
use crate::task::Interrupted;
+
use crate::terminal;
+
use crate::ui::rm::widget::RenderProps;
+
use crate::ui::rm::widget::Widget;
+

+
const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
+
const INLINE_HEIGHT: usize = 20;
+

+
pub const RENDER_WIDTH_XSMALL: usize = 50;
+
pub const RENDER_WIDTH_SMALL: usize = 70;
+
pub const RENDER_WIDTH_MEDIUM: usize = 150;
+
pub const RENDER_WIDTH_LARGE: usize = usize::MAX;
+

+
/// The `Frontend` runs an applications' view concurrently. It handles
+
/// terminal events as well as state updates and renders the view accordingly.
+
///
+
/// Once created and run with `main_loop`, the `Frontend` will wait for new messages
+
/// being sent on either the terminal event, the state or the interrupt message channel.
+
#[derive(Default)]
+
pub struct Frontend {}
+

+
impl Frontend {
+
    /// By calling `main_loop`, the `Frontend` will wait for new messages being sent
+
    /// on either the terminal event, the state or the interrupt message channel.
+
    /// After all, it will draw the (potentially) updated root widget.
+
    ///
+
    /// Terminal event messages are being sent by a thread polling `stdin` for new user input
+
    /// and another thread polling UNIX signals, e.g. `SIGWINCH` when the terminal
+
    /// window size is being changed. Terminal events are then passed to the root widget
+
    /// of the application.
+
    ///
+
    /// State messages are being sent by the applications' `Store`. Received state updates
+
    /// will be passed to the root widget as well.
+
    ///
+
    /// Interrupt messages are being sent to broadcast channel for retrieving the
+
    /// application kill signal.
+
    pub async fn run<S, M, P>(
+
        self,
+
        mut root: Widget<S, M>,
+
        mut state_rx: UnboundedReceiver<S>,
+
        mut interrupt_rx: broadcast::Receiver<Interrupted<P>>,
+
    ) -> anyhow::Result<Interrupted<P>>
+
    where
+
        S: State<P> + 'static,
+
        M: 'static,
+
        P: Clone + Send + Sync + Debug,
+
    {
+
        let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
+

+
        let mut terminal = terminal::setup(INLINE_HEIGHT)?;
+
        let mut events_rx = terminal::events();
+

+
        let mut root = {
+
            let state = state_rx.recv().await.unwrap();
+

+
            root.update(&state);
+
            root
+
        };
+

+
        let result: anyhow::Result<Interrupted<P>> = loop {
+
            tokio::select! {
+
                // Tick to terminate the select every N milliseconds
+
                _ = ticker.tick() => (),
+
                Some(event) = events_rx.recv() => match event {
+
                    Event::Key(key) => root.handle_event(key),
+
                    Event::Resize => (),
+
                },
+
                // Handle state updates
+
                Some(state) = state_rx.recv() => {
+
                    root.update(&state);
+
                },
+
                // Catch and handle interrupt signal to gracefully shutdown
+
                Ok(interrupted) = interrupt_rx.recv() => {
+
                    let size = terminal.get_frame().size();
+
                    let _ = terminal.set_cursor(size.x, size.y);
+

+
                    break Ok(interrupted);
+
                }
+
            }
+
            terminal.draw(|frame| root.render(RenderProps::from(frame.size()), frame))?;
+
        };
+

+
        terminal::restore(&mut terminal)?;
+

+
        result
+
    }
+
}
modified src/ui/rm/widget/container.rs
@@ -8,7 +8,7 @@ use ratatui::widgets::{Block, BorderType, Borders, Row};

use crate::ui::ext::{FooterBlock, FooterBlockType, HeaderBlock};
use crate::ui::theme::{style, Theme};
-
use crate::ui::{RENDER_WIDTH_LARGE, RENDER_WIDTH_MEDIUM, RENDER_WIDTH_SMALL};
+
use crate::ui::rm::{RENDER_WIDTH_LARGE, RENDER_WIDTH_MEDIUM, RENDER_WIDTH_SMALL};

use super::{PredefinedLayout, RenderProps, View, ViewProps, ViewState, Widget};