Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Introduce custom list & table state conversions
Erik Kundt committed 2 years ago
commit 652720da7687f97027bf7f2eb4fe15df89542d5f
parent 0a90b23d0fe18c3c51e662ddec05b7558c4e8c9a
10 files changed +136 -83
modified bin/commands/issue/select/event.rs
@@ -1,8 +1,9 @@
use radicle::issue::IssueId;
+
use tui::ui::state::ItemState;
use tui::SelectionExit;
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
-
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
+
use tuirealm::{MockComponent, NoUserEvent};

use radicle_tui as tui;

@@ -35,9 +36,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<GlobalListener> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<IdSelect> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        let mut submit = || -> Option<radicle::cob::patch::PatchId> {
-
            let result = self.perform(Cmd::Submit);
-
            match result {
-
                CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
+
            match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
                    let item = self.items().get(selected)?;
                    Some(item.id().to_owned())
                }
@@ -78,9 +79,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<IdSelect> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<OperationSelect> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        let mut submit = || -> Option<radicle::cob::patch::PatchId> {
-
            let result = self.perform(Cmd::Submit);
-
            match result {
-
                CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
+
            match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
                    let item = self.items().get(selected)?;
                    Some(item.id().to_owned())
                }
modified bin/commands/issue/select/page.rs
@@ -2,7 +2,8 @@ use std::collections::HashMap;

use anyhow::Result;

-
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, SubClause};
+
use tui::ui::state::ItemState;
+
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, Sub, SubClause};

use radicle_tui as tui;

@@ -44,12 +45,17 @@ impl ListView {
        theme: &Theme,
    ) -> Result<()> {
        let state = app.state(&Cid::List(ListCid::PatchBrowser))?;
-
        let progress = match state {
-
            State::Tup2((StateValue::Usize(step), StateValue::Usize(total))) => {
-
                Progress::Step(step.saturating_add(1), total)
-
            }
-
            _ => Progress::None,
+
        let progress = match ItemState::try_from(state) {
+
            Ok(state) => Progress::Step(
+
                state
+
                    .selected()
+
                    .map(|s| s.saturating_add(1))
+
                    .unwrap_or_default(),
+
                state.len(),
+
            ),
+
            Err(_) => Progress::None,
        };
+

        let context = common::ui::browse_context(context, theme, self.filter.clone(), progress);

        app.remount(Cid::List(ListCid::Context), context.to_boxed(), vec![])?;
modified bin/commands/issue/suite/event.rs
@@ -1,4 +1,5 @@
use radicle::cob::issue::IssueId;
+
use tui::ui::state::ItemState;
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection, Position};
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers};
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
@@ -51,45 +52,41 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<AppHeader> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<ui::LargeList> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        match event {
-
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => match self.state() {
-
                State::Tup2((StateValue::Usize(selected), StateValue::Usize(_))) => {
-
                    let item = self.items().get(selected)?;
-
                    Some(Message::Issue(IssueMessage::Leave(Some(
-
                        item.id().to_owned(),
-
                    ))))
-
                }
-
                _ => None,
-
            },
+
            Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
+
                let selected = ItemState::try_from(self.state()).ok()?.selected()?;
+
                let item = self.items().get(selected)?;
+

+
                Some(Message::Issue(IssueMessage::Leave(Some(
+
                    item.id().to_owned(),
+
                ))))
+
            }
            Event::Keyboard(KeyEvent { code: Key::Up, .. })
            | Event::Keyboard(KeyEvent {
                code: Key::Char('k'),
                ..
-
            }) => {
-
                let result = self.perform(Cmd::Move(MoveDirection::Up));
-
                match result {
-
                    CmdResult::Changed(State::One(StateValue::Usize(selected))) => {
-
                        let item = self.items().get(selected)?;
-
                        Some(Message::Issue(IssueMessage::Changed(item.id().to_owned())))
-
                    }
-
                    _ => None,
+
            }) => match self.perform(Cmd::Move(MoveDirection::Up)) {
+
                CmdResult::Changed(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
+
                    let item = self.items().get(selected)?;
+

+
                    Some(Message::Issue(IssueMessage::Changed(item.id().to_owned())))
                }
-
            }
+
                _ => None,
+
            },
            Event::Keyboard(KeyEvent {
                code: Key::Down, ..
            })
            | Event::Keyboard(KeyEvent {
                code: Key::Char('j'),
                ..
-
            }) => {
-
                let result = self.perform(Cmd::Move(MoveDirection::Down));
-
                match result {
-
                    CmdResult::Changed(State::One(StateValue::Usize(selected))) => {
-
                        let item = self.items().get(selected)?;
-
                        Some(Message::Issue(IssueMessage::Changed(item.id().to_owned())))
-
                    }
-
                    _ => None,
+
            }) => match self.perform(Cmd::Move(MoveDirection::Down)) {
+
                CmdResult::Changed(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
+
                    let item = self.items().get(selected)?;
+
                    Some(Message::Issue(IssueMessage::Changed(item.id().to_owned())))
                }
-
            }
+
                _ => None,
+
            },
            Event::Keyboard(KeyEvent {
                code: Key::Enter, ..
            }) => Some(Message::Issue(IssueMessage::Focus(IssueCid::Details))),
@@ -235,9 +232,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<Form> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<ui::IssueBrowser> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        let mut submit = || -> Option<IssueId> {
-
            let result = self.perform(Cmd::Submit);
-
            match result {
-
                CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
+
            match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
                    let item = self.items().get(selected)?;
                    Some(item.id().to_owned())
                }
modified bin/commands/issue/suite/page.rs
@@ -4,6 +4,7 @@ use anyhow::Result;

use radicle::cob::issue::{Issue, IssueId};

+
use tui::ui::state::ItemState;
use tuirealm::event::Key;
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, SubClause};

@@ -80,13 +81,18 @@ impl ListPage {
        theme: &Theme,
    ) -> Result<()> {
        let state = app.state(&Cid::List(ListCid::IssueBrowser))?;
-
        let progress = match state {
-
            State::Tup2((StateValue::Usize(step), StateValue::Usize(total))) => {
-
                Progress::Step(step.saturating_add(1), total)
-
            }
-
            _ => Progress::None,
+
        let progress = match ItemState::try_from(state) {
+
            Ok(state) => Progress::Step(
+
                state
+
                    .selected()
+
                    .map(|s| s.saturating_add(1))
+
                    .unwrap_or_default(),
+
                state.len(),
+
            ),
+
            Err(_) => Progress::None,
        };
        let context = ui::browse_context(context, theme, progress);
+

        app.remount(Cid::List(ListCid::Context), context.to_boxed(), vec![])?;

        Ok(())
modified bin/commands/patch/select/event.rs
@@ -1,8 +1,9 @@
use radicle::patch::PatchId;
+
use tui::ui::state::ItemState;
use tui::SelectionExit;
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
-
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
+
use tuirealm::{MockComponent, NoUserEvent};

use radicle_tui as tui;

@@ -35,9 +36,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<GlobalListener> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<IdSelect> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        let mut submit = || -> Option<radicle::cob::patch::PatchId> {
-
            let result = self.perform(Cmd::Submit);
-
            match result {
-
                CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
+
            match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
                    let item = self.items().get(selected)?;
                    Some(item.id().to_owned())
                }
@@ -78,9 +79,9 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<IdSelect> {
impl tuirealm::Component<Message, NoUserEvent> for Widget<OperationSelect> {
    fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
        let mut submit = || -> Option<radicle::cob::patch::PatchId> {
-
            let result = self.perform(Cmd::Submit);
-
            match result {
-
                CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
+
            match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
                    let item = self.items().get(selected)?;
                    Some(item.id().to_owned())
                }
modified bin/commands/patch/select/page.rs
@@ -2,7 +2,8 @@ use std::collections::HashMap;

use anyhow::Result;

-
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, SubClause};
+
use tui::ui::state::ItemState;
+
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, Sub, SubClause};

use radicle_tui as tui;

@@ -44,12 +45,17 @@ impl ListView {
        theme: &Theme,
    ) -> Result<()> {
        let state = app.state(&Cid::List(ListCid::PatchBrowser))?;
-
        let progress = match state {
-
            State::Tup2((StateValue::Usize(step), StateValue::Usize(total))) => {
-
                Progress::Step(step.saturating_add(1), total)
-
            }
-
            _ => Progress::None,
+
        let progress = match ItemState::try_from(state) {
+
            Ok(state) => Progress::Step(
+
                state
+
                    .selected()
+
                    .map(|s| s.saturating_add(1))
+
                    .unwrap_or_default(),
+
                state.len(),
+
            ),
+
            Err(_) => Progress::None,
        };
+

        let context = common::ui::browse_context(context, theme, self.filter.clone(), progress);

        app.remount(Cid::List(ListCid::Context), context.to_boxed(), vec![])?;
modified bin/commands/patch/suite/event.rs
@@ -1,3 +1,4 @@
+
use radicle_tui::ui::state::ItemState;
use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
@@ -67,16 +68,14 @@ impl tuirealm::Component<Message, NoUserEvent> for Widget<common::ui::PatchBrows
            }
            Event::Keyboard(KeyEvent {
                code: Key::Enter, ..
-
            }) => {
-
                let result = self.perform(Cmd::Submit);
-
                match result {
-
                    CmdResult::Submit(State::One(StateValue::Usize(selected))) => {
-
                        let item = self.items().get(selected)?;
-
                        Some(Message::Patch(PatchMessage::Show(item.id().to_owned())))
-
                    }
-
                    _ => None,
+
            }) => match self.perform(Cmd::Submit) {
+
                CmdResult::Submit(state) => {
+
                    let selected = ItemState::try_from(state).ok()?.selected()?;
+
                    let item = self.items().get(selected)?;
+
                    Some(Message::Patch(PatchMessage::Show(item.id().to_owned())))
                }
-
            }
+
                _ => None,
+
            },
            _ => None,
        }
    }
modified bin/commands/patch/suite/page.rs
@@ -4,7 +4,8 @@ use anyhow::Result;

use radicle::cob::patch::{Patch, PatchId};

-
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, SubClause};
+
use tui::ui::state::ItemState;
+
use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, Sub, SubClause};

use radicle_tui as tui;

@@ -62,11 +63,15 @@ impl ListView {
        theme: &Theme,
    ) -> Result<()> {
        let state = app.state(&Cid::List(ListCid::PatchBrowser))?;
-
        let progress = match state {
-
            State::Tup2((StateValue::Usize(step), StateValue::Usize(total))) => {
-
                Progress::Step(step.saturating_add(1), total)
-
            }
-
            _ => Progress::None,
+
        let progress = match ItemState::try_from(state) {
+
            Ok(state) => Progress::Step(
+
                state
+
                    .selected()
+
                    .map(|s| s.saturating_add(1))
+
                    .unwrap_or_default(),
+
                state.len(),
+
            ),
+
            Err(_) => Progress::None,
        };
        let context = ui::browse_context(context, theme, progress);

modified src/ui/state.rs
@@ -1,4 +1,7 @@
+
use anyhow::anyhow;
+

use tuirealm::tui::widgets::{ListState, TableState};
+
use tuirealm::{State, StateValue};

/// State that holds the index of a selected tab item and the count of all tab items.
/// The index can be increased and will start at 0, if length was reached.
@@ -30,7 +33,11 @@ impl ItemState {
    }

    pub fn selected(&self) -> Option<usize> {
-
        self.selected
+
        if !self.is_empty() {
+
            self.selected
+
        } else {
+
            None
+
        }
    }

    pub fn select_previous(&mut self) -> Option<usize> {
@@ -65,6 +72,31 @@ impl ItemState {
            None
        }
    }
+

+
    pub fn len(&self) -> usize {
+
        self.len
+
    }
+

+
    pub fn is_empty(&self) -> bool {
+
        self.len == 0
+
    }
+
}
+

+
impl TryFrom<State> for ItemState {
+
    type Error = anyhow::Error;
+

+
    fn try_from(state: State) -> Result<Self, Self::Error> {
+
        match state {
+
            State::Tup2((StateValue::Usize(selected), StateValue::Usize(len))) => Ok(Self {
+
                selected: Some(selected),
+
                len,
+
            }),
+
            _ => Err(anyhow!(format!(
+
                "Cannot convert into item state: {:?}",
+
                state
+
            ))),
+
        }
+
    }
}

impl From<&ItemState> for TableState {
modified src/ui/widget/list.rs
@@ -284,15 +284,15 @@ where
        use tuirealm::command::Direction;
        match cmd {
            Cmd::Move(Direction::Up) => match self.state.select_previous() {
-
                Some(selected) => CmdResult::Changed(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Changed(self.state()),
                None => CmdResult::None,
            },
            Cmd::Move(Direction::Down) => match self.state.select_next() {
-
                Some(selected) => CmdResult::Changed(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Changed(self.state()),
                None => CmdResult::None,
            },
            Cmd::Submit => match self.state.selected() {
-
                Some(selected) => CmdResult::Submit(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Submit(self.state()),
                None => CmdResult::None,
            },
            _ => CmdResult::None,
@@ -358,15 +358,15 @@ where
        use tuirealm::command::Direction;
        match cmd {
            Cmd::Move(Direction::Up) => match self.state.select_previous() {
-
                Some(selected) => CmdResult::Changed(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Changed(self.state()),
                None => CmdResult::None,
            },
            Cmd::Move(Direction::Down) => match self.state.select_next() {
-
                Some(selected) => CmdResult::Changed(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Changed(self.state()),
                None => CmdResult::None,
            },
            Cmd::Submit => match self.state.selected() {
-
                Some(selected) => CmdResult::Submit(State::One(StateValue::Usize(selected))),
+
                Some(_) => CmdResult::Submit(self.state()),
                None => CmdResult::None,
            },
            _ => CmdResult::None,