Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
bin: Use common window widget
Erik Kundt committed 2 years ago
commit b56c9f01e6e02cb6d873799e9803f0059aecb690
parent 102cd0226f7710a7e41634d10af88f0c45e5881e
6 files changed +75 -317
modified bin/commands/inbox/select.rs
@@ -18,14 +18,16 @@ use tui::cob::inbox::{self};
use tui::store;
use tui::store::StateValue;
use tui::task::{self, Interrupted};
-
use tui::terminal;
+
use tui::terminal::Backend;
use tui::ui::items::{Filter, NotificationItem, NotificationItemFilter};
+
use tui::ui::widget::{Properties, View, Window, WindowProps};
use tui::ui::Frontend;
use tui::Exit;

use tui::PageStack;

-
use crate::tui_inbox::select::ui::Window;
+
use self::ui::BrowsePage;
+
use self::ui::HelpPage;

use super::common::{Mode, RepositoryMode};

@@ -274,15 +276,27 @@ impl App {
    pub async fn run(&self) -> Result<Option<Selection>> {
        let (terminator, mut interrupt_rx) = task::create_termination();
        let (store, state_rx) = store::Store::<Action, State, Selection>::new();
-
        let (frontend, action_rx) = Frontend::<Action>::new();
+
        let (frontend, action_tx, action_rx) = Frontend::new();
        let state = State::try_from(&self.context)?;

+
        let window: Window<Backend, State, Action, Page> = Window::new(&state, action_tx.clone())
+
            .page(
+
                Page::Browse,
+
                BrowsePage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .page(
+
                Page::Help,
+
                HelpPage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .on_update(|state| {
+
                WindowProps::default()
+
                    .current_page(state.pages.peek().unwrap_or(&Page::Browse).clone())
+
                    .to_boxed()
+
            });
+

        tokio::try_join!(
            store.main_loop(state, terminator, action_rx, interrupt_rx.resubscribe()),
-
            frontend.main_loop::<State, Window<terminal::Backend>, Selection>(
-
                state_rx,
-
                interrupt_rx.resubscribe()
-
            ),
+
            frontend.main_loop(Some(window), state_rx, interrupt_rx.resubscribe()),
        )?;

        if let Ok(reason) = interrupt_rx.recv().await {
modified bin/commands/inbox/select/ui.rs
@@ -30,107 +30,11 @@ use tui::Selection;

use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMode};

-
use super::{Action, Page, State};
+
use super::{Action, State};

type BoxedWidget<B> = widget::BoxedWidget<B, State, Action>;

#[derive(Clone)]
-
pub struct WindowProps {
-
    page: Page,
-
}
-

-
impl From<&State> for WindowProps {
-
    fn from(state: &State) -> Self {
-
        Self {
-
            page: state.pages.peek().unwrap_or(&Page::Browse).clone(),
-
        }
-
    }
-
}
-

-
impl Properties for WindowProps {}
-

-
pub struct Window<B: Backend> {
-
    /// Internal properties
-
    props: WindowProps,
-
    /// Message sender
-
    _action_tx: UnboundedSender<Action>,
-
    /// Custom update handler
-
    on_update: Option<UpdateCallback<State>>,
-
    /// Additional custom event handler
-
    on_event: Option<EventCallback<Action>>,
-
    /// All pages known
-
    pages: HashMap<Page, BoxedWidget<B>>,
-
}
-

-
impl<'a: 'static, B> View<State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn new(state: &State, action_tx: UnboundedSender<Action>) -> Self
-
    where
-
        Self: Sized,
-
    {
-
        Self {
-
            _action_tx: action_tx.clone(),
-
            props: WindowProps::from(state),
-
            pages: HashMap::from([
-
                (
-
                    Page::Browse,
-
                    BrowsePage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
                (
-
                    Page::Help,
-
                    HelpPage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
            ]),
-
            on_update: None,
-
            on_event: None,
-
        }
-
    }
-

-
    fn on_update(mut self, callback: UpdateCallback<State>) -> Self {
-
        self.on_update = Some(callback);
-
        self
-
    }
-

-
    fn on_event(mut self, callback: EventCallback<Action>) -> Self {
-
        self.on_event = Some(callback);
-
        self
-
    }
-

-
    fn update(&mut self, state: &State) {
-
        self.props = WindowProps::from(state);
-

-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.update(state);
-
        }
-
    }
-

-
    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.handle_key_event(key);
-
        }
-
    }
-
}
-

-
impl<'a: 'static, B> Widget<B, State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, props: Option<Box<dyn Any>>) {
-
        let props = props
-
            .and_then(WindowProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
-

-
        let area = frame.size();
-

-
        if let Some(page) = self.pages.get(&props.page) {
-
            page.render(frame, area, None);
-
        }
-
    }
-
}
-

-
#[derive(Clone)]
struct BrowsePageProps<'a> {
    notifications: Vec<NotificationItem>,
    selected: Option<usize>,
@@ -203,7 +107,7 @@ impl<'a> From<&State> for BrowsePageProps<'a> {

impl<'a> Properties for BrowsePageProps<'a> {}

-
struct BrowsePage<'a, B> {
+
pub struct BrowsePage<'a, B> {
    /// Internal properties
    props: BrowsePageProps<'a>,
    /// Message sender
modified bin/commands/issue/select.rs
@@ -15,14 +15,16 @@ use tui::cob::issue;
use tui::store::StateValue;
use tui::task;
use tui::task::Interrupted;
-
use tui::terminal;
+
use tui::terminal::Backend;
use tui::ui::items::{Filter, IssueItem, IssueItemFilter};
+
use tui::ui::widget::{Properties, View, Window, WindowProps};
use tui::ui::Frontend;
use tui::Exit;
use tui::{store, PageStack};

+
use self::ui::{BrowsePage, HelpPage};
+

use super::common::Mode;
-
use super::select::ui::Window;

type Selection = tui::Selection<IssueId>;

@@ -193,15 +195,27 @@ impl App {
    pub async fn run(&self) -> Result<Option<Selection>> {
        let (terminator, mut interrupt_rx) = task::create_termination();
        let (store, state_rx) = store::Store::<Action, State, Selection>::new();
-
        let (frontend, action_rx) = Frontend::<Action>::new();
+
        let (frontend, action_tx, action_rx) = Frontend::new();
        let state = State::try_from(&self.context)?;

+
        let window: Window<Backend, State, Action, Page> = Window::new(&state, action_tx.clone())
+
            .page(
+
                Page::Browse,
+
                BrowsePage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .page(
+
                Page::Help,
+
                HelpPage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .on_update(|state| {
+
                WindowProps::default()
+
                    .current_page(state.pages.peek().unwrap_or(&Page::Browse).clone())
+
                    .to_boxed()
+
            });
+

        tokio::try_join!(
            store.main_loop(state, terminator, action_rx, interrupt_rx.resubscribe()),
-
            frontend.main_loop::<State, Window<terminal::Backend>, Selection>(
-
                state_rx,
-
                interrupt_rx.resubscribe()
-
            ),
+
            frontend.main_loop(Some(window), state_rx, interrupt_rx.resubscribe()),
        )?;

        if let Ok(reason) = interrupt_rx.recv().await {
modified bin/commands/issue/select/ui.rs
@@ -33,107 +33,11 @@ use tui::Selection;
use crate::tui_issue::common::IssueOperation;
use crate::tui_issue::common::Mode;

-
use super::{Action, Page, State};
+
use super::{Action, State};

type BoxedWidget<B> = widget::BoxedWidget<B, State, Action>;

#[derive(Clone)]
-
pub struct WindowProps {
-
    page: Page,
-
}
-

-
impl From<&State> for WindowProps {
-
    fn from(state: &State) -> Self {
-
        Self {
-
            page: state.pages.peek().unwrap_or(&Page::Browse).clone(),
-
        }
-
    }
-
}
-

-
impl Properties for WindowProps {}
-

-
pub struct Window<B: Backend> {
-
    /// Internal properties
-
    props: WindowProps,
-
    /// Message sender
-
    _action_tx: UnboundedSender<Action>,
-
    /// Custom update handler
-
    on_update: Option<UpdateCallback<State>>,
-
    /// Additional custom event handler
-
    on_event: Option<EventCallback<Action>>,
-
    /// All pages known
-
    pages: HashMap<Page, BoxedWidget<B>>,
-
}
-

-
impl<'a: 'static, B> View<State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn new(state: &State, action_tx: UnboundedSender<Action>) -> Self
-
    where
-
        Self: Sized,
-
    {
-
        Self {
-
            _action_tx: action_tx.clone(),
-
            props: WindowProps::from(state),
-
            pages: HashMap::from([
-
                (
-
                    Page::Browse,
-
                    BrowsePage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
                (
-
                    Page::Help,
-
                    HelpPage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
            ]),
-
            on_update: None,
-
            on_event: None,
-
        }
-
    }
-

-
    fn on_update(mut self, callback: UpdateCallback<State>) -> Self {
-
        self.on_update = Some(callback);
-
        self
-
    }
-

-
    fn on_event(mut self, callback: EventCallback<Action>) -> Self {
-
        self.on_event = Some(callback);
-
        self
-
    }
-

-
    fn update(&mut self, state: &State) {
-
        self.props = WindowProps::from(state);
-

-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.update(state);
-
        }
-
    }
-

-
    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.handle_key_event(key);
-
        }
-
    }
-
}
-

-
impl<'a: 'static, B> Widget<B, State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, props: Option<Box<dyn Any>>) {
-
        let props = props
-
            .and_then(WindowProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
-

-
        let area = frame.size();
-

-
        if let Some(page) = self.pages.get(&props.page) {
-
            page.render(frame, area, None);
-
        }
-
    }
-
}
-

-
#[derive(Clone)]
struct BrowsePageProps<'a> {
    mode: Mode,
    issues: Vec<IssueItem>,
@@ -217,7 +121,7 @@ impl<'a> From<&State> for BrowsePageProps<'a> {

impl<'a> Properties for BrowsePageProps<'a> {}

-
struct BrowsePage<'a, B> {
+
pub struct BrowsePage<'a, B> {
    /// Internal properties
    props: BrowsePageProps<'a>,
    /// Message sender
modified bin/commands/patch/select.rs
@@ -15,13 +15,19 @@ use tui::cob::patch;
use tui::store;
use tui::task;
use tui::task::Interrupted;
-
use tui::terminal;
+
use tui::terminal::Backend;
use tui::ui::items::{Filter, PatchItem, PatchItemFilter};
+
use tui::ui::widget::Properties;
+
use tui::ui::widget::View;
+
use tui::ui::widget::Window;
+
use tui::ui::widget::WindowProps;
use tui::ui::Frontend;
use tui::Exit;

use tui::PageStack;
-
use ui::Window;
+

+
use self::ui::BrowsePage;
+
use self::ui::HelpPage;

use super::common::Mode;

@@ -194,15 +200,27 @@ impl App {
    pub async fn run(&self) -> Result<Option<Selection>> {
        let (terminator, mut interrupt_rx) = task::create_termination();
        let (store, state_rx) = store::Store::<Action, State, Selection>::new();
-
        let (frontend, action_rx) = Frontend::<Action>::new();
+
        let (frontend, action_tx, action_rx) = Frontend::<Action>::new();
        let state = State::try_from(&self.context)?;

+
        let window: Window<Backend, State, Action, Page> = Window::new(&state, action_tx.clone())
+
            .page(
+
                Page::Browse,
+
                BrowsePage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .page(
+
                Page::Help,
+
                HelpPage::new(&state, action_tx.clone()).to_boxed(),
+
            )
+
            .on_update(|state| {
+
                WindowProps::default()
+
                    .current_page(state.pages.peek().unwrap_or(&Page::Browse).clone())
+
                    .to_boxed()
+
            });
+

        tokio::try_join!(
            store.main_loop(state, terminator, action_rx, interrupt_rx.resubscribe()),
-
            frontend.main_loop::<State, Window<terminal::Backend>, Selection>(
-
                state_rx,
-
                interrupt_rx.resubscribe()
-
            ),
+
            frontend.main_loop(Some(window), state_rx, interrupt_rx.resubscribe()),
        )?;

        if let Ok(reason) = interrupt_rx.recv().await {
modified bin/commands/patch/select/ui.rs
@@ -35,108 +35,12 @@ use tui::Selection;
use crate::tui_patch::common::Mode;
use crate::tui_patch::common::PatchOperation;

-
use super::{Action, Page, State};
+
use super::{Action, State};

type BoxedWidget<B> = widget::BoxedWidget<B, State, Action>;

#[derive(Clone)]
-
pub struct WindowProps {
-
    page: Page,
-
}
-

-
impl From<&State> for WindowProps {
-
    fn from(state: &State) -> Self {
-
        Self {
-
            page: state.pages.peek().unwrap_or(&Page::Browse).clone(),
-
        }
-
    }
-
}
-

-
impl Properties for WindowProps {}
-

-
pub struct Window<B: Backend> {
-
    /// Internal properties
-
    props: WindowProps,
-
    /// Message sender
-
    _action_tx: UnboundedSender<Action>,
-
    /// Custom update handler
-
    on_update: Option<UpdateCallback<State>>,
-
    /// Additional custom event handler
-
    on_event: Option<EventCallback<Action>>,
-
    /// All pages known
-
    pages: HashMap<Page, BoxedWidget<B>>,
-
}
-

-
impl<'a: 'static, B> View<State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn new(state: &State, action_tx: UnboundedSender<Action>) -> Self
-
    where
-
        Self: Sized,
-
    {
-
        Self {
-
            _action_tx: action_tx.clone(),
-
            props: WindowProps::from(state),
-
            pages: HashMap::from([
-
                (
-
                    Page::Browse,
-
                    BrowsePage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
                (
-
                    Page::Help,
-
                    HelpPage::new(state, action_tx.clone()).to_boxed() as BoxedWidget<B>,
-
                ),
-
            ]),
-
            on_update: None,
-
            on_event: None,
-
        }
-
    }
-

-
    fn on_update(mut self, callback: UpdateCallback<State>) -> Self {
-
        self.on_update = Some(callback);
-
        self
-
    }
-

-
    fn on_event(mut self, callback: EventCallback<Action>) -> Self {
-
        self.on_event = Some(callback);
-
        self
-
    }
-

-
    fn update(&mut self, state: &State) {
-
        self.props = WindowProps::from(state);
-

-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.update(state);
-
        }
-
    }
-

-
    fn handle_key_event(&mut self, key: termion::event::Key) {
-
        if let Some(page) = self.pages.get_mut(&self.props.page) {
-
            page.handle_key_event(key);
-
        }
-
    }
-
}
-

-
impl<'a: 'static, B> Widget<B, State, Action> for Window<B>
-
where
-
    B: Backend + 'a,
-
{
-
    fn render(&self, frame: &mut ratatui::Frame, _area: Rect, props: Option<Box<dyn Any>>) {
-
        let props = props
-
            .and_then(WindowProps::from_boxed_any)
-
            .unwrap_or(self.props.clone());
-

-
        let area = frame.size();
-

-
        if let Some(page) = self.pages.get(&props.page) {
-
            page.render(frame, area, None);
-
        }
-
    }
-
}
-

-
#[derive(Clone)]
-
struct BrowsePageProps<'a> {
+
pub struct BrowsePageProps<'a> {
    mode: Mode,
    patches: Vec<PatchItem>,
    selected: Option<usize>,
@@ -218,7 +122,7 @@ impl<'a> From<&State> for BrowsePageProps<'a> {

impl<'a: 'static> Properties for BrowsePageProps<'a> {}

-
struct BrowsePage<'a, B> {
+
pub struct BrowsePage<'a, B> {
    /// Internal properties
    props: BrowsePageProps<'a>,
    /// Message sender
@@ -640,7 +544,7 @@ where
}

#[derive(Clone)]
-
struct HelpPageProps<'a> {
+
pub struct HelpPageProps<'a> {
    focus: bool,
    page_size: usize,
    help_progress: usize,