diff --git a/src/App.axaml.cs b/src/App.axaml.cs index a1eaee6a..1015ee34 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -15,6 +15,7 @@ using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Fonts; +using Avalonia.Platform.Storage; using Avalonia.Styling; using Avalonia.Threading; @@ -41,6 +42,13 @@ namespace SourceGit public partial class App : Application { + public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => OpenDialog(new Views.Preference())); + public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => OpenDialog(new Views.Hotkeys())); + public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => Native.OS.OpenInFileManager(Native.OS.DataDir)); + public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => OpenDialog(new Views.About())); + public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => Check4Update(true)); + public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + [STAThread] public static void Main(string[] args) { @@ -89,47 +97,36 @@ namespace SourceGit return builder; } - public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => + public override void Initialize() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + AvaloniaXamlLoader.Load(this); - var dialog = new Views.Preference(); - dialog.ShowDialog(toplevel); - }); + var pref = ViewModels.Preference.Instance; + SetLocale(pref.Locale); + SetTheme(pref.Theme, pref.ThemeOverrides); + } - public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => + public override void OnFrameworkInitializationCompleted() { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + BindingPlugins.DataValidators.RemoveAt(0); - var dialog = new Views.Hotkeys(); - dialog.ShowDialog(toplevel); - }); + if (TryLaunchedAsCoreEditor(desktop)) + return; - public static readonly SimpleCommand OpenAppDataDirCommand = new SimpleCommand(() => + if (TryLaunchedAsAskpass(desktop)) + return; + + TryLaunchedAsNormal(desktop); + } + } + + public static void OpenDialog(Window window) { - Native.OS.OpenInFileManager(Native.OS.DataDir); - }); - - public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => - { - var toplevel = GetTopLevel() as Window; - if (toplevel == null) - return; - - var dialog = new Views.About(); - dialog.ShowDialog(toplevel); - }); - - public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => - { - Check4Update(true); - }); - - public static readonly SimpleCommand QuitCommand = new SimpleCommand(() => Quit(0)); + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + window.ShowDialog(desktop.MainWindow); + } public static void RaiseException(string context, string message) { @@ -258,17 +255,74 @@ namespace SourceGit return icon; } - public static TopLevel GetTopLevel() + public static IStorageProvider GetStorageProvider() { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + return desktop.MainWindow.StorageProvider; + + return null; + } + + public static ViewModels.Launcher GetLauncer() + { + return Current is App app ? app._launcher : null; + } + + public static ViewModels.Repository FindOpenedRepository(string repoPath) + { + if (Current is App app && app._launcher != null) { - return desktop.MainWindow; + foreach (var page in app._launcher.Pages) + { + var id = page.Node.Id.Replace("\\", "/"); + if (id == repoPath && page.Data is ViewModels.Repository repo) + return repo; + } } return null; } - public static void Check4Update(bool manually = false) + public static void Quit(int exitCode) + { + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow?.Close(); + desktop.Shutdown(exitCode); + } + else + { + Environment.Exit(exitCode); + } + } + + private static void LogException(Exception ex) + { + if (ex == null) + return; + + var builder = new StringBuilder(); + builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); + builder.Append("----------------------------\n"); + builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); + builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); + builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); + builder.Append($"Source: {ex.Source}\n"); + builder.Append($"---------------------------\n\n"); + builder.Append(ex.StackTrace); + while (ex.InnerException != null) + { + ex = ex.InnerException; + builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); + builder.Append(ex.StackTrace); + } + + var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); + File.WriteAllText(file, builder.ToString()); + } + + private static void Check4Update(bool manually = false) { Task.Run(async () => { @@ -309,104 +363,11 @@ namespace SourceGit }); } - public static ViewModels.Launcher GetLauncer() - { - return Current is App app ? app._launcher : null; - } - - public static ViewModels.Repository FindOpenedRepository(string repoPath) - { - if (Current is App app && app._launcher != null) - { - foreach (var page in app._launcher.Pages) - { - var id = page.Node.Id.Replace("\\", "/"); - if (id == repoPath && page.Data is ViewModels.Repository repo) - return repo; - } - } - - return null; - } - - public static void Quit(int exitCode) - { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow?.Close(); - desktop.Shutdown(exitCode); - } - else - { - Environment.Exit(exitCode); - } - } - - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - - var pref = ViewModels.Preference.Instance; - - SetLocale(pref.Locale); - SetTheme(pref.Theme, pref.ThemeOverrides); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - BindingPlugins.DataValidators.RemoveAt(0); - - if (TryLaunchedAsCoreEditor(desktop)) - return; - - if (TryLaunchedAsAskpass(desktop)) - return; - - TryLaunchedAsNormal(desktop); - } - } - - private static void LogException(Exception ex) - { - if (ex == null) - return; - - var builder = new StringBuilder(); - builder.Append($"Crash::: {ex.GetType().FullName}: {ex.Message}\n\n"); - builder.Append("----------------------------\n"); - builder.Append($"Version: {Assembly.GetExecutingAssembly().GetName().Version}\n"); - builder.Append($"OS: {Environment.OSVersion.ToString()}\n"); - builder.Append($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}\n"); - builder.Append($"Source: {ex.Source}\n"); - builder.Append($"---------------------------\n\n"); - builder.Append(ex.StackTrace); - while (ex.InnerException != null) - { - ex = ex.InnerException; - builder.Append($"\n\nInnerException::: {ex.GetType().FullName}: {ex.Message}\n"); - builder.Append(ex.StackTrace); - } - - var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - var file = Path.Combine(Native.OS.DataDir, $"crash_{time}.log"); - File.WriteAllText(file, builder.ToString()); - } - private static void ShowSelfUpdateResult(object data) { Dispatcher.UIThread.Post(() => { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop) - { - var dialog = new Views.SelfUpdate() - { - DataContext = new ViewModels.SelfUpdate() { Data = data } - }; - - dialog.Show(desktop.MainWindow); - } + OpenDialog(new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } }); }); } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index f6c01259..1462d701 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -380,12 +380,12 @@ namespace SourceGit.ViewModels saveAs.IsEnabled = file.Type == Models.ObjectType.Blob; saveAs.Click += async (_, ev) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FolderPickerOpenOptions() { AllowMultiple = false }; - var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + var selected = await storageProvider.OpenFolderPickerAsync(options); if (selected.Count == 1) { var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path)); diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 86a211fa..ad597985 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -428,12 +428,12 @@ namespace SourceGit.ViewModels saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); saveToPatch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FolderPickerOpenOptions() { AllowMultiple = false }; - var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + var selected = await storageProvider.OpenFolderPickerAsync(options); if (selected.Count == 1) { var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec(); diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index e304be0b..82a03f64 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -940,9 +940,7 @@ namespace SourceGit.ViewModels }; } - var launcher = App.GetTopLevel().DataContext as Launcher; - if (launcher != null) - launcher.OpenRepositoryInTab(node, null); + App.GetLauncer()?.OpenRepositoryInTab(node, null); } public void AddWorktree() @@ -971,9 +969,7 @@ namespace SourceGit.ViewModels }; } - var launcher = App.GetTopLevel().DataContext as Launcher; - if (launcher != null) - launcher.OpenRepositoryInTab(node, null); + App.GetLauncer()?.OpenRepositoryInTab(node, null); } public ContextMenu CreateContextMenuForGitFlow() @@ -1130,12 +1126,8 @@ namespace SourceGit.ViewModels { locks.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, _remotes[0].Name) }; - dialog.Show(topLevel); + App.OpenDialog(dialog); e.Handled = true; }; } @@ -1148,12 +1140,8 @@ namespace SourceGit.ViewModels lockRemote.Header = remoteName; lockRemote.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) }; - dialog.Show(topLevel); + App.OpenDialog(dialog); e.Handled = true; }; locks.Items.Add(lockRemote); @@ -1878,16 +1866,9 @@ namespace SourceGit.ViewModels target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); target.Click += (_, e) => { - var topLevel = App.GetTopLevel() as Window; - if (topLevel == null) - return; - - var wnd = new Views.BranchCompare() - { - DataContext = new BranchCompare(_fullpath, branch, dup) - }; - - wnd.Show(topLevel); + App.OpenDialog(new Views.BranchCompare() { + DataContext = new BranchCompare(_fullpath, branch, dup) + }); e.Handled = true; }; diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index e34f06c5..90e6b6c9 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -303,16 +303,10 @@ namespace SourceGit.ViewModels public void OpenAssumeUnchanged() { - var toplevel = App.GetTopLevel() as Window; - if (toplevel == null) - return; - - var dialog = new Views.AssumeUnchangedManager() + App.OpenDialog(new Views.AssumeUnchangedManager() { DataContext = new AssumeUnchangedManager(_repo.FullPath) - }; - - dialog.ShowDialog(toplevel); + }); } public void StashAll(bool autoStart) @@ -530,8 +524,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -539,7 +533,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); @@ -853,8 +847,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -862,7 +856,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); @@ -938,8 +932,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -947,7 +941,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); @@ -1085,9 +1079,8 @@ namespace SourceGit.ViewModels stash.Click += (_, e) => { if (PopupHost.CanCreatePopup()) - { PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false)); - } + e.Handled = true; }; @@ -1096,8 +1089,8 @@ namespace SourceGit.ViewModels patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Click += async (_, e) => { - var topLevel = App.GetTopLevel(); - if (topLevel == null) + var storageProvider = App.GetStorageProvider(); + if (storageProvider == null) return; var options = new FilePickerSaveOptions(); @@ -1105,7 +1098,7 @@ namespace SourceGit.ViewModels options.DefaultExtension = ".patch"; options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }]; - var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options); + var storageFile = await storageProvider.SaveFilePickerAsync(options); if (storageFile != null) { var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index e73ad1a9..d2672c5e 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -59,7 +59,7 @@ namespace SourceGit.Views // Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar) if (!OperatingSystem.IsMacOS() && e.KeyModifiers == (KeyModifiers.Control | KeyModifiers.Shift) && e.Key == Key.P) { - App.OpenPreferenceCommand.Execute(null); + App.OpenDialog(new Preference()); e.Handled = true; return; }