diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-06 17:45:39 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-06 17:45:39 +0300 |
| commit | 7ec046a4646e94311fced28a193ff1f10676a7a6 (patch) | |
| tree | fd5f49fab133310582291fc4dd8fa31da9243c74 /src | |
| parent | c1e100be8fa15bc51f3c2e0b1edb38aa95a7cccd (diff) | |
fix: defer new tab creation until dialog confirm
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/input.rs | 98 | ||||
| -rw-r--r-- | src/app/mod.rs | 23 | ||||
| -rw-r--r-- | src/app/state.rs | 4 | ||||
| -rw-r--r-- | src/ui.rs | 1 |
4 files changed, 103 insertions, 23 deletions
diff --git a/src/app/input.rs b/src/app/input.rs index f13ec684..b2a721a6 100644 --- a/src/app/input.rs +++ b/src/app/input.rs @@ -862,10 +862,7 @@ fn execute_navigate_action(state: &mut AppState, action: NavigateAction) { state.next_workspace(); leave_navigate_mode(state); } - NavigateAction::NewTab => { - state.request_new_tab = true; - leave_navigate_mode(state); - } + NavigateAction::NewTab => open_new_tab_dialog(state), NavigateAction::RenameTab => open_rename_active_tab(state, false), NavigateAction::PreviousTab => { state.previous_tab(); @@ -921,6 +918,8 @@ fn open_rename_workspace(state: &mut AppState, ws_idx: usize) { } fn open_rename_active_tab(state: &mut AppState, replace_on_type: bool) { + state.creating_new_tab = false; + state.requested_new_tab_name = None; if let Some(ws) = state.active.and_then(|i| state.workspaces.get(i)) { if let Some(name) = ws.active_tab_display_name() { state.name_input = name; @@ -930,6 +929,19 @@ fn open_rename_active_tab(state: &mut AppState, replace_on_type: bool) { } } +fn open_new_tab_dialog(state: &mut AppState) { + let next_tab_name = state + .active + .and_then(|i| state.workspaces.get(i)) + .map(|ws| (ws.tabs.len() + 1).to_string()) + .unwrap_or_else(|| "1".to_string()); + state.creating_new_tab = true; + state.requested_new_tab_name = None; + state.name_input = next_tab_name; + state.name_input_replace_on_type = true; + state.mode = Mode::RenameTab; +} + /// Return to the appropriate mode after completing a modal action. /// Goes to Terminal if a workspace is active, otherwise Navigate. fn leave_modal(state: &mut AppState) { @@ -1017,21 +1029,32 @@ fn apply_rename_action(state: &mut AppState, action: ModalAction) { } else { state.name_input.trim().to_string() }; - if !new_name.is_empty() { - match state.mode { - Mode::RenameWorkspace if !state.workspaces.is_empty() => { + match state.mode { + Mode::RenameWorkspace if !state.workspaces.is_empty() => { + if !new_name.is_empty() { state.workspaces[state.selected].set_custom_name(new_name); } - Mode::RenameTab => { + } + Mode::RenameTab if state.creating_new_tab => { + state.request_new_tab = true; + state.requested_new_tab_name = if new_name.is_empty() { + None + } else { + Some(new_name) + }; + } + Mode::RenameTab => { + if !new_name.is_empty() { if let Some(ws) = state.active.and_then(|i| state.workspaces.get_mut(i)) { if let Some(tab) = ws.active_tab_mut() { tab.set_custom_name(new_name); } } } - _ => {} } + _ => {} } + state.creating_new_tab = false; state.name_input.clear(); state.name_input_replace_on_type = false; leave_modal(state); @@ -1041,6 +1064,8 @@ fn apply_rename_action(state: &mut AppState, action: ModalAction) { state.name_input_replace_on_type = false; } ModalAction::Cancel => { + state.creating_new_tab = false; + state.requested_new_tab_name = None; state.name_input.clear(); state.name_input_replace_on_type = false; leave_modal(state); @@ -1145,8 +1170,7 @@ fn apply_context_menu_action(state: &mut AppState, menu: ContextMenuState, idx: state.selected = ws_idx; state.active = Some(ws_idx); state.switch_tab(tab_idx); - state.request_new_tab = true; - state.mode = Mode::Terminal; + open_new_tab_dialog(state); } (ContextMenuKind::Tab { ws_idx, tab_idx }, Some("Rename")) => { state.selected = ws_idx; @@ -1941,8 +1965,7 @@ impl AppState { return None; } if self.on_new_tab_button(mouse.column, mouse.row) { - self.request_new_tab = true; - self.mode = Mode::Terminal; + open_new_tab_dialog(self); return None; } @@ -3069,6 +3092,55 @@ mod tests { } #[test] + fn new_tab_action_opens_dialog_without_creating_tab() { + let mut state = state_with_workspaces(&["test"]); + + execute_navigate_action(&mut state, NavigateAction::NewTab); + + assert_eq!(state.mode, Mode::RenameTab); + assert!(state.creating_new_tab); + assert_eq!(state.name_input, "2"); + assert!(state.name_input_replace_on_type); + assert!(!state.request_new_tab); + assert_eq!(state.workspaces[0].tabs.len(), 1); + } + + #[test] + fn cancel_new_tab_dialog_leaves_workspace_unchanged() { + let mut state = state_with_workspaces(&["test"]); + open_new_tab_dialog(&mut state); + + handle_rename_key( + &mut state, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Terminal); + assert!(!state.creating_new_tab); + assert!(!state.request_new_tab); + assert!(state.requested_new_tab_name.is_none()); + assert_eq!(state.workspaces[0].tabs.len(), 1); + } + + #[test] + fn saving_new_tab_dialog_requests_creation_with_name() { + let mut state = state_with_workspaces(&["test"]); + open_new_tab_dialog(&mut state); + state.name_input = "logs".into(); + state.name_input_replace_on_type = false; + + handle_rename_key( + &mut state, + KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Terminal); + assert!(!state.creating_new_tab); + assert!(state.request_new_tab); + assert_eq!(state.requested_new_tab_name.as_deref(), Some("logs")); + } + + #[test] fn confirm_close_keyboard_actions_are_direct_not_focused() { let mut state = state_with_workspaces(&["a", "b"]); state.mode = Mode::ConfirmClose; diff --git a/src/app/mod.rs b/src/app/mod.rs index 901d9528..14274210 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -201,6 +201,8 @@ impl App { should_quit: false, request_new_workspace: false, request_new_tab: false, + creating_new_tab: false, + requested_new_tab_name: None, request_complete_onboarding: false, name_input: String::new(), name_input_replace_on_type: false, @@ -1906,6 +1908,7 @@ impl App { } fn create_tab(&mut self) { + let custom_name = self.state.requested_new_tab_name.take(); let initial_cwd = self .state .active @@ -1913,16 +1916,16 @@ impl App { .or_else(|| std::env::current_dir().ok()) .unwrap_or_else(|| std::path::PathBuf::from("/")); match self.create_tab_with_options(initial_cwd, true) { - Ok(_) => { - if let Some(ws) = self - .state - .active - .and_then(|ws_idx| self.state.workspaces.get(ws_idx)) - { - if let Some(name) = ws.active_tab_display_name() { - self.state.name_input = name; - self.state.name_input_replace_on_type = true; - self.state.mode = Mode::RenameTab; + Ok(tab_idx) => { + if let Some(name) = custom_name { + if let Some(ws) = self + .state + .active + .and_then(|ws_idx| self.state.workspaces.get_mut(ws_idx)) + { + if let Some(tab) = ws.tabs.get_mut(tab_idx) { + tab.set_custom_name(name); + } } } } diff --git a/src/app/state.rs b/src/app/state.rs index 9f2e8e5b..74daeeb8 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -558,6 +558,8 @@ pub struct AppState { pub should_quit: bool, pub request_new_workspace: bool, pub request_new_tab: bool, + pub creating_new_tab: bool, + pub requested_new_tab_name: Option<String>, pub request_complete_onboarding: bool, pub name_input: String, pub name_input_replace_on_type: bool, @@ -657,6 +659,8 @@ impl AppState { should_quit: false, request_new_workspace: false, request_new_tab: false, + creating_new_tab: false, + requested_new_tab_name: None, request_complete_onboarding: false, name_input: String::new(), name_input_replace_on_type: false, @@ -2098,6 +2098,7 @@ fn render_rename_overlay(app: &AppState, frame: &mut Frame, area: Rect) { let title = match app.mode { Mode::RenameWorkspace => "rename workspace", + Mode::RenameTab if app.creating_new_tab => "new tab", Mode::RenameTab => "rename tab", _ => return, }; |
