diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md index 7394d123..f5e0b540 100644 --- a/THIRD-PARTY-LICENSES.md +++ b/THIRD-PARTY-LICENSES.md @@ -7,7 +7,7 @@ The project uses the following third-party libraries or assets ### AvaloniaUI - **Source**: https://github.com/AvaloniaUI/Avalonia -- **Version**: 11.3.12 +- **Version**: 11.3.13 - **License**: MIT License - **License Link**: https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md @@ -22,7 +22,7 @@ The project uses the following third-party libraries or assets ### LiveChartsCore.SkiaSharpView.Avalonia - **Source**: https://github.com/beto-rodriguez/LiveCharts2 -- **Version**: 2.0.0-rc6.1 +- **Version**: 2.0.0 - **License**: MIT License - **License Link**: https://github.com/beto-rodriguez/LiveCharts2/blob/master/LICENSE @@ -36,7 +36,7 @@ The project uses the following third-party libraries or assets ### OpenAI .NET SDK - **Source**: https://github.com/openai/openai-dotnet -- **Version**: 2.9.1 +- **Version**: 2.10.0 - **License**: MIT License - **License Link**: https://github.com/openai/openai-dotnet/blob/main/LICENSE diff --git a/TRANSLATION.md b/TRANSLATION.md index a126d45c..813b763c 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -6,19 +6,23 @@ This document shows the translation status of each locale file in the repository ### ![en_US](https://img.shields.io/badge/en__US-%E2%88%9A-brightgreen) -### ![de__DE](https://img.shields.io/badge/de__DE-98.04%25-yellow) +### ![de__DE](https://img.shields.io/badge/de__DE-97.53%25-yellow)
Missing keys in de_DE.axaml - Text.AIAssistant.Use - Text.Apply.3Way +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions - Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column - Text.ConfirmEmptyCommit.StageSelectedThenCommit +- Text.Discard.IncludeModified - Text.GotoRevisionSelector - Text.Hotkeys.Repo.CreateBranch - Text.Hotkeys.Repo.GoToChild @@ -27,6 +31,7 @@ This document shows the translation status of each locale file in the repository - Text.Preferences.AI.AdditionalPrompt - Text.Preferences.General.Use24Hours - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.Worktree.Branch - Text.Worktree.Head - Text.Worktree.Path @@ -35,7 +40,7 @@ This document shows the translation status of each locale file in the repository ### ![es__ES](https://img.shields.io/badge/es__ES-%E2%88%9A-brightgreen) -### ![fr__FR](https://img.shields.io/badge/fr__FR-91.84%25-yellow) +### ![fr__FR](https://img.shields.io/badge/fr__FR-91.37%25-yellow)
Missing keys in fr_FR.axaml @@ -52,6 +57,9 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions @@ -61,6 +69,7 @@ This document shows the translation status of each locale file in the repository - Text.Compare.WithHead - Text.Configure.Git.AskBeforeAutoUpdatingSubmodules - Text.ConfirmEmptyCommit.StageSelectedThenCommit +- Text.Discard.IncludeModified - Text.EditBranchDescription - Text.EditBranchDescription.Target - Text.FileCM.CustomAction @@ -110,6 +119,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Fixup - Text.SquashOrFixup.Into - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.TagCM.CompareTwo - Text.TagCM.CompareWith - Text.TagCM.CompareWithHead @@ -122,7 +132,7 @@ This document shows the translation status of each locale file in the repository
-### ![id__ID](https://img.shields.io/badge/id__ID-89.67%25-yellow) +### ![id__ID](https://img.shields.io/badge/id__ID-89.21%25-yellow)
Missing keys in id_ID.axaml @@ -143,6 +153,9 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions @@ -157,6 +170,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.DealWithLocalChanges.DoNothing +- Text.Discard.IncludeModified - Text.DropHead - Text.DropHead.Commit - Text.DropHead.NewHead @@ -218,6 +232,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Fixup - Text.SquashOrFixup.Into - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.TagCM.CompareTwo - Text.TagCM.CompareWith - Text.TagCM.CompareWithHead @@ -230,7 +245,7 @@ This document shows the translation status of each locale file in the repository
-### ![it__IT](https://img.shields.io/badge/it__IT-97.42%25-yellow) +### ![it__IT](https://img.shields.io/badge/it__IT-96.92%25-yellow)
Missing keys in it_IT.axaml @@ -238,12 +253,16 @@ This document shows the translation status of each locale file in the repository - Text.AIAssistant.Use - Text.Apply.3Way - Text.ChangeCM.ResetFileTo +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions - Text.CommandPalette.RevisionFiles - Text.CommitMessageTextBox.Column - Text.ConfirmEmptyCommit.StageSelectedThenCommit +- Text.Discard.IncludeModified - Text.GotoRevisionSelector - Text.Histories.Header.DateTime - Text.Histories.ShowColumns @@ -257,37 +276,43 @@ This document shows the translation status of each locale file in the repository - Text.SelfUpdate.CurrentVersion - Text.SelfUpdate.ReleaseDate - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.Worktree.Branch - Text.Worktree.Head - Text.Worktree.Path
-### ![ja__JP](https://img.shields.io/badge/ja__JP-98.35%25-yellow) +### ![ja__JP](https://img.shields.io/badge/ja__JP-97.84%25-yellow)
Missing keys in ja_JP.axaml - Text.AIAssistant.Use - Text.Apply.3Way +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions - Text.CommandPalette.RevisionFiles - Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.DealWithLocalChanges.DoNothing +- Text.Discard.IncludeModified - Text.Hotkeys.Repo.CreateBranch - Text.Init.CommandTip - Text.Init.ErrorMessageTip - Text.Preferences.AI.AdditionalPrompt - Text.Preferences.General.Use24Hours +- Text.StashCM.Branch - Text.Worktree.Branch - Text.Worktree.Head - Text.Worktree.Path
-### ![ko__KR](https://img.shields.io/badge/ko__KR-89.98%25-yellow) +### ![ko__KR](https://img.shields.io/badge/ko__KR-89.52%25-yellow)
Missing keys in ko_KR.axaml @@ -308,6 +333,9 @@ This document shows the translation status of each locale file in the repository - Text.ChangeCM.MergeExternal - Text.ChangeCM.ResetFileTo - Text.Checkout.WarnUpdatingSubmodules +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions @@ -320,6 +348,7 @@ This document shows the translation status of each locale file in the repository - Text.ConfigureCustomActionControls.StringValue.Tip - Text.ConfirmEmptyCommit.StageSelectedThenCommit - Text.DealWithLocalChanges.DoNothing +- Text.Discard.IncludeModified - Text.EditBranchDescription - Text.EditBranchDescription.Target - Text.FileCM.CustomAction @@ -379,6 +408,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Fixup - Text.SquashOrFixup.Into - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.Submodule.Status.Unmerged - Text.TagCM.CompareTwo - Text.TagCM.CompareWith @@ -392,7 +422,7 @@ This document shows the translation status of each locale file in the repository
-### ![pt__BR](https://img.shields.io/badge/pt__BR-68.08%25-red) +### ![pt__BR](https://img.shields.io/badge/pt__BR-67.73%25-red)
Missing keys in pt_BR.axaml @@ -416,6 +446,9 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.Clone.RecurseSubmodules - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags @@ -509,6 +542,7 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate +- Text.Discard.IncludeModified - Text.Discard.IncludeUntracked - Text.DropHead - Text.DropHead.Commit @@ -655,6 +689,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Into - Text.Stash.Mode - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.StashCM.CopyMessage - Text.StashCM.SaveAsPatch - Text.Submodule.Branch @@ -711,7 +746,7 @@ This document shows the translation status of each locale file in the repository ### ![ru__RU](https://img.shields.io/badge/ru__RU-%E2%88%9A-brightgreen) -### ![ta__IN](https://img.shields.io/badge/ta__IN-70.14%25-red) +### ![ta__IN](https://img.shields.io/badge/ta__IN-69.78%25-red)
Missing keys in ta_IN.axaml @@ -764,6 +799,9 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions @@ -843,6 +881,7 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate +- Text.Discard.IncludeModified - Text.Discard.IncludeUntracked - Text.DropHead - Text.DropHead.Commit @@ -957,6 +996,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Into - Text.Stash.Mode - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.StashCM.CopyMessage - Text.Submodule.Branch - Text.Submodule.CopyBranch @@ -1008,7 +1048,7 @@ This document shows the translation status of each locale file in the repository
-### ![uk__UA](https://img.shields.io/badge/uk__UA-70.97%25-red) +### ![uk__UA](https://img.shields.io/badge/uk__UA-70.61%25-red)
Missing keys in uk_UA.axaml @@ -1061,6 +1101,9 @@ This document shows the translation status of each locale file in the repository - Text.Checkout.WarnUpdatingSubmodules - Text.Checkout.WithFastForward - Text.Checkout.WithFastForward.Upstream +- Text.CheckoutBranchFromStash +- Text.CheckoutBranchFromStash.Branch +- Text.CheckoutBranchFromStash.Stash - Text.CommandPalette.Branches - Text.CommandPalette.BranchesAndTags - Text.CommandPalette.RepositoryActions @@ -1136,6 +1179,7 @@ This document shows the translation status of each locale file in the repository - Text.DirtyState.HasLocalChanges - Text.DirtyState.HasPendingPullOrPush - Text.DirtyState.UpToDate +- Text.Discard.IncludeModified - Text.Discard.IncludeUntracked - Text.DropHead - Text.DropHead.Commit @@ -1250,6 +1294,7 @@ This document shows the translation status of each locale file in the repository - Text.SquashOrFixup.Into - Text.Stash.Mode - Text.StashCM.ApplyFileChanges +- Text.StashCM.Branch - Text.StashCM.CopyMessage - Text.Submodule.Branch - Text.Submodule.CopyBranch diff --git a/VERSION b/VERSION index 2a61ee68..1628770a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026.07 \ No newline at end of file +2026.08 \ No newline at end of file diff --git a/depends/AvaloniaEdit b/depends/AvaloniaEdit index 87e83c5c..1e6f595f 160000 --- a/depends/AvaloniaEdit +++ b/depends/AvaloniaEdit @@ -1 +1 @@ -Subproject commit 87e83c5c0e4c3886d5db9acf3d0b37366690f762 +Subproject commit 1e6f595ff0da0dc20637322cbab9531892c12cbc diff --git a/src/AI/Agent.cs b/src/AI/Agent.cs index 8431293a..2979dadb 100644 --- a/src/AI/Agent.cs +++ b/src/AI/Agent.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; -using Azure.AI.OpenAI; -using OpenAI; using OpenAI.Chat; namespace SourceGit.AI @@ -18,12 +16,10 @@ namespace SourceGit.AI public async Task GenerateCommitMessageAsync(string repo, string changeList, Action onUpdate, CancellationToken cancellation) { - var endPoint = new Uri(_service.Server); - var client = _service.Server.Contains("openai.azure.com/", StringComparison.Ordinal) - ? new AzureOpenAIClient(endPoint, _service.Credential) - : new OpenAIClient(_service.Credential, new() { Endpoint = endPoint }); + var chatClient = _service.GetChatClient(); + if (chatClient == null) + throw new Exception("Failed to fetch available models from this service. Please check your configuration and try again."); - var chatClient = client.GetChatClient(_service.Model); var options = new ChatCompletionOptions() { Tools = { ChatTools.GetDetailChangesInFile } }; var userMessageBuilder = new StringBuilder(); @@ -31,8 +27,8 @@ namespace SourceGit.AI .AppendLine("Generate a commit message (follow the rule of conventional commit message) for given git repository.") .AppendLine("- Read all given changed files before generating. Only binary files (such as images, audios ...) can be skipped.") .AppendLine("- Output the conventional commit message (with detail changes in list) directly. Do not explain your output nor introduce your answer.") - .AppendLine(string.IsNullOrEmpty(_service.AdditionalPrompt) ? string.Empty : _service.AdditionalPrompt) - .Append("Reposiory path: ").AppendLine(repo.Quoted()) + .AppendLine(_service.AdditionalPrompt) + .Append("Repository path: ").AppendLine(repo.Quoted()) .AppendLine("Changed files ('A' means added, 'M' means modified, 'D' means deleted, 'T' means type changed, 'R' means renamed, 'C' means copied): ") .Append(changeList); @@ -65,7 +61,7 @@ namespace SourceGit.AI foreach (var call in completion.ToolCalls) { - var result = await ChatTools.Process(call, onUpdate); + var result = await ChatTools.ProcessAsync(call, onUpdate); messages.Add(result); } @@ -73,7 +69,7 @@ namespace SourceGit.AI break; } case ChatFinishReason.ContentFilter: - throw new Exception("Ommitted content due to a content filter flag"); + throw new Exception("Omitted content due to a content filter flag"); default: break; } diff --git a/src/AI/ChatTools.cs b/src/AI/ChatTools.cs index afc2cca0..b9e2f80c 100644 --- a/src/AI/ChatTools.cs +++ b/src/AI/ChatTools.cs @@ -32,7 +32,7 @@ namespace SourceGit.AI } """)), false); - public static async Task Process(ChatToolCall call, Action output) + public static async Task ProcessAsync(ChatToolCall call, Action output) { using var doc = JsonDocument.Parse(call.FunctionArguments); diff --git a/src/AI/Service.cs b/src/AI/Service.cs index 2482a8f8..f7eb25e6 100644 --- a/src/AI/Service.cs +++ b/src/AI/Service.cs @@ -1,16 +1,93 @@ using System; using System.ClientModel; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Azure.AI.OpenAI; +using CommunityToolkit.Mvvm.ComponentModel; +using OpenAI; +using OpenAI.Chat; namespace SourceGit.AI { - public class Service + public class Service : ObservableObject { - public string Name { get; set; } - public string Server { get; set; } - public string Model { get; set; } - public string ApiKey { get; set; } - public bool ReadApiKeyFromEnv { get; set; } - public string AdditionalPrompt { get; set; } - public ApiKeyCredential Credential => new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey); + public string Name + { + get => _name; + set => SetProperty(ref _name, value); + } + + public string Server + { + get; + set; + } = string.Empty; + + public string ApiKey + { + get; + set; + } = string.Empty; + + public bool ReadApiKeyFromEnv + { + get; + set; + } = false; + + public string AdditionalPrompt + { + get; + set; + } = string.Empty; + + [JsonIgnore] + public List AvailableModels + { + get; + private set; + } = []; + + public string Model + { + get; + set; + } = string.Empty; + + public async Task> FetchAvailableModelsAsync() + { + var allModels = GetOpenAIClient().GetOpenAIModelClient().GetModels(); + AvailableModels = new List(); + foreach (var model in allModels.Value) + AvailableModels.Add(model.Id); + + if (AvailableModels.Count > 0) + { + if (string.IsNullOrEmpty(Model) || !AvailableModels.Contains(Model)) + Model = AvailableModels[0]; + } + else + { + Model = null; + } + + return AvailableModels; + } + + public ChatClient GetChatClient() + { + return !string.IsNullOrEmpty(Model) ? GetOpenAIClient().GetChatClient(Model) : null; + } + + private OpenAIClient GetOpenAIClient() + { + var credential = new ApiKeyCredential(ReadApiKeyFromEnv ? Environment.GetEnvironmentVariable(ApiKey) : ApiKey); + return Server.Contains("openai.azure.com/", StringComparison.Ordinal) + ? new AzureOpenAIClient(new Uri(Server), credential, new AzureOpenAIClientOptions() { UserAgentApplicationId = string.Empty }) + : new OpenAIClient(credential, new() { Endpoint = new Uri(Server), UserAgentApplicationId = string.Empty }); + } + + private string _name = string.Empty; } } diff --git a/src/App.Commands.cs b/src/App.Commands.cs index 194adc07..54175070 100644 --- a/src/App.Commands.cs +++ b/src/App.Commands.cs @@ -1,7 +1,5 @@ using System; using System.Windows.Input; - -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; namespace SourceGit @@ -45,16 +43,6 @@ namespace SourceGit public static readonly Command OpenAboutCommand = new Command(async _ => await ShowDialog(new Views.About())); public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true)); public static readonly Command QuitCommand = new Command(_ => Quit(0)); - public static readonly Command CopyTextBlockCommand = new Command(async p => - { - if (p is not TextBlock textBlock) - return; - - if (textBlock.Inlines is { Count: > 0 } inlines) - await CopyTextAsync(inlines.Text); - else if (!string.IsNullOrEmpty(textBlock.Text)) - await CopyTextAsync(textBlock.Text); - }); public static readonly Command HideAppCommand = new Command(_ => { diff --git a/src/App.axaml.cs b/src/App.axaml.cs index f9c6117c..44dbc977 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,19 +1,15 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Net.Http; -using System.Reflection; using System.Text; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; -using Avalonia.Input.Platform; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Fonts; @@ -32,7 +28,7 @@ namespace SourceGit AppDomain.CurrentDomain.UnhandledException += (_, e) => { - LogException(e.ExceptionObject as Exception); + Native.OS.LogException(e.ExceptionObject as Exception); }; TaskScheduler.UnobservedTaskException += (_, e) => @@ -51,7 +47,7 @@ namespace SourceGit } catch (Exception ex) { - LogException(ex); + Native.OS.LogException(ex); } } @@ -76,52 +72,9 @@ namespace SourceGit Native.OS.SetupApp(builder); return builder; } - - public static void LogException(Exception ex) - { - if (ex == null) - return; - - var crashDir = Path.Combine(Native.OS.DataDir, "crashes"); - if (!Directory.Exists(crashDir)) - Directory.CreateDirectory(crashDir); - - var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); - var file = Path.Combine(crashDir, $"{time}.log"); - using var writer = new StreamWriter(file); - writer.WriteLine($"Crash::: {ex.GetType().FullName}: {ex.Message}"); - writer.WriteLine(); - writer.WriteLine("----------------------------"); - writer.WriteLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version}"); - writer.WriteLine($"OS: {Environment.OSVersion}"); - writer.WriteLine($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}"); - writer.WriteLine($"Source: {ex.Source}"); - writer.WriteLine($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}"); - writer.WriteLine($"App Start Time: {Process.GetCurrentProcess().StartTime}"); - writer.WriteLine($"Exception Time: {DateTime.Now}"); - writer.WriteLine($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB"); - writer.WriteLine("----------------------------"); - writer.WriteLine(); - writer.WriteLine(ex); - writer.Flush(); - } #endregion #region Utility Functions - public static Control CreateViewForViewModel(object data) - { - var dataTypeName = data.GetType().FullName; - if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal)) - return null; - - var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views."); - var viewType = Type.GetType(viewTypeName); - if (viewType != null) - return Activator.CreateInstance(viewType) as Control; - - return null; - } - public static Task ShowDialog(object data, Window owner = null) { if (owner == null) @@ -135,7 +88,7 @@ namespace SourceGit if (data is Views.ChromelessWindow window) return window.ShowDialog(owner); - window = CreateViewForViewModel(data) as Views.ChromelessWindow; + window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow; if (window != null) { window.DataContext = data; @@ -149,7 +102,7 @@ namespace SourceGit { if (data is not Views.ChromelessWindow window) { - window = CreateViewForViewModel(data) as Views.ChromelessWindow; + window = Views.ControlExtensions.CreateFromViewModels(data) as Views.ChromelessWindow; if (window == null) return; @@ -221,18 +174,6 @@ namespace SourceGit return Models.ConfirmEmptyCommitResult.Cancel; } - public static void RaiseException(string context, string message) - { - if (Current is App { _launcher: not null } app) - app._launcher.DispatchNotification(context, message, true); - } - - public static void SendNotification(string context, string message) - { - if (Current is App { _launcher: not null } app) - app._launcher.DispatchNotification(context, message, false); - } - public static void SetLocale(string localeKey) { if (Current is not App app || @@ -342,19 +283,6 @@ namespace SourceGit } } - public static async Task CopyTextAsync(string data) - { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow.Clipboard: { } clipboard }) - await clipboard.SetTextAsync(data ?? ""); - } - - public static async Task GetClipboardTextAsync() - { - if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow.Clipboard: { } clipboard }) - return await clipboard.TryGetTextAsync(); - return null; - } - public static string Text(string key, params object[] args) { var fmt = Current?.FindResource($"Text.{key}") as string; @@ -367,19 +295,6 @@ namespace SourceGit return string.Format(fmt, args); } - public static Avalonia.Controls.Shapes.Path CreateMenuIcon(string key) - { - var icon = new Avalonia.Controls.Shapes.Path(); - icon.Width = 12; - icon.Height = 12; - icon.Stretch = Stretch.Uniform; - - if (Current?.FindResource(key) is StreamGeometry geo) - icon.Data = geo; - - return icon; - } - public static ViewModels.Launcher GetLauncher() { return Current is App app ? app._launcher : null; @@ -439,32 +354,12 @@ namespace SourceGit if (TryLaunchAsAskpass(desktop)) return; - _ipcChannel = new Models.IpcChannel(); - if (!_ipcChannel.IsFirstInstance) - { - var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty; - if (!string.IsNullOrEmpty(arg)) - { - if (arg.StartsWith('"') && arg.EndsWith('"')) - arg = arg.Substring(1, arg.Length - 2).Trim(); - - if (arg.Length > 0 && !Path.IsPathFullyQualified(arg)) - arg = Path.GetFullPath(arg); - } - - _ipcChannel.SendToFirstInstance(arg); - Environment.Exit(0); - } - else - { - _ipcChannel.MessageReceived += TryOpenRepository; - desktop.Exit += (_, _) => _ipcChannel.Dispose(); - TryLaunchAsNormal(desktop); - } + TryLaunchAsNormal(desktop); } } #endregion + #region Launch Ways private static bool TryLaunchAsRebaseTodoEditor(string[] args, out int exitCode) { exitCode = -1; @@ -625,6 +520,24 @@ namespace SourceGit private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop) { + _ipcChannel = new Models.IpcChannel(); + if (!_ipcChannel.IsFirstInstance) + { + var arg = desktop.Args is { Length: > 0 } ? desktop.Args[0].Trim() : string.Empty; + if (!string.IsNullOrEmpty(arg)) + { + if (arg.StartsWith('"') && arg.EndsWith('"')) + arg = arg.Substring(1, arg.Length - 2).Trim(); + + if (arg.Length > 0 && !Path.IsPathFullyQualified(arg)) + arg = Path.GetFullPath(arg); + } + + _ipcChannel.SendToFirstInstance(arg); + Environment.Exit(0); + return; + } + Native.OS.SetupExternalTools(); Models.AvatarManager.Instance.Start(); @@ -639,40 +552,26 @@ namespace SourceGit desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; + _ipcChannel.MessageReceived += repo => + { + Dispatcher.UIThread.Invoke(() => + { + _launcher.TryOpenRepositoryFromPath(repo); + if (desktop.MainWindow is Views.Launcher main) + main.BringToTop(); + }); + }; + + desktop.Exit += (_, _) => _ipcChannel.Dispose(); + #if !DISABLE_UPDATE_DETECTION if (pref.ShouldCheck4UpdateOnStartup()) Check4Update(); #endif } + #endregion - private void TryOpenRepository(string repo) - { - if (!string.IsNullOrEmpty(repo) && Directory.Exists(repo)) - { - var test = new Commands.QueryRepositoryRootPath(repo).GetResult(); - if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut)) - { - Dispatcher.UIThread.Invoke(() => - { - var node = ViewModels.Preferences.Instance.FindOrAddNodeByRepositoryPath(test.StdOut.Trim(), null, false); - ViewModels.Welcome.Instance.Refresh(); - _launcher?.OpenRepositoryInTab(node, null); - - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher wnd }) - wnd.BringToTop(); - }); - - return; - } - } - - Dispatcher.UIThread.Invoke(() => - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: Views.Launcher launcher }) - launcher.BringToTop(); - }); - } - + #region Check for Updates private void Check4Update(bool manually = false) { Task.Run(async () => @@ -728,6 +627,7 @@ namespace SourceGit // Ignore exceptions. } } + #endregion private string FixFontFamilyName(string input) { diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index e6e64f05..2c453b9a 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -70,7 +70,7 @@ namespace SourceGit.Commands catch (Exception e) { if (RaiseError) - App.RaiseException(Context, e.Message); + RaiseException(e.Message); Log?.AppendLine(string.Empty); return false; @@ -101,7 +101,7 @@ namespace SourceGit.Commands { var errMsg = string.Join("\n", errs).Trim(); if (!string.IsNullOrEmpty(errMsg)) - App.RaiseException(Context, errMsg); + RaiseException(errMsg); } return false; @@ -219,6 +219,11 @@ namespace SourceGit.Commands return start; } + protected void RaiseException(string error) + { + Models.Notification.Send(Context, error, true); + } + private void HandleOutput(string line, List errs) { if (line == null) diff --git a/src/Commands/DiffTool.cs b/src/Commands/DiffTool.cs index 5bc23db3..a102bd47 100644 --- a/src/Commands/DiffTool.cs +++ b/src/Commands/DiffTool.cs @@ -17,7 +17,7 @@ namespace SourceGit.Commands var tool = Native.OS.GetDiffMergeTool(true); if (tool == null) { - App.RaiseException(Context, "Invalid diff/merge tool in preference setting!"); + RaiseException("Invalid diff/merge tool in preference setting!"); return; } @@ -40,7 +40,7 @@ namespace SourceGit.Commands } catch (Exception ex) { - App.RaiseException(Context, ex.Message); + RaiseException(ex.Message); } } @@ -56,7 +56,7 @@ namespace SourceGit.Commands if (config.TryGetValue("merge.tool", out var mergeTool)) return CheckCLIBasedTool(mergeTool); - App.RaiseException(Context, "Missing git configuration: diff.guitool"); + RaiseException("Missing git configuration: diff.guitool"); return false; } @@ -65,7 +65,7 @@ namespace SourceGit.Commands if (tool.StartsWith("vimdiff", StringComparison.Ordinal) || tool.StartsWith("nvimdiff", StringComparison.Ordinal)) { - App.RaiseException(Context, $"CLI based diff tool \"{tool}\" is not supported by this app!"); + RaiseException($"CLI based diff tool \"{tool}\" is not supported by this app!"); return false; } diff --git a/src/Commands/Discard.cs b/src/Commands/Discard.cs index 9c0b162e..8d08b90a 100644 --- a/src/Commands/Discard.cs +++ b/src/Commands/Discard.cs @@ -10,7 +10,7 @@ namespace SourceGit.Commands /// /// Discard all local changes (unstaged & staged) /// - public static async Task AllAsync(string repo, bool includeUntracked, bool includeIgnored, Models.ICommandLog log) + public static async Task AllAsync(string repo, bool includeModified, bool includeUntracked, bool includeIgnored, Models.ICommandLog log) { if (includeUntracked) { @@ -33,11 +33,11 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}"); + Models.Notification.Send(repo, $"Failed to discard changes. Reason: {e.Message}", true); } if (includeIgnored) - await new Clean(repo, Models.CleanMode.All).Use(log).ExecAsync().ConfigureAwait(false); + await new Clean(repo, Models.CleanMode.UntrackedAndIgnoredFiles).Use(log).ExecAsync().ConfigureAwait(false); else await new Clean(repo, Models.CleanMode.OnlyUntrackedFiles).Use(log).ExecAsync().ConfigureAwait(false); } @@ -46,7 +46,8 @@ namespace SourceGit.Commands await new Clean(repo, Models.CleanMode.OnlyIgnoredFiles).Use(log).ExecAsync().ConfigureAwait(false); } - await new Reset(repo, "", "--hard").Use(log).ExecAsync().ConfigureAwait(false); + if (includeModified) + await new Reset(repo, "", "--hard").Use(log).ExecAsync().ConfigureAwait(false); } /// @@ -79,7 +80,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, $"Failed to discard changes. Reason: {e.Message}"); + Models.Notification.Send(repo, $"Failed to discard changes. Reason: {e.Message}", true); } if (restores.Count > 0) diff --git a/src/Commands/GitFlow.cs b/src/Commands/GitFlow.cs index 56002e22..c2052f0e 100644 --- a/src/Commands/GitFlow.cs +++ b/src/Commands/GitFlow.cs @@ -42,7 +42,7 @@ namespace SourceGit.Commands start.Args = $"flow hotfix start {name}"; break; default: - App.RaiseException(repo, "Bad git-flow branch type!!!"); + Models.Notification.Send(repo, "Bad git-flow branch type!!!", true); return false; } @@ -66,7 +66,7 @@ namespace SourceGit.Commands builder.Append("hotfix"); break; default: - App.RaiseException(repo, "Bad git-flow branch type!!!"); + Models.Notification.Send(repo, "Bad git-flow branch type!!!", true); return false; } diff --git a/src/Commands/MergeTool.cs b/src/Commands/MergeTool.cs index 0f15fb61..45e17cc6 100644 --- a/src/Commands/MergeTool.cs +++ b/src/Commands/MergeTool.cs @@ -17,7 +17,7 @@ namespace SourceGit.Commands var tool = Native.OS.GetDiffMergeTool(false); if (tool == null) { - App.RaiseException(Context, "Invalid diff/merge tool in preference setting!"); + RaiseException("Invalid diff/merge tool in preference setting!"); return false; } @@ -46,14 +46,14 @@ namespace SourceGit.Commands if (string.IsNullOrEmpty(tool)) { - App.RaiseException(Context, "Missing git configuration: merge.guitool"); + RaiseException("Missing git configuration: merge.guitool"); return false; } if (tool.StartsWith("vimdiff", StringComparison.Ordinal) || tool.StartsWith("nvimdiff", StringComparison.Ordinal)) { - App.RaiseException(Context, $"CLI based merge tool \"{tool}\" is not supported by this app!"); + RaiseException($"CLI based merge tool \"{tool}\" is not supported by this app!"); return false; } diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index 0e46ead8..0005abd6 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -103,7 +103,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(Context, $"Failed to query commits. Reason: {e.Message}"); + RaiseException($"Failed to query commits. Reason: {e.Message}"); } return commits; diff --git a/src/Commands/QueryCommitsForInteractiveRebase.cs b/src/Commands/QueryCommitsForInteractiveRebase.cs index e1f96488..8af420d3 100644 --- a/src/Commands/QueryCommitsForInteractiveRebase.cs +++ b/src/Commands/QueryCommitsForInteractiveRebase.cs @@ -21,7 +21,7 @@ namespace SourceGit.Commands var rs = await ReadToEndAsync().ConfigureAwait(false); if (!rs.IsSuccess) { - App.RaiseException(Context, $"Failed to query commits for interactive-rebase. Reason: {rs.StdErr}"); + RaiseException($"Failed to query commits for interactive-rebase. Reason: {rs.StdErr}"); return commits; } diff --git a/src/Commands/QueryFileContent.cs b/src/Commands/QueryFileContent.cs index 66f96460..d111b5f2 100644 --- a/src/Commands/QueryFileContent.cs +++ b/src/Commands/QueryFileContent.cs @@ -27,7 +27,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, $"Failed to query file content: {e}"); + Models.Notification.Send(repo, $"Failed to query file content: {e}", true); } stream.Position = 0; @@ -58,7 +58,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, $"Failed to query file content: {e}"); + Models.Notification.Send(repo, $"Failed to query file content: {e}", true); } stream.Position = 0; diff --git a/src/Commands/SaveChangesAsPatch.cs b/src/Commands/SaveChangesAsPatch.cs index 51bc1319..4ec83a6a 100644 --- a/src/Commands/SaveChangesAsPatch.cs +++ b/src/Commands/SaveChangesAsPatch.cs @@ -69,7 +69,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, "Save change to patch failed: " + e.Message); + Models.Notification.Send(repo, "Save change to patch failed: " + e.Message, true); return false; } } diff --git a/src/Commands/SaveRevisionFile.cs b/src/Commands/SaveRevisionFile.cs index e05d6d35..410d15b0 100644 --- a/src/Commands/SaveRevisionFile.cs +++ b/src/Commands/SaveRevisionFile.cs @@ -55,7 +55,7 @@ namespace SourceGit.Commands } catch (Exception e) { - App.RaiseException(repo, "Save file failed: " + e.Message); + Models.Notification.Send(repo, "Save file failed: " + e.Message, true); } } } diff --git a/src/Commands/Stash.cs b/src/Commands/Stash.cs index 3bf72308..31b5b71c 100644 --- a/src/Commands/Stash.cs +++ b/src/Commands/Stash.cs @@ -76,6 +76,12 @@ namespace SourceGit.Commands return await ExecAsync().ConfigureAwait(false); } + public async Task CheckoutBranchAsync(string name, string branch) + { + Args = $"stash branch {branch.Quoted()} {name.Quoted()}"; + return await ExecAsync().ConfigureAwait(false); + } + public async Task PopAsync(string name) { Args = $"stash pop -q --index {name.Quoted()}"; diff --git a/src/Commands/UpdateIndexInfo.cs b/src/Commands/UpdateIndexInfo.cs index 3633a282..d8c47ca9 100644 --- a/src/Commands/UpdateIndexInfo.cs +++ b/src/Commands/UpdateIndexInfo.cs @@ -74,13 +74,13 @@ namespace SourceGit.Commands var rs = proc.ExitCode == 0; if (!rs) - App.RaiseException(_repo, err); + Models.Notification.Send(_repo, err, true); return rs; } catch (Exception e) { - App.RaiseException(_repo, "Failed to update index: " + e.Message); + Models.Notification.Send(_repo, "Failed to update index: " + e.Message, true); return false; } } diff --git a/src/Models/CleanMode.cs b/src/Models/CleanMode.cs index fde11387..0c7d7858 100644 --- a/src/Models/CleanMode.cs +++ b/src/Models/CleanMode.cs @@ -4,6 +4,6 @@ { OnlyUntrackedFiles = 0, OnlyIgnoredFiles, - All, + UntrackedAndIgnoredFiles, } } diff --git a/src/Models/Notification.cs b/src/Models/Notification.cs index 473947b0..f033213f 100644 --- a/src/Models/Notification.cs +++ b/src/Models/Notification.cs @@ -1,8 +1,23 @@ -namespace SourceGit.Models +using System; + +namespace SourceGit.Models { public class Notification { - public bool IsError { get; set; } = false; - public string Message { get; set; } = string.Empty; + public static event Action Raised; + + public string Group { get; set; } + public string Message { get; set; } + public bool IsError { get; set; } + + public static void Send(string group, string message, bool isError = false) + { + Raised?.Invoke(new Notification + { + Group = group, + Message = message, + IsError = isError + }); + } } } diff --git a/src/Models/Remote.cs b/src/Models/Remote.cs index 18b57c41..b6dd15a1 100644 --- a/src/Models/Remote.cs +++ b/src/Models/Remote.cs @@ -66,7 +66,12 @@ namespace SourceGit.Models if (URL.StartsWith("http://", StringComparison.Ordinal) || URL.StartsWith("https://", StringComparison.Ordinal)) { - url = URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL; + var trimmed = URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL; + var uri = new Uri(trimmed); + if (uri.Port != 80 && uri.Port != 443) + url = $"{uri.Scheme}://{uri.Host}:{uri.Port}{uri.AbsolutePath}"; + else + url = $"{uri.Scheme}://{uri.Host}{uri.AbsolutePath}"; return true; } diff --git a/src/Models/RepositorySettings.cs b/src/Models/RepositorySettings.cs index c5d6c7d8..4cc1e377 100644 --- a/src/Models/RepositorySettings.cs +++ b/src/Models/RepositorySettings.cs @@ -30,18 +30,6 @@ namespace SourceGit.Models set; } = string.Empty; - public bool EnableAutoFetch - { - get; - set; - } = false; - - public int AutoFetchInterval - { - get; - set; - } = 10; - public bool AskBeforeAutoUpdatingSubmodules { get; diff --git a/src/Models/TextInlineChange.cs b/src/Models/TextInlineChange.cs index bc1873e2..e49a6d03 100644 --- a/src/Models/TextInlineChange.cs +++ b/src/Models/TextInlineChange.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Globalization; namespace SourceGit.Models { @@ -26,6 +27,13 @@ namespace SourceGit.Models AddedLeft, } + private enum CharCategory : byte + { + Other, // default: whitespace, control, punctuation, symbols, etc. + Letter, // Ll/Lu/Lt/Lm + digit: ASCII and euro letters (latin, greek, cyrillic, etc.) + OtherLetter, // Lo: CJK, hiragana, katakana, hangul, Thai, Arabic, etc. + } + private class EditResult { public Edit State; @@ -100,22 +108,25 @@ namespace SourceGit.Models var start = 0; var size = text.Length; var chunks = new List(); - var delims = new HashSet(" \t+-*/=!,:;.'\"/?|&#@%`<>()[]{}\\".ToCharArray()); + if (size == 0) + return chunks; - for (int i = 0; i < size; i++) + var prev = GetCategory(text[0]); + + for (var i = 1; i < size; i++) { var ch = text[i]; - if (delims.Contains(ch)) + var category = GetCategory(ch); + if (prev != category || category == CharCategory.Other) { - if (start != i) - AddChunk(chunks, hashes, text.Substring(start, i - start), start); - AddChunk(chunks, hashes, text.Substring(i, 1), i); - start = i + 1; + AddChunk(chunks, hashes, text[start..i], start); + start = i; } + prev = category; } if (start < size) - AddChunk(chunks, hashes, text.Substring(start), start); + AddChunk(chunks, hashes, text[start..], start); return chunks; } @@ -302,5 +313,33 @@ namespace SourceGit.Models } chunks.Add(new Chunk(hash, start, data.Length)); } + + private static CharCategory[] BuildCategoryCache() + { + // Pre-compute category for all char values. + // All entries default to Other (0). + var cache = new CharCategory[65536]; + for (int i = 0; i < 65536; i++) + { + var ch = (char)i; + // Unicode Lo: CJK, hiragana, katakana, hangul, Thai, Arabic, Hebrew, etc. + // → group consecutive chars into one chunk (no space delimiter in these languages) + if (char.GetUnicodeCategory(ch) == UnicodeCategory.OtherLetter) + cache[i] = CharCategory.OtherLetter; + + // Unicode Ll/Lu/Lt/Lm + digit: latin, greek, cyrillic and their diacritic variants + // → group consecutive chars into one chunk (words in space-delimited languages) + else if (char.IsLetterOrDigit(ch)) + cache[i] = CharCategory.Letter; + + // everything else (whitespace, control, punctuation, symbols) → Other (default) + } + + return cache; + } + + private static CharCategory GetCategory(char ch) => s_charCategoryCache[ch]; + + private static readonly CharCategory[] s_charCategoryCache = BuildCategoryCache(); } } diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index b192015b..881c9bcf 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -136,7 +136,7 @@ namespace SourceGit.Native } catch (Exception e) { - App.RaiseException(workdir, $"Failed to start '{OS.ShellOrTerminal}'. Reason: {e.Message}"); + Models.Notification.Send(workdir, $"Failed to start '{OS.ShellOrTerminal}'. Reason: {e.Message}", true); } } @@ -148,7 +148,7 @@ namespace SourceGit.Native proc.WaitForExit(); if (proc.ExitCode != 0) - App.RaiseException("", $"Failed to open: {file}"); + Models.Notification.Send("", $"Failed to open: {file}", true); proc.Close(); } diff --git a/src/Native/OS.cs b/src/Native/OS.cs index 1044887a..7c12287a 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; - +using System.Threading; using Avalonia; using Avalonia.Controls; @@ -153,6 +154,35 @@ namespace SourceGit.Native _backend.SetupWindow(window); } + public static void LogException(Exception ex) + { + if (ex == null) + return; + + var crashDir = Path.Combine(DataDir, "crashes"); + if (!Directory.Exists(crashDir)) + Directory.CreateDirectory(crashDir); + + var time = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); + var file = Path.Combine(crashDir, $"{time}.log"); + using var writer = new StreamWriter(file); + writer.WriteLine($"Crash::: {ex.GetType().FullName}: {ex.Message}"); + writer.WriteLine(); + writer.WriteLine("----------------------------"); + writer.WriteLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version}"); + writer.WriteLine($"OS: {Environment.OSVersion}"); + writer.WriteLine($"Framework: {AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName}"); + writer.WriteLine($"Source: {ex.Source}"); + writer.WriteLine($"Thread Name: {Thread.CurrentThread.Name ?? "Unnamed"}"); + writer.WriteLine($"App Start Time: {Process.GetCurrentProcess().StartTime}"); + writer.WriteLine($"Exception Time: {DateTime.Now}"); + writer.WriteLine($"Memory Usage: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024} MB"); + writer.WriteLine("----------------------------"); + writer.WriteLine(); + writer.WriteLine(ex); + writer.Flush(); + } + public static string FindGitExecutable() { return _backend.FindGitExecutable(); @@ -217,7 +247,7 @@ namespace SourceGit.Native public static void OpenTerminal(string workdir) { if (string.IsNullOrEmpty(ShellOrTerminal)) - App.RaiseException(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured."); + Models.Notification.Send(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.", true); else _backend.OpenTerminal(workdir, ShellOrTerminalArgs); } diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index 6fbcd880..e71c0de7 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -166,7 +166,7 @@ namespace SourceGit.Native if (!File.Exists(terminal)) { - App.RaiseException(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured."); + Models.Notification.Send(workdir, "Terminal is not specified! Please confirm that the correct shell/terminal has been configured.", true); return; } diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index b36f1fc2..7db1f934 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -22,6 +22,7 @@ Neuen Branch erstellen Existierender Branch AI-Assistent + Modell NEU GENERIEREN Verwende AI, um Commit-Nachrichten zu generieren SourceGit minimieren @@ -580,11 +581,11 @@ $1, $2, … Werte der Eingabe-Steuerelemente Öffnen in externem Merge-Tool Optional. Neue Registerkarte erstellen - Lesezeichen Registerkarte schließen Andere Registerkarten schließen Registerkarten zur Rechten schließen Kopiere Repository-Pfad + Bearbeiten In Arbeitsumgebung verschieben Aktualisieren Repositorys @@ -602,7 +603,6 @@ $1, $2, … Werte der Eingabe-Steuerelemente Einstellungen AI API-Schlüssel - Modell Name Der eingegebene Wert ist der Name der Umgebungsvariable, aus der der API-Schlüssel gelesen wird Server diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index ee25ba33..a14c66a1 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -18,6 +18,7 @@ Create New Branch Existing Branch AI Assistant + MODEL RE-GENERATE Use AI to generate commit message Use @@ -115,6 +116,9 @@ The following submodules need to be updated:{0}Do you want to update them? Checkout & Fast-Forward Fast-Forward to: + Checkout Branch From Stash + New Branch: + Stash: Cherry Pick Append source to commit message Commit(s): @@ -382,6 +386,7 @@ All local changes in working copy. Changes: Include ignored files + Include modified/deleted files Include untracked files {0} changes will be discarded You can't undo this action!!! @@ -589,11 +594,11 @@ Open in External Merge Tool Optional. Create New Tab - Bookmark Close Tab Close Other Tabs Close Tabs to the Right Copy Repository Path + Edit Move to Workspace Refresh Repositories @@ -612,7 +617,6 @@ AI Additional Prompt (Use `-` to list your requirements) API Key - Model Name Entered value is the name to load API key from ENV Server @@ -857,6 +861,7 @@ Stash Local Changes Apply Apply Changes + Checkout New Branch Copy Message Drop Save as Patch... diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 0a9cca55..be11ae58 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -22,6 +22,7 @@ Crear Nueva Rama Rama Existente Asistente OpenAI + Modelo RE-GENERAR Usar OpenAI para generar mensaje de commit Usar @@ -119,6 +120,9 @@ Los siguientes submódulos necesitan ser actualizados:{0} ¿Quieres actualizarlos? Checkout & Fast-Forward Fast-Forward a: + Checkout a la Rama desde el Stash + Nueva Rama: + Stash: Cherry Pick Añadir fuente al mensaje de commit Commit(s): @@ -386,6 +390,7 @@ Todos los cambios locales en la copia de trabajo. Cambios: Incluir archivos ignorados + Incluir archivos modificados/eliminados Incluir archivos no rastreados Total {0} cambios serán descartados ¡No puedes deshacer esta acción! @@ -593,11 +598,11 @@ Abrir en Herramienta de Merge Opcional. Crear Nueva Página - Marcador Cerrar Pestaña Cerrar Otras Pestañas Cerrar Pestañas a la Derecha Copiar Ruta del Repositorio + Editar Mover al Espacio de trabajo Actualizar Repositorios @@ -616,7 +621,6 @@ OPEN AI Prompt adicional (Usa `-` para listar tus requerimientos) Clave API - Modelo Nombre El valor ingresado es el nombre de la clave API a cargar desde ENV Servidor @@ -861,6 +865,7 @@ Stash Cambios Locales Aplicar Aplicar Cambios + Checkout a Nueva Rama Copiar Mensaje Eliminar Guardar como Parche... diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 0ec2396e..7a62a8ac 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -21,6 +21,7 @@ Créer une nouvelle branche Branche existante Assistant IA + Modèle RE-GÉNÉRER Utiliser l'IA pour générer un message de commit Masquer SourceGit @@ -541,11 +542,11 @@ Ouvrir dans l'outil de fusion Optionnel. Créer un nouvel onglet - Bookmark Fermer l'onglet Fermer les autres onglets Fermer les onglets à droite Copier le chemin vers le dépôt + Éditer Dépôts Coller il y a {0} jours @@ -561,7 +562,6 @@ Préférences IA Clé d'API - Modèle Nom La valeur saisie est le nom pour charger la clé API depuis l'ENV Serveur diff --git a/src/Resources/Locales/id_ID.axaml b/src/Resources/Locales/id_ID.axaml index 11c2f739..f9e1b6b8 100644 --- a/src/Resources/Locales/id_ID.axaml +++ b/src/Resources/Locales/id_ID.axaml @@ -20,6 +20,7 @@ Buat Branch Baru Branch Yang Ada Asisten AI + Model BUAT ULANG Gunakan AI untuk membuat pesan commit Sembunyikan SourceGit @@ -515,11 +516,11 @@ Buka di Merge Tool Opsional. Buat Tab Baru - Bookmark Tutup Tab Tutup Tab Lain Tutup Tab di Kanan Salin Jalur Repositori + Sunting Repositori Tempel {0} hari lalu @@ -535,7 +536,6 @@ Preferensi AI API Key - Model Nama Nilai yang dimasukkan adalah nama untuk memuat API key dari ENV Server diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index 869a426c..df7ab6f1 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -22,6 +22,7 @@ Crea nuovo branch Branch esistente Assistente AI + Modello RIGENERA Usa AI per generare il messaggio di commit Nascondi SourceGit @@ -576,11 +577,11 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Apri nello Strumento di Merge Opzionale. Crea Nuova Pagina - Segnalibro Chiudi Tab Chiudi Altri Tab Chiudi i Tab a Destra Copia Percorso Repository + Modifica Sposta nel Workspace Aggiorna Repository @@ -598,7 +599,6 @@ ${pure_files:N} Come ${files:N}, ma senza cartelle Preferenze AI Chiave API - Modello Nome Il valore inserito è il nome per caricare la chiave API da ENV Server diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 57385e10..5d4969ab 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -22,6 +22,7 @@ 新しいブランチを作成 既存のブランチ AI アシスタント + モデル 再生成 AI を使用してコミットメッセージを生成 SourceGit を隠す @@ -582,11 +583,11 @@ 外部のマージツールで開く 省略可能 新しいタブを作成 - ブックマーク タブを閉じる 他のタブを閉じる 右側のタブを閉じる リポジトリへのパスをコピー + 編集 ワークスペースに移動 再読み込み リポジトリ @@ -604,7 +605,6 @@ 設定 AI API キー - モデル 名前 この値を環境変数の名前とし、そこから API キーを読み込む サーバー diff --git a/src/Resources/Locales/ko_KR.axaml b/src/Resources/Locales/ko_KR.axaml index 9e1b439d..7a278e27 100644 --- a/src/Resources/Locales/ko_KR.axaml +++ b/src/Resources/Locales/ko_KR.axaml @@ -18,6 +18,7 @@ 새 브랜치 생성 기존 브랜치 AI 어시스턴트 + 모델 재생성 AI를 사용하여 커밋 메시지 생성 SourceGit 숨기기 @@ -517,11 +518,11 @@ 병합 도구에서 열기 선택 사항. 새 탭 만들기 - 북마크 탭 닫기 다른 탭 닫기 오른쪽 탭 닫기 저장소 경로 복사 + 편집 저장소 붙여넣기 {0}일 전 @@ -537,7 +538,6 @@ 환경설정 AI API 키 - 모델 이름 입력된 값은 환경변수(ENV)에서 API 키를 불러올 이름입니다 서버 diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 45937501..8c2f1120 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -22,6 +22,7 @@ Criar Novo Branch Branch Existente Assietente IA + Modelo Gerar novamente Utilizar IA para gerar mensagem de commit Esconder SourceGit @@ -398,11 +399,11 @@ Abrir na Ferramenta de Mesclagem Opcional. Criar Nova Página - Adicionar aos Favoritos Fechar Aba Fechar Outras Abas Fechar Abas à Direita Copiar Caminho do Repositório + Editar Repositórios Colar {0} dias atrás @@ -418,7 +419,6 @@ Preferências INTELIGÊNCIA ARTIFICIAL Chave da API - Modelo Nome Servidor APARÊNCIA diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 6128e9b8..5b59a22f 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -22,6 +22,7 @@ Создать новую ветку Ветку из списка Помощник OpenAI + Модель ПЕРЕСОЗДАТЬ Использовать OpenAI для создания сообщения о ревизии Использовать @@ -119,6 +120,9 @@ Подмодулям требуется обновление:{0}Обновить их? Переключиться и перемотать Перемотать к: + Переключить ветку из отложенного + Новая ветка: + Отложенный: Частичный выбор Добавить источник для ревизии сообщения Ревизия(и): @@ -386,6 +390,7 @@ Все локальные изменения в рабочей копии. Изменения: Включить игнорируемые файлы + Включить изменённые/удалённые файлы Включить неотслеживаемые файлы {0} изменений будут отменены Вы не можете отменить это действие!!! @@ -593,11 +598,11 @@ Открыть в инструменте слияния Необязательно. Создать новую вкладку - Закладка Закрыть вкладку Закрыть другие вкладки Закрыть вкладки справа Копировать путь репозитория + Редактировать... Переместить в рабочее пространство Обновить Репозитории @@ -616,7 +621,6 @@ ОТКРЫТЬ ИИ Дополнительная подсказка (Для перечисления ваших требований используйте `-`) Ключ API - Модель Имя: Введённое значение — это имя для загрузки API-ключа из ENV Сервер @@ -861,6 +865,7 @@ Отложить локальные изменения Принять Применить изменения + Переключить на новую ветку Копировать сообщение Отбросить Сохранить как заплатку... diff --git a/src/Resources/Locales/ta_IN.axaml b/src/Resources/Locales/ta_IN.axaml index e87ab7c0..1dea63e9 100644 --- a/src/Resources/Locales/ta_IN.axaml +++ b/src/Resources/Locales/ta_IN.axaml @@ -17,6 +17,7 @@ புதிய கிளையை உருவாக்கு ஏற்கனவே உள்ள கிளை செநு உதவியாளர் + மாதிரி மறு-உருவாக்கு உறுதிமொழி செய்தியை உருவாக்க செநுவைப் பயன்படுத்து ஒட்டு @@ -395,11 +396,11 @@ ஒன்றிணை கருவியில் திற விருப்பத்தேர்வு. புதிய பக்கத்தை உருவாக்கு - புத்தகக்குறி மூடு தாவல் பிற தாவல்களை மூடு வலதுபுறத்தில் உள்ள தாவல்களை மூடு களஞ்சிய பாதை நகலெடு + திருத்து களஞ்சியங்கள் ஒட்டு {0} நாட்களுக்கு முன்பு @@ -415,7 +416,6 @@ விருப்பத்தேர்வுகள் செநு பநிஇ திறவுகோல் - மாதிரி பெயர் சேவையகம் தோற்றம் diff --git a/src/Resources/Locales/uk_UA.axaml b/src/Resources/Locales/uk_UA.axaml index 23966d81..e21cc44c 100644 --- a/src/Resources/Locales/uk_UA.axaml +++ b/src/Resources/Locales/uk_UA.axaml @@ -17,6 +17,7 @@ Створити нову гілку Наявна гілка AI Асистент + Модель ПЕРЕГЕНЕРУВАТИ Використати AI для генерації повідомлення коміту Застосувати @@ -399,11 +400,11 @@ Відкрити в інструменті злиття Необов'язково. Створити нову вкладку - Закладка Закрити вкладку Закрити інші вкладки Закрити вкладки праворуч Копіювати шлях до сховища + Редагувати Сховища Вставити {0} днів тому @@ -419,7 +420,6 @@ Налаштування AI Ключ API - Модель Назва Сервер ВИГЛЯД diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index d7342a5d..b8b926bf 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -22,6 +22,7 @@ 创建新分支 已有分支 AI助手 + 模型 重新生成 使用AI助手生成提交信息 应用所选 @@ -119,6 +120,9 @@ 以下子模块需要更新:{0}是否立即更新? 检出分支并快进 上游分支 : + 从所选贮藏检出分支 + 新分支 : + 所选贮藏 : 挑选提交 提交信息中追加来源信息 提交列表 : @@ -386,6 +390,7 @@ 所有本仓库未提交的修改。 变更 : 包括所有已忽略的文件 + 包括已修改或删除的文件 包括未跟踪的文件 总计{0}项选中更改 本操作不支持回退,请确认后继续!!! @@ -593,11 +598,11 @@ 使用外部对比工具查看 选填。 新建空白页 - 设置书签 关闭标签页 关闭其他标签页 关闭右侧标签页 复制仓库路径 + 编辑 移至工作区 刷新 新标签页 @@ -616,7 +621,6 @@ AI 附加提示词 (请使用 `-` 列出您的要求) API密钥 - 模型 配置名称 从环境变量(填写环境变量名)中读取API密钥 服务地址 @@ -861,6 +865,7 @@ 贮藏本地变更 应用(apply) 应用(apply)选中变更 + 检出新分支 复制描述信息 删除(drop) 另存为补丁... diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 6561521a..4751416e 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -22,6 +22,7 @@ 建立新分支 已有分支 AI 助理 + 模型 重新產生 使用 AI 產生提交訊息 套用選取 @@ -119,6 +120,9 @@ 以下子模組需要更新: {0},您要立即更新嗎? 簽出分支並快轉 上游分支: + 從所選擱置簽出分支 + 新分支: + 所選擱置: 揀選提交 提交資訊中追加來源資訊 提交列表: @@ -386,6 +390,7 @@ 所有本機未提交的變更。 變更: 包括所有已忽略的檔案 + 包括已變更或刪除的檔案 包含未追蹤檔案 將捨棄總計 {0} 項已選取的變更 您無法復原此操作,請確認後再繼續! @@ -593,11 +598,11 @@ 使用外部比對工具檢視 選填。 新增分頁 - 設定書籤 關閉分頁 關閉其他分頁 關閉右側分頁 複製存放庫路徑 + 編輯 移至工作區 重新整理 新分頁 @@ -616,7 +621,6 @@ AI 附加提示詞 (請使用 '-' 列出您的要求) API 金鑰 - 模型 名稱 從環境變數中 (輸入環境變數名稱) 讀取 API 金鑰 伺服器 @@ -861,6 +865,7 @@ 擱置本機變更 套用 (apply) 套用 (apply) 所選變更 + 簽出分支 複製描述訊息 刪除 (drop) 另存為修補檔 (patch)... diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 08366af8..0c4dbb7f 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -1,6 +1,5 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/BranchSelector.axaml.cs b/src/Views/BranchSelector.axaml.cs new file mode 100644 index 00000000..7740dd0f --- /dev/null +++ b/src/Views/BranchSelector.axaml.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace SourceGit.Views +{ + public partial class BranchSelector : UserControl + { + public static readonly StyledProperty> BranchesProperty = + AvaloniaProperty.Register>(nameof(Branches)); + + public List Branches + { + get => GetValue(BranchesProperty); + set => SetValue(BranchesProperty, value); + } + + public static readonly StyledProperty> VisibleBranchesProperty = + AvaloniaProperty.Register>(nameof(VisibleBranches)); + + public List VisibleBranches + { + get => GetValue(VisibleBranchesProperty); + set => SetValue(VisibleBranchesProperty, value); + } + + public static readonly StyledProperty SelectedBranchProperty = + AvaloniaProperty.Register(nameof(SelectedBranch)); + + public Models.Branch SelectedBranch + { + get => GetValue(SelectedBranchProperty); + set => SetValue(SelectedBranchProperty, value); + } + + public static readonly StyledProperty IsDropDownOpenedProperty = + AvaloniaProperty.Register(nameof(IsDropDownOpened)); + + public bool IsDropDownOpened + { + get => GetValue(IsDropDownOpenedProperty); + set => SetValue(IsDropDownOpenedProperty, value); + } + + public static readonly StyledProperty SearchFilterProperty = + AvaloniaProperty.Register(nameof(SearchFilter)); + + public string SearchFilter + { + get => GetValue(SearchFilterProperty); + set => SetValue(SearchFilterProperty, value); + } + + public BranchSelector() + { + Focusable = true; + InitializeComponent(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == BranchesProperty || change.Property == SearchFilterProperty) + { + var branches = Branches; + var filter = SearchFilter; + if (branches is not { Count: > 0 }) + { + SetCurrentValue(VisibleBranchesProperty, []); + } + else if (string.IsNullOrEmpty(filter)) + { + SetCurrentValue(VisibleBranchesProperty, Branches); + } + else + { + var visible = new List(); + var oldSelection = SelectedBranch; + var keepSelection = false; + + foreach (var b in Branches) + { + if (b.FriendlyName.Contains(SearchFilter, StringComparison.OrdinalIgnoreCase)) + { + visible.Add(b); + if (!keepSelection) + keepSelection = (b == oldSelection); + } + } + + SetCurrentValue(VisibleBranchesProperty, visible); + if (!keepSelection && visible.Count > 0) + SetCurrentValue(SelectedBranchProperty, visible[0]); + } + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + if (_popup != null) + { + _popup.Opened -= OnPopupOpened; + _popup.Closed -= OnPopupClosed; + } + + _popup = e.NameScope.Get("PART_Popup"); + _popup.Opened += OnPopupOpened; + _popup.Closed += OnPopupClosed; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.Key == Key.Space && !IsDropDownOpened) + { + IsDropDownOpened = true; + e.Handled = true; + } + else if (e.Key == Key.Escape && IsDropDownOpened) + { + IsDropDownOpened = false; + e.Handled = true; + } + } + + private void OnPopupOpened(object sender, EventArgs e) + { + var listBox = _popup?.Child?.FindDescendantOfType(); + listBox?.Focus(); + } + + private void OnPopupClosed(object sender, EventArgs e) + { + Focus(NavigationMethod.Directional); + } + + private void OnToggleDropDown(object sender, PointerPressedEventArgs e) + { + IsDropDownOpened = !IsDropDownOpened; + e.Handled = true; + } + + private void OnSearchBoxKeyDown(object _, KeyEventArgs e) + { + if (e.Key == Key.Tab) + { + var listBox = _popup?.Child?.FindDescendantOfType(); + listBox?.Focus(); + e.Handled = true; + } + else if (e.Key == Key.Up) + { + var listBox = _popup?.Child?.FindDescendantOfType(); + if (listBox != null) + { + if (listBox.SelectedIndex > 0) + listBox.SelectedIndex--; + listBox.Focus(); + } + + e.Handled = true; + } + else if (e.Key == Key.Down) + { + var listBox = _popup?.Child?.FindDescendantOfType(); + if (listBox != null) + { + if (listBox.SelectedIndex < listBox.Items.Count - 1) + listBox.SelectedIndex++; + listBox.Focus(); + } + + e.Handled = true; + } + } + + private void OnClearSearchFilter(object sender, RoutedEventArgs e) + { + SearchFilter = string.Empty; + e.Handled = true; + } + + private void OnDropDownListKeyDown(object _, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + IsDropDownOpened = false; + e.Handled = true; + } + else if (e.Key == Key.F && e.KeyModifiers == (OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) + { + var searchBox = _popup?.Child?.FindDescendantOfType(); + if (searchBox != null) + { + searchBox.CaretIndex = SearchFilter?.Length ?? 0; + searchBox.Focus(); + } + + e.Handled = true; + } + } + + private void OnDropDownItemPointerPressed(object sender, PointerPressedEventArgs e) + { + if (sender is Control { DataContext: Models.Branch branch }) + SelectedBranch = branch; + + IsDropDownOpened = false; + e.Handled = true; + } + + private Popup _popup = null; + } +} diff --git a/src/Views/BranchTree.axaml.cs b/src/Views/BranchTree.axaml.cs index 367e5424..c7ca5276 100644 --- a/src/Views/BranchTree.axaml.cs +++ b/src/Views/BranchTree.axaml.cs @@ -534,7 +534,7 @@ namespace SourceGit.Views { var compare = new MenuItem(); compare.Header = App.Text("BranchCM.CompareTwo"); - compare.Icon = App.CreateMenuIcon("Icons.Compare"); + compare.Icon = this.CreateMenuIcon("Icons.Compare"); compare.Click += (_, ev) => { App.ShowWindow(new ViewModels.Compare(repo, branches[0], branches[1])); @@ -547,7 +547,7 @@ namespace SourceGit.Views { var mergeMulti = new MenuItem(); mergeMulti.Header = App.Text("BranchCM.MergeMultiBranches", branches.Count); - mergeMulti.Icon = App.CreateMenuIcon("Icons.Merge"); + mergeMulti.Icon = this.CreateMenuIcon("Icons.Merge"); mergeMulti.Click += (_, ev) => { repo.MergeMultipleBranches(branches); @@ -556,7 +556,7 @@ namespace SourceGit.Views var deleteMulti = new MenuItem(); deleteMulti.Header = App.Text("BranchCM.DeleteMultiBranches", branches.Count); - deleteMulti.Icon = App.CreateMenuIcon("Icons.Clear"); + deleteMulti.Icon = this.CreateMenuIcon("Icons.Clear"); deleteMulti.Click += (_, ev) => { repo.DeleteMultipleBranches(branches, branches[0].IsLocal); @@ -689,7 +689,7 @@ namespace SourceGit.Views var push = new MenuItem(); push.Header = App.Text("BranchCM.Push", branch.Name); - push.Icon = App.CreateMenuIcon("Icons.Push"); + push.Icon = this.CreateMenuIcon("Icons.Push"); push.IsEnabled = repo.Remotes.Count > 0; push.Click += (_, e) => { @@ -706,7 +706,7 @@ namespace SourceGit.Views { var fastForward = new MenuItem(); fastForward.Header = App.Text("BranchCM.FastForward", upstream.FriendlyName); - fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); + fastForward.Icon = this.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = branch.Ahead.Count == 0 && branch.Behind.Count > 0; fastForward.Click += async (_, e) => { @@ -717,7 +717,7 @@ namespace SourceGit.Views var pull = new MenuItem(); pull.Header = App.Text("BranchCM.Pull", upstream.FriendlyName); - pull.Icon = App.CreateMenuIcon("Icons.Pull"); + pull.Icon = this.CreateMenuIcon("Icons.Pull"); pull.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -735,7 +735,7 @@ namespace SourceGit.Views var compareWith = new MenuItem(); compareWith.Header = App.Text("BranchCM.CompareWith"); - compareWith.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWith.Icon = this.CreateMenuIcon("Icons.Compare"); compareWith.Click += (_, _) => { new ViewModels.CompareCommandPalette(repo, branch).Open(); @@ -749,7 +749,7 @@ namespace SourceGit.Views var checkout = new MenuItem(); checkout.Header = App.Text(hasNoWorktree ? "BranchCM.Checkout" : "BranchCM.SwitchToWorktree", branch.Name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Icon = this.CreateMenuIcon("Icons.Check"); checkout.IsEnabled = !repo.IsBare || !hasNoWorktree; checkout.Click += async (_, e) => { @@ -763,7 +763,7 @@ namespace SourceGit.Views { var fastForward = new MenuItem(); fastForward.Header = App.Text("BranchCM.FastForward", upstream.FriendlyName); - fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); + fastForward.Icon = this.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = branch.Ahead.Count == 0 && branch.Behind.Count > 0; fastForward.Click += async (_, e) => { @@ -775,7 +775,7 @@ namespace SourceGit.Views var fetchInto = new MenuItem(); fetchInto.Header = App.Text("BranchCM.FetchInto", upstream.FriendlyName, branch.Name); - fetchInto.Icon = App.CreateMenuIcon("Icons.Fetch"); + fetchInto.Icon = this.CreateMenuIcon("Icons.Fetch"); fetchInto.IsEnabled = branch.Ahead.Count == 0; fetchInto.Click += async (_, e) => { @@ -794,7 +794,7 @@ namespace SourceGit.Views { var merge = new MenuItem(); merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -804,7 +804,7 @@ namespace SourceGit.Views var rebase = new MenuItem(); rebase.Header = App.Text("BranchCM.Rebase", current.Name, branch.Name); - rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); + rebase.Icon = this.CreateMenuIcon("Icons.Rebase"); rebase.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -814,7 +814,7 @@ namespace SourceGit.Views var interactiveRebase = new MenuItem(); interactiveRebase.Header = App.Text("BranchCM.InteractiveRebase.Manually", current.Name, branch.Name); - interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase"); interactiveRebase.IsEnabled = !current.Head.Equals(branch.Head, StringComparison.Ordinal); interactiveRebase.Click += async (_, e) => { @@ -836,7 +836,7 @@ namespace SourceGit.Views { var move = new MenuItem(); move.Header = App.Text("BranchCM.ResetToSelectedCommit", branch.Name, selectedCommit.SHA.Substring(0, 10)); - move.Icon = App.CreateMenuIcon("Icons.Reset"); + move.Icon = this.CreateMenuIcon("Icons.Reset"); move.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -850,7 +850,7 @@ namespace SourceGit.Views var compareWithCurrent = new MenuItem(); compareWithCurrent.Header = App.Text("BranchCM.CompareWithHead"); - compareWithCurrent.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithCurrent.Icon = this.CreateMenuIcon("Icons.Compare"); compareWithCurrent.Click += (_, _) => { App.ShowWindow(new ViewModels.Compare(repo, branch, current)); @@ -858,7 +858,7 @@ namespace SourceGit.Views var compareWith = new MenuItem(); compareWith.Header = App.Text("BranchCM.CompareWith"); - compareWith.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWith.Icon = this.CreateMenuIcon("Icons.Compare"); compareWith.Click += (_, _) => { new ViewModels.CompareCommandPalette(repo, branch).Open(); @@ -875,7 +875,7 @@ namespace SourceGit.Views { var finish = new MenuItem(); finish.Header = App.Text("BranchCM.Finish", branch.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Icon = this.CreateMenuIcon("Icons.GitFlow"); finish.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -891,7 +891,7 @@ namespace SourceGit.Views { var editDescription = new MenuItem(); editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name); - editDescription.Icon = App.CreateMenuIcon("Icons.Edit"); + editDescription.Icon = this.CreateMenuIcon("Icons.Edit"); editDescription.Click += async (_, e) => { var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description"); @@ -902,7 +902,7 @@ namespace SourceGit.Views var rename = new MenuItem(); rename.Header = App.Text("BranchCM.Rename", branch.Name); - rename.Icon = App.CreateMenuIcon("Icons.Rename"); + rename.Icon = this.CreateMenuIcon("Icons.Rename"); rename.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -912,7 +912,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("BranchCM.Delete", branch.Name); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.IsEnabled = !branch.IsCurrent; delete.Click += (_, e) => { @@ -928,7 +928,7 @@ namespace SourceGit.Views } var createBranch = new MenuItem(); - createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); + createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); createBranch.Click += (_, e) => { @@ -938,7 +938,7 @@ namespace SourceGit.Views }; var createTag = new MenuItem(); - createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); + createTag.Icon = this.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); createTag.Click += (_, e) => { @@ -958,7 +958,7 @@ namespace SourceGit.Views { var createPR = new MenuItem(); createPR.Header = App.Text("BranchCM.CreatePRForUpstream", upstream.FriendlyName); - createPR.Icon = App.CreateMenuIcon("Icons.CreatePR"); + createPR.Icon = this.CreateMenuIcon("Icons.CreatePR"); createPR.Click += (_, e) => { Native.OS.OpenBrowser(prURL); @@ -984,7 +984,7 @@ namespace SourceGit.Views { var tracking = new MenuItem(); tracking.Header = App.Text("BranchCM.Tracking"); - tracking.Icon = App.CreateMenuIcon("Icons.Track"); + tracking.Icon = this.CreateMenuIcon("Icons.Track"); tracking.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -996,7 +996,7 @@ namespace SourceGit.Views } var archive = new MenuItem(); - archive.Icon = App.CreateMenuIcon("Icons.Archive"); + archive.Icon = this.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); archive.Click += (_, e) => { @@ -1009,10 +1009,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(branch.Name); + await this.CopyTextAsync(branch.Name); e.Handled = true; }; menu.Items.Add(copy); @@ -1028,7 +1028,7 @@ namespace SourceGit.Views { var visit = new MenuItem(); visit.Header = App.Text("RemoteCM.OpenInBrowser"); - visit.Icon = App.CreateMenuIcon("Icons.OpenWith"); + visit.Icon = this.CreateMenuIcon("Icons.OpenWith"); visit.Click += (_, e) => { Native.OS.OpenBrowser(visitURL); @@ -1041,7 +1041,7 @@ namespace SourceGit.Views var fetch = new MenuItem(); fetch.Header = App.Text("RemoteCM.Fetch"); - fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); + fetch.Icon = this.CreateMenuIcon("Icons.Fetch"); fetch.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1051,7 +1051,7 @@ namespace SourceGit.Views var prune = new MenuItem(); prune.Header = App.Text("RemoteCM.Prune"); - prune.Icon = App.CreateMenuIcon("Icons.Clean"); + prune.Icon = this.CreateMenuIcon("Icons.Clean"); prune.Click += async (_, e) => { if (repo.CanCreatePopup()) @@ -1061,7 +1061,7 @@ namespace SourceGit.Views var edit = new MenuItem(); edit.Header = App.Text("RemoteCM.Edit"); - edit.Icon = App.CreateMenuIcon("Icons.Edit"); + edit.Icon = this.CreateMenuIcon("Icons.Edit"); edit.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1071,7 +1071,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("RemoteCM.Delete"); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1081,10 +1081,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("RemoteCM.CopyURL"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(remote.URL); + await this.CopyTextAsync(remote.URL); e.Handled = true; }; @@ -1106,7 +1106,7 @@ namespace SourceGit.Views var checkout = new MenuItem(); checkout.Header = App.Text("BranchCM.Checkout", name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Icon = this.CreateMenuIcon("Icons.Check"); checkout.Click += async (_, e) => { await repo.CheckoutBranchAsync(branch); @@ -1119,7 +1119,7 @@ namespace SourceGit.Views { var pull = new MenuItem(); pull.Header = App.Text("BranchCM.PullInto", name, current.Name); - pull.Icon = App.CreateMenuIcon("Icons.Pull"); + pull.Icon = this.CreateMenuIcon("Icons.Pull"); pull.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1129,7 +1129,7 @@ namespace SourceGit.Views var merge = new MenuItem(); merge.Header = App.Text("BranchCM.Merge", name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1139,7 +1139,7 @@ namespace SourceGit.Views var rebase = new MenuItem(); rebase.Header = App.Text("BranchCM.Rebase", current.Name, name); - rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); + rebase.Icon = this.CreateMenuIcon("Icons.Rebase"); rebase.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1149,7 +1149,7 @@ namespace SourceGit.Views var interactiveRebase = new MenuItem(); interactiveRebase.Header = App.Text("BranchCM.InteractiveRebase.Manually", current.Name, name); - interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase"); interactiveRebase.IsEnabled = !current.Head.Equals(branch.Head, StringComparison.Ordinal); interactiveRebase.Click += async (_, e) => { @@ -1160,7 +1160,7 @@ namespace SourceGit.Views var compareWithHead = new MenuItem(); compareWithHead.Header = App.Text("BranchCM.CompareWithHead"); - compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare"); compareWithHead.Click += (_, _) => { App.ShowWindow(new ViewModels.Compare(repo, branch, current)); @@ -1168,7 +1168,7 @@ namespace SourceGit.Views var compareWith = new MenuItem(); compareWith.Header = App.Text("BranchCM.CompareWith"); - compareWith.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWith.Icon = this.CreateMenuIcon("Icons.Compare"); compareWith.Click += (_, _) => { new ViewModels.CompareCommandPalette(repo, branch).Open(); @@ -1188,7 +1188,7 @@ namespace SourceGit.Views var editDescription = new MenuItem(); editDescription.Header = App.Text("BranchCM.EditDescription", branch.Name); - editDescription.Icon = App.CreateMenuIcon("Icons.Edit"); + editDescription.Icon = this.CreateMenuIcon("Icons.Edit"); editDescription.Click += async (_, e) => { var desc = await new Commands.Config(repo.FullPath).GetAsync($"branch.{branch.Name}.description"); @@ -1199,7 +1199,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("BranchCM.Delete", name); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1212,7 +1212,7 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = "-" }); var createBranch = new MenuItem(); - createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); + createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); createBranch.Click += (_, e) => { @@ -1222,7 +1222,7 @@ namespace SourceGit.Views }; var createTag = new MenuItem(); - createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); + createTag.Icon = this.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); createTag.Click += (_, e) => { @@ -1239,7 +1239,7 @@ namespace SourceGit.Views { var createPR = new MenuItem(); createPR.Header = App.Text("BranchCM.CreatePR"); - createPR.Icon = App.CreateMenuIcon("Icons.CreatePR"); + createPR.Icon = this.CreateMenuIcon("Icons.CreatePR"); createPR.Click += (_, e) => { Native.OS.OpenBrowser(prURL); @@ -1252,7 +1252,7 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = "-" }); var archive = new MenuItem(); - archive.Icon = App.CreateMenuIcon("Icons.Archive"); + archive.Icon = this.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); archive.Click += (_, e) => { @@ -1263,10 +1263,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(name); + await this.CopyTextAsync(name); e.Handled = true; }; @@ -1285,13 +1285,13 @@ namespace SourceGit.Views var custom = new MenuItem(); custom.Header = App.Text("BranchCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { @@ -1314,13 +1314,13 @@ namespace SourceGit.Views var custom = new MenuItem(); custom.Header = App.Text("RemoteCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { diff --git a/src/Views/ChangeCollectionView.axaml b/src/Views/ChangeCollectionView.axaml index a00570f5..8d8f56bc 100644 --- a/src/Views/ChangeCollectionView.axaml +++ b/src/Views/ChangeCollectionView.axaml @@ -37,38 +37,43 @@ SelectionChanged="OnRowSelectionChanged"> - - - - + + + + + - - - - - - + + + + @@ -82,25 +87,28 @@ SelectionChanged="OnRowSelectionChanged"> - - + - - - - + + + - - + + @@ -114,21 +122,25 @@ SelectionChanged="OnRowSelectionChanged"> - - + - - - - - + + + + diff --git a/src/Views/ChangeCollectionView.axaml.cs b/src/Views/ChangeCollectionView.axaml.cs index 237a69da..d23bf5bc 100644 --- a/src/Views/ChangeCollectionView.axaml.cs +++ b/src/Views/ChangeCollectionView.axaml.cs @@ -231,17 +231,17 @@ namespace SourceGit.Views private void OnRowDataContextChanged(object sender, EventArgs e) { - if (sender is not Control control) + if (sender is not Control { DataContext: { } ctx } control) return; - if (control.DataContext is ViewModels.ChangeTreeNode node) + if (ctx is ViewModels.ChangeTreeNode node) { if (node.Change is { } c) UpdateRowTips(control, c); else ToolTip.SetTip(control, node.FullPath); } - else if (control.DataContext is Models.Change change) + else if (ctx is Models.Change change) { UpdateRowTips(control, change); } @@ -253,8 +253,10 @@ namespace SourceGit.Views private void OnRowDoubleTapped(object sender, TappedEventArgs e) { - var grid = sender as Grid; - if (grid?.DataContext is ViewModels.ChangeTreeNode node) + if (sender is not Control { DataContext: { } ctx }) + return; + + if (ctx is ViewModels.ChangeTreeNode node) { if (node.IsFolder) { @@ -269,7 +271,7 @@ namespace SourceGit.Views RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent)); } } - else if (grid?.DataContext is Models.Change) + else if (ctx is Models.Change) { RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent)); } diff --git a/src/Views/CheckoutBranchFromStash.axaml b/src/Views/CheckoutBranchFromStash.axaml new file mode 100644 index 00000000..0e030896 --- /dev/null +++ b/src/Views/CheckoutBranchFromStash.axaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/CheckoutBranchFromStash.axaml.cs b/src/Views/CheckoutBranchFromStash.axaml.cs new file mode 100644 index 00000000..b05e4fce --- /dev/null +++ b/src/Views/CheckoutBranchFromStash.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class CheckoutBranchFromStash : UserControl + { + public CheckoutBranchFromStash() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/Clone.axaml.cs b/src/Views/Clone.axaml.cs index 9316721a..91e8c7ad 100644 --- a/src/Views/Clone.axaml.cs +++ b/src/Views/Clone.axaml.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Controls; +using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.Platform.Storage; @@ -12,6 +13,29 @@ namespace SourceGit.Views InitializeComponent(); } + protected override async void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + if (DataContext is not ViewModels.Clone vm) + return; + + var clipboard = TopLevel.GetTopLevel(this)?.Clipboard; + if (clipboard != null) + { + try + { + var text = await clipboard.TryGetTextAsync(); + if (Models.Remote.IsValidURL(text)) + vm.Remote = text; + } + catch + { + // Ignore exceptions here. + } + } + } + private async void SelectParentFolder(object _, RoutedEventArgs e) { var options = new FolderPickerOpenOptions() { AllowMultiple = false }; @@ -31,7 +55,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to select parent folder: {exception.Message}"); + Models.Notification.Send(null, $"Failed to select parent folder: {exception.Message}", true); } e.Handled = true; diff --git a/src/Views/CommandPaletteDataTemplates.cs b/src/Views/CommandPaletteDataTemplates.cs index eec8dd71..fb9a4f3c 100644 --- a/src/Views/CommandPaletteDataTemplates.cs +++ b/src/Views/CommandPaletteDataTemplates.cs @@ -5,7 +5,7 @@ namespace SourceGit.Views { public class CommandPaletteDataTemplates : IDataTemplate { - public Control Build(object param) => App.CreateViewForViewModel(param); + public Control Build(object param) => ControlExtensions.CreateFromViewModels(param); public bool Match(object data) => data is ViewModels.ICommandPalette; } } diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index 9184ba62..7af9d59b 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -203,6 +203,7 @@ @@ -213,6 +214,26 @@ + + + + + + + + + + + + + + + + diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index f78ec9df..63395235 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -63,7 +63,7 @@ namespace SourceGit.Views private async void OnCopyCommitSHA(object sender, RoutedEventArgs e) { if (sender is Button { DataContext: Models.Commit commit }) - await App.CopyTextAsync(commit.SHA); + await this.CopyTextAsync(commit.SHA); e.Handled = true; } @@ -151,28 +151,28 @@ namespace SourceGit.Views var copyName = new MenuItem(); copyName.Header = App.Text("CommitDetail.Info.CopyName"); - copyName.Icon = App.CreateMenuIcon("Icons.Copy"); + copyName.Icon = this.CreateMenuIcon("Icons.Copy"); copyName.Click += async (_, ev) => { - await App.CopyTextAsync(user.Name); + await this.CopyTextAsync(user.Name); ev.Handled = true; }; var copyEmail = new MenuItem(); copyEmail.Header = App.Text("CommitDetail.Info.CopyEmail"); - copyEmail.Icon = App.CreateMenuIcon("Icons.Email"); + copyEmail.Icon = this.CreateMenuIcon("Icons.Email"); copyEmail.Click += async (_, ev) => { - await App.CopyTextAsync(user.Email); + await this.CopyTextAsync(user.Email); ev.Handled = true; }; var copyUser = new MenuItem(); copyUser.Header = App.Text("CommitDetail.Info.CopyNameAndEmail"); - copyUser.Icon = App.CreateMenuIcon("Icons.User"); + copyUser.Icon = this.CreateMenuIcon("Icons.User"); copyUser.Click += async (_, ev) => { - await App.CopyTextAsync(user.ToString()); + await this.CopyTextAsync(user.ToString()); ev.Handled = true; }; @@ -183,5 +183,12 @@ namespace SourceGit.Views menu.Open(control); e.Handled = true; } + + private async void OnCopyAllCommitMessage(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.CommitDetail detail) + await this.CopyTextAsync(detail.FullMessage.Message); + e.Handled = true; + } } } diff --git a/src/Views/CommitChanges.axaml.cs b/src/Views/CommitChanges.axaml.cs index 70fc23c9..0c1dea0d 100644 --- a/src/Views/CommitChanges.axaml.cs +++ b/src/Views/CommitChanges.axaml.cs @@ -62,7 +62,7 @@ namespace SourceGit.Views builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; } else if (e.Key == Key.F && e.KeyModifiers == cmdKey) diff --git a/src/Views/CommitDetail.axaml.cs b/src/Views/CommitDetail.axaml.cs index 16e76984..f0e55665 100644 --- a/src/Views/CommitDetail.axaml.cs +++ b/src/Views/CommitDetail.axaml.cs @@ -25,7 +25,7 @@ namespace SourceGit.Views var fullPath = Native.OS.GetAbsPath(repo.FullPath, node.FullPath); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = Directory.Exists(fullPath); explore.Click += (_, ev) => { @@ -35,7 +35,7 @@ namespace SourceGit.Views var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { App.ShowWindow(new ViewModels.DirHistories(repo, node.FullPath, commit.SHA)); @@ -44,7 +44,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -67,7 +67,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -75,21 +75,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(node.FullPath); + await this.CopyTextAsync(node.FullPath); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(fullPath); + await this.CopyTextAsync(fullPath); e.Handled = true; }; @@ -112,7 +112,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -135,7 +135,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -149,7 +149,7 @@ namespace SourceGit.Views { var resetToThisRevision = new MenuItem(); resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); - resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToThisRevision.Click += async (_, ev) => { await vm.ResetMultipleToThisRevisionAsync(changes); @@ -158,7 +158,7 @@ namespace SourceGit.Views var resetToFirstParent = new MenuItem(); resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision"); - resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToFirstParent.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToFirstParent.IsEnabled = commit.Parents.Count > 0; resetToFirstParent.Click += async (_, ev) => { @@ -173,7 +173,7 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { @@ -181,13 +181,13 @@ namespace SourceGit.Views foreach (var c in changes) builder.AppendLine(c.Path); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { @@ -195,7 +195,7 @@ namespace SourceGit.Views foreach (var c in changes) builder.AppendLine(Native.OS.GetAbsPath(repo.FullPath, c.Path)); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; }; @@ -211,7 +211,7 @@ namespace SourceGit.Views var openWith = new MenuItem(); openWith.Header = App.Text("Open"); - openWith.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWith.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWith.IsEnabled = change.Index != Models.ChangeState.Deleted; if (openWith.IsEnabled) { @@ -249,7 +249,7 @@ namespace SourceGit.Views var openWithMerger = new MenuItem(); openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; openWithMerger.Click += (_, ev) => { @@ -260,7 +260,7 @@ namespace SourceGit.Views var fullPath = Native.OS.GetAbsPath(repo.FullPath, change.Path); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(fullPath); explore.Click += (_, ev) => { @@ -270,7 +270,7 @@ namespace SourceGit.Views var history = new MenuItem(); history.Header = App.Text("FileHistory"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path, commit.SHA)); @@ -279,7 +279,7 @@ namespace SourceGit.Views var blame = new MenuItem(); blame.Header = App.Text("Blame"); - blame.Icon = App.CreateMenuIcon("Icons.Blame"); + blame.Icon = this.CreateMenuIcon("Icons.Blame"); blame.IsEnabled = change.Index != Models.ChangeState.Deleted; blame.Click += (_, ev) => { @@ -289,7 +289,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -312,7 +312,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -332,7 +332,7 @@ namespace SourceGit.Views { var resetToThisRevision = new MenuItem(); resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); - resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToThisRevision.Click += async (_, ev) => { await vm.ResetToThisRevisionAsync(change); @@ -341,7 +341,7 @@ namespace SourceGit.Views var resetToFirstParent = new MenuItem(); resetToFirstParent.Header = App.Text("ChangeCM.CheckoutFirstParentRevision"); - resetToFirstParent.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToFirstParent.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToFirstParent.IsEnabled = commit.Parents.Count > 0; resetToFirstParent.Click += async (_, ev) => { @@ -357,11 +357,11 @@ namespace SourceGit.Views { var lfs = new MenuItem(); lfs.Header = App.Text("GitLFS"); - lfs.Icon = App.CreateMenuIcon("Icons.LFS"); + lfs.Icon = this.CreateMenuIcon("Icons.LFS"); var lfsLock = new MenuItem(); lfsLock.Header = App.Text("GitLFS.Locks.Lock"); - lfsLock.Icon = App.CreateMenuIcon("Icons.Lock"); + lfsLock.Icon = this.CreateMenuIcon("Icons.Lock"); if (repo.Remotes.Count == 1) { lfsLock.Click += async (_, e) => @@ -389,7 +389,7 @@ namespace SourceGit.Views var lfsUnlock = new MenuItem(); lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock"); - lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock"); + lfsUnlock.Icon = this.CreateMenuIcon("Icons.Unlock"); if (repo.Remotes.Count == 1) { lfsUnlock.Click += async (_, e) => @@ -426,13 +426,13 @@ namespace SourceGit.Views var target = new Models.CustomActionTargetFile(change.Path, vm.Commit); var custom = new MenuItem(); custom.Header = App.Text("FileCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { @@ -449,21 +449,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(fullPath); + await this.CopyTextAsync(fullPath); e.Handled = true; }; @@ -484,9 +484,9 @@ namespace SourceGit.Views e.KeyModifiers.HasFlag(OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control)) { if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + await this.CopyTextAsync(vm.GetAbsPath(change.Path)); else - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); e.Handled = true; return; diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index 4c161ddd..14c3f666 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -134,7 +134,7 @@ namespace SourceGit.Views { var open = new MenuItem(); open.Header = App.Text("SHALinkCM.NavigateTo"); - open.Icon = App.CreateMenuIcon("Icons.Commit"); + open.Icon = this.CreateMenuIcon("Icons.Commit"); open.Click += (_, ev) => { detail.NavigateTo(link); @@ -143,10 +143,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("SHALinkCM.CopySHA"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { - await App.CopyTextAsync(link); + await this.CopyTextAsync(link); ev.Handled = true; }; @@ -167,7 +167,7 @@ namespace SourceGit.Views { var open = new MenuItem(); open.Header = App.Text("IssueLinkCM.OpenInBrowser"); - open.Icon = App.CreateMenuIcon("Icons.OpenWith"); + open.Icon = this.CreateMenuIcon("Icons.OpenWith"); open.Click += (_, ev) => { Native.OS.OpenBrowser(link); @@ -176,10 +176,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("IssueLinkCM.CopyLink"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { - await App.CopyTextAsync(link); + await this.CopyTextAsync(link); ev.Handled = true; }; @@ -268,7 +268,7 @@ namespace SourceGit.Views { var currentParent = this.FindAncestorOfType(); if (currentParent is { DataContext: ViewModels.CommitDetail currentDetail } && - currentDetail.Commit.SHA == lastDetailCommit) + currentDetail.Commit.SHA.Equals(lastDetailCommit, StringComparison.Ordinal)) { _inlineCommits.TryAdd(sha, c); if (_lastHover == link && c != null) diff --git a/src/Views/CommitMessageToolBox.axaml.cs b/src/Views/CommitMessageToolBox.axaml.cs index ca448807..e5960398 100644 --- a/src/Views/CommitMessageToolBox.axaml.cs +++ b/src/Views/CommitMessageToolBox.axaml.cs @@ -337,7 +337,7 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.IsEnabled = hasSelected; copy.Click += (_, ev) => { @@ -347,7 +347,7 @@ namespace SourceGit.Views var cut = new MenuItem(); cut.Header = App.Text("Cut"); - cut.Icon = App.CreateMenuIcon("Icons.Cut"); + cut.Icon = this.CreateMenuIcon("Icons.Cut"); cut.IsEnabled = hasSelected; cut.Click += (_, ev) => { @@ -357,7 +357,7 @@ namespace SourceGit.Views var paste = new MenuItem(); paste.Header = App.Text("Paste"); - paste.Icon = App.CreateMenuIcon("Icons.Paste"); + paste.Icon = this.CreateMenuIcon("Icons.Paste"); paste.Click += (_, ev) => { Paste(); @@ -447,7 +447,7 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = App.Text("WorkingCopy.NoCommitTemplates"), - Icon = App.CreateMenuIcon("Icons.Code"), + Icon = this.CreateMenuIcon("Icons.Code"), IsEnabled = false }); } @@ -455,7 +455,7 @@ namespace SourceGit.Views { for (int i = 0; i < templateCount; i++) { - var icon = App.CreateMenuIcon("Icons.Code"); + var icon = this.CreateMenuIcon("Icons.Code"); icon.Fill = foreground; var template = repo.Settings.CommitTemplates[i]; @@ -484,7 +484,7 @@ namespace SourceGit.Views friendlyName = $"~{gitTemplate.AsSpan(prefixLen)}"; } - var icon = App.CreateMenuIcon("Icons.Code"); + var icon = this.CreateMenuIcon("Icons.Code"); icon.Fill = foreground; var gitTemplateItem = new MenuItem(); @@ -508,7 +508,7 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = App.Text("WorkingCopy.NoCommitHistories"), - Icon = App.CreateMenuIcon("Icons.Histories"), + Icon = this.CreateMenuIcon("Icons.Histories"), IsEnabled = false }); } @@ -524,7 +524,7 @@ namespace SourceGit.Views TextTrimming = TextTrimming.CharacterEllipsis }; - var icon = App.CreateMenuIcon("Icons.Histories"); + var icon = this.CreateMenuIcon("Icons.Histories"); icon.Fill = foreground; var item = new MenuItem(); @@ -541,7 +541,7 @@ namespace SourceGit.Views menu.Items.Add(new MenuItem() { Header = "-" }); - var clearIcon = App.CreateMenuIcon("Icons.Clear"); + var clearIcon = this.CreateMenuIcon("Icons.Clear"); clearIcon.Fill = foreground; var clearHistoryItem = new MenuItem(); @@ -573,7 +573,7 @@ namespace SourceGit.Views if (vm.Staged == null || vm.Staged.Count == 0) { - App.RaiseException(repo.FullPath, "No files added to commit!"); + repo.SendNotification("No files added to commit!", true); e.Handled = true; return; } @@ -581,7 +581,7 @@ namespace SourceGit.Views var services = repo.GetPreferredOpenAIServices(); if (services.Count == 0) { - App.RaiseException(repo.FullPath, "Bad configuration for OpenAI"); + repo.SendNotification("Bad configuration for OpenAI", true); e.Handled = true; return; } diff --git a/src/Views/Compare.axaml.cs b/src/Views/Compare.axaml.cs index ee820933..a65bf39e 100644 --- a/src/Views/Compare.axaml.cs +++ b/src/Views/Compare.axaml.cs @@ -24,7 +24,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = this.StorageProvider; @@ -42,12 +42,14 @@ namespace SourceGit.Views if (storageFile != null) { var saveTo = storageFile.Path.LocalPath; - await vm.SaveChangesAsPatchAsync(selected, saveTo); + var succ = await vm.SaveChangesAsPatchAsync(selected, saveTo); + if (succ) + await new Alert().ShowAsync(this, "Save patch successfully.", false); } } catch (Exception exception) { - App.RaiseException(null, $"Failed to save as patch: {exception.Message}"); + await new Alert().ShowAsync(this, $"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -58,7 +60,7 @@ namespace SourceGit.Views var change = selected[0]; var openWithMerger = new MenuItem(); openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; openWithMerger.Click += (_, ev) => { @@ -72,7 +74,7 @@ namespace SourceGit.Views var full = vm.GetAbsPath(change.Path); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(full); explore.Click += (_, ev) => { @@ -89,7 +91,7 @@ namespace SourceGit.Views { var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName); - resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.Click += async (_, ev) => { await vm.ResetToLeftAsync(change); @@ -98,7 +100,7 @@ namespace SourceGit.Views var resetToRight = new MenuItem(); resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName); - resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToRight.Click += async (_, ev) => { await vm.ResetToRightAsync(change); @@ -112,21 +114,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - await App.CopyTextAsync(vm.GetAbsPath(change.Path)); + await this.CopyTextAsync(vm.GetAbsPath(change.Path)); ev.Handled = true; }; @@ -142,7 +144,7 @@ namespace SourceGit.Views { var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName); - resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.Click += async (_, ev) => { await vm.ResetMultipleToLeftAsync(selected); @@ -151,7 +153,7 @@ namespace SourceGit.Views var resetToRight = new MenuItem(); resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName); - resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToRight.Click += async (_, ev) => { await vm.ResetMultipleToRightAsync(selected); @@ -165,7 +167,7 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { @@ -173,13 +175,13 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(c.Path); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { @@ -187,7 +189,7 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(vm.GetAbsPath(c.Path)); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -233,7 +235,7 @@ namespace SourceGit.Views builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; } else if (e.Key == Key.F && e.KeyModifiers == cmdKey) diff --git a/src/Views/ConfigureWorkspace.axaml.cs b/src/Views/ConfigureWorkspace.axaml.cs index 19104f81..3ce2b4a8 100644 --- a/src/Views/ConfigureWorkspace.axaml.cs +++ b/src/Views/ConfigureWorkspace.axaml.cs @@ -40,7 +40,7 @@ namespace SourceGit.Views } catch (Exception ex) { - App.RaiseException(string.Empty, $"Failed to select default clone directory: {ex.Message}"); + await new Alert().ShowAsync(this, $"Failed to select default clone directory: {ex.Message}", true); } e.Handled = true; diff --git a/src/Views/ControlExtensions.cs b/src/Views/ControlExtensions.cs new file mode 100644 index 00000000..5f043d8a --- /dev/null +++ b/src/Views/ControlExtensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Media; + +namespace SourceGit.Views +{ + public static class ControlExtensions + { + public static Control CreateFromViewModels(object data) + { + var dataTypeName = data.GetType().FullName; + if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal)) + return null; + + var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views."); + var viewType = Type.GetType(viewTypeName); + if (viewType != null) + return Activator.CreateInstance(viewType) as Control; + + return null; + } + + public static async Task CopyTextAsync(this Control control, string text) + { + var clipboard = TopLevel.GetTopLevel(control)?.Clipboard; + if (clipboard != null) + await clipboard.SetTextAsync(text); + } + + public static Path CreateMenuIcon(this Control control, string iconKey) + { + if (control?.FindResource(iconKey) is StreamGeometry geo) + { + return new Path() + { + Data = geo, + Width = 12, + Height = 12, + Stretch = Stretch.Uniform + }; + } + + return null; + } + } +} diff --git a/src/Views/CreateBranch.axaml b/src/Views/CreateBranch.axaml index 92ecbf8a..ed9d7d88 100644 --- a/src/Views/CreateBranch.axaml +++ b/src/Views/CreateBranch.axaml @@ -35,7 +35,7 @@ - + diff --git a/src/Views/Discard.axaml b/src/Views/Discard.axaml index 748e1af6..33ea6d02 100644 --- a/src/Views/Discard.axaml +++ b/src/Views/Discard.axaml @@ -21,7 +21,7 @@ - + + + - - + - + - + diff --git a/src/Views/ExecuteCustomAction.axaml.cs b/src/Views/ExecuteCustomAction.axaml.cs index ad9ff984..e3aadb39 100644 --- a/src/Views/ExecuteCustomAction.axaml.cs +++ b/src/Views/ExecuteCustomAction.axaml.cs @@ -39,7 +39,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to select parent folder: {exception.Message}"); + Models.Notification.Send(null, $"Failed to select parent folder: {exception.Message}", true); } } else diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 8b21e197..eb5f90cd 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -268,13 +268,5 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/> - - - - - - - - diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index 25254327..ca0b969d 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -60,18 +60,12 @@ namespace SourceGit.Views if (sender is Button { DataContext: ViewModels.FileHistoriesSingleRevision single }) { await single.ResetToSelectedRevisionAsync(); - NotifyDonePanel.IsVisible = true; + await new Alert().ShowAsync(this, "Reset to selected revision successfully.", false); } e.Handled = true; } - private void OnCloseNotifyPanel(object _, PointerPressedEventArgs e) - { - NotifyDonePanel.IsVisible = false; - e.Handled = true; - } - private async void OnSaveAsPatch(object sender, RoutedEventArgs e) { if (sender is Button { DataContext: ViewModels.FileHistoriesCompareRevisions compare }) @@ -84,14 +78,16 @@ namespace SourceGit.Views try { var storageFile = await StorageProvider.SaveFilePickerAsync(options); - if (storageFile != null) - await compare.SaveAsPatch(storageFile.Path.LocalPath); + if (storageFile == null) + return; - NotifyDonePanel.IsVisible = true; + var succ = await compare.SaveAsPatch(storageFile.Path.LocalPath); + if (succ) + await new Alert().ShowAsync(this, "Saved as patch successfully.", false); } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to save as patch: {exception.Message}"); + await new Alert().ShowAsync(this, $"Failed to save as patch: {exception.Message}", true); } e.Handled = true; diff --git a/src/Views/FilterModeSwitchButton.axaml.cs b/src/Views/FilterModeSwitchButton.axaml.cs index 97880eb1..d5504d43 100644 --- a/src/Views/FilterModeSwitchButton.axaml.cs +++ b/src/Views/FilterModeSwitchButton.axaml.cs @@ -97,7 +97,7 @@ namespace SourceGit.Views } var include = new MenuItem(); - include.Icon = App.CreateMenuIcon("Icons.Filter"); + include.Icon = this.CreateMenuIcon("Icons.Filter"); include.Header = App.Text("Repository.FilterCommits.Include"); include.IsEnabled = current != Models.FilterMode.Included; include.Click += (_, ev) => @@ -107,7 +107,7 @@ namespace SourceGit.Views }; var exclude = new MenuItem(); - exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); + exclude.Icon = this.CreateMenuIcon("Icons.EyeClose"); exclude.Header = App.Text("Repository.FilterCommits.Exclude"); exclude.IsEnabled = current != Models.FilterMode.Excluded; exclude.Click += (_, ev) => @@ -142,7 +142,7 @@ namespace SourceGit.Views } var include = new MenuItem(); - include.Icon = App.CreateMenuIcon("Icons.Filter"); + include.Icon = this.CreateMenuIcon("Icons.Filter"); include.Header = App.Text("Repository.FilterCommits.Include"); include.IsEnabled = current != Models.FilterMode.Included; include.Click += (_, ev) => @@ -152,7 +152,7 @@ namespace SourceGit.Views }; var exclude = new MenuItem(); - exclude.Icon = App.CreateMenuIcon("Icons.EyeClose"); + exclude.Icon = this.CreateMenuIcon("Icons.EyeClose"); exclude.Header = App.Text("Repository.FilterCommits.Exclude"); exclude.IsEnabled = current != Models.FilterMode.Excluded; exclude.Click += (_, ev) => diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 98a8664d..39312ec6 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -352,7 +352,7 @@ namespace SourceGit.Views var authorColumn = new MenuItem(); authorColumn.Header = App.Text("Histories.Header.Author"); if (vm.IsAuthorColumnVisible) - authorColumn.Icon = App.CreateMenuIcon("Icons.Check"); + authorColumn.Icon = this.CreateMenuIcon("Icons.Check"); authorColumn.Click += (_, ev) => { vm.IsAuthorColumnVisible = !vm.IsAuthorColumnVisible; @@ -362,7 +362,7 @@ namespace SourceGit.Views var shaColumn = new MenuItem(); shaColumn.Header = App.Text("Histories.Header.SHA"); if (vm.IsSHAColumnVisible) - shaColumn.Icon = App.CreateMenuIcon("Icons.Check"); + shaColumn.Icon = this.CreateMenuIcon("Icons.Check"); shaColumn.Click += (_, ev) => { vm.IsSHAColumnVisible = !vm.IsSHAColumnVisible; @@ -372,7 +372,7 @@ namespace SourceGit.Views var timeColumn = new MenuItem(); timeColumn.Header = App.Text("Histories.Header.DateTime"); if (vm.IsDateTimeColumnVisible) - timeColumn.Icon = App.CreateMenuIcon("Icons.Check"); + timeColumn.Icon = this.CreateMenuIcon("Icons.Check"); timeColumn.Click += (_, ev) => { vm.IsDateTimeColumnVisible = !vm.IsDateTimeColumnVisible; @@ -406,7 +406,7 @@ namespace SourceGit.Views builder.Append(commit.SHA.AsSpan(0, 10)).Append(" - ").AppendLine(commit.Subject); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; return; } @@ -489,7 +489,7 @@ namespace SourceGit.Views { var cherryPick = new MenuItem(); cherryPick.Header = App.Text("CommitCM.CherryPickMultiple"); - cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick"); + cherryPick.Icon = this.CreateMenuIcon("Icons.CherryPick"); cherryPick.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -503,7 +503,7 @@ namespace SourceGit.Views { var merge = new MenuItem(); merge.Header = App.Text("CommitCM.MergeMultiple"); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -518,7 +518,7 @@ namespace SourceGit.Views } var saveToPatch = new MenuItem(); - saveToPatch.Icon = App.CreateMenuIcon("Icons.Save"); + saveToPatch.Icon = this.CreateMenuIcon("Icons.Save"); saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); saveToPatch.Click += async (_, e) => { @@ -543,12 +543,12 @@ namespace SourceGit.Views } if (succ) - App.SendNotification(repo.FullPath, App.Text("SaveAsPatchSuccess")); + repo.SendNotification(App.Text("SaveAsPatchSuccess")); } } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -565,39 +565,39 @@ namespace SourceGit.Views foreach (var c in selected) builder.Append(c.SHA.AsSpan(0, 10)).Append(" - ").AppendLine(c.Subject); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; }; var copyShas = new MenuItem(); copyShas.Header = App.Text("CommitCM.CopySHA"); - copyShas.Icon = App.CreateMenuIcon("Icons.Hash"); + copyShas.Icon = this.CreateMenuIcon("Icons.Hash"); copyShas.Click += async (_, e) => { var builder = new StringBuilder(); foreach (var c in selected) builder.AppendLine(c.SHA); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; }; var copySubjects = new MenuItem(); copySubjects.Header = App.Text("CommitCM.CopySubject"); - copySubjects.Icon = App.CreateMenuIcon("Icons.Subject"); + copySubjects.Icon = this.CreateMenuIcon("Icons.Subject"); copySubjects.Click += async (_, e) => { var builder = new StringBuilder(); foreach (var c in selected) builder.AppendLine(c.Subject); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; }; var copyMessage = new MenuItem(); copyMessage.Header = App.Text("CommitCM.CopyCommitMessage"); - copyMessage.Icon = App.CreateMenuIcon("Icons.Message"); + copyMessage.Icon = this.CreateMenuIcon("Icons.Message"); copyMessage.Click += async (_, e) => { var vm = DataContext as ViewModels.Histories; @@ -608,13 +608,13 @@ namespace SourceGit.Views messages.Add(message); } - await App.CopyTextAsync(string.Join("\n-----\n", messages)); + await this.CopyTextAsync(string.Join("\n-----\n", messages)); e.Handled = true; }; var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Items.Add(copyInfos); copy.Items.Add(new MenuItem() { Header = "-" }); copy.Items.Add(copyShas); @@ -672,7 +672,7 @@ namespace SourceGit.Views } var createBranch = new MenuItem(); - createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); + createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); createBranch.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+B" : "Ctrl+Shift+B"; createBranch.Click += (_, e) => @@ -684,7 +684,7 @@ namespace SourceGit.Views menu.Items.Add(createBranch); var createTag = new MenuItem(); - createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); + createTag.Icon = this.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); createTag.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+T" : "Ctrl+Shift+T"; createTag.Click += (_, e) => @@ -706,7 +706,7 @@ namespace SourceGit.Views { var reword = new MenuItem(); reword.Header = App.Text("CommitCM.Reword"); - reword.Icon = App.CreateMenuIcon("Icons.Edit"); + reword.Icon = this.CreateMenuIcon("Icons.Edit"); reword.Click += async (_, e) => { await vm.RewordHeadAsync(commit); @@ -716,7 +716,7 @@ namespace SourceGit.Views var squash = new MenuItem(); squash.Header = App.Text("CommitCM.Squash"); - squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); + squash.Icon = this.CreateMenuIcon("Icons.SquashIntoParent"); squash.IsEnabled = commit.Parents.Count == 1; squash.Click += async (_, e) => { @@ -727,7 +727,7 @@ namespace SourceGit.Views var fixup = new MenuItem(); fixup.Header = App.Text("CommitCM.Fixup"); - fixup.Icon = App.CreateMenuIcon("Icons.Fix"); + fixup.Icon = this.CreateMenuIcon("Icons.Fix"); fixup.IsEnabled = commit.Parents.Count == 1; fixup.Click += async (_, e) => { @@ -740,7 +740,7 @@ namespace SourceGit.Views { var reset = new MenuItem(); reset.Header = App.Text("CommitCM.Reset", current.Name, target); - reset.Icon = App.CreateMenuIcon("Icons.Reset"); + reset.Icon = this.CreateMenuIcon("Icons.Reset"); reset.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -754,7 +754,7 @@ namespace SourceGit.Views { var rebase = new MenuItem(); rebase.Header = App.Text("CommitCM.Rebase", current.Name, target); - rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); + rebase.Icon = this.CreateMenuIcon("Icons.Rebase"); rebase.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -767,7 +767,7 @@ namespace SourceGit.Views { var merge = new MenuItem(); merge.Header = App.Text("CommitCM.Merge", current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -780,7 +780,7 @@ namespace SourceGit.Views var cherryPick = new MenuItem(); cherryPick.Header = App.Text("CommitCM.CherryPick"); - cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick"); + cherryPick.Icon = this.CreateMenuIcon("Icons.CherryPick"); cherryPick.Click += async (_, e) => { await vm.CherryPickAsync(commit); @@ -791,7 +791,7 @@ namespace SourceGit.Views var revert = new MenuItem(); revert.Header = App.Text("CommitCM.Revert"); - revert.Icon = App.CreateMenuIcon("Icons.Undo"); + revert.Icon = this.CreateMenuIcon("Icons.Undo"); revert.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -804,7 +804,7 @@ namespace SourceGit.Views { var dropHead = new MenuItem(); dropHead.Header = App.Text("CommitCM.Drop"); - dropHead.Icon = App.CreateMenuIcon("Icons.Clear"); + dropHead.Icon = this.CreateMenuIcon("Icons.Clear"); dropHead.Click += async (_, e) => { await vm.DropHeadAsync(commit); @@ -816,7 +816,7 @@ namespace SourceGit.Views { var checkoutCommit = new MenuItem(); checkoutCommit.Header = App.Text("CommitCM.Checkout"); - checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached"); + checkoutCommit.Icon = this.CreateMenuIcon("Icons.Detached"); checkoutCommit.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -829,7 +829,7 @@ namespace SourceGit.Views { var manually = new MenuItem(); manually.Header = App.Text("CommitCM.InteractiveRebase.Manually", current.Name, target); - manually.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + manually.Icon = this.CreateMenuIcon("Icons.InteractiveRebase"); manually.Click += async (_, e) => { await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit)); @@ -838,7 +838,7 @@ namespace SourceGit.Views var reword = new MenuItem(); reword.Header = App.Text("CommitCM.InteractiveRebase.Reword"); - reword.Icon = App.CreateMenuIcon("Icons.Rename"); + reword.Icon = this.CreateMenuIcon("Icons.Rename"); reword.Click += async (_, e) => { await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Reword); @@ -847,7 +847,7 @@ namespace SourceGit.Views var edit = new MenuItem(); edit.Header = App.Text("CommitCM.InteractiveRebase.Edit"); - edit.Icon = App.CreateMenuIcon("Icons.Edit"); + edit.Icon = this.CreateMenuIcon("Icons.Edit"); edit.Click += async (_, e) => { await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Edit); @@ -856,7 +856,7 @@ namespace SourceGit.Views var squash = new MenuItem(); squash.Header = App.Text("CommitCM.InteractiveRebase.Squash"); - squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); + squash.Icon = this.CreateMenuIcon("Icons.SquashIntoParent"); squash.Click += async (_, e) => { await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Squash); @@ -865,7 +865,7 @@ namespace SourceGit.Views var fixup = new MenuItem(); fixup.Header = App.Text("CommitCM.InteractiveRebase.Fixup"); - fixup.Icon = App.CreateMenuIcon("Icons.Fix"); + fixup.Icon = this.CreateMenuIcon("Icons.Fix"); fixup.Click += async (_, e) => { await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Fixup); @@ -874,7 +874,7 @@ namespace SourceGit.Views var drop = new MenuItem(); drop.Header = App.Text("CommitCM.InteractiveRebase.Drop"); - drop.Icon = App.CreateMenuIcon("Icons.Clear"); + drop.Icon = this.CreateMenuIcon("Icons.Clear"); drop.Click += async (_, e) => { await vm.InteractiveRebaseAsync(commit, Models.InteractiveRebaseAction.Drop); @@ -883,7 +883,7 @@ namespace SourceGit.Views var interactiveRebase = new MenuItem(); interactiveRebase.Header = App.Text("CommitCM.InteractiveRebase"); - interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase"); interactiveRebase.Items.Add(manually); interactiveRebase.Items.Add(new MenuItem() { Header = "-" }); interactiveRebase.Items.Add(reword); @@ -899,7 +899,7 @@ namespace SourceGit.Views { var interactiveRebase = new MenuItem(); interactiveRebase.Header = App.Text("CommitCM.InteractiveRebase.Manually", current.Name, target); - interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); + interactiveRebase.Icon = this.CreateMenuIcon("Icons.InteractiveRebase"); interactiveRebase.Click += async (_, e) => { await App.ShowDialog(new ViewModels.InteractiveRebase(repo, commit)); @@ -921,7 +921,7 @@ namespace SourceGit.Views var upstream = repo.Branches.Find(x => x.FullName.Equals(current.Upstream, StringComparison.Ordinal)); var pushRevision = new MenuItem(); pushRevision.Header = App.Text("CommitCM.PushRevision", commit.SHA.Substring(0, 10), upstream.FriendlyName); - pushRevision.Icon = App.CreateMenuIcon("Icons.Push"); + pushRevision.Icon = this.CreateMenuIcon("Icons.Push"); pushRevision.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -934,7 +934,7 @@ namespace SourceGit.Views var compareWithHead = new MenuItem(); compareWithHead.Header = App.Text("CommitCM.CompareWithHead"); - compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare"); compareWithHead.Click += async (_, e) => { var head = await vm.CompareWithHeadAsync(commit); @@ -949,7 +949,7 @@ namespace SourceGit.Views { var compareWithWorktree = new MenuItem(); compareWithWorktree.Header = App.Text("CommitCM.CompareWithWorktree"); - compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithWorktree.Icon = this.CreateMenuIcon("Icons.Compare"); compareWithWorktree.Click += (_, e) => { vm.CompareWithWorktree(commit); @@ -962,7 +962,7 @@ namespace SourceGit.Views } var saveToPatch = new MenuItem(); - saveToPatch.Icon = App.CreateMenuIcon("Icons.Save"); + saveToPatch.Icon = this.CreateMenuIcon("Icons.Save"); saveToPatch.Header = App.Text("CommitCM.SaveAsPatch"); saveToPatch.Click += async (_, e) => { @@ -983,7 +983,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -991,7 +991,7 @@ namespace SourceGit.Views menu.Items.Add(saveToPatch); var archive = new MenuItem(); - archive.Icon = App.CreateMenuIcon("Icons.Archive"); + archive.Icon = this.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); archive.Click += (_, e) => { @@ -1007,13 +1007,13 @@ namespace SourceGit.Views { var custom = new MenuItem(); custom.Header = App.Text("CommitCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { @@ -1033,59 +1033,59 @@ namespace SourceGit.Views copyInfo.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyInfo.Click += async (_, e) => { - await App.CopyTextAsync($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}"); + await this.CopyTextAsync($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}"); e.Handled = true; }; var copySHA = new MenuItem(); copySHA.Header = App.Text("CommitCM.CopySHA"); - copySHA.Icon = App.CreateMenuIcon("Icons.Hash"); + copySHA.Icon = this.CreateMenuIcon("Icons.Hash"); copySHA.Click += async (_, e) => { - await App.CopyTextAsync(commit.SHA); + await this.CopyTextAsync(commit.SHA); e.Handled = true; }; var copySubject = new MenuItem(); copySubject.Header = App.Text("CommitCM.CopySubject"); - copySubject.Icon = App.CreateMenuIcon("Icons.Subject"); + copySubject.Icon = this.CreateMenuIcon("Icons.Subject"); copySubject.Click += async (_, e) => { - await App.CopyTextAsync(commit.Subject); + await this.CopyTextAsync(commit.Subject); e.Handled = true; }; var copyMessage = new MenuItem(); copyMessage.Header = App.Text("CommitCM.CopyCommitMessage"); - copyMessage.Icon = App.CreateMenuIcon("Icons.Message"); + copyMessage.Icon = this.CreateMenuIcon("Icons.Message"); copyMessage.Click += async (_, e) => { var message = await vm.GetCommitFullMessageAsync(commit); - await App.CopyTextAsync(message); + await this.CopyTextAsync(message); e.Handled = true; }; var copyAuthor = new MenuItem(); copyAuthor.Header = App.Text("CommitCM.CopyAuthor"); - copyAuthor.Icon = App.CreateMenuIcon("Icons.User"); + copyAuthor.Icon = this.CreateMenuIcon("Icons.User"); copyAuthor.Click += async (_, e) => { - await App.CopyTextAsync(commit.Author.ToString()); + await this.CopyTextAsync(commit.Author.ToString()); e.Handled = true; }; var copyCommitter = new MenuItem(); copyCommitter.Header = App.Text("CommitCM.CopyCommitter"); - copyCommitter.Icon = App.CreateMenuIcon("Icons.User"); + copyCommitter.Icon = this.CreateMenuIcon("Icons.User"); copyCommitter.Click += async (_, e) => { - await App.CopyTextAsync(commit.Committer.ToString()); + await this.CopyTextAsync(commit.Committer.ToString()); e.Handled = true; }; var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Items.Add(copyInfo); copy.Items.Add(new MenuItem() { Header = "-" }); copy.Items.Add(copySHA); @@ -1101,7 +1101,7 @@ namespace SourceGit.Views private void FillCurrentBranchMenu(ContextMenu menu, ViewModels.Repository repo, Models.Branch current) { var submenu = new MenuItem(); - submenu.Icon = App.CreateMenuIcon("Icons.Branch"); + submenu.Icon = this.CreateMenuIcon("Icons.Branch"); submenu.Header = current.Name; var visibility = new MenuItem(); @@ -1116,7 +1116,7 @@ namespace SourceGit.Views var fastForward = new MenuItem(); fastForward.Header = App.Text("BranchCM.FastForward", upstream); - fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); + fastForward.Icon = this.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = current.Ahead.Count == 0 && current.Behind.Count > 0; fastForward.Click += async (_, e) => { @@ -1133,7 +1133,7 @@ namespace SourceGit.Views var pull = new MenuItem(); pull.Header = App.Text("BranchCM.Pull", upstream); - pull.Icon = App.CreateMenuIcon("Icons.Pull"); + pull.Icon = this.CreateMenuIcon("Icons.Pull"); pull.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1145,7 +1145,7 @@ namespace SourceGit.Views var push = new MenuItem(); push.Header = App.Text("BranchCM.Push", current.Name); - push.Icon = App.CreateMenuIcon("Icons.Push"); + push.Icon = this.CreateMenuIcon("Icons.Push"); push.IsEnabled = repo.Remotes.Count > 0; push.Click += (_, e) => { @@ -1157,7 +1157,7 @@ namespace SourceGit.Views var rename = new MenuItem(); rename.Header = App.Text("BranchCM.Rename", current.Name); - rename.Icon = App.CreateMenuIcon("Icons.Rename"); + rename.Icon = this.CreateMenuIcon("Icons.Rename"); rename.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1174,7 +1174,7 @@ namespace SourceGit.Views { var finish = new MenuItem(); finish.Header = App.Text("BranchCM.Finish", current.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Icon = this.CreateMenuIcon("Icons.GitFlow"); finish.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1188,10 +1188,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(current.Name); + await this.CopyTextAsync(current.Name); e.Handled = true; }; submenu.Items.Add(copy); @@ -1202,7 +1202,7 @@ namespace SourceGit.Views private void FillOtherLocalBranchMenu(ContextMenu menu, ViewModels.Repository repo, Models.Branch branch, Models.Branch current, bool merged) { var submenu = new MenuItem(); - submenu.Icon = App.CreateMenuIcon("Icons.Branch"); + submenu.Icon = this.CreateMenuIcon("Icons.Branch"); submenu.Header = branch.Name; var visibility = new MenuItem(); @@ -1215,7 +1215,7 @@ namespace SourceGit.Views { var checkout = new MenuItem(); checkout.Header = App.Text("BranchCM.Checkout", branch.Name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Icon = this.CreateMenuIcon("Icons.Check"); checkout.Click += async (_, e) => { await repo.CheckoutBranchAsync(branch); @@ -1225,7 +1225,7 @@ namespace SourceGit.Views var merge = new MenuItem(); merge.Header = App.Text("BranchCM.Merge", branch.Name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; merge.Click += (_, e) => { @@ -1238,7 +1238,7 @@ namespace SourceGit.Views var rename = new MenuItem(); rename.Header = App.Text("BranchCM.Rename", branch.Name); - rename.Icon = App.CreateMenuIcon("Icons.Rename"); + rename.Icon = this.CreateMenuIcon("Icons.Rename"); rename.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1249,7 +1249,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("BranchCM.Delete", branch.Name); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1266,7 +1266,7 @@ namespace SourceGit.Views { var finish = new MenuItem(); finish.Header = App.Text("BranchCM.Finish", branch.Name); - finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); + finish.Icon = this.CreateMenuIcon("Icons.GitFlow"); finish.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1280,10 +1280,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(branch.Name); + await this.CopyTextAsync(branch.Name); e.Handled = true; }; submenu.Items.Add(copy); @@ -1296,7 +1296,7 @@ namespace SourceGit.Views var name = branch.FriendlyName; var submenu = new MenuItem(); - submenu.Icon = App.CreateMenuIcon("Icons.Branch"); + submenu.Icon = this.CreateMenuIcon("Icons.Branch"); submenu.Header = name; var visibility = new MenuItem(); @@ -1307,7 +1307,7 @@ namespace SourceGit.Views var checkout = new MenuItem(); checkout.Header = App.Text("BranchCM.Checkout", name); - checkout.Icon = App.CreateMenuIcon("Icons.Check"); + checkout.Icon = this.CreateMenuIcon("Icons.Check"); checkout.Click += async (_, e) => { await repo.CheckoutBranchAsync(branch); @@ -1317,7 +1317,7 @@ namespace SourceGit.Views var merge = new MenuItem(); merge.Header = App.Text("BranchCM.Merge", name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; merge.Click += (_, e) => { @@ -1330,7 +1330,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("BranchCM.Delete", name); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1342,10 +1342,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(name); + await this.CopyTextAsync(name); e.Handled = true; }; submenu.Items.Add(copy); @@ -1357,7 +1357,7 @@ namespace SourceGit.Views { var submenu = new MenuItem(); submenu.Header = tag.Name; - submenu.Icon = App.CreateMenuIcon("Icons.Tag"); + submenu.Icon = this.CreateMenuIcon("Icons.Tag"); submenu.MinWidth = 200; var visibility = new MenuItem(); @@ -1368,7 +1368,7 @@ namespace SourceGit.Views var push = new MenuItem(); push.Header = App.Text("TagCM.Push", tag.Name); - push.Icon = App.CreateMenuIcon("Icons.Push"); + push.Icon = this.CreateMenuIcon("Icons.Push"); push.IsEnabled = repo.Remotes.Count > 0; push.Click += (_, e) => { @@ -1382,7 +1382,7 @@ namespace SourceGit.Views { var merge = new MenuItem(); merge.Header = App.Text("TagCM.Merge", tag.Name, current.Name); - merge.Icon = App.CreateMenuIcon("Icons.Merge"); + merge.Icon = this.CreateMenuIcon("Icons.Merge"); merge.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1394,7 +1394,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("TagCM.Delete", tag.Name); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1406,10 +1406,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("TagCM.CopyName"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, e) => { - await App.CopyTextAsync(tag.Name); + await this.CopyTextAsync(tag.Name); e.Handled = true; }; submenu.Items.Add(copy); diff --git a/src/Views/Launcher.axaml.cs b/src/Views/Launcher.axaml.cs index 985c60fb..2e0e20c9 100644 --- a/src/Views/Launcher.axaml.cs +++ b/src/Views/Launcher.axaml.cs @@ -106,13 +106,16 @@ namespace SourceGit.Views Activate(); } - protected override void OnOpened(EventArgs e) + protected override async void OnOpened(EventArgs e) { base.OnOpened(e); - var state = ViewModels.Preferences.Instance.Layout.LauncherWindowState; + var preferences = ViewModels.Preferences.Instance; + var state = preferences.Layout.LauncherWindowState; if (state == WindowState.Maximized || state == WindowState.FullScreen) WindowState = WindowState.Maximized; + + await preferences.UpdateAvailableAIModelsAsync(); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) @@ -370,7 +373,7 @@ namespace SourceGit.Views { var workspace = pref.Workspaces[i]; - var icon = App.CreateMenuIcon(workspace.IsActive ? "Icons.Check" : "Icons.Workspace"); + var icon = this.CreateMenuIcon(workspace.IsActive ? "Icons.Check" : "Icons.Workspace"); icon.Fill = workspace.Brush; var item = new MenuItem(); diff --git a/src/Views/LauncherPage.axaml.cs b/src/Views/LauncherPage.axaml.cs index 13c523be..74e82aae 100644 --- a/src/Views/LauncherPage.axaml.cs +++ b/src/Views/LauncherPage.axaml.cs @@ -71,7 +71,7 @@ namespace SourceGit.Views private async void OnCopyNotification(object sender, RoutedEventArgs e) { if (sender is Button { DataContext: Models.Notification notice }) - await App.CopyTextAsync(notice.Message); + await this.CopyTextAsync(notice.Message); e.Handled = true; } diff --git a/src/Views/LauncherTabBar.axaml.cs b/src/Views/LauncherTabBar.axaml.cs index 0d832bea..a7ec5248 100644 --- a/src/Views/LauncherTabBar.axaml.cs +++ b/src/Views/LauncherTabBar.axaml.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Avalonia; using Avalonia.Controls; @@ -284,7 +285,7 @@ namespace SourceGit.Views { var refresh = new MenuItem(); refresh.Header = App.Text("PageTabBar.Tab.Refresh"); - refresh.Icon = App.CreateMenuIcon("Icons.Loading"); + refresh.Icon = this.CreateMenuIcon("Icons.Loading"); refresh.Tag = "F5"; refresh.Click += (_, ev) => { @@ -295,53 +296,38 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("PageTabBar.Tab.CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Click += async (_, ev) => { - await page.CopyPathAsync(); + var dir = new DirectoryInfo(repo.FullPath); + await this.CopyTextAsync(dir.FullName); ev.Handled = true; }; menu.Items.Add(copyPath); menu.Items.Add(new MenuItem() { Header = "-" }); - var bookmark = new MenuItem(); - bookmark.Header = App.Text("PageTabBar.Tab.Bookmark"); - bookmark.Icon = App.CreateMenuIcon("Icons.Bookmark"); - - for (int i = 0; i < Models.Bookmarks.Brushes.Length; i++) + var edit = new MenuItem(); + edit.Header = App.Text("PageTabBar.Tab.Edit"); + edit.Icon = this.CreateMenuIcon("Icons.Edit"); + edit.Click += (_, e) => { - var brush = Models.Bookmarks.Brushes[i]; - var icon = App.CreateMenuIcon("Icons.Bookmark"); - if (brush != null) - icon.Fill = brush; - - var dupIdx = i; - var setter = new MenuItem() { Header = icon }; - if (i == page.Node.Bookmark) - setter.Icon = App.CreateMenuIcon("Icons.Check"); - else - setter.Click += (_, ev) => - { - page.Node.Bookmark = dupIdx; - ev.Handled = true; - }; - - bookmark.Items.Add(setter); - } - menu.Items.Add(bookmark); + page.Node.Edit(); + e.Handled = true; + }; + menu.Items.Add(edit); var workspaces = ViewModels.Preferences.Instance.Workspaces; if (workspaces.Count > 1) { var moveTo = new MenuItem(); moveTo.Header = App.Text("PageTabBar.Tab.MoveToWorkspace"); - moveTo.Icon = App.CreateMenuIcon("Icons.MoveTo"); + moveTo.Icon = this.CreateMenuIcon("Icons.MoveTo"); foreach (var ws in workspaces) { var dupWs = ws; var isCurrent = dupWs == vm.ActiveWorkspace; - var icon = App.CreateMenuIcon(isCurrent ? "Icons.Check" : "Icons.Workspace"); + var icon = this.CreateMenuIcon(isCurrent ? "Icons.Check" : "Icons.Workspace"); icon.Fill = dupWs.Brush; var target = new MenuItem(); diff --git a/src/Views/MergeConflictEditor.axaml.cs b/src/Views/MergeConflictEditor.axaml.cs index 5ee5a6fd..1d2352dd 100644 --- a/src/Views/MergeConflictEditor.axaml.cs +++ b/src/Views/MergeConflictEditor.axaml.cs @@ -391,10 +391,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { - await App.CopyTextAsync(selected); + await this.CopyTextAsync(selected); ev.Handled = true; }; diff --git a/src/Views/PopupDataTemplates.cs b/src/Views/PopupDataTemplates.cs index 4720a234..bfb2673a 100644 --- a/src/Views/PopupDataTemplates.cs +++ b/src/Views/PopupDataTemplates.cs @@ -14,7 +14,7 @@ namespace SourceGit.Views public Control Build(object param) { - var control = App.CreateViewForViewModel(param); + var control = ControlExtensions.CreateFromViewModels(param); control.Loaded += (o, e) => { diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index 0a4681ed..3b2ee062 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -309,7 +309,7 @@ - + + + + + + + + + @@ -868,9 +888,6 @@ - - - { repo.OpenWorktree(worktree); @@ -107,7 +107,7 @@ namespace SourceGit.Views { var unlock = new MenuItem(); unlock.Header = App.Text("Worktree.Unlock"); - unlock.Icon = App.CreateMenuIcon("Icons.Unlock"); + unlock.Icon = this.CreateMenuIcon("Icons.Unlock"); unlock.Click += async (_, ev) => { await repo.UnlockWorktreeAsync(worktree); @@ -119,7 +119,7 @@ namespace SourceGit.Views { var loc = new MenuItem(); loc.Header = App.Text("Worktree.Lock"); - loc.Icon = App.CreateMenuIcon("Icons.Lock"); + loc.Icon = this.CreateMenuIcon("Icons.Lock"); loc.IsEnabled = !worktree.IsMain; loc.Click += async (_, ev) => { @@ -131,7 +131,7 @@ namespace SourceGit.Views var remove = new MenuItem(); remove.Header = App.Text("Worktree.Remove"); - remove.Icon = App.CreateMenuIcon("Icons.Clear"); + remove.Icon = this.CreateMenuIcon("Icons.Clear"); remove.IsEnabled = !worktree.IsCurrent && !worktree.IsMain; remove.Click += (_, ev) => { @@ -143,10 +143,10 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("Worktree.CopyPath"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { - await App.CopyTextAsync(worktree.FullPath); + await this.CopyTextAsync(worktree.FullPath); ev.Handled = true; }; menu.Items.Add(new MenuItem() { Header = "-" }); @@ -354,7 +354,7 @@ namespace SourceGit.Views var horizontal = new MenuItem(); horizontal.Header = App.Text("Repository.HistoriesLayout.Horizontal"); if (isHorizontal) - horizontal.Icon = App.CreateMenuIcon("Icons.Check"); + horizontal.Icon = this.CreateMenuIcon("Icons.Check"); horizontal.Click += (_, ev) => { pref.UseTwoColumnsLayoutInHistories = true; @@ -364,7 +364,7 @@ namespace SourceGit.Views var vertical = new MenuItem(); vertical.Header = App.Text("Repository.HistoriesLayout.Vertical"); if (!isHorizontal) - vertical.Icon = App.CreateMenuIcon("Icons.Check"); + vertical.Icon = this.CreateMenuIcon("Icons.Check"); vertical.Click += (_, ev) => { pref.UseTwoColumnsLayoutInHistories = false; @@ -379,7 +379,7 @@ namespace SourceGit.Views reflog.Header = App.Text("Repository.ShowLostCommits"); reflog.Tag = "--reflog"; if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.Reflog)) - reflog.Icon = App.CreateMenuIcon("Icons.Check"); + reflog.Icon = this.CreateMenuIcon("Icons.Check"); reflog.Click += (_, ev) => { repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.Reflog); @@ -390,7 +390,7 @@ namespace SourceGit.Views firstParentOnly.Header = App.Text("Repository.ShowFirstParentOnly"); firstParentOnly.Tag = "--first-parent"; if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly)) - firstParentOnly.Icon = App.CreateMenuIcon("Icons.Check"); + firstParentOnly.Icon = this.CreateMenuIcon("Icons.Check"); firstParentOnly.Click += (_, ev) => { repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.FirstParentOnly); @@ -401,7 +401,7 @@ namespace SourceGit.Views simplifyByDecoration.Header = App.Text("Repository.ShowDecoratedCommitsOnly"); simplifyByDecoration.Tag = "--simplify-by-decoration"; if (repo.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.SimplifyByDecoration)) - simplifyByDecoration.Icon = App.CreateMenuIcon("Icons.Check"); + simplifyByDecoration.Icon = this.CreateMenuIcon("Icons.Check"); simplifyByDecoration.Click += (_, ev) => { repo.ToggleHistoryShowFlag(Models.HistoryShowFlags.SimplifyByDecoration); @@ -416,7 +416,7 @@ namespace SourceGit.Views dateOrder.Header = App.Text("Repository.HistoriesOrder.ByDate"); dateOrder.Tag = "--date-order"; if (!repo.EnableTopoOrderInHistory) - dateOrder.Icon = App.CreateMenuIcon("Icons.Check"); + dateOrder.Icon = this.CreateMenuIcon("Icons.Check"); dateOrder.Click += (_, ev) => { repo.EnableTopoOrderInHistory = false; @@ -427,7 +427,7 @@ namespace SourceGit.Views topoOrder.Header = App.Text("Repository.HistoriesOrder.Topo"); topoOrder.Tag = "--topo-order"; if (repo.EnableTopoOrderInHistory) - topoOrder.Icon = App.CreateMenuIcon("Icons.Check"); + topoOrder.Icon = this.CreateMenuIcon("Icons.Check"); topoOrder.Click += (_, ev) => { repo.EnableTopoOrderInHistory = true; @@ -462,7 +462,7 @@ namespace SourceGit.Views var byNameAsc = new MenuItem(); byNameAsc.Header = App.Text("Repository.BranchSort.ByName"); if (isSortByName) - byNameAsc.Icon = App.CreateMenuIcon("Icons.Check"); + byNameAsc.Icon = this.CreateMenuIcon("Icons.Check"); byNameAsc.Click += (_, ev) => { if (!isSortByName) @@ -473,7 +473,7 @@ namespace SourceGit.Views var byCommitterDate = new MenuItem(); byCommitterDate.Header = App.Text("Repository.BranchSort.ByCommitterDate"); if (!isSortByName) - byCommitterDate.Icon = App.CreateMenuIcon("Icons.Check"); + byCommitterDate.Icon = this.CreateMenuIcon("Icons.Check"); byCommitterDate.Click += (_, ev) => { if (isSortByName) @@ -499,7 +499,7 @@ namespace SourceGit.Views var byNameAsc = new MenuItem(); byNameAsc.Header = App.Text("Repository.BranchSort.ByName"); if (isSortByName) - byNameAsc.Icon = App.CreateMenuIcon("Icons.Check"); + byNameAsc.Icon = this.CreateMenuIcon("Icons.Check"); byNameAsc.Click += (_, ev) => { if (!isSortByName) @@ -510,7 +510,7 @@ namespace SourceGit.Views var byCommitterDate = new MenuItem(); byCommitterDate.Header = App.Text("Repository.BranchSort.ByCommitterDate"); if (!isSortByName) - byCommitterDate.Icon = App.CreateMenuIcon("Icons.Check"); + byCommitterDate.Icon = this.CreateMenuIcon("Icons.Check"); byCommitterDate.Click += (_, ev) => { if (isSortByName) @@ -536,7 +536,7 @@ namespace SourceGit.Views var byCreatorDate = new MenuItem(); byCreatorDate.Header = App.Text("Repository.Tags.OrderByCreatorDate"); if (!isSortByName) - byCreatorDate.Icon = App.CreateMenuIcon("Icons.Check"); + byCreatorDate.Icon = this.CreateMenuIcon("Icons.Check"); byCreatorDate.Click += (_, ev) => { if (isSortByName) @@ -547,7 +547,7 @@ namespace SourceGit.Views var byName = new MenuItem(); byName.Header = App.Text("Repository.Tags.OrderByName"); if (isSortByName) - byName.Icon = App.CreateMenuIcon("Icons.Check"); + byName.Icon = this.CreateMenuIcon("Icons.Check"); byName.Click += (_, ev) => { if (!isSortByName) diff --git a/src/Views/RepositoryConfigure.axaml b/src/Views/RepositoryConfigure.axaml index 88d69ed4..d08052fc 100644 --- a/src/Views/RepositoryConfigure.axaml +++ b/src/Views/RepositoryConfigure.axaml @@ -44,7 +44,7 @@ - + - - - - - - - - diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs index 81d9a287..e224b1ba 100644 --- a/src/Views/RepositoryToolbar.axaml.cs +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -30,7 +30,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.Header = App.Text("Repository.Explore"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.Click += (_, e) => { Native.OS.OpenInFileManager(fullpath); @@ -39,7 +39,7 @@ namespace SourceGit.Views var terminal = new MenuItem(); terminal.Header = App.Text("Repository.Terminal"); - terminal.Icon = App.CreateMenuIcon("Icons.Terminal"); + terminal.Icon = this.CreateMenuIcon("Icons.Terminal"); terminal.Click += (_, e) => { Native.OS.OpenTerminal(fullpath); @@ -118,7 +118,7 @@ namespace SourceGit.Views var item = new MenuItem(); item.Header = App.Text("Repository.Visit", name); - item.Icon = App.CreateMenuIcon("Icons.Remotes"); + item.Icon = this.CreateMenuIcon("Icons.Remotes"); item.Click += (_, e) => { Native.OS.OpenBrowser(dupUrl); @@ -235,7 +235,7 @@ namespace SourceGit.Views { var startFeature = new MenuItem(); startFeature.Header = App.Text("GitFlow.StartFeature"); - startFeature.Icon = App.CreateMenuIcon("Icons.GitFlow.Feature"); + startFeature.Icon = this.CreateMenuIcon("Icons.GitFlow.Feature"); startFeature.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -245,7 +245,7 @@ namespace SourceGit.Views var startRelease = new MenuItem(); startRelease.Header = App.Text("GitFlow.StartRelease"); - startRelease.Icon = App.CreateMenuIcon("Icons.GitFlow.Release"); + startRelease.Icon = this.CreateMenuIcon("Icons.GitFlow.Release"); startRelease.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -255,7 +255,7 @@ namespace SourceGit.Views var startHotfix = new MenuItem(); startHotfix.Header = App.Text("GitFlow.StartHotfix"); - startHotfix.Icon = App.CreateMenuIcon("Icons.GitFlow.Hotfix"); + startHotfix.Icon = this.CreateMenuIcon("Icons.GitFlow.Hotfix"); startHotfix.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -271,11 +271,11 @@ namespace SourceGit.Views { var init = new MenuItem(); init.Header = App.Text("GitFlow.Init"); - init.Icon = App.CreateMenuIcon("Icons.Init"); + init.Icon = this.CreateMenuIcon("Icons.Init"); init.Click += (_, e) => { if (repo.CurrentBranch == null) - App.RaiseException(repo.FullPath, "Git flow init failed: No branch found!!!"); + repo.SendNotification("Git flow init failed: No branch found!!!", true); else if (repo.CanCreatePopup()) repo.ShowPopup(new ViewModels.InitGitFlow(repo)); @@ -301,7 +301,7 @@ namespace SourceGit.Views { var addPattern = new MenuItem(); addPattern.Header = App.Text("GitLFS.AddTrackPattern"); - addPattern.Icon = App.CreateMenuIcon("Icons.File.Add"); + addPattern.Icon = this.CreateMenuIcon("Icons.File.Add"); addPattern.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -314,7 +314,7 @@ namespace SourceGit.Views var fetch = new MenuItem(); fetch.Header = App.Text("GitLFS.Fetch"); - fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); + fetch.Icon = this.CreateMenuIcon("Icons.Fetch"); fetch.IsEnabled = repo.Remotes.Count > 0; fetch.Click += async (_, e) => { @@ -332,7 +332,7 @@ namespace SourceGit.Views var pull = new MenuItem(); pull.Header = App.Text("GitLFS.Pull"); - pull.Icon = App.CreateMenuIcon("Icons.Pull"); + pull.Icon = this.CreateMenuIcon("Icons.Pull"); pull.IsEnabled = repo.Remotes.Count > 0; pull.Click += async (_, e) => { @@ -350,7 +350,7 @@ namespace SourceGit.Views var push = new MenuItem(); push.Header = App.Text("GitLFS.Push"); - push.Icon = App.CreateMenuIcon("Icons.Push"); + push.Icon = this.CreateMenuIcon("Icons.Push"); push.IsEnabled = repo.Remotes.Count > 0; push.Click += async (_, e) => { @@ -368,7 +368,7 @@ namespace SourceGit.Views var prune = new MenuItem(); prune.Header = App.Text("GitLFS.Prune"); - prune.Icon = App.CreateMenuIcon("Icons.Clean"); + prune.Icon = this.CreateMenuIcon("Icons.Clean"); prune.Click += async (_, e) => { if (repo.CanCreatePopup()) @@ -381,7 +381,7 @@ namespace SourceGit.Views var locks = new MenuItem(); locks.Header = App.Text("GitLFS.Locks"); - locks.Icon = App.CreateMenuIcon("Icons.Lock"); + locks.Icon = this.CreateMenuIcon("Icons.Lock"); locks.IsEnabled = repo.Remotes.Count > 0; if (repo.Remotes.Count == 1) { @@ -414,7 +414,7 @@ namespace SourceGit.Views { var install = new MenuItem(); install.Header = App.Text("GitLFS.Install"); - install.Icon = App.CreateMenuIcon("Icons.Init"); + install.Icon = this.CreateMenuIcon("Icons.Init"); install.Click += async (_, e) => { await repo.InstallLFSAsync(); @@ -435,9 +435,9 @@ namespace SourceGit.Views repo.CanCreatePopup()) { if (repo.LocalChangesCount > 0) - App.RaiseException(repo.FullPath, "You have un-committed local changes. Please discard or stash them first."); + repo.SendNotification("You have un-committed local changes. Please discard or stash them first.", true); else if (repo.IsBisectCommandRunning || repo.BisectState != Models.BisectState.None) - App.RaiseException(repo.FullPath, "Bisect is running! Please abort it before starting a new one."); + repo.SendNotification("Bisect is running! Please abort it before starting a new one.", true); else await repo.ExecBisectCommandAsync("start"); } @@ -468,7 +468,7 @@ namespace SourceGit.Views { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { diff --git a/src/Views/ResetWithoutCheckout.axaml b/src/Views/ResetWithoutCheckout.axaml index 2e6afac3..5f3dcb8d 100644 --- a/src/Views/ResetWithoutCheckout.axaml +++ b/src/Views/ResetWithoutCheckout.axaml @@ -38,7 +38,7 @@ - + diff --git a/src/Views/RevisionCompare.axaml.cs b/src/Views/RevisionCompare.axaml.cs index 4911df14..2eece1f8 100644 --- a/src/Views/RevisionCompare.axaml.cs +++ b/src/Views/RevisionCompare.axaml.cs @@ -23,7 +23,7 @@ namespace SourceGit.Views { var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -46,7 +46,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to save as patch: {exception.Message}"); + Models.Notification.Send(null, $"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -60,7 +60,7 @@ namespace SourceGit.Views var openWithMerger = new MenuItem(); openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; openWithMerger.Click += (_, ev) => { @@ -73,7 +73,7 @@ namespace SourceGit.Views { var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(changeFullPath); explore.Click += (_, ev) => { @@ -85,7 +85,7 @@ namespace SourceGit.Views var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.LeftSideDesc); - resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.IsEnabled = vm.CanResetToLeft; resetToLeft.Click += async (_, ev) => { @@ -95,7 +95,7 @@ namespace SourceGit.Views var resetToRight = new MenuItem(); resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.RightSideDesc); - resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToRight.IsEnabled = vm.CanResetToRight; resetToRight.Click += async (_, ev) => { @@ -105,21 +105,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { - await App.CopyTextAsync(changeFullPath); + await this.CopyTextAsync(changeFullPath); ev.Handled = true; }; @@ -136,7 +136,7 @@ namespace SourceGit.Views { var resetToLeft = new MenuItem(); resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.LeftSideDesc); - resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToLeft.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToLeft.IsEnabled = vm.CanResetToLeft; resetToLeft.Click += async (_, ev) => { @@ -146,7 +146,7 @@ namespace SourceGit.Views var resetToRight = new MenuItem(); resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.RightSideDesc); - resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToRight.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToRight.IsEnabled = vm.CanResetToRight; resetToRight.Click += async (_, ev) => { @@ -156,7 +156,7 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { @@ -164,13 +164,13 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(c.Path); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { @@ -178,7 +178,7 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(vm.GetAbsPath(c.Path)); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); ev.Handled = true; }; @@ -227,7 +227,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to save as patch: {exception.Message}"); + Models.Notification.Send(null, $"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -256,7 +256,7 @@ namespace SourceGit.Views builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; } else if (e.Key == Key.F && e.KeyModifiers == cmdKey) diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs index acb2017f..eddb7b95 100644 --- a/src/Views/RevisionFileContentViewer.axaml.cs +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -110,7 +110,7 @@ namespace SourceGit.Views var copy = new MenuItem() { Header = App.Text("Copy") }; copy.Click += async (_, ev) => { - await App.CopyTextAsync(selected); + await this.CopyTextAsync(selected); ev.Handled = true; }; diff --git a/src/Views/RevisionFileTreeView.axaml.cs b/src/Views/RevisionFileTreeView.axaml.cs index 6d955591..4ac1901c 100644 --- a/src/Views/RevisionFileTreeView.axaml.cs +++ b/src/Views/RevisionFileTreeView.axaml.cs @@ -142,7 +142,7 @@ namespace SourceGit.Views if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) path = detail.GetAbsPath(path); - await App.CopyTextAsync(path); + await this.CopyTextAsync(path); e.Handled = true; } } @@ -171,7 +171,7 @@ namespace SourceGit.Views } catch (Exception ex) { - App.RaiseException(detail.Repository.FullPath, $"Failed to save file: {ex.Message}"); + detail.Repository.SendNotification($"Failed to save file: {ex.Message}", true); } e.Handled = true; @@ -459,7 +459,7 @@ namespace SourceGit.Views var fullPath = Native.OS.GetAbsPath(repo.FullPath, path); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = Directory.Exists(fullPath); explore.Click += (_, ev) => { @@ -469,7 +469,7 @@ namespace SourceGit.Views var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { App.ShowWindow(new ViewModels.DirHistories(repo, path, commit.SHA)); @@ -478,21 +478,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(path); + await this.CopyTextAsync(path); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(fullPath); + await this.CopyTextAsync(fullPath); e.Handled = true; }; @@ -513,7 +513,7 @@ namespace SourceGit.Views var openWith = new MenuItem(); openWith.Header = App.Text("Open"); - openWith.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWith.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWith.IsEnabled = file.Type == Models.ObjectType.Blob; if (openWith.IsEnabled) { @@ -552,7 +552,7 @@ namespace SourceGit.Views var saveAs = new MenuItem(); saveAs.Header = App.Text("SaveAs"); - saveAs.Icon = App.CreateMenuIcon("Icons.Save"); + saveAs.Icon = this.CreateMenuIcon("Icons.Save"); saveAs.IsEnabled = file.Type == Models.ObjectType.Blob; saveAs.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+S" : "Ctrl+Shift+S"; saveAs.Click += async (_, ev) => @@ -575,7 +575,7 @@ namespace SourceGit.Views } catch (Exception e) { - App.RaiseException(repo.FullPath, $"Failed to save file: {e.Message}"); + repo.SendNotification($"Failed to save file: {e.Message}", true); } ev.Handled = true; @@ -583,7 +583,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(fullPath) || Directory.Exists(fullPath); explore.Click += (_, ev) => { @@ -598,7 +598,7 @@ namespace SourceGit.Views var history = new MenuItem(); history.Header = App.Text("FileHistory"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, file.Path, commit.SHA)); @@ -607,7 +607,7 @@ namespace SourceGit.Views var blame = new MenuItem(); blame.Header = App.Text("Blame"); - blame.Icon = App.CreateMenuIcon("Icons.Blame"); + blame.Icon = this.CreateMenuIcon("Icons.Blame"); blame.IsEnabled = file.Type == Models.ObjectType.Blob; blame.Click += (_, ev) => { @@ -623,7 +623,7 @@ namespace SourceGit.Views { var resetToThisRevision = new MenuItem(); resetToThisRevision.Header = App.Text("ChangeCM.CheckoutThisRevision"); - resetToThisRevision.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + resetToThisRevision.Icon = this.CreateMenuIcon("Icons.File.Checkout"); resetToThisRevision.Click += async (_, ev) => { await vm.ResetToThisRevisionAsync(file.Path); @@ -637,11 +637,11 @@ namespace SourceGit.Views { var lfs = new MenuItem(); lfs.Header = App.Text("GitLFS"); - lfs.Icon = App.CreateMenuIcon("Icons.LFS"); + lfs.Icon = this.CreateMenuIcon("Icons.LFS"); var lfsLock = new MenuItem(); lfsLock.Header = App.Text("GitLFS.Locks.Lock"); - lfsLock.Icon = App.CreateMenuIcon("Icons.Lock"); + lfsLock.Icon = this.CreateMenuIcon("Icons.Lock"); if (repo.Remotes.Count == 1) { lfsLock.Click += async (_, e) => @@ -669,7 +669,7 @@ namespace SourceGit.Views var lfsUnlock = new MenuItem(); lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock"); - lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock"); + lfsUnlock.Icon = this.CreateMenuIcon("Icons.Unlock"); if (repo.Remotes.Count == 1) { lfsUnlock.Click += async (_, e) => @@ -706,13 +706,13 @@ namespace SourceGit.Views var target = new Models.CustomActionTargetFile(file.Path, vm.Commit); var custom = new MenuItem(); custom.Header = App.Text("FileCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => { @@ -729,21 +729,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { - await App.CopyTextAsync(file.Path); + await this.CopyTextAsync(file.Path); ev.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(fullPath); + await this.CopyTextAsync(fullPath); e.Handled = true; }; diff --git a/src/Views/ScanRepositories.axaml.cs b/src/Views/ScanRepositories.axaml.cs index 3f218153..10692f72 100644 --- a/src/Views/ScanRepositories.axaml.cs +++ b/src/Views/ScanRepositories.axaml.cs @@ -35,7 +35,7 @@ namespace SourceGit.Views } catch (Exception ex) { - App.RaiseException(string.Empty, $"Failed to select root scanning directory: {ex.Message}"); + Models.Notification.Send(null, $"Failed to select root scanning directory: {ex.Message}", true); } e.Handled = true; diff --git a/src/Views/SetUpstream.axaml b/src/Views/SetUpstream.axaml index ba116e2d..367dca66 100644 --- a/src/Views/SetUpstream.axaml +++ b/src/Views/SetUpstream.axaml @@ -5,6 +5,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" + xmlns:v="using:SourceGit.Views" x:Class="SourceGit.Views.SetUpstream" x:DataType="vm:SetUpstream"> @@ -32,23 +33,12 @@ HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,0" Text="{DynamicResource Text.SetUpstream.Upstream}"/> - - - - - - - - - - + { vm.Apply(stash); ev.Handled = true; }; + var branch = new MenuItem(); + branch.Header = App.Text("StashCM.Branch"); + branch.Icon = this.CreateMenuIcon("Icons.Branch.Add"); + branch.Click += (_, ev) => + { + vm.CheckoutBranch(stash); + ev.Handled = true; + }; + var drop = new MenuItem(); drop.Header = App.Text("StashCM.Drop"); - drop.Icon = App.CreateMenuIcon("Icons.Clear"); + drop.Icon = this.CreateMenuIcon("Icons.Clear"); drop.Tag = "Back/Delete"; drop.Click += (_, ev) => { @@ -71,7 +80,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("StashCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, ev) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -91,7 +100,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to save as patch: {exception.Message}"); + Models.Notification.Send(null, $"Failed to save as patch: {exception.Message}", true); } ev.Handled = true; @@ -99,16 +108,17 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("StashCM.CopyMessage"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copy.Click += async (_, ev) => { - await App.CopyTextAsync(stash.Message); + await this.CopyTextAsync(stash.Message); ev.Handled = true; }; var menu = new ContextMenu(); menu.Items.Add(apply); + menu.Items.Add(branch); menu.Items.Add(drop); menu.Items.Add(new MenuItem { Header = "-" }); menu.Items.Add(patch); @@ -143,7 +153,7 @@ namespace SourceGit.Views var openWithMerger = new MenuItem(); openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; openWithMerger.Click += (_, ev) => { @@ -153,7 +163,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(fullPath); explore.Click += (_, ev) => { @@ -168,7 +178,7 @@ namespace SourceGit.Views var applyChanges = new MenuItem(); applyChanges.Header = App.Text("StashCM.ApplyFileChanges"); - applyChanges.Icon = App.CreateMenuIcon("Icons.Diff"); + applyChanges.Icon = this.CreateMenuIcon("Icons.Diff"); applyChanges.Click += async (_, ev) => { await vm.ApplySelectedChanges(selected); @@ -177,7 +187,7 @@ namespace SourceGit.Views var checkoutFiles = new MenuItem(); checkoutFiles.Header = App.Text("ChangeCM.CheckoutThisRevision"); - checkoutFiles.Icon = App.CreateMenuIcon("Icons.File.Checkout"); + checkoutFiles.Icon = this.CreateMenuIcon("Icons.File.Checkout"); checkoutFiles.Click += async (_, ev) => { await vm.CheckoutFilesAsync(selected); @@ -186,13 +196,13 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, ev) => { if (selected.Count == 1) { - await App.CopyTextAsync(selected[0].Path); + await this.CopyTextAsync(selected[0].Path); } else { @@ -200,7 +210,7 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(c.Path); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); } ev.Handled = true; @@ -208,13 +218,13 @@ namespace SourceGit.Views var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, ev) => { if (selected.Count == 1) { - await App.CopyTextAsync(vm.GetAbsPath(selected[0].Path)); + await this.CopyTextAsync(vm.GetAbsPath(selected[0].Path)); } else { @@ -222,7 +232,7 @@ namespace SourceGit.Views foreach (var c in selected) builder.AppendLine(vm.GetAbsPath(c.Path)); - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); } ev.Handled = true; @@ -261,7 +271,7 @@ namespace SourceGit.Views builder.AppendLine(copyAbsPath ? vm.GetAbsPath(c.Path) : c.Path); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); e.Handled = true; } } diff --git a/src/Views/SubmodulesView.axaml.cs b/src/Views/SubmodulesView.axaml.cs index da7a5a0f..e45be0ca 100644 --- a/src/Views/SubmodulesView.axaml.cs +++ b/src/Views/SubmodulesView.axaml.cs @@ -183,7 +183,7 @@ namespace SourceGit.Views { var open = new MenuItem(); open.Header = App.Text("Submodule.Open"); - open.Icon = App.CreateMenuIcon("Icons.Folder.Open"); + open.Icon = this.CreateMenuIcon("Icons.Folder.Open"); open.IsEnabled = submodule.Status != Models.SubmoduleStatus.NotInited; open.Click += (_, ev) => { @@ -193,7 +193,7 @@ namespace SourceGit.Views var update = new MenuItem(); update.Header = App.Text("Submodule.Update"); - update.Icon = App.CreateMenuIcon("Icons.Loading"); + update.Icon = this.CreateMenuIcon("Icons.Loading"); update.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -203,7 +203,7 @@ namespace SourceGit.Views var move = new MenuItem(); move.Header = App.Text("Submodule.Move"); - move.Icon = App.CreateMenuIcon("Icons.MoveTo"); + move.Icon = this.CreateMenuIcon("Icons.MoveTo"); move.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -213,7 +213,7 @@ namespace SourceGit.Views var setURL = new MenuItem(); setURL.Header = App.Text("Submodule.SetURL"); - setURL.Icon = App.CreateMenuIcon("Icons.Edit"); + setURL.Icon = this.CreateMenuIcon("Icons.Edit"); setURL.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -223,7 +223,7 @@ namespace SourceGit.Views var setBranch = new MenuItem(); setBranch.Header = App.Text("Submodule.SetBranch"); - setBranch.Icon = App.CreateMenuIcon("Icons.Track"); + setBranch.Icon = this.CreateMenuIcon("Icons.Track"); setBranch.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -233,7 +233,7 @@ namespace SourceGit.Views var deinit = new MenuItem(); deinit.Header = App.Text("Submodule.Deinit"); - deinit.Icon = App.CreateMenuIcon("Icons.Undo"); + deinit.Icon = this.CreateMenuIcon("Icons.Undo"); deinit.IsEnabled = submodule.Status != Models.SubmoduleStatus.NotInited; deinit.Click += (_, ev) => { @@ -244,7 +244,7 @@ namespace SourceGit.Views var rm = new MenuItem(); rm.Header = App.Text("Submodule.Remove"); - rm.Icon = App.CreateMenuIcon("Icons.Clear"); + rm.Icon = this.CreateMenuIcon("Icons.Clear"); rm.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -254,7 +254,7 @@ namespace SourceGit.Views var histories = new MenuItem(); histories.Header = App.Text("Submodule.Histories"); - histories.Icon = App.CreateMenuIcon("Icons.Histories"); + histories.Icon = this.CreateMenuIcon("Icons.Histories"); histories.Click += (_, ev) => { App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, submodule.Path)); @@ -263,43 +263,43 @@ namespace SourceGit.Views var copySHA = new MenuItem(); copySHA.Header = App.Text("CommitDetail.Info.SHA"); - copySHA.Icon = App.CreateMenuIcon("Icons.Hash"); + copySHA.Icon = this.CreateMenuIcon("Icons.Hash"); copySHA.Click += async (_, ev) => { - await App.CopyTextAsync(submodule.SHA); + await this.CopyTextAsync(submodule.SHA); ev.Handled = true; }; var copyBranch = new MenuItem(); copyBranch.Header = App.Text("Submodule.CopyBranch"); - copyBranch.Icon = App.CreateMenuIcon("Icons.Branch"); + copyBranch.Icon = this.CreateMenuIcon("Icons.Branch"); copyBranch.Click += async (_, ev) => { - await App.CopyTextAsync(submodule.Branch); + await this.CopyTextAsync(submodule.Branch); ev.Handled = true; }; var copyRelativePath = new MenuItem(); copyRelativePath.Header = App.Text("Submodule.CopyPath"); - copyRelativePath.Icon = App.CreateMenuIcon("Icons.Folder"); + copyRelativePath.Icon = this.CreateMenuIcon("Icons.Folder"); copyRelativePath.Click += async (_, ev) => { - await App.CopyTextAsync(submodule.Path); + await this.CopyTextAsync(submodule.Path); ev.Handled = true; }; var copyURL = new MenuItem(); copyURL.Header = App.Text("Submodule.URL"); - copyURL.Icon = App.CreateMenuIcon("Icons.Link"); + copyURL.Icon = this.CreateMenuIcon("Icons.Link"); copyURL.Click += async (_, ev) => { - await App.CopyTextAsync(submodule.URL); + await this.CopyTextAsync(submodule.URL); ev.Handled = true; }; var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Items.Add(copySHA); copy.Items.Add(copyBranch); copy.Items.Add(copyRelativePath); diff --git a/src/Views/TagsView.axaml.cs b/src/Views/TagsView.axaml.cs index d27ff473..0e66a5cc 100644 --- a/src/Views/TagsView.axaml.cs +++ b/src/Views/TagsView.axaml.cs @@ -236,7 +236,7 @@ namespace SourceGit.Views var tag = selected[0]; var createBranch = new MenuItem(); - createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); + createBranch.Icon = this.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); createBranch.Click += (_, ev) => { @@ -247,7 +247,7 @@ namespace SourceGit.Views var pushTag = new MenuItem(); pushTag.Header = App.Text("TagCM.Push", tag.Name); - pushTag.Icon = App.CreateMenuIcon("Icons.Push"); + pushTag.Icon = this.CreateMenuIcon("Icons.Push"); pushTag.IsEnabled = repo.Remotes.Count > 0; pushTag.Click += (_, ev) => { @@ -258,7 +258,7 @@ namespace SourceGit.Views var deleteTag = new MenuItem(); deleteTag.Header = App.Text("TagCM.Delete", tag.Name); - deleteTag.Icon = App.CreateMenuIcon("Icons.Clear"); + deleteTag.Icon = this.CreateMenuIcon("Icons.Clear"); deleteTag.Click += (_, ev) => { if (repo.CanCreatePopup()) @@ -268,7 +268,7 @@ namespace SourceGit.Views var compareWithHead = new MenuItem(); compareWithHead.Header = App.Text("TagCM.CompareWithHead"); - compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWithHead.Icon = this.CreateMenuIcon("Icons.Compare"); compareWithHead.Click += (_, _) => { App.ShowWindow(new ViewModels.Compare(repo, tag, repo.CurrentBranch)); @@ -276,14 +276,14 @@ namespace SourceGit.Views var compareWith = new MenuItem(); compareWith.Header = App.Text("TagCM.CompareWith"); - compareWith.Icon = App.CreateMenuIcon("Icons.Compare"); + compareWith.Icon = this.CreateMenuIcon("Icons.Compare"); compareWith.Click += (_, _) => { new ViewModels.CompareCommandPalette(repo, tag).Open(); }; var archive = new MenuItem(); - archive.Icon = App.CreateMenuIcon("Icons.Archive"); + archive.Icon = this.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); archive.Click += (_, ev) => { @@ -309,13 +309,13 @@ namespace SourceGit.Views { var custom = new MenuItem(); custom.Header = App.Text("TagCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, ev) => { @@ -332,24 +332,24 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); var copyName = new MenuItem(); copyName.Header = App.Text("TagCM.Copy.Name"); - copyName.Icon = App.CreateMenuIcon("Icons.Tag"); + copyName.Icon = this.CreateMenuIcon("Icons.Tag"); copyName.Click += async (_, ev) => { - await App.CopyTextAsync(tag.Name); + await this.CopyTextAsync(tag.Name); ev.Handled = true; }; var copyMessage = new MenuItem(); copyMessage.Header = App.Text("TagCM.Copy.Message"); - copyMessage.Icon = App.CreateMenuIcon("Icons.Info"); + copyMessage.Icon = this.CreateMenuIcon("Icons.Info"); copyMessage.IsEnabled = !string.IsNullOrEmpty(tag.Message); copyMessage.Click += async (_, ev) => { - await App.CopyTextAsync(tag.Message); + await this.CopyTextAsync(tag.Message); ev.Handled = true; }; @@ -360,10 +360,10 @@ namespace SourceGit.Views { var copyCreator = new MenuItem(); copyCreator.Header = App.Text("TagCM.Copy.Tagger"); - copyCreator.Icon = App.CreateMenuIcon("Icons.User"); + copyCreator.Icon = this.CreateMenuIcon("Icons.User"); copyCreator.Click += async (_, ev) => { - await App.CopyTextAsync(tag.Creator.ToString()); + await this.CopyTextAsync(tag.Creator.ToString()); ev.Handled = true; }; copy.Items.Add(copyCreator); @@ -380,7 +380,7 @@ namespace SourceGit.Views { var compare = new MenuItem(); compare.Header = App.Text("TagCM.CompareTwo"); - compare.Icon = App.CreateMenuIcon("Icons.Compare"); + compare.Icon = this.CreateMenuIcon("Icons.Compare"); compare.Click += (_, ev) => { var (based, to) = (selected[0], selected[1]); @@ -395,7 +395,7 @@ namespace SourceGit.Views var deleteMultiple = new MenuItem(); deleteMultiple.Header = App.Text("TagCM.DeleteMultiple", selected.Count); - deleteMultiple.Icon = App.CreateMenuIcon("Icons.Clear"); + deleteMultiple.Icon = this.CreateMenuIcon("Icons.Clear"); deleteMultiple.Click += (_, ev) => { if (repo.CanCreatePopup()) diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 2f844b68..b7be2ef3 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -647,7 +647,7 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("Copy"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { await CopyWithoutIndicatorsAsync(); @@ -823,7 +823,7 @@ namespace SourceGit.Views var selection = TextArea.Selection; if (selection.IsEmpty) { - await App.CopyTextAsync(string.Empty); + await this.CopyTextAsync(string.Empty); return; } @@ -841,9 +841,9 @@ namespace SourceGit.Views if (startIdx == endIdx) { if (lines[startIdx].Type is Models.TextDiffLineType.Indicator or Models.TextDiffLineType.None) - await App.CopyTextAsync(string.Empty); + await this.CopyTextAsync(string.Empty); else - await App.CopyTextAsync(SelectedText); + await this.CopyTextAsync(SelectedText); return; } @@ -883,7 +883,7 @@ namespace SourceGit.Views builder.Append(line.Content).Append('\n'); } - await App.CopyTextAsync(builder.ToString()); + await this.CopyTextAsync(builder.ToString()); } private bool _execSizeChanged; diff --git a/src/Views/ViewLogs.axaml.cs b/src/Views/ViewLogs.axaml.cs index b88147d3..24bbf961 100644 --- a/src/Views/ViewLogs.axaml.cs +++ b/src/Views/ViewLogs.axaml.cs @@ -18,16 +18,16 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("ViewLogs.CopyLog"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Click += async (_, ev) => { - await App.CopyTextAsync(log.Content); + await this.CopyTextAsync(log.Content); ev.Handled = true; }; var rm = new MenuItem(); rm.Header = App.Text("ViewLogs.Delete"); - rm.Icon = App.CreateMenuIcon("Icons.Clear"); + rm.Icon = this.CreateMenuIcon("Icons.Clear"); rm.Click += (_, ev) => { vm.Logs.Remove(log); diff --git a/src/Views/Welcome.axaml.cs b/src/Views/Welcome.axaml.cs index 0917baeb..9328c967 100644 --- a/src/Views/Welcome.axaml.cs +++ b/src/Views/Welcome.axaml.cs @@ -105,7 +105,7 @@ namespace SourceGit.Views { var openAll = new MenuItem(); openAll.Header = App.Text("Welcome.OpenAllInNode"); - openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open"); + openAll.Icon = this.CreateMenuIcon("Icons.Folder.Open"); openAll.Click += (_, e) => { node.Open(); @@ -120,7 +120,7 @@ namespace SourceGit.Views { var open = new MenuItem(); open.Header = App.Text("Welcome.OpenOrInit"); - open.Icon = App.CreateMenuIcon("Icons.Folder.Open"); + open.Icon = this.CreateMenuIcon("Icons.Folder.Open"); open.Click += (_, e) => { node.Open(); @@ -129,7 +129,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.Header = App.Text("Repository.Explore"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.Click += (_, e) => { node.OpenInFileManager(); @@ -138,7 +138,7 @@ namespace SourceGit.Views var terminal = new MenuItem(); terminal.Header = App.Text("Repository.Terminal"); - terminal.Icon = App.CreateMenuIcon("Icons.Terminal"); + terminal.Icon = this.CreateMenuIcon("Icons.Terminal"); terminal.Click += (_, e) => { node.OpenTerminal(); @@ -155,7 +155,7 @@ namespace SourceGit.Views { var addSubFolder = new MenuItem(); addSubFolder.Header = App.Text("Welcome.AddSubFolder"); - addSubFolder.Icon = App.CreateMenuIcon("Icons.Folder.Add"); + addSubFolder.Icon = this.CreateMenuIcon("Icons.Folder.Add"); addSubFolder.Click += (_, e) => { node.AddSubFolder(); @@ -166,7 +166,7 @@ namespace SourceGit.Views var edit = new MenuItem(); edit.Header = App.Text("Welcome.Edit"); - edit.Icon = App.CreateMenuIcon("Icons.Edit"); + edit.Icon = this.CreateMenuIcon("Icons.Edit"); edit.Click += (_, e) => { node.Edit(); @@ -175,7 +175,7 @@ namespace SourceGit.Views var move = new MenuItem(); move.Header = App.Text("Welcome.Move"); - move.Icon = App.CreateMenuIcon("Icons.MoveTo"); + move.Icon = this.CreateMenuIcon("Icons.MoveTo"); move.Click += (_, e) => { node.Move(); @@ -184,7 +184,7 @@ namespace SourceGit.Views var delete = new MenuItem(); delete.Header = App.Text("Welcome.Delete"); - delete.Icon = App.CreateMenuIcon("Icons.Clear"); + delete.Icon = this.CreateMenuIcon("Icons.Clear"); delete.Click += (_, e) => { node.Delete(); diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs index 7d901e35..d949c095 100644 --- a/src/Views/WelcomeToolbar.axaml.cs +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -59,7 +59,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(string.Empty, $"Failed to open repository: {exception.Message}"); + Models.Notification.Send(null, $"Failed to open repository: {exception.Message}", true); } e.Handled = true; diff --git a/src/Views/WorkingCopy.axaml.cs b/src/Views/WorkingCopy.axaml.cs index 5cd1012c..febc0853 100644 --- a/src/Views/WorkingCopy.axaml.cs +++ b/src/Views/WorkingCopy.axaml.cs @@ -120,9 +120,9 @@ namespace SourceGit.Views { var change = vm.SelectedUnstaged[0]; if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path)); + await this.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path)); else - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); e.Handled = true; } @@ -159,9 +159,9 @@ namespace SourceGit.Views { var change = vm.SelectedStaged[0]; if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - await App.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path)); + await this.CopyTextAsync(Native.OS.GetAbsPath(vm.Repository.FullPath, change.Path)); else - await App.CopyTextAsync(change.Path); + await this.CopyTextAsync(change.Path); e.Handled = true; } @@ -282,7 +282,7 @@ namespace SourceGit.Views var diffWithMerger = new MenuItem(); diffWithMerger.Header = App.Text("OpenInExternalMergeTool"); - diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + diffWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); diffWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; diffWithMerger.Click += (_, ev) => { @@ -295,7 +295,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(path) || Directory.Exists(path); explore.Click += (_, e) => { @@ -309,7 +309,7 @@ namespace SourceGit.Views if (change.IsConflicted) { var useTheirs = new MenuItem(); - useTheirs.Icon = App.CreateMenuIcon("Icons.Incoming"); + useTheirs.Icon = this.CreateMenuIcon("Icons.Incoming"); useTheirs.Click += async (_, e) => { await vm.UseTheirsAsync(selectedUnstaged); @@ -317,7 +317,7 @@ namespace SourceGit.Views }; var useMine = new MenuItem(); - useMine.Icon = App.CreateMenuIcon("Icons.Local"); + useMine.Icon = this.CreateMenuIcon("Icons.Local"); useMine.Click += async (_, e) => { await vm.UseMineAsync(selectedUnstaged); @@ -355,7 +355,7 @@ namespace SourceGit.Views { var mergeBuiltin = new MenuItem(); mergeBuiltin.Header = App.Text("ChangeCM.Merge"); - mergeBuiltin.Icon = App.CreateMenuIcon("Icons.Conflict"); + mergeBuiltin.Icon = this.CreateMenuIcon("Icons.Conflict"); mergeBuiltin.Click += async (_, e) => { var head = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync(); @@ -365,7 +365,7 @@ namespace SourceGit.Views var mergeExternal = new MenuItem(); mergeExternal.Header = App.Text("ChangeCM.MergeExternal"); - mergeExternal.Icon = App.CreateMenuIcon("Icons.OpenWith"); + mergeExternal.Icon = this.CreateMenuIcon("Icons.OpenWith"); mergeExternal.Click += async (_, e) => { await vm.UseExternalMergeToolAsync(change); @@ -382,7 +382,7 @@ namespace SourceGit.Views { var stage = new MenuItem(); stage.Header = App.Text("FileCM.Stage"); - stage.Icon = App.CreateMenuIcon("Icons.File.Add"); + stage.Icon = this.CreateMenuIcon("Icons.File.Add"); stage.Tag = "Enter/Space"; stage.Click += async (_, e) => { @@ -392,7 +392,7 @@ namespace SourceGit.Views var discard = new MenuItem(); discard.Header = App.Text("FileCM.Discard"); - discard.Icon = App.CreateMenuIcon("Icons.Undo"); + discard.Icon = this.CreateMenuIcon("Icons.Undo"); discard.Tag = "Back/Delete"; discard.Click += (_, e) => { @@ -402,7 +402,7 @@ namespace SourceGit.Views var stash = new MenuItem(); stash.Header = App.Text("FileCM.Stash"); - stash.Icon = App.CreateMenuIcon("Icons.Stashes.Add"); + stash.Icon = this.CreateMenuIcon("Icons.Stashes.Add"); stash.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -413,7 +413,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -433,7 +433,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -441,7 +441,7 @@ namespace SourceGit.Views var assumeUnchanged = new MenuItem(); assumeUnchanged.Header = App.Text("FileCM.AssumeUnchanged"); - assumeUnchanged.Icon = App.CreateMenuIcon("Icons.File.Ignore"); + assumeUnchanged.Icon = this.CreateMenuIcon("Icons.File.Ignore"); assumeUnchanged.IsVisible = change.WorkTree != Models.ChangeState.Untracked; assumeUnchanged.Click += async (_, e) => { @@ -464,7 +464,7 @@ namespace SourceGit.Views { var addToIgnore = new MenuItem(); addToIgnore.Header = App.Text("WorkingCopy.AddToGitIgnore"); - addToIgnore.Icon = App.CreateMenuIcon("Icons.GitIgnore"); + addToIgnore.Icon = this.CreateMenuIcon("Icons.GitIgnore"); if (hasSelectedFolder) { @@ -524,7 +524,7 @@ namespace SourceGit.Views { var addToIgnore = new MenuItem(); addToIgnore.Header = App.Text("WorkingCopy.AddToGitIgnore"); - addToIgnore.Icon = App.CreateMenuIcon("Icons.GitIgnore"); + addToIgnore.Icon = this.CreateMenuIcon("Icons.GitIgnore"); var ignoreFolder = new MenuItem(); ignoreFolder.Header = App.Text("WorkingCopy.AddToGitIgnore.InFolder"); @@ -544,7 +544,7 @@ namespace SourceGit.Views { var lfs = new MenuItem(); lfs.Header = App.Text("GitLFS"); - lfs.Icon = App.CreateMenuIcon("Icons.LFS"); + lfs.Icon = this.CreateMenuIcon("Icons.LFS"); var isLFSFiltered = new Commands.IsLFSFiltered(repo.FullPath, change.Path).GetResult(); if (!isLFSFiltered) @@ -576,7 +576,7 @@ namespace SourceGit.Views var lfsLock = new MenuItem(); lfsLock.Header = App.Text("GitLFS.Locks.Lock"); - lfsLock.Icon = App.CreateMenuIcon("Icons.Lock"); + lfsLock.Icon = this.CreateMenuIcon("Icons.Lock"); lfsLock.IsEnabled = repo.Remotes.Count > 0; if (repo.Remotes.Count == 1) { @@ -605,7 +605,7 @@ namespace SourceGit.Views var lfsUnlock = new MenuItem(); lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock"); - lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock"); + lfsUnlock.Icon = this.CreateMenuIcon("Icons.Unlock"); lfsUnlock.IsEnabled = repo.Remotes.Count > 0; if (repo.Remotes.Count == 1) { @@ -644,7 +644,7 @@ namespace SourceGit.Views { var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder)); @@ -658,7 +658,7 @@ namespace SourceGit.Views { var history = new MenuItem(); history.Header = App.Text("FileHistory"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path)); @@ -667,7 +667,7 @@ namespace SourceGit.Views var blame = new MenuItem(); blame.Header = App.Text("Blame") + " (HEAD-only)"; - blame.Icon = App.CreateMenuIcon("Icons.Blame"); + blame.Icon = this.CreateMenuIcon("Icons.Blame"); blame.Click += async (_, ev) => { var commit = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync(); @@ -684,21 +684,21 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("CopyPath"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copy.Click += async (_, e) => { - await App.CopyTextAsync(hasSelectedFolder ? selectedSingleFolder : change.Path); + await this.CopyTextAsync(hasSelectedFolder ? selectedSingleFolder : change.Path); e.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(hasSelectedFolder ? Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder) : path); + await this.CopyTextAsync(hasSelectedFolder ? Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder) : path); e.Handled = true; }; @@ -721,12 +721,12 @@ namespace SourceGit.Views { if (hasNonConflicts) { - App.RaiseException(repo.FullPath, "Selection contains both conflict and non-conflict changes!"); + repo.SendNotification("Selection contains both conflict and non-conflict changes!", true); return null; } var useTheirs = new MenuItem(); - useTheirs.Icon = App.CreateMenuIcon("Icons.Incoming"); + useTheirs.Icon = this.CreateMenuIcon("Icons.Incoming"); useTheirs.Click += async (_, e) => { await vm.UseTheirsAsync(selectedUnstaged); @@ -734,7 +734,7 @@ namespace SourceGit.Views }; var useMine = new MenuItem(); - useMine.Icon = App.CreateMenuIcon("Icons.Local"); + useMine.Icon = this.CreateMenuIcon("Icons.Local"); useMine.Click += async (_, e) => { await vm.UseMineAsync(selectedUnstaged); @@ -775,7 +775,7 @@ namespace SourceGit.Views var dir = Path.Combine(repo.FullPath, selectedSingleFolder); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = Directory.Exists(dir); explore.Click += (_, e) => { @@ -788,7 +788,7 @@ namespace SourceGit.Views var stage = new MenuItem(); stage.Header = App.Text("FileCM.StageMulti", selectedUnstaged.Count); - stage.Icon = App.CreateMenuIcon("Icons.File.Add"); + stage.Icon = this.CreateMenuIcon("Icons.File.Add"); stage.Tag = "Enter/Space"; stage.Click += async (_, e) => { @@ -798,7 +798,7 @@ namespace SourceGit.Views var discard = new MenuItem(); discard.Header = App.Text("FileCM.DiscardMulti", selectedUnstaged.Count); - discard.Icon = App.CreateMenuIcon("Icons.Undo"); + discard.Icon = this.CreateMenuIcon("Icons.Undo"); discard.Tag = "Back/Delete"; discard.Click += (_, e) => { @@ -808,7 +808,7 @@ namespace SourceGit.Views var stash = new MenuItem(); stash.Header = App.Text("FileCM.StashMulti", selectedUnstaged.Count); - stash.Icon = App.CreateMenuIcon("Icons.Stashes.Add"); + stash.Icon = this.CreateMenuIcon("Icons.Stashes.Add"); stash.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -819,7 +819,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -839,7 +839,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -863,12 +863,12 @@ namespace SourceGit.Views var addToIgnore = new MenuItem(); addToIgnore.Header = App.Text("WorkingCopy.AddToGitIgnore"); - addToIgnore.Icon = App.CreateMenuIcon("Icons.GitIgnore"); + addToIgnore.Icon = this.CreateMenuIcon("Icons.GitIgnore"); addToIgnore.Items.Add(ignoreFolder); var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder)); @@ -877,21 +877,21 @@ namespace SourceGit.Views var copy = new MenuItem(); copy.Header = App.Text("CopyPath"); - copy.Icon = App.CreateMenuIcon("Icons.Copy"); + copy.Icon = this.CreateMenuIcon("Icons.Copy"); copy.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copy.Click += async (_, e) => { - await App.CopyTextAsync(selectedSingleFolder); + await this.CopyTextAsync(selectedSingleFolder); e.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder)); + await this.CopyTextAsync(Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder)); e.Handled = true; }; @@ -922,7 +922,7 @@ namespace SourceGit.Views if (services.Count > 0) { ai = new MenuItem(); - ai.Icon = App.CreateMenuIcon("Icons.AIAssist"); + ai.Icon = this.CreateMenuIcon("Icons.AIAssist"); ai.Header = App.Text("ChangeCM.GenerateCommitMessage"); if (services.Count == 1) @@ -960,7 +960,7 @@ namespace SourceGit.Views var openWithMerger = new MenuItem(); openWithMerger.Header = App.Text("OpenInExternalMergeTool"); - openWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWithMerger.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWithMerger.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+D" : "Ctrl+Shift+D"; openWithMerger.Click += (_, ev) => { @@ -971,7 +971,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.IsEnabled = File.Exists(path) || Directory.Exists(path); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.Click += (_, e) => { var target = hasSelectedFolder ? Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder) : path; @@ -981,7 +981,7 @@ namespace SourceGit.Views var unstage = new MenuItem(); unstage.Header = App.Text("FileCM.Unstage"); - unstage.Icon = App.CreateMenuIcon("Icons.File.Remove"); + unstage.Icon = this.CreateMenuIcon("Icons.File.Remove"); unstage.Tag = "Enter/Space"; unstage.Click += async (_, e) => { @@ -991,7 +991,7 @@ namespace SourceGit.Views var stash = new MenuItem(); stash.Header = App.Text("FileCM.Stash"); - stash.Icon = App.CreateMenuIcon("Icons.Stashes.Add"); + stash.Icon = this.CreateMenuIcon("Icons.Stashes.Add"); stash.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1002,7 +1002,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -1022,7 +1022,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -1041,11 +1041,11 @@ namespace SourceGit.Views { var lfs = new MenuItem(); lfs.Header = App.Text("GitLFS"); - lfs.Icon = App.CreateMenuIcon("Icons.LFS"); + lfs.Icon = this.CreateMenuIcon("Icons.LFS"); var lfsLock = new MenuItem(); lfsLock.Header = App.Text("GitLFS.Locks.Lock"); - lfsLock.Icon = App.CreateMenuIcon("Icons.Lock"); + lfsLock.Icon = this.CreateMenuIcon("Icons.Lock"); lfsLock.IsEnabled = repo.Remotes.Count > 0; if (repo.Remotes.Count == 1) { @@ -1074,7 +1074,7 @@ namespace SourceGit.Views var lfsUnlock = new MenuItem(); lfsUnlock.Header = App.Text("GitLFS.Locks.Unlock"); - lfsUnlock.Icon = App.CreateMenuIcon("Icons.Unlock"); + lfsUnlock.Icon = this.CreateMenuIcon("Icons.Unlock"); lfsUnlock.IsEnabled = repo.Remotes.Count > 0; if (repo.Remotes.Count == 1) { @@ -1115,7 +1115,7 @@ namespace SourceGit.Views { var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder)); @@ -1129,7 +1129,7 @@ namespace SourceGit.Views { var history = new MenuItem(); history.Header = App.Text("FileHistory"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path)); @@ -1138,7 +1138,7 @@ namespace SourceGit.Views var blame = new MenuItem(); blame.Header = App.Text("Blame") + " (HEAD-only)"; - blame.Icon = App.CreateMenuIcon("Icons.Blame"); + blame.Icon = this.CreateMenuIcon("Icons.Blame"); blame.Click += async (_, e) => { var commit = await new Commands.QuerySingleCommit(repo.FullPath, "HEAD").GetResultAsync(); @@ -1155,22 +1155,22 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, e) => { - await App.CopyTextAsync(hasSelectedFolder ? selectedSingleFolder : change.Path); + await this.CopyTextAsync(hasSelectedFolder ? selectedSingleFolder : change.Path); e.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { var target = hasSelectedFolder ? Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder) : path; - await App.CopyTextAsync(target); + await this.CopyTextAsync(target); e.Handled = true; }; @@ -1185,7 +1185,7 @@ namespace SourceGit.Views var explore = new MenuItem(); explore.IsEnabled = Directory.Exists(dir); explore.Header = App.Text("RevealFile"); - explore.Icon = App.CreateMenuIcon("Icons.Explore"); + explore.Icon = this.CreateMenuIcon("Icons.Explore"); explore.Click += (_, e) => { Native.OS.OpenInFileManager(dir); @@ -1198,7 +1198,7 @@ namespace SourceGit.Views var unstage = new MenuItem(); unstage.Header = App.Text("FileCM.UnstageMulti", selectedStaged.Count); - unstage.Icon = App.CreateMenuIcon("Icons.File.Remove"); + unstage.Icon = this.CreateMenuIcon("Icons.File.Remove"); unstage.Tag = "Enter/Space"; unstage.Click += async (_, e) => { @@ -1208,7 +1208,7 @@ namespace SourceGit.Views var stash = new MenuItem(); stash.Header = App.Text("FileCM.StashMulti", selectedStaged.Count); - stash.Icon = App.CreateMenuIcon("Icons.Stashes.Add"); + stash.Icon = this.CreateMenuIcon("Icons.Stashes.Add"); stash.Click += (_, e) => { if (repo.CanCreatePopup()) @@ -1219,7 +1219,7 @@ namespace SourceGit.Views var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); - patch.Icon = App.CreateMenuIcon("Icons.Save"); + patch.Icon = this.CreateMenuIcon("Icons.Save"); patch.Click += async (_, e) => { var storageProvider = TopLevel.GetTopLevel(this)?.StorageProvider; @@ -1239,7 +1239,7 @@ namespace SourceGit.Views } catch (Exception exception) { - App.RaiseException(repo.FullPath, $"Failed to save as patch: {exception.Message}"); + repo.SendNotification($"Failed to save as patch: {exception.Message}", true); } e.Handled = true; @@ -1259,7 +1259,7 @@ namespace SourceGit.Views { var history = new MenuItem(); history.Header = App.Text("DirHistories"); - history.Icon = App.CreateMenuIcon("Icons.Histories"); + history.Icon = this.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { App.ShowWindow(new ViewModels.DirHistories(repo, selectedSingleFolder)); @@ -1268,21 +1268,21 @@ namespace SourceGit.Views var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); - copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyPath.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C"; copyPath.Click += async (_, e) => { - await App.CopyTextAsync(selectedSingleFolder); + await this.CopyTextAsync(selectedSingleFolder); e.Handled = true; }; var copyFullPath = new MenuItem(); copyFullPath.Header = App.Text("CopyFullPath"); - copyFullPath.Icon = App.CreateMenuIcon("Icons.Copy"); + copyFullPath.Icon = this.CreateMenuIcon("Icons.Copy"); copyFullPath.Tag = OperatingSystem.IsMacOS() ? "⌘+⇧+C" : "Ctrl+Shift+C"; copyFullPath.Click += async (_, e) => { - await App.CopyTextAsync(Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder)); + await this.CopyTextAsync(Native.OS.GetAbsPath(repo.FullPath, selectedSingleFolder)); e.Handled = true; }; @@ -1301,7 +1301,7 @@ namespace SourceGit.Views { var openWith = new MenuItem(); openWith.Header = App.Text("Open"); - openWith.Icon = App.CreateMenuIcon("Icons.OpenWith"); + openWith.Icon = this.CreateMenuIcon("Icons.OpenWith"); openWith.IsEnabled = File.Exists(fullpath); if (openWith.IsEnabled) { @@ -1349,13 +1349,13 @@ namespace SourceGit.Views var target = new Models.CustomActionTargetFile(path, null); var custom = new MenuItem(); custom.Header = App.Text("FileCM.CustomAction"); - custom.Icon = App.CreateMenuIcon("Icons.Action"); + custom.Icon = this.CreateMenuIcon("Icons.Action"); foreach (var action in actions) { var (dup, label) = action; var item = new MenuItem(); - item.Icon = App.CreateMenuIcon("Icons.Action"); + item.Icon = this.CreateMenuIcon("Icons.Action"); item.Header = label; item.Click += async (_, e) => {