feat(tui): Add input for entry rename and related handle events
This commit is contained in:
parent
a135e2417f
commit
4ea4644b90
12
src/main.rs
12
src/main.rs
@ -26,22 +26,24 @@ struct Projects {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
struct Project(PathBuf);
|
struct Project {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn path(&self) -> &PathBuf {
|
pub fn path(&self) -> &PathBuf {
|
||||||
&self.0
|
&self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> String {
|
||||||
let name = self.0.file_name().unwrap();
|
let name = self.path.file_name().unwrap();
|
||||||
name.to_string_lossy().to_string()
|
name.to_string_lossy().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PathBuf> for Project {
|
impl From<PathBuf> for Project {
|
||||||
fn from(value: PathBuf) -> Self {
|
fn from(path: PathBuf) -> Self {
|
||||||
Self(value)
|
Self { path }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
src/tui.rs
51
src/tui.rs
@ -23,6 +23,7 @@ enum Message {
|
|||||||
SelectNext,
|
SelectNext,
|
||||||
Confirm,
|
Confirm,
|
||||||
SearchUpdate,
|
SearchUpdate,
|
||||||
|
RenameEntry,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ enum Mode {
|
|||||||
struct State {
|
struct State {
|
||||||
projects: Projects,
|
projects: Projects,
|
||||||
search: Input,
|
search: Input,
|
||||||
|
rename: Input,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
should_exit: bool,
|
should_exit: bool,
|
||||||
project_table: TableState,
|
project_table: TableState,
|
||||||
@ -51,6 +53,7 @@ impl State {
|
|||||||
Self {
|
Self {
|
||||||
projects,
|
projects,
|
||||||
search: Input::default(),
|
search: Input::default(),
|
||||||
|
rename: Input::default(),
|
||||||
mode: Mode::default(),
|
mode: Mode::default(),
|
||||||
should_exit: false,
|
should_exit: false,
|
||||||
project_table,
|
project_table,
|
||||||
@ -117,6 +120,19 @@ fn handle_key_event(state: &mut State, tx: &mut mpsc::Sender<Message>, event: Ke
|
|||||||
state.search.handle(InputRequest::DeletePrevChar);
|
state.search.handle(InputRequest::DeletePrevChar);
|
||||||
Message::SearchUpdate
|
Message::SearchUpdate
|
||||||
}
|
}
|
||||||
|
(Mode::Search, KeyModifiers::CONTROL, KeyCode::Char('r')) => {
|
||||||
|
state.mode = Mode::Rename;
|
||||||
|
Message::Noop
|
||||||
|
}
|
||||||
|
(Mode::Rename, KeyModifiers::NONE, KeyCode::Esc) => {
|
||||||
|
state.mode = Mode::Search;
|
||||||
|
Message::Noop
|
||||||
|
}
|
||||||
|
(Mode::Rename, KeyModifiers::NONE, KeyCode::Char(c)) => {
|
||||||
|
state.rename.handle(InputRequest::InsertChar(c));
|
||||||
|
Message::Noop
|
||||||
|
}
|
||||||
|
(Mode::Rename, KeyModifiers::NONE, KeyCode::Enter) => Message::RenameEntry,
|
||||||
(_, _, KeyCode::Enter) => Message::Confirm,
|
(_, _, KeyCode::Enter) => Message::Confirm,
|
||||||
|
|
||||||
_ => Message::Noop,
|
_ => Message::Noop,
|
||||||
@ -160,6 +176,7 @@ fn handle_messages(state: &mut State, rx: &mut mpsc::Receiver<Message>) -> Resul
|
|||||||
Message::SearchUpdate => {
|
Message::SearchUpdate => {
|
||||||
state.project_table.select_first();
|
state.project_table.select_first();
|
||||||
}
|
}
|
||||||
|
Message::RenameEntry => {}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,37 +188,29 @@ fn draw(state: &mut State, frame: &mut Frame) {
|
|||||||
let block = Block::default().borders(Borders::ALL);
|
let block = Block::default().borders(Borders::ALL);
|
||||||
frame.render_widget(&block, frame.area());
|
frame.render_widget(&block, frame.area());
|
||||||
|
|
||||||
match state.mode {
|
let layout = Layout::vertical([Constraint::Fill(1), Constraint::Max(2)]);
|
||||||
Mode::Search => {
|
let inner_area = block.inner(frame.area());
|
||||||
let layout = Layout::vertical([Constraint::Fill(1), Constraint::Max(2)]);
|
let layout_rects = layout.split(inner_area);
|
||||||
let inner_area = block.inner(frame.area());
|
draw_list(state, frame, layout_rects[0]);
|
||||||
let layout_rects = layout.split(inner_area);
|
draw_status(state, frame, layout_rects[1]);
|
||||||
draw_list(state, frame, layout_rects[0]);
|
|
||||||
draw_search(state, frame, layout_rects[1]);
|
|
||||||
}
|
|
||||||
Mode::Rename => {
|
|
||||||
let layout = Layout::vertical([Constraint::Fill(1)]);
|
|
||||||
let inner_area = block.inner(frame.area());
|
|
||||||
let layout_rects = layout.split(inner_area);
|
|
||||||
draw_list(state, frame, layout_rects[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn draw_search(state: &mut State, frame: &mut Frame, area: Rect) {
|
fn draw_status(state: &mut State, frame: &mut Frame, area: Rect) {
|
||||||
const PROMPT_PREFIX: &str = "Search: ";
|
let (prefix, input) = match &state.mode {
|
||||||
|
Mode::Search => ("Search: ", &state.search),
|
||||||
|
Mode::Rename => ("Rename: ", &state.rename),
|
||||||
|
};
|
||||||
|
|
||||||
let search_input = &state.search;
|
let scroll_offset = (0, input.visual_scroll(area.width as usize) as u16);
|
||||||
|
|
||||||
let scroll_offset = (0, search_input.visual_scroll(area.width as usize) as u16);
|
let prompt = format!("{prefix}{}", input.value());
|
||||||
|
|
||||||
let prompt = format!("{PROMPT_PREFIX}{}", search_input.value());
|
|
||||||
let search = Paragraph::new(prompt).scroll(scroll_offset);
|
let search = Paragraph::new(prompt).scroll(scroll_offset);
|
||||||
frame.render_widget(search, area);
|
frame.render_widget(search, area);
|
||||||
|
|
||||||
let cursor_pos = (
|
let cursor_pos = (
|
||||||
area.x + (search_input.visual_cursor() + PROMPT_PREFIX.len()) as u16,
|
area.x + (input.visual_cursor() + prefix.len()) as u16,
|
||||||
area.y,
|
area.y,
|
||||||
);
|
);
|
||||||
frame.set_cursor_position(cursor_pos)
|
frame.set_cursor_position(cursor_pos)
|
||||||
|
Loading…
Reference in New Issue
Block a user