Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
bin: Use unified buffered value implementation
Erik Kundt committed 1 year ago
commit c6bc64671283fbe613b79ccace8de049f03d3f1a
parent dd5e3e8c23218c3001750605d7c2b18e4fc279e1
6 files changed +101 -102
modified bin/commands/inbox/select.rs
@@ -21,7 +21,6 @@ use radicle::Profile;
use radicle_tui as tui;

use tui::store;
-
use tui::store::StateValue;
use tui::ui::rm::widget::container::{Container, Footer, FooterProps, Header, HeaderProps};
use tui::ui::rm::widget::input::{TextView, TextViewProps, TextViewState};
use tui::ui::rm::widget::window::{
@@ -29,6 +28,7 @@ use tui::ui::rm::widget::window::{
};
use tui::ui::rm::widget::{ToWidget, Widget};
use tui::ui::span;
+
use tui::ui::BufferedValue;
use tui::ui::Column;
use tui::{BoxedAny, Channel, Exit, PageStack};

@@ -67,7 +67,7 @@ pub struct BrowserState {
    items: Vec<NotificationItem>,
    selected: Option<usize>,
    filter: NotificationItemFilter,
-
    search: store::StateValue<String>,
+
    search: BufferedValue<String>,
    show_search: bool,
}

@@ -102,7 +102,7 @@ impl TryFrom<&Context> for State {
        let doc = context.repository.identity_doc()?;
        let project = doc.project()?;

-
        let search = StateValue::new(String::new());
+
        let search = BufferedValue::new(String::new());
        let filter = NotificationItemFilter::from_str(&search.read()).unwrap_or_default();

        let mut notifications = match &context.mode.repository() {
modified bin/commands/issue/select.rs
@@ -21,7 +21,6 @@ use radicle::Profile;
use radicle_tui as tui;

use tui::store;
-
use tui::store::StateValue;
use tui::ui::rm::widget::container::{
    Container, ContainerProps, Footer, FooterProps, Header, HeaderProps, SectionGroup,
    SectionGroupProps, SplitContainer, SplitContainerFocus, SplitContainerProps,
@@ -32,9 +31,9 @@ use tui::ui::rm::widget::window::{
    Page, PageProps, Shortcuts, ShortcutsProps, Window, WindowProps,
};
use tui::ui::rm::widget::{PredefinedLayout, ToWidget, Widget};
-
use tui::ui::span;
use tui::ui::theme::Theme;
use tui::ui::Column;
+
use tui::ui::{span, BufferedValue};
use tui::{BoxedAny, Channel, Exit, PageStack};

use crate::cob::issue;
@@ -177,7 +176,7 @@ impl TryFrom<(&Context, &TerminalInfo)> for State {
        let settings = settings::Settings::default();

        let issues = issue::all(&context.profile, &context.repository)?;
-
        let search = StateValue::new(context.filter.to_string());
+
        let search = BufferedValue::new(context.filter.to_string());
        let filter = IssueItemFilter::from_str(&search.read()).unwrap_or_default();

        let default_bundle = ThemeBundle::default();
modified bin/commands/patch/select.rs
@@ -22,7 +22,7 @@ use tui::ui::rm::widget::window::{
    Page, PageProps, Shortcuts, ShortcutsProps, Window, WindowProps,
};
use tui::ui::rm::widget::{ToWidget, Widget};
-
use tui::ui::span;
+
use tui::ui::{span, BufferedValue};
use tui::ui::Column;

use tui::{BoxedAny, Channel, Exit, PageStack};
@@ -107,7 +107,7 @@ impl TryFrom<&Context> for State {

    fn try_from(context: &Context) -> Result<Self, Self::Error> {
        let patches = patch::all(&context.profile, &context.repository)?;
-
        let search = store::StateValue::new(context.filter.to_string());
+
        let search = BufferedValue::new(context.filter.to_string());
        let filter = PatchItemFilter::from_str(&context.filter.to_string()).unwrap_or_default();

        // Convert into UI items
modified bin/commands/patch/select/imui.rs
@@ -11,10 +11,11 @@ use ratatui::Frame;

use radicle_tui as tui;

+
use tui::ui::im;
use tui::ui::im::widget::{GroupState, TableState, TextEditState, TextViewState, Window};
-
use tui::ui::im::{self, Show};
-
use tui::ui::im::{Borders, BufferedValue};
-
use tui::ui::Column;
+
use tui::ui::im::Borders;
+
use tui::ui::im::Show;
+
use tui::ui::{BufferedValue, Column};
use tui::{store, Exit};

use crate::cob::patch;
@@ -99,22 +100,6 @@ pub struct App {
    filter: PatchItemFilter,
}

-
impl App {
-
    pub fn selected_patch(&self) -> Option<&PatchItem> {
-
        let patches = self
-
            .storage
-
            .patches
-
            .iter()
-
            .filter(|patch| self.filter.matches(patch))
-
            .collect::<Vec<_>>();
-

-
        self.patches
-
            .selected()
-
            .and_then(|selected| patches.get(selected))
-
            .copied()
-
    }
-
}
-

impl TryFrom<&Context> for App {
    type Error = anyhow::Error;

@@ -226,74 +211,6 @@ impl store::Update<Message> for App {
    }
}

-
impl App {
-
    pub fn show_patches(&self, frame: &mut Frame, ui: &mut im::Ui<Message>) {
-
        let patches = self
-
            .storage
-
            .patches
-
            .iter()
-
            .filter(|patch| self.filter.matches(patch))
-
            .cloned()
-
            .collect::<Vec<_>>();
-
        let mut selected = self.patches.selected();
-

-
        let header = [
-
            Column::new(Span::raw(" ● ").bold(), Constraint::Length(3)),
-
            Column::new(Span::raw("ID").bold(), Constraint::Length(8)),
-
            Column::new(Span::raw("Title").bold(), Constraint::Fill(1)),
-
            Column::new(Span::raw("Author").bold(), Constraint::Length(16)).hide_small(),
-
            Column::new("", Constraint::Length(16)).hide_medium(),
-
            Column::new(Span::raw("Head").bold(), Constraint::Length(8)).hide_small(),
-
            Column::new(Span::raw("+").bold(), Constraint::Length(6)).hide_small(),
-
            Column::new(Span::raw("-").bold(), Constraint::Length(6)).hide_small(),
-
            Column::new(Span::raw("Updated").bold(), Constraint::Length(16)).hide_small(),
-
        ];
-

-
        let table = ui.headered_table(frame, &mut selected, &patches, header);
-
        if table.changed {
-
            ui.send_message(Message::PatchesChanged {
-
                state: TableState::new(selected),
-
            });
-
        }
-

-
        // TODO(erikli): Should only work if table has focus
-
        if ui.input_global(|key| key == Key::Char('/')) {
-
            ui.send_message(Message::ShowSearch);
-
        }
-
    }
-

-
    pub fn show_search_text_edit(&self, frame: &mut Frame, ui: &mut im::Ui<Message>) {
-
        let (mut search_text, mut search_cursor) = (
-
            self.search.clone().read().text,
-
            self.search.clone().read().cursor,
-
        );
-
        let mut search = self.search.clone();
-

-
        let text_edit = ui.text_edit_labeled_singleline(
-
            frame,
-
            &mut search_text,
-
            &mut search_cursor,
-
            "Search".to_string(),
-
            Some(Borders::Spacer { top: 0, left: 0 }),
-
        );
-

-
        if text_edit.changed {
-
            search.write(TextEditState {
-
                text: search_text,
-
                cursor: search_cursor,
-
            });
-
            ui.send_message(Message::UpdateSearch { search });
-
        }
-

-
        if ui.input_global(|key| key == Key::Esc) {
-
            ui.send_message(Message::HideSearch { apply: false });
-
        }
-
        if ui.input_global(|key| key == Key::Char('\n')) {
-
            ui.send_message(Message::HideSearch { apply: true });
-
        }
-
    }
-
}
-

impl Show<Message> for App {
    fn show(&self, ctx: &im::Context<Message>, frame: &mut Frame) -> Result<()> {
        Window::default().show(ctx, |ui| {
@@ -462,6 +379,90 @@ impl Show<Message> for App {
    }
}

+
impl App {
+
    pub fn show_patches(&self, frame: &mut Frame, ui: &mut im::Ui<Message>) {
+
        let patches = self
+
            .storage
+
            .patches
+
            .iter()
+
            .filter(|patch| self.filter.matches(patch))
+
            .cloned()
+
            .collect::<Vec<_>>();
+
        let mut selected = self.patches.selected();
+

+
        let header = [
+
            Column::new(Span::raw(" ● ").bold(), Constraint::Length(3)),
+
            Column::new(Span::raw("ID").bold(), Constraint::Length(8)),
+
            Column::new(Span::raw("Title").bold(), Constraint::Fill(1)),
+
            Column::new(Span::raw("Author").bold(), Constraint::Length(16)).hide_small(),
+
            Column::new("", Constraint::Length(16)).hide_medium(),
+
            Column::new(Span::raw("Head").bold(), Constraint::Length(8)).hide_small(),
+
            Column::new(Span::raw("+").bold(), Constraint::Length(6)).hide_small(),
+
            Column::new(Span::raw("-").bold(), Constraint::Length(6)).hide_small(),
+
            Column::new(Span::raw("Updated").bold(), Constraint::Length(16)).hide_small(),
+
        ];
+

+
        let table = ui.headered_table(frame, &mut selected, &patches, header);
+
        if table.changed {
+
            ui.send_message(Message::PatchesChanged {
+
                state: TableState::new(selected),
+
            });
+
        }
+

+
        // TODO(erikli): Should only work if table has focus
+
        if ui.input_global(|key| key == Key::Char('/')) {
+
            ui.send_message(Message::ShowSearch);
+
        }
+
    }
+

+
    pub fn show_search_text_edit(&self, frame: &mut Frame, ui: &mut im::Ui<Message>) {
+
        let (mut search_text, mut search_cursor) = (
+
            self.search.clone().read().text,
+
            self.search.clone().read().cursor,
+
        );
+
        let mut search = self.search.clone();
+

+
        let text_edit = ui.text_edit_labeled_singleline(
+
            frame,
+
            &mut search_text,
+
            &mut search_cursor,
+
            "Search".to_string(),
+
            Some(Borders::Spacer { top: 0, left: 0 }),
+
        );
+

+
        if text_edit.changed {
+
            search.write(TextEditState {
+
                text: search_text,
+
                cursor: search_cursor,
+
            });
+
            ui.send_message(Message::UpdateSearch { search });
+
        }
+

+
        if ui.input_global(|key| key == Key::Esc) {
+
            ui.send_message(Message::HideSearch { apply: false });
+
        }
+
        if ui.input_global(|key| key == Key::Char('\n')) {
+
            ui.send_message(Message::HideSearch { apply: true });
+
        }
+
    }
+
}
+

+
impl App {
+
    pub fn selected_patch(&self) -> Option<&PatchItem> {
+
        let patches = self
+
            .storage
+
            .patches
+
            .iter()
+
            .filter(|patch| self.filter.matches(patch))
+
            .collect::<Vec<_>>();
+

+
        self.patches
+
            .selected()
+
            .and_then(|selected| patches.get(selected))
+
            .copied()
+
    }
+
}
+

fn browser_context<'a>(ui: &im::Ui<Message>, app: &'a App) -> Vec<Column<'a>> {
    let search = app.search.read().text;
    let total_count = app.storage.patches.len();
modified bin/ui/im.rs
@@ -6,8 +6,8 @@ use ratatui::Frame;
use radicle_tui as tui;

use tui::ui::im::widget::{TableState, TextEditState, Widget};
-
use tui::ui::im::{Borders, BufferedValue, Response, Ui};
-
use tui::ui::{Column, ToRow};
+
use tui::ui::im::{Borders, Response, Ui};
+
use tui::ui::{BufferedValue, Column, ToRow};

pub struct UiExt<'a, M>(&'a mut Ui<M>);

modified bin/ui/rm.rs
@@ -10,10 +10,9 @@ use ratatui::Frame;

use radicle_tui as tui;

-
use tui::store;
use tui::ui::rm::widget::{RenderProps, View, ViewProps};
use tui::ui::theme::style;
-
use tui::ui::{layout, span};
+
use tui::ui::{layout, span, BufferedValue};

use super::format;
use super::items::IssueItem;
@@ -29,7 +28,7 @@ pub struct BrowserState<I, F> {
    items: Vec<I>,
    selected: Option<usize>,
    filter: F,
-
    search: store::StateValue<String>,
+
    search: BufferedValue<String>,
    show_search: bool,
}

@@ -43,7 +42,7 @@ where
            items: vec![],
            selected: None,
            filter: F::default(),
-
            search: store::StateValue::new(String::default()),
+
            search: BufferedValue::new(String::default()),
            show_search: false,
        }
    }
@@ -54,7 +53,7 @@ where
    I: Clone,
    F: Filter<I> + Default + FromStr,
{
-
    pub fn build(items: Vec<I>, filter: F, search: store::StateValue<String>) -> Self {
+
    pub fn build(items: Vec<I>, filter: F, search: BufferedValue<String>) -> Self {
        let selected = items.first().map(|_| 0);

        Self {