diff --git a/TRANSLATION.md b/TRANSLATION.md
index d810b5b2..1a9d00d1 100644
--- a/TRANSLATION.md
+++ b/TRANSLATION.md
@@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository
### 
-### 
+### 
Missing keys in de_DE.axaml
@@ -14,9 +14,11 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Repository.ShowSubmodulesAsTree
-- Text.Repository.WorkspaceSwitcher
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
@@ -26,17 +28,19 @@ This document shows the translation status of each locale file in the repository
-### 
+### 
Missing keys in es_ES.axaml
- Text.Hotkeys.Global.SwitchWorkspace
-- Text.Repository.WorkspaceSwitcher
+- Text.Hotkeys.Global.SwitchTab
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
-### 
+### 
Missing keys in fr_FR.axaml
@@ -61,7 +65,10 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
@@ -70,7 +77,6 @@ This document shows the translation status of each locale file in the repository
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
-- Text.Repository.WorkspaceSwitcher
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
@@ -89,17 +95,19 @@ This document shows the translation status of each locale file in the repository
-### 
+### 
Missing keys in it_IT.axaml
- Text.Hotkeys.Global.SwitchWorkspace
-- Text.Repository.WorkspaceSwitcher
+- Text.Hotkeys.Global.SwitchTab
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
-### 
+### 
Missing keys in ja_JP.axaml
@@ -124,7 +132,10 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
@@ -135,7 +146,6 @@ This document shows the translation status of each locale file in the repository
- Text.Repository.Tags.OrderByNameDes
- Text.Repository.ViewLogs
- Text.Repository.Visit
-- Text.Repository.WorkspaceSwitcher
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
@@ -154,7 +164,7 @@ This document shows the translation status of each locale file in the repository
-### 
+### 
Missing keys in pt_BR.axaml
@@ -208,11 +218,14 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.Clone
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
- Text.InProgress.CherryPick.Head
- Text.InProgress.Merge.Operating
- Text.InProgress.Rebase.StoppedAt
- Text.InProgress.Revert.Head
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Merge.Source
- Text.MergeMultiple
- Text.MergeMultiple.CommitChanges
@@ -245,7 +258,6 @@ This document shows the translation status of each locale file in the repository
- Text.Repository.UseRelativeTimeInHistories
- Text.Repository.ViewLogs
- Text.Repository.Visit
-- Text.Repository.WorkspaceSwitcher
- Text.SetUpstream
- Text.SetUpstream.Local
- Text.SetUpstream.Unset
@@ -274,9 +286,17 @@ This document shows the translation status of each locale file in the repository
-### 
+### 
-### 
+
+Missing keys in ru_RU.axaml
+
+- Text.Hotkeys.Global.SwitchTab
+- Text.Launcher.Pages
+
+
+
+### 
Missing keys in ta_IN.axaml
@@ -301,7 +321,10 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
@@ -310,7 +333,6 @@ This document shows the translation status of each locale file in the repository
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
-- Text.Repository.WorkspaceSwitcher
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
@@ -329,7 +351,7 @@ This document shows the translation status of each locale file in the repository
-### 
+### 
Missing keys in uk_UA.axaml
@@ -350,7 +372,10 @@ This document shows the translation status of each locale file in the repository
- Text.GitFlow.FinishWithPush
- Text.GitFlow.FinishWithSquash
- Text.Hotkeys.Global.SwitchWorkspace
+- Text.Hotkeys.Global.SwitchTab
- Text.Hotkeys.TextEditor.OpenExternalMergeTool
+- Text.Launcher.Workspaces
+- Text.Launcher.Pages
- Text.Preferences.Git.IgnoreCRAtEOLInDiff
- Text.Repository.BranchSort
- Text.Repository.BranchSort.ByCommitterDate
@@ -359,7 +384,6 @@ This document shows the translation status of each locale file in the repository
- Text.Repository.ShowSubmodulesAsTree
- Text.Repository.ViewLogs
- Text.Repository.Visit
-- Text.Repository.WorkspaceSwitcher
- Text.Submodule.Status
- Text.Submodule.Status.Modified
- Text.Submodule.Status.NotInited
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 4ce0a1ad..bb259272 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -387,6 +387,7 @@
Create new page
Open Preferences dialog
Switch active workspace
+ Switch active page
REPOSITORY
Commit staged changes
Commit and push staged changes
@@ -429,6 +430,8 @@
Open in Browser
ERROR
NOTICE
+ Workspaces
+ Pages
Merge Branch
Into:
Merge Option:
@@ -635,7 +638,6 @@
Use relative time in histories
View Logs
Visit '{0}' in Browser
- Switch Workspace
WORKTREES
Add Worktree
Prune
diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml
index 1fabffda..77a7ba4f 100644
--- a/src/Resources/Locales/ru_RU.axaml
+++ b/src/Resources/Locales/ru_RU.axaml
@@ -433,6 +433,7 @@
Открыть в браузере
ОШИБКА
УВЕДОМЛЕНИЕ
+ Рабочие места
Влить ветку
В:
Опции слияния:
@@ -639,7 +640,6 @@
Использовать относительное время в историях
Просмотр журналов
Посетить '{0}' в браузере
- Переключить рабочее место
РАБОЧИЕ КАТАЛОГИ
ДОБАВИТЬ РАБОЧИЙ КАТАЛОГ
ОБРЕЗАТЬ
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index 1fd1a610..18569a14 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -391,6 +391,7 @@
新建页面
打开偏好设置面板
切换工作区
+ 切换显示页面
仓库页面快捷键
提交暂存区更改
提交暂存区更改并推送
@@ -433,6 +434,8 @@
在浏览器中访问
出错了
系统提示
+ 工作区列表
+ 页面列表
合并分支
目标分支 :
合并方式 :
@@ -639,7 +642,6 @@
在提交列表中使用相对时间
查看命令日志
访问远程仓库 '{0}'
- 切换工作区
工作树列表
新增工作树
清理
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index eca8214c..ded99a14 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -391,6 +391,7 @@
新增頁面
開啟偏好設定面板
切換工作區
+ 切換目前頁面
存放庫頁面快速鍵
提交暫存區變更
提交暫存區變更並推送
@@ -433,6 +434,8 @@
在瀏覽器中開啟連結
發生錯誤
系統提示
+ 工作區列表
+ 頁面列表
合併分支
目標分支:
合併方式:
@@ -639,7 +642,6 @@
在提交列表中使用相對時間
檢視 Git 指令記錄
檢視遠端存放庫 '{0}'
- 切換工作區
工作區列表
新增工作區
清理
diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs
index 555954d9..f21d2636 100644
--- a/src/ViewModels/Histories.cs
+++ b/src/ViewModels/Histories.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
@@ -387,7 +387,7 @@ namespace SourceGit.ViewModels
{
var builder = new StringBuilder();
foreach (var c in selected)
- builder.AppendLine($"{c.SHA.Substring(0, 10)} - {c.Subject}");
+ builder.AppendLine($"{c.SHA.AsSpan(0, 10)} - {c.Subject}");
App.CopyText(builder.ToString());
e.Handled = true;
@@ -780,7 +780,7 @@ namespace SourceGit.ViewModels
copyInfo.Icon = App.CreateMenuIcon("Icons.Info");
copyInfo.Click += (_, e) =>
{
- App.CopyText($"{commit.SHA.Substring(0, 10)} - {commit.Subject}");
+ App.CopyText($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}");
e.Handled = true;
};
diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs
index 9a54bb32..4c0714df 100644
--- a/src/ViewModels/Launcher.cs
+++ b/src/ViewModels/Launcher.cs
@@ -23,12 +23,6 @@ namespace SourceGit.ViewModels
private set;
}
- public WorkspaceSwitcher WorkspaceSwitcher
- {
- get => _workspaceSwitcher;
- set => SetProperty(ref _workspaceSwitcher, value);
- }
-
public Workspace ActiveWorkspace
{
get => _activeWorkspace;
@@ -50,6 +44,12 @@ namespace SourceGit.ViewModels
}
}
+ public IDisposable Switcher
+ {
+ get => _switcher;
+ private set => SetProperty(ref _switcher, value);
+ }
+
public Launcher(string startupRepo)
{
_ignoreIndexChange = true;
@@ -138,17 +138,23 @@ namespace SourceGit.ViewModels
public void OpenWorkspaceSwitcher()
{
- WorkspaceSwitcher = new WorkspaceSwitcher(this);
+ Switcher = new WorkspaceSwitcher(this);
}
- public void CancelWorkspaceSwitcher()
+ public void OpenTabSwitcher()
{
- WorkspaceSwitcher = null;
+ Switcher = new LauncherPageSwitcher(this);
+ }
+
+ public void CancelSwitcher()
+ {
+ Switcher?.Dispose();
+ Switcher = null;
}
public void SwitchWorkspace(Workspace to)
{
- if (to.IsActive)
+ if (to == null || to.IsActive)
return;
foreach (var one in Pages)
@@ -618,6 +624,6 @@ namespace SourceGit.ViewModels
private LauncherPage _activePage = null;
private bool _ignoreIndexChange = false;
private string _title = string.Empty;
- private WorkspaceSwitcher _workspaceSwitcher = null;
+ private IDisposable _switcher = null;
}
}
diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs
new file mode 100644
index 00000000..5f53021d
--- /dev/null
+++ b/src/ViewModels/LauncherPageSwitcher.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace SourceGit.ViewModels
+{
+ public class LauncherPageSwitcher : ObservableObject, IDisposable
+ {
+ public List VisiblePages
+ {
+ get => _visiblePages;
+ private set => SetProperty(ref _visiblePages, value);
+ }
+
+ public string SearchFilter
+ {
+ get => _searchFilter;
+ set
+ {
+ if (SetProperty(ref _searchFilter, value))
+ UpdateVisiblePages();
+ }
+ }
+
+ public LauncherPage SelectedPage
+ {
+ get => _selectedPage;
+ set => SetProperty(ref _selectedPage, value);
+ }
+
+ public LauncherPageSwitcher(Launcher launcher)
+ {
+ _launcher = launcher;
+ UpdateVisiblePages();
+ }
+
+ public void ClearFilter()
+ {
+ SearchFilter = string.Empty;
+ }
+
+ public void Switch()
+ {
+ _launcher.ActivePage = _selectedPage ?? _launcher.ActivePage;
+ _launcher.CancelSwitcher();
+ }
+
+ public void Dispose()
+ {
+ _visiblePages.Clear();
+ _selectedPage = null;
+ _searchFilter = string.Empty;
+ }
+
+ private void UpdateVisiblePages()
+ {
+ var visible = new List();
+ if (string.IsNullOrEmpty(_searchFilter))
+ {
+ visible.AddRange(_launcher.Pages);
+ }
+ else
+ {
+ foreach (var page in _launcher.Pages)
+ {
+ if (page.Node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase) ||
+ (page.Node.IsRepository && page.Node.Id.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)))
+ {
+ visible.Add(page);
+ }
+ }
+ }
+
+ VisiblePages = visible;
+ SelectedPage = visible.Count > 0 ? visible[0] : null;
+ }
+
+ private Launcher _launcher = null;
+ private List _visiblePages = [];
+ private string _searchFilter = string.Empty;
+ private LauncherPage _selectedPage = null;
+ }
+}
diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs
index d24f6fbf..a3f63251 100644
--- a/src/ViewModels/Repository.cs
+++ b/src/ViewModels/Repository.cs
@@ -2632,7 +2632,7 @@ namespace SourceGit.ViewModels
if (node.Path.Equals(path, StringComparison.Ordinal))
return node;
- if (path!.StartsWith(node.Path, StringComparison.Ordinal))
+ if (path.StartsWith(node.Path, StringComparison.Ordinal))
{
var founded = FindBranchNode(node.Children, path);
if (founded != null)
diff --git a/src/ViewModels/WorkspaceSwitcher.cs b/src/ViewModels/WorkspaceSwitcher.cs
index 01d62744..7a2da9be 100644
--- a/src/ViewModels/WorkspaceSwitcher.cs
+++ b/src/ViewModels/WorkspaceSwitcher.cs
@@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
- public class WorkspaceSwitcher : ObservableObject
+ public class WorkspaceSwitcher : ObservableObject, IDisposable
{
public List VisibleWorkspaces
{
@@ -41,10 +41,15 @@ namespace SourceGit.ViewModels
public void Switch()
{
- if (_selectedWorkspace is { })
- _launcher.SwitchWorkspace(_selectedWorkspace);
+ _launcher.SwitchWorkspace(_selectedWorkspace);
+ _launcher.CancelSwitcher();
+ }
- _launcher.CancelWorkspaceSwitcher();
+ public void Dispose()
+ {
+ _visibleWorkspaces.Clear();
+ _selectedWorkspace = null;
+ _searchFilter = string.Empty;
}
private void UpdateVisibleWorkspaces()
diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml
index 5d9a6f9f..5275f264 100644
--- a/src/Views/Hotkeys.axaml
+++ b/src/Views/Hotkeys.axaml
@@ -45,8 +45,8 @@
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}"
Margin="0,0,0,8"/>
-
-
+
+
@@ -55,7 +55,7 @@
-
+
@@ -70,8 +70,11 @@
-
+
+
+
+
-
-
+
+ IsVisible="{Binding Switcher, Converter={x:Static ObjectConverters.IsNotNull}}"
+ PointerPressed="OnCancelSwitcher">
-
+
+
+
+
+
diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs
index 02cc4f08..be4cdf5b 100644
--- a/src/Views/Launcher.axaml.cs
+++ b/src/Views/Launcher.axaml.cs
@@ -133,8 +133,8 @@ namespace SourceGit.Views
return;
}
- // Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar)
- if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: (KeyModifiers.Control | KeyModifiers.Shift), Key: Key.P })
+ // Ctrl+, opens preference dialog (macOS use hotkeys in system menu bar)
+ if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: KeyModifiers.Control, Key: Key.OemComma })
{
App.ShowWindow(new Preferences(), true);
e.Handled = true;
@@ -149,7 +149,7 @@ namespace SourceGit.Views
}
// Ctrl+Q quits the application (macOS use hotkeys in system menu bar)
- if (!OperatingSystem.IsMacOS() && e.KeyModifiers == KeyModifiers.Control && e.Key == Key.Q)
+ if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: KeyModifiers.Control, Key: Key.Q })
{
App.Quit(0);
return;
@@ -157,10 +157,18 @@ namespace SourceGit.Views
if (e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control))
{
- if (e.Key == Key.P)
+ if (e.KeyModifiers.HasFlag(KeyModifiers.Shift) && e.Key == Key.P)
{
vm.OpenWorkspaceSwitcher();
e.Handled = true;
+ return;
+ }
+
+ if (e.Key == Key.P)
+ {
+ vm.OpenTabSwitcher();
+ e.Handled = true;
+ return;
}
if (e.Key == Key.W)
@@ -257,7 +265,7 @@ namespace SourceGit.Views
else if (e.Key == Key.Escape)
{
vm.ActivePage.CancelPopup();
- vm.CancelWorkspaceSwitcher();
+ vm.CancelSwitcher();
e.Handled = true;
return;
}
@@ -314,6 +322,13 @@ namespace SourceGit.Views
e.Handled = true;
}
+ private void OnCancelSwitcher(object sender, PointerPressedEventArgs e)
+ {
+ if (e.Source == sender)
+ (DataContext as ViewModels.Launcher)?.CancelSwitcher();
+ e.Handled = true;
+ }
+
private KeyModifiers _unhandledModifiers = KeyModifiers.None;
private WindowState _lastWindowState = WindowState.Normal;
}
diff --git a/src/Views/LauncherTabsSelector.axaml b/src/Views/LauncherPageSwitcher.axaml
similarity index 70%
rename from src/Views/LauncherTabsSelector.axaml
rename to src/Views/LauncherPageSwitcher.axaml
index 109a2ce7..4d785b55 100644
--- a/src/Views/LauncherTabsSelector.axaml
+++ b/src/Views/LauncherPageSwitcher.axaml
@@ -3,19 +3,27 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
+ xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="SourceGit.Views.LauncherTabsSelector"
- x:Name="ThisControl">
-
-
+
+
+
+
+ VerticalContentAlignment="Center"
+ v:AutoFocusBehaviour.IsEnabled="True">
-
+ ItemsSource="{Binding VisiblePages, Mode=OneWay}"
+ SelectedItem="{Binding SelectedPage, Mode=TwoWay}">
@@ -72,30 +83,28 @@
-
+
-
-
diff --git a/src/Views/LauncherPageSwitcher.axaml.cs b/src/Views/LauncherPageSwitcher.axaml.cs
new file mode 100644
index 00000000..1effb93c
--- /dev/null
+++ b/src/Views/LauncherPageSwitcher.axaml.cs
@@ -0,0 +1,49 @@
+using Avalonia.Controls;
+using Avalonia.Input;
+
+namespace SourceGit.Views
+{
+ public partial class LauncherPageSwitcher : UserControl
+ {
+ public LauncherPageSwitcher()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+
+ if (e.Key == Key.Enter && DataContext is ViewModels.LauncherPageSwitcher switcher)
+ {
+ switcher.Switch();
+ e.Handled = true;
+ }
+ }
+
+ private void OnItemDoubleTapped(object sender, TappedEventArgs e)
+ {
+ if (DataContext is ViewModels.LauncherPageSwitcher switcher)
+ {
+ switcher.Switch();
+ e.Handled = true;
+ }
+ }
+
+ private void OnSearchBoxKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Down && PagesListBox.ItemCount > 0)
+ {
+ PagesListBox.Focus(NavigationMethod.Directional);
+
+ if (PagesListBox.SelectedIndex < 0)
+ PagesListBox.SelectedIndex = 0;
+ else if (PagesListBox.SelectedIndex < PagesListBox.ItemCount)
+ PagesListBox.SelectedIndex++;
+
+ e.Handled = true;
+ }
+ }
+ }
+}
+
diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml
index 0376e259..a56da2b0 100644
--- a/src/Views/LauncherTabBar.axaml
+++ b/src/Views/LauncherTabBar.axaml
@@ -40,7 +40,7 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs
index 12bca91f..270902bc 100644
--- a/src/Views/LauncherTabBar.axaml.cs
+++ b/src/Views/LauncherTabBar.axaml.cs
@@ -1,6 +1,7 @@
using System;
using Avalonia;
+using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
@@ -19,6 +20,20 @@ namespace SourceGit.Views
set => SetValue(IsScrollerVisibleProperty, value);
}
+ public static readonly StyledProperty SearchFilterProperty =
+ AvaloniaProperty.Register(nameof(SearchFilter));
+
+ public string SearchFilter
+ {
+ get => GetValue(SearchFilterProperty);
+ set => SetValue(SearchFilterProperty, value);
+ }
+
+ public AvaloniaList SelectablePages
+ {
+ get;
+ } = [];
+
public LauncherTabBar()
{
InitializeComponent();
@@ -126,6 +141,14 @@ namespace SourceGit.Views
context.DrawGeometry(fill, stroke, geo);
}
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == SearchFilterProperty)
+ UpdateSelectablePages();
+ }
+
private void ScrollTabs(object _, PointerWheelEventArgs e)
{
if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift))
@@ -248,13 +271,95 @@ namespace SourceGit.Views
e.Handled = true;
}
- private void OnGotoSelectedPage(object sender, LauncherTabSelectedEventArgs e)
+ private void OnTabsDropdownOpened(object sender, EventArgs e)
{
- if (DataContext is ViewModels.Launcher vm)
- vm.ActivePage = e.Page;
+ UpdateSelectablePages();
+ }
- PageSelector.Flyout?.Hide();
- e.Handled = true;
+ private void OnTabsDropdownClosed(object sender, EventArgs e)
+ {
+ SelectablePages.Clear();
+ SearchFilter = string.Empty;
+ }
+
+ private void OnTabsDropdownKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Escape)
+ {
+ PageSelector.Flyout?.Hide();
+ e.Handled = true;
+ }
+ else if (e.Key == Key.Enter)
+ {
+ if (TabsDropdownList.SelectedItem is ViewModels.LauncherPage page &&
+ DataContext is ViewModels.Launcher vm)
+ {
+ vm.ActivePage = page;
+ PageSelector.Flyout?.Hide();
+ e.Handled = true;
+ }
+ }
+ }
+
+ private void OnTabsDropdownSearchBoxKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Down && TabsDropdownList.ItemCount > 0)
+ {
+ TabsDropdownList.Focus(NavigationMethod.Directional);
+
+ if (TabsDropdownList.SelectedIndex < 0)
+ TabsDropdownList.SelectedIndex = 0;
+ else if (TabsDropdownList.SelectedIndex < TabsDropdownList.ItemCount)
+ TabsDropdownList.SelectedIndex++;
+
+ e.Handled = true;
+ }
+ }
+
+ private void OnTabsDropdownLostFocus(object sender, RoutedEventArgs e)
+ {
+ if (sender is Control { IsFocused: false, IsKeyboardFocusWithin: false })
+ PageSelector.Flyout?.Hide();
+ }
+
+ private void OnClearSearchFilter(object sender, RoutedEventArgs e)
+ {
+ SearchFilter = string.Empty;
+ }
+
+ private void OnTabsDropdownItemDoubleTapped(object sender, TappedEventArgs e)
+ {
+ if (sender is Control { DataContext: ViewModels.LauncherPage page } &&
+ DataContext is ViewModels.Launcher vm)
+ {
+ vm.ActivePage = page;
+ PageSelector.Flyout?.Hide();
+ e.Handled = true;
+ }
+ }
+
+ private void UpdateSelectablePages()
+ {
+ if (DataContext is not ViewModels.Launcher vm)
+ return;
+
+ SelectablePages.Clear();
+
+ var pages = vm.Pages;
+ var filter = SearchFilter?.Trim() ?? "";
+ if (string.IsNullOrEmpty(filter))
+ {
+ SelectablePages.AddRange(pages);
+ return;
+ }
+
+ foreach (var page in pages)
+ {
+ var node = page.Node;
+ if (node.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
+ (node.IsRepository && node.Id.Contains(filter, StringComparison.OrdinalIgnoreCase)))
+ SelectablePages.Add(page);
+ }
}
private bool _pressedTab = false;
diff --git a/src/Views/LauncherTabsSelector.axaml.cs b/src/Views/LauncherTabsSelector.axaml.cs
deleted file mode 100644
index 61d7a966..00000000
--- a/src/Views/LauncherTabsSelector.axaml.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using System;
-
-using Avalonia;
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Interactivity;
-
-namespace SourceGit.Views
-{
- public class LauncherTabSelectedEventArgs : RoutedEventArgs
- {
- public ViewModels.LauncherPage Page { get; }
-
- public LauncherTabSelectedEventArgs(ViewModels.LauncherPage page)
- {
- RoutedEvent = LauncherTabsSelector.PageSelectedEvent;
- Page = page;
- }
- }
-
- public partial class LauncherTabsSelector : UserControl
- {
- public static readonly StyledProperty> PagesProperty =
- AvaloniaProperty.Register>(nameof(Pages));
-
- public AvaloniaList Pages
- {
- get => GetValue(PagesProperty);
- set => SetValue(PagesProperty, value);
- }
-
- public static readonly StyledProperty SearchFilterProperty =
- AvaloniaProperty.Register(nameof(SearchFilter));
-
- public string SearchFilter
- {
- get => GetValue(SearchFilterProperty);
- set => SetValue(SearchFilterProperty, value);
- }
-
- public static readonly RoutedEvent PageSelectedEvent =
- RoutedEvent.Register(nameof(PageSelected), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
-
- public event EventHandler PageSelected
- {
- add { AddHandler(PageSelectedEvent, value); }
- remove { RemoveHandler(PageSelectedEvent, value); }
- }
-
- public AvaloniaList VisiblePages
- {
- get;
- private set;
- }
-
- public LauncherTabsSelector()
- {
- VisiblePages = new AvaloniaList();
- InitializeComponent();
- }
-
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- base.OnPropertyChanged(change);
-
- if (change.Property == PagesProperty || change.Property == SearchFilterProperty)
- UpdateVisiblePages();
- }
-
- private void OnClearSearchFilter(object sender, RoutedEventArgs e)
- {
- SearchFilter = string.Empty;
- }
-
- private void OnPageSelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- if (sender is ListBox { SelectedItem: ViewModels.LauncherPage page })
- {
- _isProcessingSelection = true;
- RaiseEvent(new LauncherTabSelectedEventArgs(page));
- _isProcessingSelection = false;
- }
-
- e.Handled = true;
- }
-
- private void UpdateVisiblePages()
- {
- if (_isProcessingSelection)
- return;
-
- VisiblePages.Clear();
-
- if (Pages == null)
- return;
-
- var filter = SearchFilter?.Trim() ?? "";
- if (string.IsNullOrEmpty(filter))
- {
- foreach (var p in Pages)
- VisiblePages.Add(p);
-
- return;
- }
-
- foreach (var page in Pages)
- {
- if (!page.Node.IsRepository)
- continue;
-
- if (page.Node.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
- page.Node.Id.Contains(filter, StringComparison.OrdinalIgnoreCase))
- VisiblePages.Add(page);
- }
- }
-
- private bool _isProcessingSelection = false;
- }
-}
-
diff --git a/src/Views/WorkspaceSwitcher.axaml b/src/Views/WorkspaceSwitcher.axaml
index 49fed451..4a324f3e 100644
--- a/src/Views/WorkspaceSwitcher.axaml
+++ b/src/Views/WorkspaceSwitcher.axaml
@@ -7,15 +7,15 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.WorkspaceSwitcher"
x:DataType="vm:WorkspaceSwitcher">
-
+
+ HorizontalAlignment="Center"/>
-
+