mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-16 00:10:38 +08:00
lib: Rework page shortcuts
This commit is contained in:
@@ -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, ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user