refactor: rewrite SourceGit.App

This commit is contained in:
leo 2024-08-19 12:49:04 +08:00
parent 32e59c4048
commit 24dde77548
No known key found for this signature in database
6 changed files with 121 additions and 186 deletions

View file

@ -15,6 +15,7 @@ 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.Platform.Storage;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
@ -41,6 +42,13 @@ namespace SourceGit
public partial class App : Application 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] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
@ -89,47 +97,36 @@ namespace SourceGit
return builder; return builder;
} }
public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => public override void Initialize()
{ {
var toplevel = GetTopLevel() as Window; AvaloniaXamlLoader.Load(this);
if (toplevel == null)
return;
var dialog = new Views.Preference(); var pref = ViewModels.Preference.Instance;
dialog.ShowDialog(toplevel); 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 (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
if (toplevel == null) {
return; BindingPlugins.DataValidators.RemoveAt(0);
var dialog = new Views.Hotkeys(); if (TryLaunchedAsCoreEditor(desktop))
dialog.ShowDialog(toplevel); 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); if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
}); window.ShowDialog(desktop.MainWindow);
}
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));
public static void RaiseException(string context, string message) public static void RaiseException(string context, string message)
{ {
@ -258,17 +255,74 @@ namespace SourceGit
return icon; return icon;
} }
public static TopLevel GetTopLevel() public static IStorageProvider GetStorageProvider()
{ {
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 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; 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 () => 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) private static void ShowSelfUpdateResult(object data)
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop) OpenDialog(new Views.SelfUpdate() { DataContext = new ViewModels.SelfUpdate() { Data = data } });
{
var dialog = new Views.SelfUpdate()
{
DataContext = new ViewModels.SelfUpdate() { Data = data }
};
dialog.Show(desktop.MainWindow);
}
}); });
} }

View file

@ -380,12 +380,12 @@ namespace SourceGit.ViewModels
saveAs.IsEnabled = file.Type == Models.ObjectType.Blob; saveAs.IsEnabled = file.Type == Models.ObjectType.Blob;
saveAs.Click += async (_, ev) => saveAs.Click += async (_, ev) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FolderPickerOpenOptions() { AllowMultiple = false }; var options = new FolderPickerOpenOptions() { AllowMultiple = false };
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); var selected = await storageProvider.OpenFolderPickerAsync(options);
if (selected.Count == 1) if (selected.Count == 1)
{ {
var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path)); var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path));

View file

@ -428,12 +428,12 @@ namespace SourceGit.ViewModels
saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); saveToPatch.Header = App.Text("CommitCM.SaveAsPatch");
saveToPatch.Click += async (_, e) => saveToPatch.Click += async (_, e) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FolderPickerOpenOptions() { AllowMultiple = false }; var options = new FolderPickerOpenOptions() { AllowMultiple = false };
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); var selected = await storageProvider.OpenFolderPickerAsync(options);
if (selected.Count == 1) if (selected.Count == 1)
{ {
var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec(); var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec();

View file

@ -940,9 +940,7 @@ namespace SourceGit.ViewModels
}; };
} }
var launcher = App.GetTopLevel().DataContext as Launcher; App.GetLauncer()?.OpenRepositoryInTab(node, null);
if (launcher != null)
launcher.OpenRepositoryInTab(node, null);
} }
public void AddWorktree() public void AddWorktree()
@ -971,9 +969,7 @@ namespace SourceGit.ViewModels
}; };
} }
var launcher = App.GetTopLevel().DataContext as Launcher; App.GetLauncer()?.OpenRepositoryInTab(node, null);
if (launcher != null)
launcher.OpenRepositoryInTab(node, null);
} }
public ContextMenu CreateContextMenuForGitFlow() public ContextMenu CreateContextMenuForGitFlow()
@ -1130,12 +1126,8 @@ namespace SourceGit.ViewModels
{ {
locks.Click += (_, e) => 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) }; var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, _remotes[0].Name) };
dialog.Show(topLevel); App.OpenDialog(dialog);
e.Handled = true; e.Handled = true;
}; };
} }
@ -1148,12 +1140,8 @@ namespace SourceGit.ViewModels
lockRemote.Header = remoteName; lockRemote.Header = remoteName;
lockRemote.Click += (_, e) => lockRemote.Click += (_, e) =>
{ {
var topLevel = App.GetTopLevel() as Window;
if (topLevel == null)
return;
var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) }; var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) };
dialog.Show(topLevel); App.OpenDialog(dialog);
e.Handled = true; e.Handled = true;
}; };
locks.Items.Add(lockRemote); locks.Items.Add(lockRemote);
@ -1878,16 +1866,9 @@ namespace SourceGit.ViewModels
target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch");
target.Click += (_, e) => target.Click += (_, e) =>
{ {
var topLevel = App.GetTopLevel() as Window; App.OpenDialog(new Views.BranchCompare() {
if (topLevel == null)
return;
var wnd = new Views.BranchCompare()
{
DataContext = new BranchCompare(_fullpath, branch, dup) DataContext = new BranchCompare(_fullpath, branch, dup)
}; });
wnd.Show(topLevel);
e.Handled = true; e.Handled = true;
}; };

View file

@ -303,16 +303,10 @@ namespace SourceGit.ViewModels
public void OpenAssumeUnchanged() public void OpenAssumeUnchanged()
{ {
var toplevel = App.GetTopLevel() as Window; App.OpenDialog(new Views.AssumeUnchangedManager()
if (toplevel == null)
return;
var dialog = new Views.AssumeUnchangedManager()
{ {
DataContext = new AssumeUnchangedManager(_repo.FullPath) DataContext = new AssumeUnchangedManager(_repo.FullPath)
}; });
dialog.ShowDialog(toplevel);
} }
public void StashAll(bool autoStart) public void StashAll(bool autoStart)
@ -530,8 +524,8 @@ namespace SourceGit.ViewModels
patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => patch.Click += async (_, e) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FilePickerSaveOptions(); var options = new FilePickerSaveOptions();
@ -539,7 +533,7 @@ namespace SourceGit.ViewModels
options.DefaultExtension = ".patch"; options.DefaultExtension = ".patch";
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.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) if (storageFile != null)
{ {
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); 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.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => patch.Click += async (_, e) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FilePickerSaveOptions(); var options = new FilePickerSaveOptions();
@ -862,7 +856,7 @@ namespace SourceGit.ViewModels
options.DefaultExtension = ".patch"; options.DefaultExtension = ".patch";
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.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) if (storageFile != null)
{ {
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath)); 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.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => patch.Click += async (_, e) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FilePickerSaveOptions(); var options = new FilePickerSaveOptions();
@ -947,7 +941,7 @@ namespace SourceGit.ViewModels
options.DefaultExtension = ".patch"; options.DefaultExtension = ".patch";
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.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) if (storageFile != null)
{ {
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); 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) => stash.Click += (_, e) =>
{ {
if (PopupHost.CanCreatePopup()) if (PopupHost.CanCreatePopup())
{
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false)); PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
}
e.Handled = true; e.Handled = true;
}; };
@ -1096,8 +1089,8 @@ namespace SourceGit.ViewModels
patch.Icon = App.CreateMenuIcon("Icons.Diff"); patch.Icon = App.CreateMenuIcon("Icons.Diff");
patch.Click += async (_, e) => patch.Click += async (_, e) =>
{ {
var topLevel = App.GetTopLevel(); var storageProvider = App.GetStorageProvider();
if (topLevel == null) if (storageProvider == null)
return; return;
var options = new FilePickerSaveOptions(); var options = new FilePickerSaveOptions();
@ -1105,7 +1098,7 @@ namespace SourceGit.ViewModels
options.DefaultExtension = ".patch"; options.DefaultExtension = ".patch";
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.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) if (storageFile != null)
{ {
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath)); var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));

View file

@ -59,7 +59,7 @@ namespace SourceGit.Views
// Ctrl+Shift+P opens preference dialog (macOS use hotkeys in system menu bar) // 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) if (!OperatingSystem.IsMacOS() && e.KeyModifiers == (KeyModifiers.Control | KeyModifiers.Shift) && e.Key == Key.P)
{ {
App.OpenPreferenceCommand.Execute(null); App.OpenDialog(new Preference());
e.Handled = true; e.Handled = true;
return; return;
} }