lib: Rework page shortcuts

This commit is contained in:
Erik Kundt
2024-04-11 10:58:45 +02:00
parent 1c8f4d2b22
commit aee8b37caa
4 changed files with 110 additions and 129 deletions
+23 -35
View File
@@ -17,7 +17,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;
use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMode};
@@ -25,7 +25,6 @@ use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMo
use super::{Action, State};
pub struct ListPageProps {
mode: Mode,
show_search: bool,
show_help: bool,
}
@@ -33,7 +32,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
fn from(state: &State) -> Self {
Self {
mode: state.mode.clone(),
show_search: state.ui.show_search,
show_help: state.ui.show_help,
}
@@ -66,7 +64,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
notifications: Notifications::new(state, action_tx.clone()),
search: Search::new(state, action_tx.clone()),
help: Help::new(state, action_tx.clone()),
shortcuts: Shortcuts::new(state, action_tx.clone()),
shortcuts: Shortcuts::new(&(), action_tx.clone()),
}
.move_with_state(state)
}
@@ -75,9 +73,28 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
where
Self: Sized,
{
let shorts = if state.ui.show_search {
vec![("esc", "cancel"), ("enter", "apply")]
} else if state.ui.show_help {
vec![("?", "close")]
} else {
match state.mode.selection() {
SelectionMode::Id => vec![("enter", "select"), ("/", "search")],
SelectionMode::Operation => vec![
("enter", "show"),
("c", "clear"),
("/", "search"),
("?", "help"),
],
}
};
let shortcuts = self.shortcuts.move_with_state(&());
let shortcuts = shortcuts.shortcuts(&shorts);
ListPage {
notifications: self.notifications.move_with_state(state),
shortcuts: self.shortcuts.move_with_state(state),
shortcuts,
help: self.help.move_with_state(state),
props: ListPageProps::from(state),
..self
@@ -116,28 +133,6 @@ impl<'a> Render<()> for ListPage<'a> {
let area = frame.size();
let layout = tui::ui::layout::default_page(area, 0u16, 1u16);
let shortcuts = if self.props.show_search {
vec![
Shortcut::new("esc", "cancel"),
Shortcut::new("enter", "apply"),
]
} else if self.props.show_help {
vec![Shortcut::new("?", "close")]
} else {
match self.props.mode.selection() {
SelectionMode::Id => vec![
Shortcut::new("enter", "select"),
Shortcut::new("/", "search"),
],
SelectionMode::Operation => vec![
Shortcut::new("enter", "show"),
Shortcut::new("c", "clear"),
Shortcut::new("/", "search"),
Shortcut::new("?", "help"),
],
}
};
if self.props.show_search {
let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
.split(layout.component);
@@ -152,14 +147,7 @@ impl<'a> Render<()> for ListPage<'a> {
self.notifications.render::<B>(frame, layout.component, ());
}
self.shortcuts.render::<B>(
frame,
layout.shortcuts,
ShortcutsProps {
shortcuts,
divider: '∙',
},
);
self.shortcuts.render::<B>(frame, layout.shortcuts, ());
}
}
+23 -35
View File
@@ -19,7 +19,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;
use crate::tui_issue::common::IssueOperation;
@@ -28,7 +28,6 @@ use crate::tui_issue::common::Mode;
use super::{Action, State};
pub struct ListPageProps {
mode: Mode,
show_search: bool,
show_help: bool,
}
@@ -36,7 +35,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
fn from(state: &State) -> Self {
Self {
mode: state.mode.clone(),
show_search: state.ui.show_search,
show_help: state.ui.show_help,
}
@@ -69,7 +67,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
issues: Issues::new(state, action_tx.clone()),
search: Search::new(state, action_tx.clone()),
help: Help::new(state, action_tx.clone()),
shortcuts: Shortcuts::new(state, action_tx.clone()),
shortcuts: Shortcuts::new(&(), action_tx.clone()),
}
.move_with_state(state)
}
@@ -78,9 +76,28 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
where
Self: Sized,
{
let shorts = if state.ui.show_search {
vec![("esc", "cancel"), ("enter", "apply")]
} else if state.ui.show_help {
vec![("?", "close")]
} else {
match state.mode {
Mode::Id => vec![("enter", "select"), ("/", "search")],
Mode::Operation => vec![
("enter", "show"),
("e", "edit"),
("/", "search"),
("?", "help"),
],
}
};
let shortcuts = self.shortcuts.move_with_state(&());
let shortcuts = shortcuts.shortcuts(&shorts);
ListPage {
issues: self.issues.move_with_state(state),
shortcuts: self.shortcuts.move_with_state(state),
shortcuts,
help: self.help.move_with_state(state),
props: ListPageProps::from(state),
..self
@@ -116,28 +133,6 @@ impl<'a> Render<()> for ListPage<'a> {
let area = frame.size();
let layout = tui::ui::layout::default_page(area, 0u16, 1u16);
let shortcuts = if self.props.show_search {
vec![
Shortcut::new("esc", "cancel"),
Shortcut::new("enter", "apply"),
]
} else if self.props.show_help {
vec![Shortcut::new("?", "close")]
} else {
match self.props.mode {
Mode::Id => vec![
Shortcut::new("enter", "select"),
Shortcut::new("/", "search"),
],
Mode::Operation => vec![
Shortcut::new("enter", "show"),
Shortcut::new("e", "edit"),
Shortcut::new("/", "search"),
Shortcut::new("?", "help"),
],
}
};
if self.props.show_search {
let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
.split(layout.component);
@@ -151,14 +146,7 @@ impl<'a> Render<()> for ListPage<'a> {
self.issues.render::<B>(frame, layout.component, ());
}
self.shortcuts.render::<B>(
frame,
layout.shortcuts,
ShortcutsProps {
shortcuts,
divider: '∙',
},
);
self.shortcuts.render::<B>(frame, layout.shortcuts, ());
}
}
+24 -36
View File
@@ -20,7 +20,7 @@ use tui::ui::span;
use tui::ui::widget::container::{Footer, FooterProps, Header};
use tui::ui::widget::input::{TextField, TextFieldProps};
use tui::ui::widget::text::{Paragraph, ParagraphProps};
use tui::ui::widget::{Column, Render, Shortcut, Shortcuts, ShortcutsProps, Table, Widget};
use tui::ui::widget::{Column, Render, Shortcuts, Table, Widget};
use tui::Selection;
use crate::tui_patch::common::Mode;
@@ -29,7 +29,6 @@ use crate::tui_patch::common::PatchOperation;
use super::{Action, State};
pub struct ListPageProps {
mode: Mode,
show_search: bool,
show_help: bool,
}
@@ -37,7 +36,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
fn from(state: &State) -> Self {
Self {
mode: state.mode.clone(),
show_search: state.ui.show_search,
show_help: state.ui.show_help,
}
@@ -70,7 +68,7 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
patches: Patches::new(state, action_tx.clone()),
search: Search::new(state, action_tx.clone()),
help: Help::new(state, action_tx.clone()),
shortcuts: Shortcuts::new(state, action_tx),
shortcuts: Shortcuts::new(&(), action_tx),
}
.move_with_state(state)
}
@@ -79,10 +77,30 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
where
Self: Sized,
{
let shorts = if state.ui.show_search {
vec![("esc", "cancel"), ("enter", "apply")]
} else if state.ui.show_help {
vec![("?", "close")]
} else {
match state.mode {
Mode::Id => vec![("enter", "select"), ("/", "search")],
Mode::Operation => vec![
("enter", "show"),
("c", "checkout"),
("d", "diff"),
("/", "search"),
("?", "help"),
],
}
};
let shortcuts = self.shortcuts.move_with_state(&());
let shortcuts = shortcuts.shortcuts(&shorts);
ListPage {
patches: self.patches.move_with_state(state),
search: self.search.move_with_state(state),
shortcuts: self.shortcuts.move_with_state(state),
shortcuts,
help: self.help.move_with_state(state),
props: ListPageProps::from(state),
..self
@@ -118,29 +136,6 @@ impl<'a> Render<()> for ListPage<'a> {
let area = frame.size();
let layout = tui::ui::layout::default_page(area, 0u16, 1u16);
let shortcuts = if self.props.show_search {
vec![
Shortcut::new("esc", "cancel"),
Shortcut::new("enter", "apply"),
]
} else if self.props.show_help {
vec![Shortcut::new("?", "close")]
} else {
match self.props.mode {
Mode::Id => vec![
Shortcut::new("enter", "select"),
Shortcut::new("/", "search"),
],
Mode::Operation => vec![
Shortcut::new("enter", "show"),
Shortcut::new("c", "checkout"),
Shortcut::new("d", "diff"),
Shortcut::new("/", "search"),
Shortcut::new("?", "help"),
],
}
};
if self.props.show_search {
let component_layout = Layout::vertical([Constraint::Min(1), Constraint::Length(2)])
.split(layout.component);
@@ -154,14 +149,7 @@ impl<'a> Render<()> for ListPage<'a> {
self.patches.render::<B>(frame, layout.component, ());
}
self.shortcuts.render::<B>(
frame,
layout.shortcuts,
ShortcutsProps {
shortcuts,
divider: '∙',
},
);
self.shortcuts.render::<B>(frame, layout.shortcuts, ());
}
}
+40 -23
View File
@@ -37,41 +37,57 @@ pub trait ToRow {
fn to_row(&self) -> Vec<Cell>;
}
pub struct Shortcut {
pub short: String,
pub long: String,
pub struct ShortcutsProps {
pub shortcuts: Vec<(String, String)>,
pub divider: char,
}
impl Shortcut {
pub fn new(short: &str, long: &str) -> Self {
impl Default for ShortcutsProps {
fn default() -> Self {
Self {
short: short.to_string(),
long: long.to_string(),
shortcuts: vec![],
divider: '∙',
}
}
}
pub struct ShortcutsProps {
pub shortcuts: Vec<Shortcut>,
pub divider: char,
}
pub struct Shortcuts<A> {
/// Message sender
pub action_tx: UnboundedSender<A>,
/// Internal properties
props: ShortcutsProps,
}
impl<S, A> Widget<S, A> for Shortcuts<A> {
fn new(state: &S, action_tx: UnboundedSender<A>) -> Self
impl<A> Shortcuts<A> {
pub fn divider(mut self, divider: char) -> Self {
self.props.divider = divider;
self
}
pub fn shortcuts(mut self, shortcuts: &[(&str, &str)]) -> Self {
self.props.shortcuts.clear();
for (short, long) in shortcuts {
self.props
.shortcuts
.push((short.to_string(), long.to_string()));
}
self
}
}
impl<A> Widget<(), A> for Shortcuts<A> {
fn new(state: &(), action_tx: UnboundedSender<A>) -> Self
where
Self: Sized,
{
Self {
action_tx: action_tx.clone(),
props: ShortcutsProps::default(),
}
.move_with_state(state)
}
fn move_with_state(self, _state: &S) -> Self
fn move_with_state(self, _state: &()) -> Self
where
Self: Sized,
{
@@ -81,22 +97,23 @@ impl<S, A> Widget<S, A> for Shortcuts<A> {
fn handle_key_event(&mut self, _key: termion::event::Key) {}
}
impl<A> Render<ShortcutsProps> for Shortcuts<A> {
fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, props: ShortcutsProps) {
impl<A> Render<()> for Shortcuts<A> {
fn render<B: Backend>(&self, frame: &mut ratatui::Frame, area: Rect, _props: ()) {
use ratatui::widgets::Table;
let mut shortcuts = props.shortcuts.iter().peekable();
let mut shortcuts = self.props.shortcuts.iter().peekable();
let mut row = vec![];
while let Some(shortcut) = shortcuts.next() {
let short = Text::from(shortcut.short.clone()).style(style::gray());
let long = Text::from(shortcut.long.clone()).style(style::gray().dim());
let short = Text::from(shortcut.0.clone()).style(style::gray());
let long = Text::from(shortcut.1.clone()).style(style::gray().dim());
let spacer = Text::from(String::new());
let divider = Text::from(format!(" {} ", props.divider)).style(style::gray().dim());
let divider =
Text::from(format!(" {} ", self.props.divider)).style(style::gray().dim());
row.push((shortcut.short.chars().count(), short));
row.push((shortcut.0.chars().count(), short));
row.push((1, spacer));
row.push((shortcut.long.chars().count(), long));
row.push((shortcut.1.chars().count(), long));
if shortcuts.peek().is_some() {
row.push((3, divider));