diff --git a/i18n/en/cosmic_term.ftl b/i18n/en/cosmic_term.ftl index 38a0b13..7db8a16 100644 --- a/i18n/en/cosmic_term.ftl +++ b/i18n/en/cosmic_term.ftl @@ -132,6 +132,8 @@ zoom-reset = Default text size zoom-out = Smaller text next-tab = Next tab previous-tab = Previous tab +move-tab-left = Move tab left +move-tab-right = Move tab right split-horizontal = Split horizontal split-vertical = Split vertical pane-toggle-maximize = Maximize or restore pane diff --git a/src/main.rs b/src/main.rs index 4b96013..35a83d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use cosmic::iced::keyboard::key::Physical; use cosmic::widget::menu::action::MenuAction; use cosmic::widget::menu::key_bind::KeyBind; use cosmic::widget::pane_grid::Pane; -use cosmic::widget::segmented_button::ReorderEvent; +use cosmic::widget::segmented_button::{InsertPosition, ReorderEvent}; use cosmic::{ Application, ApplicationExt, Element, action, app::{Core, Settings, Task, context_drawer}, @@ -281,6 +281,8 @@ pub enum Action { TabNewNoProfile, TabNext, TabPrev, + TabMoveLeft, + TabMoveRight, ToggleFullscreen, WindowClose, WindowNew, @@ -334,6 +336,8 @@ impl Action { Self::TabNewNoProfile => Message::TabNewNoProfile, Self::TabNext => Message::TabNext, Self::TabPrev => Message::TabPrev, + Self::TabMoveLeft => Message::TabMoveLeft, + Self::TabMoveRight => Message::TabMoveRight, Self::ToggleFullscreen => Message::ToggleFullscreen, Self::WindowClose => Message::WindowClose, Self::WindowNew => Message::WindowNew, @@ -442,6 +446,8 @@ pub enum Message { TabNewNoProfile, TabNext, TabPrev, + TabMoveLeft, + TabMoveRight, TermEvent(pane_grid::Pane, segmented_button::Entity, TermEvent), TermEventTx(mpsc::UnboundedSender<(pane_grid::Pane, segmented_button::Entity, TermEvent)>), ToggleFullscreen, @@ -2960,6 +2966,33 @@ impl Application for App { } } } + Message::TabMoveLeft => { + if let Some(tab_model) = self.pane_model.active_mut() { + let pos: usize = tab_model + .position(tab_model.active()) + .expect("at least one tab is always open") + .into(); + if pos > 0 { + let dragged = tab_model.iter().nth(pos).expect("tab unavailable"); + let target = tab_model.iter().nth(pos - 1).expect("previous tab missing"); + tab_model.reorder(dragged, target, InsertPosition::Before); + } + } + } + Message::TabMoveRight => { + if let Some(tab_model) = self.pane_model.active_mut() { + let len = tab_model.iter().count(); + let pos: usize = tab_model + .position(tab_model.active()) + .expect("at least one tab is always open") + .into(); + if pos < len - 1 && len > 1 { + let dragged = tab_model.iter().nth(pos).expect("tab unavailable"); + let target = tab_model.iter().nth(pos + 1).expect("next tab missing"); + tab_model.reorder(dragged, target, InsertPosition::After); + } + } + } Message::TermEvent(pane, entity, event) => { match event { TermEvent::Bell => { diff --git a/src/menu.rs b/src/menu.rs index bc66cac..09087d1 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -195,6 +195,9 @@ pub fn menu_bar<'a>( MenuItem::Button(fl!("next-tab"), None, Action::TabNext), MenuItem::Button(fl!("previous-tab"), None, Action::TabPrev), MenuItem::Divider, + MenuItem::Button(fl!("move-tab-left"), None, Action::TabMoveLeft), + MenuItem::Button(fl!("move-tab-right"), None, Action::TabMoveRight), + MenuItem::Divider, MenuItem::Button( fl!("split-horizontal"), None, diff --git a/src/shortcuts.rs b/src/shortcuts.rs index bf5d18c..21e7e1b 100644 --- a/src/shortcuts.rs +++ b/src/shortcuts.rs @@ -87,6 +87,8 @@ pub enum KeyBindAction { TabNew, TabNext, TabPrev, + TabMoveLeft, + TabMoveRight, ToggleFullscreen, WindowClose, WindowNew, @@ -127,6 +129,8 @@ impl KeyBindAction { Self::TabNew => Some(Action::TabNew), Self::TabNext => Some(Action::TabNext), Self::TabPrev => Some(Action::TabPrev), + Self::TabMoveLeft => Some(Action::TabMoveLeft), + Self::TabMoveRight => Some(Action::TabMoveRight), Self::ToggleFullscreen => Some(Action::ToggleFullscreen), Self::WindowClose => Some(Action::WindowClose), Self::WindowNew => Some(Action::WindowNew), @@ -288,6 +292,8 @@ pub fn action_label(action: KeyBindAction) -> String { KeyBindAction::TabNew => fl!("new-tab"), KeyBindAction::TabNext => fl!("next-tab"), KeyBindAction::TabPrev => fl!("previous-tab"), + KeyBindAction::TabMoveLeft => fl!("move-tab-left"), + KeyBindAction::TabMoveRight => fl!("move-tab-right"), KeyBindAction::ToggleFullscreen => fl!("toggle-fullscreen"), KeyBindAction::WindowClose => fl!("close-window"), KeyBindAction::WindowNew => fl!("new-window"), @@ -322,6 +328,8 @@ pub fn shortcut_groups() -> Vec { KeyBindAction::TabClose, KeyBindAction::TabNext, KeyBindAction::TabPrev, + KeyBindAction::TabMoveLeft, + KeyBindAction::TabMoveRight, KeyBindAction::TabActivate0, KeyBindAction::TabActivate1, KeyBindAction::TabActivate2, @@ -470,6 +478,10 @@ fn fallback_shortcuts() -> Shortcuts { bind!([Ctrl], "Tab", TabNext); bind!([Ctrl, Shift], "Tab", TabPrev); + // Ctrl+Shift+PageUp/Down move tabs left and right + bind!([Ctrl, Shift], "PageUp", TabMoveLeft); + bind!([Ctrl, Shift], "PageDown", TabMoveRight); + // Ctrl+Shift+# activates tabs by index bind!([Ctrl, Shift], "1", TabActivate0); bind!([Ctrl, Shift], "2", TabActivate1);