mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2025-01-23 01:36:57 -08:00
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
This commit is contained in:
parent
620f411e99
commit
8930f1d614
13 changed files with 155 additions and 19 deletions
|
@ -37,6 +37,7 @@ namespace SourceGit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly Command Unminimize = new Command(_ => ShowWindow());
|
||||||
public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences()));
|
public static readonly Command OpenPreferencesCommand = new Command(_ => OpenDialog(new Views.Preferences()));
|
||||||
public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys()));
|
public static readonly Command OpenHotkeysCommand = new Command(_ => OpenDialog(new Views.Hotkeys()));
|
||||||
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
|
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
|
||||||
|
|
|
@ -14,6 +14,8 @@ using Avalonia.Data.Core.Plugins;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Fonts;
|
using Avalonia.Media.Fonts;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.Platform;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
@ -167,6 +169,46 @@ namespace SourceGit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetupTrayIcon(bool enable)
|
||||||
|
{
|
||||||
|
if (enable && Native.OS.EnsureSingleInstance())
|
||||||
|
{
|
||||||
|
var icons = new TrayIcons {
|
||||||
|
new TrayIcon {
|
||||||
|
Icon = new WindowIcon(new Bitmap(AssetLoader.Open(new Uri("avares://SourceGit/App.ico")))),
|
||||||
|
Menu = [
|
||||||
|
new NativeMenuItem(Text("Open")) {Command = Unminimize},
|
||||||
|
new NativeMenuItem(Text("Preferences")) {Command = OpenPreferencesCommand},
|
||||||
|
new NativeMenuItemSeparator(),
|
||||||
|
new NativeMenuItem(Text("Quit")) {Command = QuitCommand},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
icons[0].Clicked += (_, _) => ToggleWindow();
|
||||||
|
TrayIcon.SetIcons(Current, icons);
|
||||||
|
_createdSystemTrayIcon = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ToggleWindow() {
|
||||||
|
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
|
||||||
|
if (desktop.MainWindow.IsVisible) {
|
||||||
|
desktop.MainWindow.Hide();
|
||||||
|
} else {
|
||||||
|
ShowWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ShowWindow()
|
||||||
|
{
|
||||||
|
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
|
||||||
|
desktop.MainWindow.WindowState = WindowState.Normal;
|
||||||
|
desktop.MainWindow.Show();
|
||||||
|
desktop.MainWindow.BringIntoView();
|
||||||
|
desktop.MainWindow.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
public static void SetFonts(string defaultFont, string monospaceFont, bool onlyUseMonospaceFontInEditor)
|
public static void SetFonts(string defaultFont, string monospaceFont, bool onlyUseMonospaceFontInEditor)
|
||||||
{
|
{
|
||||||
var app = Current as App;
|
var app = Current as App;
|
||||||
|
@ -320,6 +362,7 @@ namespace SourceGit
|
||||||
|
|
||||||
TryLaunchAsNormal(desktop);
|
TryLaunchAsNormal(desktop);
|
||||||
}
|
}
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -476,11 +519,17 @@ namespace SourceGit
|
||||||
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))
|
if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]))
|
||||||
startupRepo = desktop.Args[0];
|
startupRepo = desktop.Args[0];
|
||||||
|
|
||||||
_launcher = new ViewModels.Launcher(startupRepo);
|
var pref = ViewModels.Preferences.Instance;
|
||||||
|
|
||||||
|
SetupTrayIcon(pref.SystemTrayIcon);
|
||||||
|
if (_createdSystemTrayIcon) {
|
||||||
|
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
_launcher = new ViewModels.Launcher(startupRepo) { InterceptQuit = _createdSystemTrayIcon };
|
||||||
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
|
desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
|
||||||
|
|
||||||
#if !DISABLE_UPDATE_DETECTION
|
#if !DISABLE_UPDATE_DETECTION
|
||||||
var pref = ViewModels.Preferences.Instance;
|
|
||||||
if (pref.ShouldCheck4UpdateOnStartup())
|
if (pref.ShouldCheck4UpdateOnStartup())
|
||||||
Check4Update();
|
Check4Update();
|
||||||
#endif
|
#endif
|
||||||
|
@ -543,5 +592,6 @@ namespace SourceGit
|
||||||
private ResourceDictionary _activeLocale = null;
|
private ResourceDictionary _activeLocale = null;
|
||||||
private ResourceDictionary _themeOverrides = null;
|
private ResourceDictionary _themeOverrides = null;
|
||||||
private ResourceDictionary _fontsOverrides = null;
|
private ResourceDictionary _fontsOverrides = null;
|
||||||
|
private bool _createdSystemTrayIcon = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace SourceGit.Native
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
internal class Linux : OS.IBackend
|
internal class Linux : OS.IBackend
|
||||||
{
|
{
|
||||||
|
private FileStream _fs = null;
|
||||||
public void SetupApp(AppBuilder builder)
|
public void SetupApp(AppBuilder builder)
|
||||||
{
|
{
|
||||||
builder.With(new X11PlatformOptions() { EnableIme = true });
|
builder.With(new X11PlatformOptions() { EnableIme = true });
|
||||||
|
@ -97,6 +98,26 @@ namespace SourceGit.Native
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool EnsureSingleInstance()
|
||||||
|
{
|
||||||
|
var pidfile = Path.Combine(Path.GetTempPath(), "sourcegit.pid");
|
||||||
|
var pid = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Console.WriteLine("pid " + pid);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_fs = File.OpenWrite(pidfile);
|
||||||
|
_fs.Lock(0, 1000);
|
||||||
|
new StreamWriter(_fs).Write(pid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("another SourceGit is running");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string FindExecutable(string filename)
|
private string FindExecutable(string filename)
|
||||||
{
|
{
|
||||||
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
|
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
|
||||||
|
|
|
@ -86,5 +86,7 @@ namespace SourceGit.Native
|
||||||
{
|
{
|
||||||
Process.Start("open", $"\"{file}\"");
|
Process.Start("open", $"\"{file}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool EnsureSingleInstance() { return true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ namespace SourceGit.Native
|
||||||
void OpenInFileManager(string path, bool select);
|
void OpenInFileManager(string path, bool select);
|
||||||
void OpenBrowser(string url);
|
void OpenBrowser(string url);
|
||||||
void OpenWithDefaultEditor(string file);
|
void OpenWithDefaultEditor(string file);
|
||||||
|
|
||||||
|
bool EnsureSingleInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string DataDir {
|
public static string DataDir {
|
||||||
|
@ -217,6 +219,11 @@ namespace SourceGit.Native
|
||||||
[GeneratedRegex(@"^git version[\s\w]*(\d+)\.(\d+)[\.\-](\d+).*$")]
|
[GeneratedRegex(@"^git version[\s\w]*(\d+)\.(\d+)[\.\-](\d+).*$")]
|
||||||
private static partial Regex REG_GIT_VERSION();
|
private static partial Regex REG_GIT_VERSION();
|
||||||
|
|
||||||
|
public static bool EnsureSingleInstance()
|
||||||
|
{
|
||||||
|
return _backend.EnsureSingleInstance();
|
||||||
|
}
|
||||||
|
|
||||||
private static IBackend _backend = null;
|
private static IBackend _backend = null;
|
||||||
private static string _gitExecutable = string.Empty;
|
private static string _gitExecutable = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace SourceGit.Native
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
internal class Windows : OS.IBackend
|
internal class Windows : OS.IBackend
|
||||||
{
|
{
|
||||||
|
private FileStream _fs = null;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal struct RTL_OSVERSIONINFOEX
|
internal struct RTL_OSVERSIONINFOEX
|
||||||
{
|
{
|
||||||
|
@ -393,5 +395,25 @@ namespace SourceGit.Native
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool EnsureSingleInstance()
|
||||||
|
{
|
||||||
|
var pidfile = Path.Combine(Path.GetTempPath(), "sourcegit.pid");
|
||||||
|
var pid = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Console.WriteLine("pid " + pid);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_fs = File.OpenWrite(pidfile);
|
||||||
|
_fs.Lock(0, 1000);
|
||||||
|
new StreamWriter(_fs).Write(pid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("another SourceGit is running");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,6 +458,7 @@
|
||||||
<x:String x:Key="Text.Preferences.Appearance.ThemeOverrides" xml:space="preserve">Theme Overrides</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.ThemeOverrides" xml:space="preserve">Theme Overrides</x:String>
|
||||||
<x:String x:Key="Text.Preferences.Appearance.UseFixedTabWidth" xml:space="preserve">Use fixed tab width in titlebar</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.UseFixedTabWidth" xml:space="preserve">Use fixed tab width in titlebar</x:String>
|
||||||
<x:String x:Key="Text.Preferences.Appearance.UseNativeWindowFrame" xml:space="preserve">Use native window frame</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.UseNativeWindowFrame" xml:space="preserve">Use native window frame</x:String>
|
||||||
|
<x:String x:Key="Text.Preferences.Appearance.SystemTrayIcon" xml:space="preserve">System tray icon (needs restart)</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge" xml:space="preserve">DIFF/MERGE TOOL</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge.Path" xml:space="preserve">Install Path</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge.Path" xml:space="preserve">Install Path</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">Input path for diff/merge tool</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">Input path for diff/merge tool</x:String>
|
||||||
|
|
|
@ -462,6 +462,7 @@
|
||||||
<x:String x:Key="Text.Preferences.Appearance.ThemeOverrides" xml:space="preserve">Переопределение темы</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.ThemeOverrides" xml:space="preserve">Переопределение темы</x:String>
|
||||||
<x:String x:Key="Text.Preferences.Appearance.UseFixedTabWidth" xml:space="preserve">Использовать фиксированную ширину табуляции в строке заголовка.</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.UseFixedTabWidth" xml:space="preserve">Использовать фиксированную ширину табуляции в строке заголовка.</x:String>
|
||||||
<x:String x:Key="Text.Preferences.Appearance.UseNativeWindowFrame" xml:space="preserve">Использовать системное окно</x:String>
|
<x:String x:Key="Text.Preferences.Appearance.UseNativeWindowFrame" xml:space="preserve">Использовать системное окно</x:String>
|
||||||
|
<x:String x:Key="Text.Preferences.Appearance.SystemTrayIcon" xml:space="preserve">Иконка в системном лотке (нужен перезапуск)</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge" xml:space="preserve">ИНСТРУМЕНТ РАЗЛИЧИЙ/СЛИЯНИЯ</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge" xml:space="preserve">ИНСТРУМЕНТ РАЗЛИЧИЙ/СЛИЯНИЯ</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge.Path" xml:space="preserve">Путь установки</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge.Path" xml:space="preserve">Путь установки</x:String>
|
||||||
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">Введите путь для инструмента различия/слияния</x:String>
|
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">Введите путь для инструмента различия/слияния</x:String>
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _activeWorkspace, value);
|
private set => SetProperty(ref _activeWorkspace, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool InterceptQuit { get; set; } = false;
|
||||||
|
|
||||||
public LauncherPage ActivePage
|
public LauncherPage ActivePage
|
||||||
{
|
{
|
||||||
get => _activePage;
|
get => _activePage;
|
||||||
|
@ -47,7 +49,6 @@ namespace SourceGit.ViewModels
|
||||||
public Launcher(string startupRepo)
|
public Launcher(string startupRepo)
|
||||||
{
|
{
|
||||||
_ignoreIndexChange = true;
|
_ignoreIndexChange = true;
|
||||||
|
|
||||||
Pages = new AvaloniaList<LauncherPage>();
|
Pages = new AvaloniaList<LauncherPage>();
|
||||||
AddNewTab();
|
AddNewTab();
|
||||||
|
|
||||||
|
|
|
@ -348,6 +348,12 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _lastCheckUpdateTime, value);
|
set => SetProperty(ref _lastCheckUpdateTime, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SystemTrayIcon
|
||||||
|
{
|
||||||
|
get => _systemTrayIcon;
|
||||||
|
set => SetProperty(ref _systemTrayIcon, value);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsGitConfigured()
|
public bool IsGitConfigured()
|
||||||
{
|
{
|
||||||
var path = GitInstallPath;
|
var path = GitInstallPath;
|
||||||
|
@ -682,5 +688,7 @@ namespace SourceGit.ViewModels
|
||||||
private string _externalMergeToolPath = string.Empty;
|
private string _externalMergeToolPath = string.Empty;
|
||||||
|
|
||||||
private uint _statisticsSampleColor = 0xFF00FF00;
|
private uint _statisticsSampleColor = 0xFF00FF00;
|
||||||
|
|
||||||
|
private bool _systemTrayIcon = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,7 +261,13 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
protected override void OnClosing(WindowClosingEventArgs e)
|
protected override void OnClosing(WindowClosingEventArgs e)
|
||||||
{
|
{
|
||||||
(DataContext as ViewModels.Launcher)?.Quit(Width, Height);
|
var launcher = DataContext as ViewModels.Launcher;
|
||||||
|
if (launcher is { InterceptQuit: true }) {
|
||||||
|
e.Cancel = true;
|
||||||
|
Hide();
|
||||||
|
} else {
|
||||||
|
launcher?.Quit(Width, Height);
|
||||||
|
}
|
||||||
base.OnClosing(e);
|
base.OnClosing(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
<TabItem.Header>
|
<TabItem.Header>
|
||||||
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preferences.Appearance}"/>
|
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preferences.Appearance}"/>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
|
<Grid Margin="8" RowDefinitions="32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||||
Text="{DynamicResource Text.Preferences.Appearance.Theme}"
|
Text="{DynamicResource Text.Preferences.Appearance.Theme}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
|
@ -232,21 +232,26 @@
|
||||||
</TextBox.InnerRightContent>
|
</TextBox.InnerRightContent>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<CheckBox Grid.Row="5" Grid.Column="1"
|
<StackPanel Grid.Row="5" Grid.Column="1">
|
||||||
Content="{DynamicResource Text.Preferences.Appearance.OnlyUseMonoFontInEditor}"
|
<CheckBox Content="{DynamicResource Text.Preferences.Appearance.OnlyUseMonoFontInEditor}"
|
||||||
IsChecked="{Binding OnlyUseMonoFontInEditor, Mode=TwoWay}"/>
|
IsChecked="{Binding OnlyUseMonoFontInEditor, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<CheckBox Grid.Row="6" Grid.Column="1"
|
<CheckBox Height="32"
|
||||||
Height="32"
|
|
||||||
Content="{DynamicResource Text.Preferences.Appearance.UseFixedTabWidth}"
|
Content="{DynamicResource Text.Preferences.Appearance.UseFixedTabWidth}"
|
||||||
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseFixedTabWidth, Mode=TwoWay}"/>
|
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseFixedTabWidth, Mode=TwoWay}"/>
|
||||||
|
|
||||||
<CheckBox Grid.Row="7" Grid.Column="1"
|
<CheckBox Height="32"
|
||||||
Height="32"
|
|
||||||
Content="{DynamicResource Text.Preferences.Appearance.UseNativeWindowFrame}"
|
Content="{DynamicResource Text.Preferences.Appearance.UseNativeWindowFrame}"
|
||||||
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSystemWindowFrame, Mode=OneTime}"
|
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=UseSystemWindowFrame, Mode=OneTime}"
|
||||||
IsVisible="{OnPlatform False, Linux=True}"
|
IsVisible="{OnPlatform False, Linux=True}"
|
||||||
IsCheckedChanged="OnUseNativeWindowFrameChanged"/>
|
IsCheckedChanged="OnUseNativeWindowFrameChanged"/>
|
||||||
|
|
||||||
|
<CheckBox Height="32"
|
||||||
|
Content="{DynamicResource Text.Preferences.Appearance.SystemTrayIcon}"
|
||||||
|
IsChecked="{Binding Path=SystemTrayIcon, Mode=OneTime}"
|
||||||
|
IsVisible="True"
|
||||||
|
IsCheckedChanged="OnSystemTrayIconCheckedChanged"/>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
|
|
@ -333,6 +333,17 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
private void OnSystemTrayIconCheckedChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is CheckBox box)
|
||||||
|
{
|
||||||
|
ViewModels.Preferences.Instance.SystemTrayIcon = box.IsChecked == true;
|
||||||
|
var dialog = new ConfirmRestart();
|
||||||
|
App.OpenDialog(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGitInstallPathChanged(object sender, TextChangedEventArgs e)
|
private void OnGitInstallPathChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue