feature: add Hide Others app menu on macOS and correct the behaviour of Show All

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2026-04-17 12:35:45 +08:00
parent 5114c43dcd
commit 29cf5fc538
9 changed files with 110 additions and 8 deletions

View File

@@ -81,14 +81,17 @@ namespace SourceGit
public static readonly Command HideAppCommand = new Command(_ =>
{
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
lifetime.TryEnterBackground();
Native.OS.HideSelf();
});
public static readonly Command ShowAppCommand = new Command(_ =>
public static readonly Command HideOtherApplicationsCommand = new Command(_ =>
{
if (Current is App app && app.TryGetFeature(typeof(IActivatableLifetime)) is IActivatableLifetime lifetime)
lifetime.TryLeaveBackground();
Native.OS.HideOtherApplications();
});
public static readonly Command ShowAllApplicationsCommand = new Command(_ =>
{
Native.OS.ShowAllApplications();
});
}
}

View File

@@ -45,7 +45,8 @@
<NativeMenuItem Header="{DynamicResource Text.OpenAppDataDir}" Command="{x:Static s:App.OpenAppDataDirCommand}"/>
<NativeMenuItemSeparator/>
<NativeMenuItem Header="{DynamicResource Text.App.Hide}" Command="{x:Static s:App.HideAppCommand}" Gesture="⌘+H"/>
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAppCommand}"/>
<NativeMenuItem Header="{DynamicResource Text.App.HideOthers}" Command="{x:Static s:App.HideOtherApplicationsCommand}" Gesture="⌘+Alt+H"/>
<NativeMenuItem Header="{DynamicResource Text.App.ShowAll}" Command="{x:Static s:App.ShowAllApplicationsCommand}"/>
<NativeMenuItemSeparator/>
<NativeMenuItem Header="{DynamicResource Text.Quit}" Command="{x:Static s:App.QuitCommand}" Gesture="⌘+Q"/>
</NativeMenu>

View File

@@ -33,6 +33,21 @@ namespace SourceGit.Native
}
}
public void HideSelf()
{
// Do Nothing. Never used.
}
public void HideOtherApplications()
{
// Do Nothing. Never used.
}
public void ShowAllApplications()
{
// Do Nothing. Never used.
}
public string GetDataDir()
{
// AppImage supports portable mode

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia;
@@ -13,6 +14,18 @@ namespace SourceGit.Native
[SupportedOSPlatform("macOS")]
internal class MacOS : OS.IBackend
{
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_getClass")]
public static extern IntPtr objc_getClass(string name);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "sel_registerName")]
public static extern IntPtr sel_registerName(string name);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public static extern IntPtr objc_msgSendWithArg(IntPtr receiver, IntPtr selector, IntPtr arg);
public void SetupApp(AppBuilder builder)
{
builder.With(new MacOSPlatformOptions()
@@ -44,6 +57,39 @@ namespace SourceGit.Native
window.ExtendClientAreaToDecorationsHint = true;
}
public void HideSelf()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("hide:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public void HideOtherApplications()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("hideOtherApplications:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public void ShowAllApplications()
{
IntPtr nsApplicationClass = objc_getClass("NSApplication");
IntPtr nsSharedApplicationSelector = sel_registerName("sharedApplication");
IntPtr nsApp = objc_msgSend(nsApplicationClass, nsSharedApplicationSelector);
IntPtr nsMethodSelector = sel_registerName("unhideAllApplications:");
IntPtr nsDelegateSelector = sel_registerName("delegate");
IntPtr nsDelegate = objc_msgSend(nsApp, nsDelegateSelector);
objc_msgSendWithArg(nsApp, nsMethodSelector, nsDelegate);
}
public string GetDataDir()
{
return Path.Combine(

View File

@@ -18,6 +18,10 @@ namespace SourceGit.Native
void SetupApp(AppBuilder builder);
void SetupWindow(Window window);
void HideSelf();
void HideOtherApplications();
void ShowAllApplications();
string GetDataDir();
string FindGitExecutable();
string FindTerminal(Models.ShellOrTerminal shell);
@@ -154,6 +158,21 @@ namespace SourceGit.Native
_backend.SetupWindow(window);
}
public static void HideSelf()
{
_backend.HideSelf();
}
public static void HideOtherApplications()
{
_backend.HideOtherApplications();
}
public static void ShowAllApplications()
{
_backend.ShowAllApplications();
}
public static void LogException(Exception ex)
{
if (ex == null)

View File

@@ -58,6 +58,21 @@ namespace SourceGit.Native
window.BorderThickness = new Thickness(1);
}
public void HideSelf()
{
// Do Nothing. Never used.
}
public void HideOtherApplications()
{
// Do Nothing. Never used.
}
public void ShowAllApplications()
{
// Do Nothing. Never used.
}
public string GetDataDir()
{
var execFile = Process.GetCurrentProcess().MainModule!.FileName;

View File

@@ -23,6 +23,7 @@
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">Use AI to generate commit message</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">Use</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">Hide SourceGit</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">Hide Others</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">Show All</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">Patch</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">3-Way Merge</x:String>

View File

@@ -27,7 +27,8 @@
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用AI助手生成提交信息</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">应用所选</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隐藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">显示所有窗口</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">隐藏其他</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">显示全部</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">应用补丁(apply)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">尝试三路合并</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">补丁文件 </x:String>

View File

@@ -27,7 +27,8 @@
<x:String x:Key="Text.AIAssistant.Tip" xml:space="preserve">使用 AI 產生提交訊息</x:String>
<x:String x:Key="Text.AIAssistant.Use" xml:space="preserve">套用選取</x:String>
<x:String x:Key="Text.App.Hide" xml:space="preserve">隱藏 SourceGit</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">顯示所有</x:String>
<x:String x:Key="Text.App.HideOthers" xml:space="preserve">隱藏其他</x:String>
<x:String x:Key="Text.App.ShowAll" xml:space="preserve">顯示全部</x:String>
<x:String x:Key="Text.Apply" xml:space="preserve">套用修補檔 (apply patch)</x:String>
<x:String x:Key="Text.Apply.3Way" xml:space="preserve">嘗試三向合併</x:String>
<x:String x:Key="Text.Apply.File" xml:space="preserve">修補檔:</x:String>