diff --git a/src/Models/ExternalTerminal.cs b/src/Models/ExternalTerminal.cs index e0a95670..56304a36 100644 --- a/src/Models/ExternalTerminal.cs +++ b/src/Models/ExternalTerminal.cs @@ -5,14 +5,14 @@ using System.IO; namespace SourceGit.Models { - public class ExternalTerminal + public record ExternalTerminal { - public string Name { get; set; } = string.Empty; - public string Icon { get; set; } = string.Empty; - public string Executable { get; set; } = string.Empty; - public string OpenCmdArgs { get; set; } = string.Empty; + public string Name { get; init; } = string.Empty; + public string Icon { get; init; } = string.Empty; + public string Executable { get; init; } = string.Empty; + public string OpenCmdArgs { get; init; } = string.Empty; - public void Open(string repo) + public virtual void Open(string repo) { Process.Start(new ProcessStartInfo() { @@ -34,31 +34,33 @@ namespace SourceGit.Models public void WindowsGitBash(Func platform_finder) { - TryAdd("Git Bash", "git-bash.png", "bash", "\"{0}\"", platform_finder); + TryAdd("Git Bash", "git-bash.png", "bash", "", platform_finder); } public void Gnome(Func platform_finder) { - TryAdd("gnome-terminal", "gnome.png", "/usr/bin/gnome-terminal", "--working-directory=\"{0}\"", platform_finder); + TryAdd("gnome-terminal", "gnome.png", "gnome", "--working-directory=\"{0}\"", platform_finder); } public void Konsole(Func platform_finder) { - TryAdd("gnome-terminal", "konsole.png", "/usr/bin/konsole", "--workdir \"{0}\"", platform_finder); + TryAdd("konsole", "konsole.png", "konsole", "--workdir \"{0}\"", platform_finder); } - public void osaScript(Func platform_finder) + public void AppleScript(ExternalTerminal terminal) { - TryAdd("AppleScript", "osascript.png", "/usr/bin/osascript", - """ - on run argv - tell application "Terminal" - do script "cd '{0}'" - activate - end tell - end run - """, - platform_finder); + var path = terminal.Executable; + + if (string.IsNullOrEmpty(path) || !File.Exists(path)) + { + return; + } + + Terminals.Add(terminal with + { + Name = "AppleScript", + Icon = "osascript.png", + }); } public void PowerShell(Func platform_finder) @@ -73,7 +75,7 @@ namespace SourceGit.Models public void Xfce4(Func platform_finder) { - TryAdd("gnome-terminal", "xfce4.png", "/usr/bin/xfce4-terminal", "--working-directory=\"{0}\"", platform_finder); + TryAdd("xfce4", "xfce4.png", "xfce4", "--working-directory=\"{0}\"", platform_finder); } private void TryAdd(string name, string icon, string cmd, string args, Func finder) diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index d94797dc..9b5b23de 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -31,7 +31,7 @@ namespace SourceGit.Native public List FindExternalTerminals() { var finder = new Models.ExternalTerminalFinder(); - finder.osaScript(() => "/usr/bin/osascript"); + finder.AppleScript(new AppleScriptTerminal()); return finder.Terminals; } @@ -64,25 +64,39 @@ namespace SourceGit.Native public void OpenTerminal(string workdir) { - var dir = string.IsNullOrEmpty(workdir) ? "~" : workdir; - var builder = new StringBuilder(); - builder.AppendLine("on run argv"); - builder.AppendLine(" tell application \"Terminal\""); - builder.AppendLine($" do script \"cd '{dir}'\""); - builder.AppendLine(" activate"); - builder.AppendLine(" end tell"); - builder.AppendLine("end run"); - - var tmp = Path.GetTempFileName(); - File.WriteAllText(tmp, builder.ToString()); - - var proc = Process.Start("/usr/bin/osascript", $"\"{tmp}\""); - proc.Exited += (o, e) => File.Delete(tmp); + new AppleScriptTerminal().Open(workdir); } public void OpenWithDefaultEditor(string file) { Process.Start("open", file); } + + private sealed record AppleScriptTerminal : Models.ExternalTerminal + { + public AppleScriptTerminal() + { + Executable = "/usr/bin/osascript"; + OpenCmdArgs = ""; + } + + public override void Open(string repo) + { + var dir = string.IsNullOrEmpty(repo) ? "~" : repo; + var tmp = Path.GetTempFileName(); + File.WriteAllText(tmp, + $""" + on run argv + tell application "Terminal" + do script "cd '{dir}'" + activate + end tell + end run + """); + + var proc = Process.Start("/usr/bin/osascript", $"\"{tmp}\""); + proc.Exited += (o, e) => File.Delete(tmp); + } + } } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 05acadd9..63b668d2 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -346,8 +346,10 @@ namespace SourceGit.ViewModels foreach (var terminal in terminals) { var dupTerminal = terminal; - var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalTerminalIcons/{dupTerminal.Icon}", UriKind.RelativeOrAbsolute)); - var item = new ExternalMenuItem(App.Text("Repository.OpenIn", dupTerminal.Name), new Bitmap(icon), () => dupTerminal.Open(_fullpath)); + var item = new ExternalMenuItem( + App.Text("Repository.OpenIn", dupTerminal.Name), + $"ExternalTerminalIcons/{dupTerminal.Icon}", + () => dupTerminal.Open(_fullpath)); items.Add(item); } @@ -370,8 +372,10 @@ namespace SourceGit.ViewModels foreach (var editor in editors) { var dupEditor = editor; - var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{dupEditor.Icon}", UriKind.RelativeOrAbsolute)); - var item = new ExternalMenuItem(App.Text("Repository.OpenIn", dupEditor.Name), new Bitmap(icon), () => dupEditor.Open(_fullpath)); + var item = new ExternalMenuItem( + App.Text("Repository.OpenIn", dupEditor.Name), + $"ExternalToolIcons/{dupEditor.Icon}", + () => dupEditor.Open(_fullpath)); items.Add(item); } @@ -1424,14 +1428,14 @@ namespace SourceGit.ViewModels public ExternalMenuItem(string header) { Header = header; - Icon = null; + IconKey = null; Command = null; } - public ExternalMenuItem(string header, Bitmap icon, Action click) + public ExternalMenuItem(string header, string iconKey, Action click) { Header = header; - Icon = icon; + IconKey = iconKey; Command = new RelayCommand(click); } @@ -1443,7 +1447,7 @@ namespace SourceGit.ViewModels /// /// The resource key of the icon. /// - public Bitmap? Icon { get; init; } + public string? IconKey { get; init; } /// /// The command when the user click the menu item. @@ -1453,6 +1457,6 @@ namespace SourceGit.ViewModels /// /// if the menu item is enabled; otherwise, . /// - public bool IsEnabled { get; init; } + public bool IsEnabled { get; init; } = true; } } diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 2978bf6c..50b41e47 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -11,12 +11,23 @@ x:DataType="vm:Repository"> - - - - + + + + + + + + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index a6d1585a..c186cd3d 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -1,11 +1,15 @@ using System; +using System.Globalization; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Data.Converters; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Media.Imaging; +using Avalonia.Platform; namespace SourceGit.Views { @@ -301,4 +305,26 @@ namespace SourceGit.Views } } } + + public sealed class ExternalIconKeyToImageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is string iconKey && !string.IsNullOrWhiteSpace(iconKey)) + { + var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/{iconKey}", UriKind.RelativeOrAbsolute)); + return new Image + { + Width = 16, Height = 16, Source = new Bitmap(icon), + }; + } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } }