Skip to content

Commit 946e1aa

Browse files
committed
feature: add Ctrl+O global hotkey to open/init repository
This fix add a global hotkey, Ctrl-O, that allows you to perform actions that were previously only possible on the Welcome screen, from anywhere. - Add global Ctrl+O (Cmd+O on macOS) hotkey for opening a repository. - Move dialog operation logic `OpenLocalRepository()` from WelcomeToolbar to Launcher, and use it by global hotkey and button action in WlcomeToolbar. - Add hotkey text to "Open repository" button tooltip in welcome view. - Add new hotkey text in Hotkeys dialog as resource (en_US, ja_JP).
1 parent 13e2597 commit 946e1aa

6 files changed

Lines changed: 84 additions & 53 deletions

File tree

src/Resources/Locales/en_US.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@
499499
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Go to next tab</x:String>
500500
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">Go to previous tab</x:String>
501501
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Create new tab</x:String>
502+
<x:String x:Key="Text.Hotkeys.Global.OpenOrInit" xml:space="preserve">Open repository</x:String>
502503
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">Open Preferences dialog</x:String>
503504
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">Show workspace dropdown menu</x:String>
504505
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">Switch active tab</x:String>

src/Resources/Locales/ja_JP.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@
491491
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">次のタブに移動</x:String>
492492
<x:String x:Key="Text.Hotkeys.Global.GotoPrevTab" xml:space="preserve">前のタブに移動</x:String>
493493
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新しいタブを作成</x:String>
494+
<x:String x:Key="Text.Hotkeys.Global.OpenOrInit" xml:space="preserve">リポジトリを開く</x:String>
494495
<x:String x:Key="Text.Hotkeys.Global.OpenPreferences" xml:space="preserve">設定ダイアログを開く</x:String>
495496
<x:String x:Key="Text.Hotkeys.Global.ShowWorkspaceDropdownMenu" xml:space="preserve">ワークスペースのドロップダウンメニューを表示</x:String>
496497
<x:String x:Key="Text.Hotkeys.Global.SwitchTab" xml:space="preserve">アクティブなタブを切り替え</x:String>

src/Views/Hotkeys.axaml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
FontSize="{Binding Source={x:Static vm:Preferences.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Increase}}"
4646
Margin="0,0,0,8"/>
4747

48-
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
48+
<Grid RowDefinitions="20,20,20,20,20,20,20,20,20,20,20" ColumnDefinitions="150,*">
4949
<TextBlock Grid.Row="0" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+\,, macOS=⌘+\,}"/>
5050
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.OpenPreferences}"/>
5151

@@ -64,17 +64,20 @@
6464
<TextBlock Grid.Row="5" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+N, macOS=⌘+N}"/>
6565
<TextBlock Grid.Row="5" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Clone}" />
6666

67-
<TextBlock Grid.Row="6" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Alt+P, macOS=⌘+⌥+P}"/>
68-
<TextBlock Grid.Row="6" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.ShowWorkspaceDropdownMenu}" />
67+
<TextBlock Grid.Row="6" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+O, macOS=⌘+O}"/>
68+
<TextBlock Grid.Row="6" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.OpenOrInit}" />
6969

70-
<TextBlock Grid.Row="7" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+P, macOS=⌘+P}"/>
71-
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.SwitchTab}" />
70+
<TextBlock Grid.Row="7" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Alt+P, macOS=⌘+⌥+P}"/>
71+
<TextBlock Grid.Row="7" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.ShowWorkspaceDropdownMenu}" />
7272

73-
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform 'Ctrl+-/=', macOS='⌘+-/='}"/>
74-
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Zoom}" />
73+
<TextBlock Grid.Row="8" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+P, macOS=⌘+P}"/>
74+
<TextBlock Grid.Row="8" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.SwitchTab}" />
7575

76-
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Q, macOS=⌘+Q}"/>
77-
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Quit}" />
76+
<TextBlock Grid.Row="9" Grid.Column="0" Classes="bold" Text="{OnPlatform 'Ctrl+-/=', macOS='⌘+-/='}"/>
77+
<TextBlock Grid.Row="9" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Global.Zoom}" />
78+
79+
<TextBlock Grid.Row="10" Grid.Column="0" Classes="bold" Text="{OnPlatform Ctrl+Q, macOS=⌘+Q}"/>
80+
<TextBlock Grid.Row="10" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Quit}" />
7881
</Grid>
7982

8083
<TextBlock Text="{DynamicResource Text.Hotkeys.Repo}"

src/Views/Launcher.axaml.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
24

35
using Avalonia;
46
using Avalonia.Controls;
@@ -7,6 +9,7 @@
79
using Avalonia.Markup.Xaml.MarkupExtensions;
810
using Avalonia.Media;
911
using Avalonia.Platform;
12+
using Avalonia.Platform.Storage;
1013
using Avalonia.VisualTree;
1114

1215
namespace SourceGit.Views
@@ -106,6 +109,56 @@ public void BringToTop()
106109
Activate();
107110
}
108111

112+
public async Task OpenLocalRepository()
113+
{
114+
var vm = App.GetLauncher();
115+
var activePage = vm.ActivePage;
116+
if (activePage == null || !activePage.CanCreatePopup())
117+
return;
118+
119+
var topLevel = TopLevel.GetTopLevel(this);
120+
if (topLevel == null)
121+
return;
122+
123+
var preference = ViewModels.Preferences.Instance;
124+
var workspace = preference.GetActiveWorkspace();
125+
var initDir = workspace.DefaultCloneDir;
126+
if (string.IsNullOrEmpty(initDir) || !Directory.Exists(initDir))
127+
initDir = preference.GitDefaultCloneDir;
128+
129+
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
130+
if (Directory.Exists(initDir))
131+
{
132+
var folder = await topLevel.StorageProvider.TryGetFolderFromPathAsync(initDir);
133+
options.SuggestedStartLocation = folder;
134+
}
135+
136+
try
137+
{
138+
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
139+
if (selected.Count == 1)
140+
{
141+
var folder = selected[0];
142+
var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString();
143+
var repoPath = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(folderPath);
144+
if (!string.IsNullOrEmpty(repoPath))
145+
{
146+
await ViewModels.Welcome.Instance.AddRepositoryAsync(repoPath, null, false, true);
147+
ViewModels.Welcome.Instance.Refresh();
148+
}
149+
else if (Directory.Exists(folderPath))
150+
{
151+
var test = await new Commands.QueryRepositoryRootPath(folderPath).GetResultAsync();
152+
ViewModels.Welcome.Instance.InitRepository(folderPath, null, test.StdErr);
153+
}
154+
}
155+
}
156+
catch (Exception exception)
157+
{
158+
Models.Notification.Send(null, $"Failed to open repository: {exception.Message}", true);
159+
}
160+
}
161+
109162
protected override async void OnOpened(EventArgs e)
110163
{
111164
base.OnOpened(e);
@@ -223,6 +276,13 @@ protected override async void OnKeyDown(KeyEventArgs e)
223276
return;
224277
}
225278

279+
if (e.Key == Key.O)
280+
{
281+
await OpenLocalRepository();
282+
e.Handled = true;
283+
return;
284+
}
285+
226286
if (e.Key == Key.N)
227287
{
228288
if (vm.ActivePage.Data is not ViewModels.Welcome)

src/Views/WelcomeToolbar.axaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@
1919
<Path Width="16" Height="16" Data="{StaticResource Icons.Clone}" Margin="0,4,0,0"/>
2020
</Button>
2121

22-
<Button Classes="icon_button" Width="32" Click="OpenLocalRepository" ToolTip.Tip="{DynamicResource Text.Welcome.OpenOrInit}">
22+
<Button Classes="icon_button" Width="32" Click="OpenLocalRepository">
23+
<ToolTip.Tip>
24+
<TextBlock>
25+
<Run Text="{DynamicResource Text.Welcome.OpenOrInit}"/>
26+
<Run Text=" "/>
27+
<Run Text="{OnPlatform Ctrl+O, macOS=⌘+O}" FontSize="11" Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"/>
28+
</TextBlock>
29+
</ToolTip.Tip>
2330
<Path Width="14" Height="14" Data="{StaticResource Icons.Folder.Open}" Margin="0,2,0,0"/>
2431
</Button>
2532

src/Views/WelcomeToolbar.axaml.cs

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using System;
2-
using System.IO;
3-
41
using Avalonia.Controls;
52
using Avalonia.Interactivity;
6-
using Avalonia.Platform.Storage;
73

84
namespace SourceGit.Views
95
{
@@ -21,47 +17,10 @@ private async void OpenLocalRepository(object _1, RoutedEventArgs e)
2117
return;
2218

2319
var topLevel = TopLevel.GetTopLevel(this);
24-
if (topLevel == null)
20+
if (topLevel is not Launcher launcher)
2521
return;
2622

27-
var preference = ViewModels.Preferences.Instance;
28-
var workspace = preference.GetActiveWorkspace();
29-
var initDir = workspace.DefaultCloneDir;
30-
if (string.IsNullOrEmpty(initDir) || !Directory.Exists(initDir))
31-
initDir = preference.GitDefaultCloneDir;
32-
33-
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
34-
if (Directory.Exists(initDir))
35-
{
36-
var folder = await topLevel.StorageProvider.TryGetFolderFromPathAsync(initDir);
37-
options.SuggestedStartLocation = folder;
38-
}
39-
40-
try
41-
{
42-
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
43-
if (selected.Count == 1)
44-
{
45-
var folder = selected[0];
46-
var folderPath = folder is { Path: { IsAbsoluteUri: true } path } ? path.LocalPath : folder?.Path.ToString();
47-
var repoPath = await ViewModels.Welcome.Instance.GetRepositoryRootAsync(folderPath);
48-
if (!string.IsNullOrEmpty(repoPath))
49-
{
50-
await ViewModels.Welcome.Instance.AddRepositoryAsync(repoPath, null, false, true);
51-
ViewModels.Welcome.Instance.Refresh();
52-
}
53-
else if (Directory.Exists(folderPath))
54-
{
55-
var test = await new Commands.QueryRepositoryRootPath(folderPath).GetResultAsync();
56-
ViewModels.Welcome.Instance.InitRepository(folderPath, null, test.StdErr);
57-
}
58-
}
59-
}
60-
catch (Exception exception)
61-
{
62-
Models.Notification.Send(null, $"Failed to open repository: {exception.Message}", true);
63-
}
64-
23+
await launcher.OpenLocalRepository();
6524
e.Handled = true;
6625
}
6726
}

0 commit comments

Comments
 (0)