Add space switching shortcuts
This commit is contained in:
@@ -23,7 +23,9 @@ actions!(
|
|||||||
OpenTaskManager,
|
OpenTaskManager,
|
||||||
Quit,
|
Quit,
|
||||||
RestoreClosedTab,
|
RestoreClosedTab,
|
||||||
|
SelectNextSpace,
|
||||||
SelectNextTab,
|
SelectNextTab,
|
||||||
|
SelectPreviousSpace,
|
||||||
SelectPreviousTab,
|
SelectPreviousTab,
|
||||||
SplitRight,
|
SplitRight,
|
||||||
ToggleFavoriteTab,
|
ToggleFavoriteTab,
|
||||||
@@ -59,6 +61,9 @@ fn main() {
|
|||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Restore Closed Tab", RestoreClosedTab),
|
MenuItem::action("Restore Closed Tab", RestoreClosedTab),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
|
MenuItem::action("Next Space", SelectNextSpace),
|
||||||
|
MenuItem::action("Previous Space", SelectPreviousSpace),
|
||||||
|
MenuItem::separator(),
|
||||||
MenuItem::action("Open Downloads", OpenDownloads),
|
MenuItem::action("Open Downloads", OpenDownloads),
|
||||||
MenuItem::action("Open History", OpenHistory),
|
MenuItem::action("Open History", OpenHistory),
|
||||||
MenuItem::action("Open Task Manager", OpenTaskManager),
|
MenuItem::action("Open Task Manager", OpenTaskManager),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ mod reading_list;
|
|||||||
mod render;
|
mod render;
|
||||||
mod sidebar;
|
mod sidebar;
|
||||||
mod site_permissions;
|
mod site_permissions;
|
||||||
|
mod spaces;
|
||||||
mod splits;
|
mod splits;
|
||||||
mod tab_groups;
|
mod tab_groups;
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ impl ElyShell {
|
|||||||
.on_action(cx.listener(Self::on_open_settings))
|
.on_action(cx.listener(Self::on_open_settings))
|
||||||
.on_action(cx.listener(Self::on_open_task_manager))
|
.on_action(cx.listener(Self::on_open_task_manager))
|
||||||
.on_action(cx.listener(Self::on_restore_closed_tab))
|
.on_action(cx.listener(Self::on_restore_closed_tab))
|
||||||
|
.on_action(cx.listener(Self::on_select_next_space))
|
||||||
.on_action(cx.listener(Self::on_select_next_tab))
|
.on_action(cx.listener(Self::on_select_next_tab))
|
||||||
|
.on_action(cx.listener(Self::on_select_previous_space))
|
||||||
.on_action(cx.listener(Self::on_select_previous_tab))
|
.on_action(cx.listener(Self::on_select_previous_tab))
|
||||||
.on_action(cx.listener(Self::on_split_right))
|
.on_action(cx.listener(Self::on_split_right))
|
||||||
.on_action(cx.listener(Self::on_toggle_favorite_tab))
|
.on_action(cx.listener(Self::on_toggle_favorite_tab))
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use gpui::{Context, Window};
|
||||||
|
|
||||||
|
use super::{ElyShell, ShellState};
|
||||||
|
use crate::{SelectNextSpace, SelectPreviousSpace};
|
||||||
|
|
||||||
|
impl ElyShell {
|
||||||
|
pub(super) fn on_select_next_space(
|
||||||
|
&mut self,
|
||||||
|
_: &SelectNextSpace,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state
|
||||||
|
&& core.select_next_space().is_ok()
|
||||||
|
{
|
||||||
|
self.sync_address_input(window, cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn on_select_previous_space(
|
||||||
|
&mut self,
|
||||||
|
_: &SelectPreviousSpace,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state
|
||||||
|
&& core.select_previous_space().is_ok()
|
||||||
|
{
|
||||||
|
self.sync_address_input(window, cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@ use gpui::{App, KeyBinding};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
||||||
OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextTab, SelectPreviousTab,
|
OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextSpace, SelectNextTab,
|
||||||
SplitRight, ToggleFavoriteTab, ToggleSidebar,
|
SelectPreviousSpace, SelectPreviousTab, SplitRight, ToggleFavoriteTab, ToggleSidebar,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
@@ -30,6 +30,8 @@ pub(crate) enum ShortcutAction {
|
|||||||
OpenNewTab,
|
OpenNewTab,
|
||||||
CloseCurrentTab,
|
CloseCurrentTab,
|
||||||
RestoreClosedTab,
|
RestoreClosedTab,
|
||||||
|
SelectNextSpace,
|
||||||
|
SelectPreviousSpace,
|
||||||
SelectNextTab,
|
SelectNextTab,
|
||||||
SelectPreviousTab,
|
SelectPreviousTab,
|
||||||
SplitRight,
|
SplitRight,
|
||||||
@@ -50,6 +52,8 @@ impl ShortcutAction {
|
|||||||
Self::OpenNewTab => "New Tab",
|
Self::OpenNewTab => "New Tab",
|
||||||
Self::CloseCurrentTab => "Close Tab",
|
Self::CloseCurrentTab => "Close Tab",
|
||||||
Self::RestoreClosedTab => "Restore Closed Tab",
|
Self::RestoreClosedTab => "Restore Closed Tab",
|
||||||
|
Self::SelectNextSpace => "Next Space",
|
||||||
|
Self::SelectPreviousSpace => "Previous Space",
|
||||||
Self::SelectNextTab => "Next Tab",
|
Self::SelectNextTab => "Next Tab",
|
||||||
Self::SelectPreviousTab => "Previous Tab",
|
Self::SelectPreviousTab => "Previous Tab",
|
||||||
Self::SplitRight => "Split Right",
|
Self::SplitRight => "Split Right",
|
||||||
@@ -69,6 +73,8 @@ impl ShortcutAction {
|
|||||||
Self::OpenNewTab
|
Self::OpenNewTab
|
||||||
| Self::CloseCurrentTab
|
| Self::CloseCurrentTab
|
||||||
| Self::RestoreClosedTab
|
| Self::RestoreClosedTab
|
||||||
|
| Self::SelectNextSpace
|
||||||
|
| Self::SelectPreviousSpace
|
||||||
| Self::SelectNextTab
|
| Self::SelectNextTab
|
||||||
| Self::SelectPreviousTab
|
| Self::SelectPreviousTab
|
||||||
| Self::SplitRight
|
| Self::SplitRight
|
||||||
@@ -87,6 +93,8 @@ impl ShortcutAction {
|
|||||||
Self::OpenNewTab => Some(">new-tab"),
|
Self::OpenNewTab => Some(">new-tab"),
|
||||||
Self::CloseCurrentTab => Some(">close-tab"),
|
Self::CloseCurrentTab => Some(">close-tab"),
|
||||||
Self::RestoreClosedTab => Some(">restore-tab"),
|
Self::RestoreClosedTab => Some(">restore-tab"),
|
||||||
|
Self::SelectNextSpace => None,
|
||||||
|
Self::SelectPreviousSpace => None,
|
||||||
Self::SelectNextTab => None,
|
Self::SelectNextTab => None,
|
||||||
Self::SelectPreviousTab => None,
|
Self::SelectPreviousTab => None,
|
||||||
Self::SplitRight => Some(">split-right"),
|
Self::SplitRight => Some(">split-right"),
|
||||||
@@ -127,6 +135,8 @@ pub(crate) const SHORTCUT_ACTIONS: &[ShortcutAction] = &[
|
|||||||
ShortcutAction::OpenNewTab,
|
ShortcutAction::OpenNewTab,
|
||||||
ShortcutAction::CloseCurrentTab,
|
ShortcutAction::CloseCurrentTab,
|
||||||
ShortcutAction::RestoreClosedTab,
|
ShortcutAction::RestoreClosedTab,
|
||||||
|
ShortcutAction::SelectNextSpace,
|
||||||
|
ShortcutAction::SelectPreviousSpace,
|
||||||
ShortcutAction::SelectNextTab,
|
ShortcutAction::SelectNextTab,
|
||||||
ShortcutAction::SelectPreviousTab,
|
ShortcutAction::SelectPreviousTab,
|
||||||
ShortcutAction::SplitRight,
|
ShortcutAction::SplitRight,
|
||||||
@@ -164,6 +174,10 @@ pub(crate) const SHORTCUT_BINDINGS: &[ShortcutBinding] = &[
|
|||||||
shortcut(ShortcutAction::ToggleFavoriteTab, ShortcutPlatform::WindowsLinux, "ctrl-shift-f"),
|
shortcut(ShortcutAction::ToggleFavoriteTab, ShortcutPlatform::WindowsLinux, "ctrl-shift-f"),
|
||||||
shortcut(ShortcutAction::FocusCommandMode, ShortcutPlatform::Macos, "cmd-shift-p"),
|
shortcut(ShortcutAction::FocusCommandMode, ShortcutPlatform::Macos, "cmd-shift-p"),
|
||||||
shortcut(ShortcutAction::FocusCommandMode, ShortcutPlatform::WindowsLinux, "ctrl-shift-p"),
|
shortcut(ShortcutAction::FocusCommandMode, ShortcutPlatform::WindowsLinux, "ctrl-shift-p"),
|
||||||
|
shortcut(ShortcutAction::SelectNextSpace, ShortcutPlatform::Macos, "cmd-alt-right"),
|
||||||
|
shortcut(ShortcutAction::SelectNextSpace, ShortcutPlatform::WindowsLinux, "ctrl-alt-right"),
|
||||||
|
shortcut(ShortcutAction::SelectPreviousSpace, ShortcutPlatform::Macos, "cmd-alt-left"),
|
||||||
|
shortcut(ShortcutAction::SelectPreviousSpace, ShortcutPlatform::WindowsLinux, "ctrl-alt-left"),
|
||||||
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::Macos, "cmd-shift-]"),
|
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::Macos, "cmd-shift-]"),
|
||||||
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::Macos, "cmd-alt-down"),
|
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::Macos, "cmd-alt-down"),
|
||||||
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::WindowsLinux, "ctrl-tab"),
|
shortcut(ShortcutAction::SelectNextTab, ShortcutPlatform::WindowsLinux, "ctrl-tab"),
|
||||||
@@ -239,7 +253,13 @@ impl ShortcutBinding {
|
|||||||
ShortcutAction::RestoreClosedTab => {
|
ShortcutAction::RestoreClosedTab => {
|
||||||
KeyBinding::new(self.keystroke, RestoreClosedTab, None)
|
KeyBinding::new(self.keystroke, RestoreClosedTab, None)
|
||||||
}
|
}
|
||||||
|
ShortcutAction::SelectNextSpace => {
|
||||||
|
KeyBinding::new(self.keystroke, SelectNextSpace, None)
|
||||||
|
}
|
||||||
ShortcutAction::SelectNextTab => KeyBinding::new(self.keystroke, SelectNextTab, None),
|
ShortcutAction::SelectNextTab => KeyBinding::new(self.keystroke, SelectNextTab, None),
|
||||||
|
ShortcutAction::SelectPreviousSpace => {
|
||||||
|
KeyBinding::new(self.keystroke, SelectPreviousSpace, None)
|
||||||
|
}
|
||||||
ShortcutAction::SelectPreviousTab => {
|
ShortcutAction::SelectPreviousTab => {
|
||||||
KeyBinding::new(self.keystroke, SelectPreviousTab, None)
|
KeyBinding::new(self.keystroke, SelectPreviousTab, None)
|
||||||
}
|
}
|
||||||
@@ -310,6 +330,35 @@ mod tests {
|
|||||||
assert_eq!(bindings, vec!["Cmd + B".to_string(), "Ctrl + B".to_string()]);
|
assert_eq!(bindings, vec!["Cmd + B".to_string(), "Ctrl + B".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn space_switch_shortcuts_have_platform_bindings() {
|
||||||
|
let next_bindings =
|
||||||
|
bindings_for_action(ShortcutAction::SelectNextSpace, ShortcutPlatform::Macos)
|
||||||
|
.chain(bindings_for_action(
|
||||||
|
ShortcutAction::SelectNextSpace,
|
||||||
|
ShortcutPlatform::WindowsLinux,
|
||||||
|
))
|
||||||
|
.map(|binding| binding.display_keystroke())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let previous_bindings =
|
||||||
|
bindings_for_action(ShortcutAction::SelectPreviousSpace, ShortcutPlatform::Macos)
|
||||||
|
.chain(bindings_for_action(
|
||||||
|
ShortcutAction::SelectPreviousSpace,
|
||||||
|
ShortcutPlatform::WindowsLinux,
|
||||||
|
))
|
||||||
|
.map(|binding| binding.display_keystroke())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
next_bindings,
|
||||||
|
vec!["Cmd + Option + RIGHT".to_string(), "Ctrl + Alt + RIGHT".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
previous_bindings,
|
||||||
|
vec!["Cmd + Option + LEFT".to_string(), "Ctrl + Alt + LEFT".to_string()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn every_declared_action_has_a_binding() {
|
fn every_declared_action_has_a_binding() {
|
||||||
for action in SHORTCUT_ACTIONS {
|
for action in SHORTCUT_ACTIONS {
|
||||||
|
|||||||
@@ -229,6 +229,32 @@ impl BrowserCore {
|
|||||||
Ok(tab_id)
|
Ok(tab_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_next_space(&mut self) -> Result<TabId, CoreError> {
|
||||||
|
let spaces = self.sorted_spaces();
|
||||||
|
let Some(active_index) =
|
||||||
|
spaces.iter().position(|space| space.id() == &self.active_space_id)
|
||||||
|
else {
|
||||||
|
return Err(CoreError::SpaceNotFound { id: self.active_space_id.clone() });
|
||||||
|
};
|
||||||
|
|
||||||
|
let next_index = (active_index + 1) % spaces.len();
|
||||||
|
let space_id = spaces[next_index].id().clone();
|
||||||
|
self.select_space(&space_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_previous_space(&mut self) -> Result<TabId, CoreError> {
|
||||||
|
let spaces = self.sorted_spaces();
|
||||||
|
let Some(active_index) =
|
||||||
|
spaces.iter().position(|space| space.id() == &self.active_space_id)
|
||||||
|
else {
|
||||||
|
return Err(CoreError::SpaceNotFound { id: self.active_space_id.clone() });
|
||||||
|
};
|
||||||
|
|
||||||
|
let previous_index = if active_index == 0 { spaces.len() - 1 } else { active_index - 1 };
|
||||||
|
let space_id = spaces[previous_index].id().clone();
|
||||||
|
self.select_space(&space_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_active_space_archive_policy(
|
pub fn set_active_space_archive_policy(
|
||||||
&mut self,
|
&mut self,
|
||||||
archive_policy: ArchivePolicy,
|
archive_policy: ArchivePolicy,
|
||||||
|
|||||||
@@ -152,6 +152,29 @@ fn space_sidebar_width_updates_selected_space() -> Result<(), Box<dyn Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn selecting_adjacent_spaces_uses_sort_order_with_wraparound() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
let work_space_id = core.snapshot()?.active_space_id;
|
||||||
|
let research_space_id = core.create_space("Research", "R", 0x9fc9a2)?;
|
||||||
|
let personal_space_id = core.create_space("Personal", "P", 0x8eb7d4)?;
|
||||||
|
|
||||||
|
core.set_space_sort_key(&work_space_id, 20)?;
|
||||||
|
core.set_space_sort_key(&research_space_id, 10)?;
|
||||||
|
core.set_space_sort_key(&personal_space_id, 30)?;
|
||||||
|
core.select_space(&work_space_id)?;
|
||||||
|
|
||||||
|
core.select_next_space()?;
|
||||||
|
assert_eq!(core.snapshot()?.active_space_id, personal_space_id);
|
||||||
|
|
||||||
|
core.select_next_space()?;
|
||||||
|
assert_eq!(core.snapshot()?.active_space_id, research_space_id);
|
||||||
|
|
||||||
|
core.select_previous_space()?;
|
||||||
|
assert_eq!(core.snapshot()?.active_space_id, personal_space_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn active_space_updated_at(
|
fn active_space_updated_at(
|
||||||
core: &BrowserCore,
|
core: &BrowserCore,
|
||||||
space_id: &ely_domain::SpaceId,
|
space_id: &ely_domain::SpaceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user