Skip to content

Commit 39b391b

Browse files
committed
System tray icon
Add code for creation of system tray icon, menu and handle menu events System tray icon - add code for creation of system tray icon, menu and handle menu events - add option to preferences dialog - add a kind of a single instance mode: only first launched instance creates system tray icon and does not quit
1 parent bc66e24 commit 39b391b

File tree

13 files changed

+157
-21
lines changed

13 files changed

+157
-21
lines changed

‎src/App.Commands.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public static bool IsCheckForUpdateCommandVisible
3636
#endif
3737
}
3838
}
39-
39+
40+
publicstaticreadonlyCommandUnminimize=newCommand(_ =>ShowWindow());
4041
publicstaticreadonlyCommandOpenPreferencesCommand=newCommand(_ =>OpenDialog(newViews.Preferences()));
4142
publicstaticreadonlyCommandOpenHotkeysCommand=newCommand(_ =>OpenDialog(newViews.Hotkeys()));
4243
publicstaticreadonlyCommandOpenAppDataDirCommand=newCommand(_ =>Native.OS.OpenInFileManager(Native.OS.DataDir));

‎src/App.axaml.cs‎

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
usingAvalonia.Markup.Xaml;
1717
usingAvalonia.Media;
1818
usingAvalonia.Media.Fonts;
19+
usingAvalonia.Media.Imaging;
20+
usingAvalonia.Platform;
1921
usingAvalonia.Platform.Storage;
2022
usingAvalonia.Styling;
2123
usingAvalonia.Threading;
@@ -169,6 +171,46 @@ public static void SetTheme(string theme, string themeOverridesFile)
169171
}
170172
}
171173

174+
publicvoidSetupTrayIcon(boolenable)
175+
{
176+
if(enable&&Native.OS.EnsureSingleInstance())
177+
{
178+
varicons=newTrayIcons{
179+
newTrayIcon{
180+
Icon=newWindowIcon(newBitmap(AssetLoader.Open(newUri("avares://SourceGit/App.ico")))),
181+
Menu=[
182+
newNativeMenuItem(Text("Open")){Command=Unminimize},
183+
newNativeMenuItem(Text("Preferences")){Command=OpenPreferencesCommand},
184+
newNativeMenuItemSeparator(),
185+
newNativeMenuItem(Text("Quit")){Command=QuitCommand},
186+
]
187+
}
188+
};
189+
icons[0].Clicked+=(_,_)=>ToggleWindow();
190+
TrayIcon.SetIcons(Current,icons);
191+
_createdSystemTrayIcon=true;
192+
}
193+
}
194+
195+
privatestaticvoidToggleWindow(){
196+
if(Current?.ApplicationLifetimeisIClassicDesktopStyleApplicationLifetimedesktop){
197+
if(desktop.MainWindow.IsVisible){
198+
desktop.MainWindow.Hide();
199+
}else{
200+
ShowWindow();
201+
}
202+
}
203+
}
204+
205+
privatestaticvoidShowWindow()
206+
{
207+
if(Current?.ApplicationLifetimeisIClassicDesktopStyleApplicationLifetimedesktop){
208+
desktop.MainWindow.WindowState=WindowState.Normal;
209+
desktop.MainWindow.Show();
210+
desktop.MainWindow.BringIntoView();
211+
desktop.MainWindow.Focus();
212+
}
213+
}
172214
publicstaticvoidSetFonts(stringdefaultFont,stringmonospaceFont,boolonlyUseMonospaceFontInEditor)
173215
{
174216
varapp=CurrentasApp;
@@ -322,6 +364,7 @@ public override void OnFrameworkInitializationCompleted()
322364

323365
TryLaunchAsNormal(desktop);
324366
}
367+
base.OnFrameworkInitializationCompleted();
325368
}
326369
#endregion
327370

@@ -477,11 +520,17 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
477520
if(desktop.Args!=null&&desktop.Args.Length==1&&Directory.Exists(desktop.Args[0]))
478521
startupRepo=desktop.Args[0];
479522

480-
_launcher=newViewModels.Launcher(startupRepo);
523+
varpref=ViewModels.Preferences.Instance;
524+
525+
SetupTrayIcon(pref.SystemTrayIcon);
526+
if(_createdSystemTrayIcon){
527+
desktop.ShutdownMode=ShutdownMode.OnExplicitShutdown;
528+
}
529+
530+
_launcher=newViewModels.Launcher(startupRepo){InterceptQuit=_createdSystemTrayIcon};
481531
desktop.MainWindow=newViews.Launcher(){DataContext=_launcher};
482532

483533
#if !DISABLE_UPDATE_DETECTION
484-
varpref=ViewModels.Preferences.Instance;
485534
if(pref.ShouldCheck4UpdateOnStartup())
486535
Check4Update();
487536
#endif
@@ -544,5 +593,6 @@ private void ShowSelfUpdateResult(object data)
544593
privateResourceDictionary_activeLocale=null;
545594
privateResourceDictionary_themeOverrides=null;
546595
privateResourceDictionary_fontsOverrides=null;
596+
privatebool_createdSystemTrayIcon=false;
547597
}
548598
}

‎src/Native/Linux.cs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace SourceGit.Native
1111
[SupportedOSPlatform("linux")]
1212
internalclassLinux:OS.IBackend
1313
{
14+
privateFileStream_fs=null;
1415
publicvoidSetupApp(AppBuilderbuilder)
1516
{
1617
builder.With(newX11PlatformOptions(){EnableIme=true});
@@ -97,6 +98,26 @@ public void OpenWithDefaultEditor(string file)
9798
}
9899
}
99100

101+
publicboolEnsureSingleInstance()
102+
{
103+
varpidfile=Path.Combine(Path.GetTempPath(),"sourcegit.pid");
104+
varpid=Process.GetCurrentProcess().Id.ToString();
105+
Console.WriteLine("pid "+pid);
106+
107+
try
108+
{
109+
_fs=File.OpenWrite(pidfile);
110+
_fs.Lock(0,1000);
111+
newStreamWriter(_fs).Write(pid);
112+
returntrue;
113+
}
114+
catch(IOException)
115+
{
116+
Console.WriteLine("another SourceGit is running");
117+
returnfalse;
118+
}
119+
}
120+
100121
privatestringFindExecutable(stringfilename)
101122
{
102123
varpathVariable=Environment.GetEnvironmentVariable("PATH")??string.Empty;

‎src/Native/MacOS.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,7 @@ public void OpenWithDefaultEditor(string file)
8888
{
8989
Process.Start("open",$"\"{file}\"");
9090
}
91+
92+
publicboolEnsureSingleInstance(){returntrue;}
9193
}
9294
}

‎src/Native/OS.cs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public interface IBackend
2323
voidOpenInFileManager(stringpath,boolselect);
2424
voidOpenBrowser(stringurl);
2525
voidOpenWithDefaultEditor(stringfile);
26+
27+
boolEnsureSingleInstance();
2628
}
2729

2830
publicstaticstringDataDir
@@ -220,6 +222,11 @@ private static void UpdateGitVersion()
220222
[GeneratedRegex(@"^git version[\s\w]*(\d+)\.(\d+)[\.\-](\d+).*$")]
221223
privatestaticpartialRegexREG_GIT_VERSION();
222224

225+
publicstaticboolEnsureSingleInstance()
226+
{
227+
return_backend.EnsureSingleInstance();
228+
}
229+
223230
privatestaticIBackend_backend=null;
224231
privatestaticstring_gitExecutable=string.Empty;
225232
}

‎src/Native/Windows.cs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace SourceGit.Native
1414
[SupportedOSPlatform("windows")]
1515
internalclassWindows:OS.IBackend
1616
{
17+
privateFileStream_fs=null;
18+
1719
[StructLayout(LayoutKind.Sequential)]
1820
internalstructRTL_OSVERSIONINFOEX
1921
{
@@ -393,5 +395,25 @@ private string FindVSSolutionFile(DirectoryInfo dir, int leftDepth)
393395

394396
returnnull;
395397
}
398+
399+
publicboolEnsureSingleInstance()
400+
{
401+
varpidfile=Path.Combine(Path.GetTempPath(),"sourcegit.pid");
402+
varpid=Process.GetCurrentProcess().Id.ToString();
403+
Console.WriteLine("pid "+pid);
404+
405+
try
406+
{
407+
_fs=File.OpenWrite(pidfile);
408+
_fs.Lock(0,1000);
409+
newStreamWriter(_fs).Write(pid);
410+
returntrue;
411+
}
412+
catch(IOException)
413+
{
414+
Console.WriteLine("another SourceGit is running");
415+
returnfalse;
416+
}
417+
}
396418
}
397419
}

‎src/Resources/Locales/en_US.axaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@
470470
<x:Stringx:Key="Text.Preferences.Appearance.ThemeOverrides"xml:space="preserve">Theme Overrides</x:String>
471471
<x:Stringx:Key="Text.Preferences.Appearance.UseFixedTabWidth"xml:space="preserve">Use fixed tab width in titlebar</x:String>
472472
<x:Stringx:Key="Text.Preferences.Appearance.UseNativeWindowFrame"xml:space="preserve">Use native window frame</x:String>
473+
<x:Stringx:Key="Text.Preferences.Appearance.SystemTrayIcon"xml:space="preserve">System tray icon (needs restart)</x:String>
473474
<x:Stringx:Key="Text.Preferences.DiffMerge"xml:space="preserve">DIFF/MERGE TOOL</x:String>
474475
<x:Stringx:Key="Text.Preferences.DiffMerge.Path"xml:space="preserve">Install Path</x:String>
475476
<x:Stringx:Key="Text.Preferences.DiffMerge.Path.Placeholder"xml:space="preserve">Input path for diff/merge tool</x:String>

‎src/Resources/Locales/ru_RU.axaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@
472472
<x:Stringx:Key="Text.Preferences.Appearance.ThemeOverrides"xml:space="preserve">Переопределение темы</x:String>
473473
<x:Stringx:Key="Text.Preferences.Appearance.UseFixedTabWidth"xml:space="preserve">Использовать фиксированную ширину табуляции в строке заголовка.</x:String>
474474
<x:Stringx:Key="Text.Preferences.Appearance.UseNativeWindowFrame"xml:space="preserve">Использовать системное окно</x:String>
475+
<x:Stringx:Key="Text.Preferences.Appearance.SystemTrayIcon"xml:space="preserve">Иконка в системном лотке (нужен перезапуск)</x:String>
475476
<x:Stringx:Key="Text.Preferences.DiffMerge"xml:space="preserve">ИНСТРУМЕНТ РАЗЛИЧИЙ/СЛИЯНИЯ</x:String>
476477
<x:Stringx:Key="Text.Preferences.DiffMerge.Path"xml:space="preserve">Путь установки</x:String>
477478
<x:Stringx:Key="Text.Preferences.DiffMerge.Path.Placeholder"xml:space="preserve">Введите путь для инструмента различия/слияния</x:String>

‎src/ViewModels/Launcher.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public Workspace ActiveWorkspace
2929
privateset=>SetProperty(ref_activeWorkspace,value);
3030
}
3131

32+
publicboolInterceptQuit{get;set;}=false;
33+
3234
publicLauncherPageActivePage
3335
{
3436
get=>_activePage;
@@ -47,7 +49,6 @@ public LauncherPage ActivePage
4749
publicLauncher(stringstartupRepo)
4850
{
4951
_ignoreIndexChange=true;
50-
5152
Pages=newAvaloniaList<LauncherPage>();
5253
AddNewTab();
5354

‎src/ViewModels/Preferences.cs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,12 @@ public double LastCheckUpdateTime
348348
set=>SetProperty(ref_lastCheckUpdateTime,value);
349349
}
350350

351+
publicboolSystemTrayIcon
352+
{
353+
get=>_systemTrayIcon;
354+
set=>SetProperty(ref_systemTrayIcon,value);
355+
}
356+
351357
publicboolIsGitConfigured()
352358
{
353359
varpath=GitInstallPath;
@@ -682,5 +688,7 @@ private string FixFontFamilyName(string name)
682688
privatestring_externalMergeToolPath=string.Empty;
683689

684690
privateuint_statisticsSampleColor=0xFF00FF00;
691+
692+
privatebool_systemTrayIcon=false;
685693
}
686694
}

0 commit comments

Comments
(0)