From 4c1ba717a77755e28e517dce2e474ce4346fd044 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 17 May 2025 20:14:09 +0800 Subject: [PATCH] refactor: rewrite workspace switcher (#1267) Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 3 +- src/Resources/Locales/zh_CN.axaml | 1 + src/Resources/Locales/zh_TW.axaml | 1 + src/ViewModels/Launcher.cs | 20 +++++ src/ViewModels/WorkspaceSwitcher.cs | 75 ++++++++++++++++++ src/Views/Hotkeys.axaml | 2 +- src/Views/Launcher.axaml | 23 +++++- src/Views/Launcher.axaml.cs | 53 ++----------- src/Views/WorkspaceSwitcher.axaml | 111 +++++++++++++++++++++++++++ src/Views/WorkspaceSwitcher.axaml.cs | 49 ++++++++++++ 10 files changed, 287 insertions(+), 51 deletions(-) create mode 100644 src/ViewModels/WorkspaceSwitcher.cs create mode 100644 src/Views/WorkspaceSwitcher.axaml create mode 100644 src/Views/WorkspaceSwitcher.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index a9114e21..4ce0a1ad 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -386,7 +386,7 @@ Go to previous page Create new page Open Preferences dialog - Switch to corresponding workspace + Switch active workspace REPOSITORY Commit staged changes Commit and push staged changes @@ -635,6 +635,7 @@ Use relative time in histories View Logs Visit '{0}' in Browser + Switch Workspace WORKTREES Add Worktree Prune diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index d196906f..1fd1a610 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -639,6 +639,7 @@ 在提交列表中使用相对时间 查看命令日志 访问远程仓库 '{0}' + 切换工作区 工作树列表 新增工作树 清理 diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 09b11406..eca8214c 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -639,6 +639,7 @@ 在提交列表中使用相對時間 檢視 Git 指令記錄 檢視遠端存放庫 '{0}' + 切換工作區 工作區列表 新增工作區 清理 diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 420d7af9..9a54bb32 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -23,6 +23,12 @@ namespace SourceGit.ViewModels private set; } + public WorkspaceSwitcher WorkspaceSwitcher + { + get => _workspaceSwitcher; + set => SetProperty(ref _workspaceSwitcher, value); + } + public Workspace ActiveWorkspace { get => _activeWorkspace; @@ -130,8 +136,21 @@ namespace SourceGit.ViewModels _ignoreIndexChange = false; } + public void OpenWorkspaceSwitcher() + { + WorkspaceSwitcher = new WorkspaceSwitcher(this); + } + + public void CancelWorkspaceSwitcher() + { + WorkspaceSwitcher = null; + } + public void SwitchWorkspace(Workspace to) { + if (to.IsActive) + return; + foreach (var one in Pages) { if (!one.CanCreatePopup() || one.Data is Repository { IsAutoFetching: true }) @@ -599,5 +618,6 @@ namespace SourceGit.ViewModels private LauncherPage _activePage = null; private bool _ignoreIndexChange = false; private string _title = string.Empty; + private WorkspaceSwitcher _workspaceSwitcher = null; } } diff --git a/src/ViewModels/WorkspaceSwitcher.cs b/src/ViewModels/WorkspaceSwitcher.cs new file mode 100644 index 00000000..01d62744 --- /dev/null +++ b/src/ViewModels/WorkspaceSwitcher.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class WorkspaceSwitcher : ObservableObject + { + public List VisibleWorkspaces + { + get => _visibleWorkspaces; + private set => SetProperty(ref _visibleWorkspaces, value); + } + + public string SearchFilter + { + get => _searchFilter; + set + { + if (SetProperty(ref _searchFilter, value)) + UpdateVisibleWorkspaces(); + } + } + + public Workspace SelectedWorkspace + { + get => _selectedWorkspace; + set => SetProperty(ref _selectedWorkspace, value); + } + + public WorkspaceSwitcher(Launcher launcher) + { + _launcher = launcher; + UpdateVisibleWorkspaces(); + } + + public void ClearFilter() + { + SearchFilter = string.Empty; + } + + public void Switch() + { + if (_selectedWorkspace is { }) + _launcher.SwitchWorkspace(_selectedWorkspace); + + _launcher.CancelWorkspaceSwitcher(); + } + + private void UpdateVisibleWorkspaces() + { + var visible = new List(); + if (string.IsNullOrEmpty(_searchFilter)) + { + visible.AddRange(Preferences.Instance.Workspaces); + } + else + { + foreach (var workspace in Preferences.Instance.Workspaces) + { + if (workspace.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) + visible.Add(workspace); + } + } + + VisibleWorkspaces = visible; + SelectedWorkspace = visible.Count == 0 ? null : visible[0]; + } + + private Launcher _launcher = null; + private List _visibleWorkspaces = null; + private string _searchFilter = string.Empty; + private Workspace _selectedWorkspace = null; + } +} diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index e64ab1e3..5d9a6f9f 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -70,7 +70,7 @@ - + diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 7a2601a6..18643583 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -72,7 +72,7 @@ - + - + - + @@ -102,5 +102,22 @@ + + + + + + + + + + + + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 24fd6dcc..02cc4f08 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -157,6 +157,12 @@ namespace SourceGit.Views if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { + if (e.Key == Key.P) + { + vm.OpenWorkspaceSwitcher(); + e.Handled = true; + } + if (e.Key == Key.W) { vm.CloseTab(null); @@ -248,17 +254,10 @@ namespace SourceGit.Views } } } - else if (e.KeyModifiers.HasFlag(KeyModifiers.Alt)) - { - if (SwitchWorkspace(e.Key)) - { - e.Handled = true; - return; - } - } else if (e.Key == Key.Escape) { vm.ActivePage.CancelPopup(); + vm.CancelWorkspaceSwitcher(); e.Handled = true; return; } @@ -314,44 +313,6 @@ namespace SourceGit.Views e.Handled = true; } - - private bool SwitchWorkspace(Key eKey) - { - var exec = (ViewModels.Launcher l, int idx) => - { - var pref = ViewModels.Preferences.Instance; - if (idx < pref.Workspaces.Count) - l.SwitchWorkspace(pref.Workspaces[idx]); - return true; // Alt+1..9 (or Option+1..9) always mark handled - }; - - if (DataContext is ViewModels.Launcher launcher) - { - switch (eKey) - { - case Key.D1 or Key.NumPad1: - return exec(launcher, 0); - case Key.D2 or Key.NumPad2: - return exec(launcher, 1); - case Key.D3 or Key.NumPad3: - return exec(launcher, 2); - case Key.D4 or Key.NumPad4: - return exec(launcher, 3); - case Key.D5 or Key.NumPad5: - return exec(launcher, 4); - case Key.D6 or Key.NumPad6: - return exec(launcher, 5); - case Key.D7 or Key.NumPad7: - return exec(launcher, 6); - case Key.D8 or Key.NumPad8: - return exec(launcher, 7); - case Key.D9 or Key.NumPad9: - return exec(launcher, 8); - } - } - - return false; - } private KeyModifiers _unhandledModifiers = KeyModifiers.None; private WindowState _lastWindowState = WindowState.Normal; diff --git a/src/Views/WorkspaceSwitcher.axaml b/src/Views/WorkspaceSwitcher.axaml new file mode 100644 index 00000000..49fed451 --- /dev/null +++ b/src/Views/WorkspaceSwitcher.axaml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/WorkspaceSwitcher.axaml.cs b/src/Views/WorkspaceSwitcher.axaml.cs new file mode 100644 index 00000000..04bdae1a --- /dev/null +++ b/src/Views/WorkspaceSwitcher.axaml.cs @@ -0,0 +1,49 @@ +using Avalonia.Controls; +using Avalonia.Input; + +namespace SourceGit.Views +{ + public partial class WorkspaceSwitcher : UserControl + { + public WorkspaceSwitcher() + { + InitializeComponent(); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.Key == Key.Enter && DataContext is ViewModels.WorkspaceSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnItemDoubleTapped(object sender, TappedEventArgs e) + { + if (DataContext is ViewModels.WorkspaceSwitcher switcher) + { + switcher.Switch(); + e.Handled = true; + } + } + + private void OnSearchBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Down && WorkspaceListBox.ItemCount > 0) + { + WorkspaceListBox.Focus(NavigationMethod.Directional); + + if (WorkspaceListBox.SelectedIndex < 0) + WorkspaceListBox.SelectedIndex = 0; + else if (WorkspaceListBox.SelectedIndex < WorkspaceListBox.ItemCount) + WorkspaceListBox.SelectedIndex++; + + e.Handled = true; + } + } + } +} +