From 9614b995d85e93dc891e6da9b0a276d4dfc02c83 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 19:36:17 +0800 Subject: [PATCH 1/7] refactor: workspace/page switcher (#1330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add `Switch Tab` popup - change hotkey to open `Preferences` to `Ctrl+,/⌘+,` - change hotkey to open `Switch Workspace` to `Ctrl+Shift+P/⌘+⇧+P` - change hotkey to open `Switch Tab` to `Ctrl+P/⌘+P` Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 4 +- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 4 +- src/Resources/Locales/zh_TW.axaml | 4 +- src/ViewModels/Launcher.cs | 25 ++-- src/ViewModels/LauncherPageSwitcher.cs | 77 +++++++++++ src/ViewModels/Repository.cs | 2 +- src/ViewModels/WorkspaceSwitcher.cs | 2 +- src/Views/Hotkeys.axaml | 11 +- src/Views/Launcher.axaml | 8 +- src/Views/Launcher.axaml.cs | 18 ++- ...ector.axaml => LauncherPageSwitcher.axaml} | 53 ++++---- src/Views/LauncherPageSwitcher.axaml.cs | 49 +++++++ src/Views/LauncherTabBar.axaml | 104 ++++++++++++++- src/Views/LauncherTabBar.axaml.cs | 111 +++++++++++++++- src/Views/LauncherTabsSelector.axaml.cs | 120 ------------------ src/Views/WorkspaceSwitcher.axaml | 4 +- 17 files changed, 418 insertions(+), 180 deletions(-) create mode 100644 src/ViewModels/LauncherPageSwitcher.cs rename src/Views/{LauncherTabsSelector.axaml => LauncherPageSwitcher.axaml} (70%) create mode 100644 src/Views/LauncherPageSwitcher.axaml.cs delete mode 100644 src/Views/LauncherTabsSelector.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 4ce0a1ad..65b9d325 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 + Switch Workspace + Switch Tab 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..a569cd97 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..1344dfed 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..a95f7211 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/Launcher.cs b/src/ViewModels/Launcher.cs index 9a54bb32..3b6a4dd8 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 object Switcher + { + get => _switcher; + set => SetProperty(ref _switcher, value); + } + public Launcher(string startupRepo) { _ignoreIndexChange = true; @@ -138,12 +138,17 @@ 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 = null; } public void SwitchWorkspace(Workspace to) @@ -618,6 +623,6 @@ namespace SourceGit.ViewModels private LauncherPage _activePage = null; private bool _ignoreIndexChange = false; private string _title = string.Empty; - private WorkspaceSwitcher _workspaceSwitcher = null; + private object _switcher = null; } } diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs new file mode 100644 index 00000000..b0dfaca3 --- /dev/null +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace SourceGit.ViewModels +{ + public class LauncherPageSwitcher : ObservableObject + { + 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() + { + if (_selectedPage is { }) + _launcher.ActivePage = _selectedPage; + + _launcher.CancelSwitcher(); + } + + 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; + } + + 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..41f47631 100644 --- a/src/ViewModels/WorkspaceSwitcher.cs +++ b/src/ViewModels/WorkspaceSwitcher.cs @@ -44,7 +44,7 @@ namespace SourceGit.ViewModels if (_selectedWorkspace is { }) _launcher.SwitchWorkspace(_selectedWorkspace); - _launcher.CancelWorkspaceSwitcher(); + _launcher.CancelSwitcher(); } 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}}"> - + + + + + diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 02cc4f08..abcbaba9 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; } 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..09f42038 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..277eb549 --- /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..f770a5d9 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..b75d93d6 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; @@ -18,6 +19,20 @@ namespace SourceGit.Views get => GetValue(IsScrollerVisibleProperty); 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() { @@ -125,7 +140,15 @@ namespace SourceGit.Views var stroke = new Pen(this.FindResource("Brush.Border0") as IBrush); 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)) @@ -247,16 +270,92 @@ 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(); + } + + 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); - PageSelector.Flyout?.Hide(); - e.Handled = true; + 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; private Point _pressedTabPosition = new Point(); private bool _startDragTab = 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..aa621b73 100644 --- a/src/Views/WorkspaceSwitcher.axaml +++ b/src/Views/WorkspaceSwitcher.axaml @@ -9,7 +9,7 @@ x:DataType="vm:WorkspaceSwitcher"> @@ -82,7 +82,7 @@ - + Date: Sun, 18 May 2025 11:36:39 +0000 Subject: [PATCH 2/7] doc: Update translation status and sort locale files --- TRANSLATION.md | 58 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index d810b5b2..26e21624 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,7 +6,7 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.48%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-98.23%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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
-### ![es__ES](https://img.shields.io/badge/es__ES-99.75%25-yellow) +### ![es__ES](https://img.shields.io/badge/es__ES-99.49%25-yellow)
Missing keys in es_ES.axaml - Text.Hotkeys.Global.SwitchWorkspace -- Text.Repository.WorkspaceSwitcher +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab
-### ![fr__FR](https://img.shields.io/badge/fr__FR-94.29%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-94.05%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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
-### ![it__IT](https://img.shields.io/badge/it__IT-99.75%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-99.49%25-yellow)
Missing keys in it_IT.axaml - Text.Hotkeys.Global.SwitchWorkspace -- Text.Repository.WorkspaceSwitcher +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchWorkspace +- Text.Launcher.SwitchTab
-### ![ja__JP](https://img.shields.io/badge/ja__JP-94.04%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-93.80%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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
-### ![pt__BR](https://img.shields.io/badge/pt__BR-85.79%25-yellow) +### ![pt__BR](https://img.shields.io/badge/pt__BR-85.57%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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
-### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) +### ![ru__RU](https://img.shields.io/badge/ru__RU-99.75%25-yellow) -### ![ta__IN](https://img.shields.io/badge/ta__IN-94.29%25-yellow) +
+Missing keys in ru_RU.axaml + +- Text.Hotkeys.Global.SwitchTab +- Text.Launcher.SwitchTab + +
+ +### ![ta__IN](https://img.shields.io/badge/ta__IN-94.05%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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
-### ![uk__UA](https://img.shields.io/badge/uk__UA-95.43%25-yellow) +### ![uk__UA](https://img.shields.io/badge/uk__UA-95.19%25-yellow)
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.SwitchWorkspace +- Text.Launcher.SwitchTab - 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 From 4b849d9d5ccdab6d97ce0671cc4d29e63c45d3cf Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 20:33:55 +0800 Subject: [PATCH 3/7] ux: update workspace/page switcher popup layout Signed-off-by: leo --- src/Resources/Locales/en_US.axaml | 4 ++-- src/Resources/Locales/ru_RU.axaml | 2 +- src/Resources/Locales/zh_CN.axaml | 4 ++-- src/Resources/Locales/zh_TW.axaml | 4 ++-- src/Views/Launcher.axaml | 4 ++-- src/Views/LauncherPageSwitcher.axaml | 10 +++++----- src/Views/WorkspaceSwitcher.axaml | 10 +++++----- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 65b9d325..bb259272 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -430,8 +430,8 @@ Open in Browser ERROR NOTICE - Switch Workspace - Switch Tab + Workspaces + Pages Merge Branch Into: Merge Option: diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index a569cd97..77a7ba4f 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -433,7 +433,7 @@ Открыть в браузере ОШИБКА УВЕДОМЛЕНИЕ - Переключить рабочее место + Рабочие места Влить ветку В: Опции слияния: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 1344dfed..18569a14 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -434,8 +434,8 @@ 在浏览器中访问 出错了 系统提示 - 切换工作区 - 切换页面 + 工作区列表 + 页面列表 合并分支 目标分支 : 合并方式 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index a95f7211..ded99a14 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -434,8 +434,8 @@ 在瀏覽器中開啟連結 發生錯誤 系統提示 - 切換工作區 - 切換目前頁面 + 工作區列表 + 頁面列表 合併分支 目標分支: 合併方式: diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index c9565452..18e307fe 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -103,13 +103,13 @@ - + - + diff --git a/src/Views/LauncherPageSwitcher.axaml b/src/Views/LauncherPageSwitcher.axaml index 09f42038..4d785b55 100644 --- a/src/Views/LauncherPageSwitcher.axaml +++ b/src/Views/LauncherPageSwitcher.axaml @@ -8,15 +8,15 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.LauncherPageSwitcher" x:DataType="vm:LauncherPageSwitcher"> - + + HorizontalAlignment="Center"/> - + + HorizontalAlignment="Center"/> Date: Sun, 18 May 2025 12:34:17 +0000 Subject: [PATCH 4/7] doc: Update translation status and sort locale files --- TRANSLATION.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 26e21624..1a9d00d1 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -16,8 +16,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Repository.ShowSubmodulesAsTree - Text.Submodule.Status - Text.Submodule.Status.Modified @@ -35,8 +35,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages
@@ -67,8 +67,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -102,8 +102,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages @@ -134,8 +134,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -224,8 +224,8 @@ This document shows the translation status of each locale file in the repository - Text.InProgress.Merge.Operating - Text.InProgress.Rebase.StoppedAt - Text.InProgress.Revert.Head -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Merge.Source - Text.MergeMultiple - Text.MergeMultiple.CommitChanges @@ -292,7 +292,7 @@ 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.SwitchTab +- Text.Launcher.Pages @@ -323,8 +323,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate @@ -374,8 +374,8 @@ This document shows the translation status of each locale file in the repository - Text.Hotkeys.Global.SwitchWorkspace - Text.Hotkeys.Global.SwitchTab - Text.Hotkeys.TextEditor.OpenExternalMergeTool -- Text.Launcher.SwitchWorkspace -- Text.Launcher.SwitchTab +- Text.Launcher.Workspaces +- Text.Launcher.Pages - Text.Preferences.Git.IgnoreCRAtEOLInDiff - Text.Repository.BranchSort - Text.Repository.BranchSort.ByCommitterDate From 5e85f6fefed553c71fb4ddb9b2dacd333f93aab6 Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 20:47:04 +0800 Subject: [PATCH 5/7] enhance: auto-select the first page by default Signed-off-by: leo --- src/ViewModels/LauncherPageSwitcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs index b0dfaca3..a73a216f 100644 --- a/src/ViewModels/LauncherPageSwitcher.cs +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -67,6 +67,7 @@ namespace SourceGit.ViewModels } VisiblePages = visible; + SelectedPage = visible.Count > 0 ? visible[0] : null; } private Launcher _launcher = null; From b78f6b0ea8da7aa857aa27326f683d7995ebcf7a Mon Sep 17 00:00:00 2001 From: qiufengshe <172344058@qq.com> Date: Sun, 18 May 2025 20:52:05 +0800 Subject: [PATCH 6/7] perf: minimize temporary strings for better performance (#1332) --- src/ViewModels/Histories.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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; }; From aff003fd6dab3e181cddcf53d2a61c823c2d41bb Mon Sep 17 00:00:00 2001 From: leo Date: Sun, 18 May 2025 22:00:35 +0800 Subject: [PATCH 7/7] enhance: cleanup unused resources Signed-off-by: leo --- src/ViewModels/Launcher.cs | 9 +++++---- src/ViewModels/LauncherPageSwitcher.cs | 19 ++++++++++++------- src/ViewModels/WorkspaceSwitcher.cs | 13 +++++++++---- src/Views/Launcher.axaml | 5 +++-- src/Views/Launcher.axaml.cs | 7 +++++++ src/Views/LauncherPageSwitcher.axaml.cs | 2 +- src/Views/LauncherTabBar.axaml | 2 +- src/Views/LauncherTabBar.axaml.cs | 20 +++++++++++++------- 8 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 3b6a4dd8..4c0714df 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -44,10 +44,10 @@ namespace SourceGit.ViewModels } } - public object Switcher + public IDisposable Switcher { get => _switcher; - set => SetProperty(ref _switcher, value); + private set => SetProperty(ref _switcher, value); } public Launcher(string startupRepo) @@ -148,12 +148,13 @@ namespace SourceGit.ViewModels 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) @@ -623,6 +624,6 @@ namespace SourceGit.ViewModels private LauncherPage _activePage = null; private bool _ignoreIndexChange = false; private string _title = string.Empty; - private object _switcher = null; + private IDisposable _switcher = null; } } diff --git a/src/ViewModels/LauncherPageSwitcher.cs b/src/ViewModels/LauncherPageSwitcher.cs index a73a216f..5f53021d 100644 --- a/src/ViewModels/LauncherPageSwitcher.cs +++ b/src/ViewModels/LauncherPageSwitcher.cs @@ -4,14 +4,14 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class LauncherPageSwitcher : ObservableObject + public class LauncherPageSwitcher : ObservableObject, IDisposable { public List VisiblePages { get => _visiblePages; private set => SetProperty(ref _visiblePages, value); } - + public string SearchFilter { get => _searchFilter; @@ -27,13 +27,13 @@ namespace SourceGit.ViewModels get => _selectedPage; set => SetProperty(ref _selectedPage, value); } - + public LauncherPageSwitcher(Launcher launcher) { _launcher = launcher; UpdateVisiblePages(); } - + public void ClearFilter() { SearchFilter = string.Empty; @@ -41,12 +41,17 @@ namespace SourceGit.ViewModels public void Switch() { - if (_selectedPage is { }) - _launcher.ActivePage = _selectedPage; - + _launcher.ActivePage = _selectedPage ?? _launcher.ActivePage; _launcher.CancelSwitcher(); } + public void Dispose() + { + _visiblePages.Clear(); + _selectedPage = null; + _searchFilter = string.Empty; + } + private void UpdateVisiblePages() { var visible = new List(); diff --git a/src/ViewModels/WorkspaceSwitcher.cs b/src/ViewModels/WorkspaceSwitcher.cs index 41f47631..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,12 +41,17 @@ namespace SourceGit.ViewModels public void Switch() { - if (_selectedWorkspace is { }) - _launcher.SwitchWorkspace(_selectedWorkspace); - + _launcher.SwitchWorkspace(_selectedWorkspace); _launcher.CancelSwitcher(); } + public void Dispose() + { + _visibleWorkspaces.Clear(); + _selectedWorkspace = null; + _searchFilter = string.Empty; + } + private void UpdateVisibleWorkspaces() { var visible = new List(); diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index 18e307fe..bd115fef 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -104,9 +104,10 @@ - + 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 abcbaba9..be4cdf5b 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -322,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/LauncherPageSwitcher.axaml.cs b/src/Views/LauncherPageSwitcher.axaml.cs index 277eb549..1effb93c 100644 --- a/src/Views/LauncherPageSwitcher.axaml.cs +++ b/src/Views/LauncherPageSwitcher.axaml.cs @@ -9,7 +9,7 @@ namespace SourceGit.Views { InitializeComponent(); } - + protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); diff --git a/src/Views/LauncherTabBar.axaml b/src/Views/LauncherTabBar.axaml index f770a5d9..a56da2b0 100644 --- a/src/Views/LauncherTabBar.axaml +++ b/src/Views/LauncherTabBar.axaml @@ -141,7 +141,7 @@