feat(tui): Add input for entry rename and related handle events

This commit is contained in:
Patrick Auernig 2025-01-06 18:52:03 +01:00
parent a135e2417f
commit 4ea4644b90
2 changed files with 37 additions and 26 deletions

View File

@ -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 }
} }
} }

View File

@ -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)