diff --git a/src/App.axaml.cs b/src/App.axaml.cs index a22f764d6..ffd307945 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,9 +1,9 @@ using System; using System.IO; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; - using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -14,12 +14,14 @@ using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Threading; +using SourceGit.Models; namespace SourceGit { public partial class App : Application { #region App Entry Point + [STAThread] public static void Main(string[] args) { @@ -56,10 +58,7 @@ public static AppBuilder BuildAvaloniaApp() builder.UsePlatformDetect(); builder.LogToTrace(); builder.WithInterFont(); - builder.With(new FontManagerOptions() - { - DefaultFamilyName = "fonts:Inter#Inter" - }); + builder.With(new FontManagerOptions() { DefaultFamilyName = "fonts:Inter#Inter" }); builder.ConfigureFonts(manager => { var monospace = new EmbeddedFontCollection( @@ -71,9 +70,11 @@ public static AppBuilder BuildAvaloniaApp() Native.OS.SetupApp(builder); return builder; } + #endregion #region Utility Functions + public static async Task AskConfirmAsync(string message, Models.ConfirmButtonType buttonType = Models.ConfirmButtonType.OkCancel) { if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner }) @@ -117,17 +118,21 @@ app.Resources[finalLocaleKey] is not ResourceDictionary targetLocale || app._activeLocale = targetLocale; } - public static void SetTheme(string theme, string themeOverridesFile) + public static void SetTheme(ThemeOverrides theme) { if (Current is not App app) return; - if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase)) + string themeOverridesFile = string.Empty; + + if (theme.Name.Equals("Light", StringComparison.OrdinalIgnoreCase)) app.RequestedThemeVariant = ThemeVariant.Light; - else if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase)) + else if (theme.Name.Equals("Dark", StringComparison.OrdinalIgnoreCase)) app.RequestedThemeVariant = ThemeVariant.Dark; - else + else if (theme.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)) app.RequestedThemeVariant = ThemeVariant.Default; + else + themeOverridesFile = theme.FilePath; if (app._themeOverrides != null) { @@ -234,16 +239,18 @@ public static void Quit(int exitCode) else Environment.Exit(exitCode); } + #endregion #region Overrides + public override void Initialize() { AvaloniaXamlLoader.Load(this); var pref = ViewModels.Preferences.Instance; SetLocale(pref.Locale); - SetTheme(pref.Theme, pref.ThemeOverrides); + SetTheme(pref.Themes.FirstOrDefault(m => m.Name == pref.Theme) ?? new ThemeOverrides("Default")); SetFonts(pref.DefaultFontFamily, pref.MonospaceFontFamily); } @@ -276,9 +283,11 @@ public override void OnFrameworkInitializationCompleted() TryLaunchAsNormal(desktop); } } + #endregion #region Launch Ways + private static bool TryLaunchAsRebaseTodoEditor(string[] args, out int exitCode) { exitCode = -1; @@ -362,17 +371,11 @@ private bool TryLaunchAsFileHistoryViewer(IClassicDesktopStyleApplicationLifetim var relativePath = Path.GetRelativePath(repo, fullPath).Replace('\\', '/'); if (File.Exists(fullPath)) { - desktop.MainWindow = new Views.FileHistories() - { - DataContext = new ViewModels.FileHistories(repo, relativePath) - }; + desktop.MainWindow = new Views.FileHistories() { DataContext = new ViewModels.FileHistories(repo, relativePath) }; } else if (Directory.Exists(fullPath)) { - desktop.MainWindow = new Views.DirHistories() - { - DataContext = new ViewModels.DirHistories(repo, relativePath.TrimEnd('/')) - }; + desktop.MainWindow = new Views.DirHistories() { DataContext = new ViewModels.DirHistories(repo, relativePath.TrimEnd('/')) }; } else { @@ -410,10 +413,7 @@ private bool TryLaunchAsBlameViewer(IClassicDesktopStyleApplicationLifetime desk } var relFile = Path.GetRelativePath(repo, file); - var viewer = new Views.Blame() - { - DataContext = new ViewModels.Blame(repo, relFile, head) - }; + var viewer = new Views.Blame() { DataContext = new ViewModels.Blame(repo, relFile, head) }; desktop.MainWindow = viewer; return true; } @@ -532,9 +532,11 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop) Check4Update(); #endif } + #endregion #region Check for Updates + private void Check4Update(bool manually = false) { if (_launcher != null) @@ -595,6 +597,7 @@ private void ShowSelfUpdateResult(object data) // Ignore exceptions. } } + #endregion private Models.IpcChannel _ipcChannel = null; diff --git a/src/Models/ThemeOverrides.cs b/src/Models/ThemeOverrides.cs index c80576dc8..f48b38ca7 100644 --- a/src/Models/ThemeOverrides.cs +++ b/src/Models/ThemeOverrides.cs @@ -5,9 +5,23 @@ namespace SourceGit.Models { public class ThemeOverrides { + public ThemeOverrides() + { + } + + public ThemeOverrides(string name) + { + Name = name; + } + + public string Version { get; set; } + public string Name { get; set; } + public string Author { get; set; } + public string Url { get; set; } public Dictionary BasicColors { get; set; } = new Dictionary(); public double GraphPenThickness { get; set; } = 2; public double OpacityForNotMergedCommits { get; set; } = 0.5; public List GraphColors { get; set; } = new List(); + public string FilePath { get; set; } } } diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index 25bbb431f..155a602d3 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; +using SourceGit.Models; namespace SourceGit.ViewModels { @@ -41,26 +43,33 @@ public string Locale } } - public string Theme + [JsonIgnore] + public List Themes { - get => _theme; + get => _themes; set { - if (SetProperty(ref _theme, value) && !_isLoading) - App.SetTheme(_theme, _themeOverrides); + SetProperty(ref _themes, value); } } - public string ThemeOverrides + [JsonIgnore] + public ThemeOverrides SelectedTheme { - get => _themeOverrides; + get + { + return Themes.FirstOrDefault(m => m.Name == Theme) ?? new ThemeOverrides("Default"); + } set { - if (SetProperty(ref _themeOverrides, value) && !_isLoading) - App.SetTheme(_theme, value); + if (SetProperty(ref _selectedTheme, value) && !_isLoading) + App.SetTheme(_selectedTheme); + Theme = _selectedTheme.Name; } } + public string Theme { get; set; } + public string DefaultFontFamily { get => _defaultFontFamily; @@ -649,6 +658,25 @@ public void Save() private static Preferences Load() { + try + { + var themesPath = Path.Combine(Native.OS.DataDir, "themes"); + var themeFiles = Directory.GetFiles(themesPath, "theme.json", SearchOption.AllDirectories); + foreach (var file in themeFiles) + { + using var stream = File.OpenRead(file); + var theme = JsonSerializer.Deserialize(stream, JsonCodeGen.Default.ThemeOverrides); + if (theme == null) + continue; + theme.FilePath = file; + _themes.Add(theme); + } + } + catch + { + // ignored + } + var path = Path.Combine(Native.OS.DataDir, "preference.json"); if (!File.Exists(path)) return new Preferences(); @@ -791,8 +819,8 @@ private bool RemoveInvalidRepositoriesRecursive(List collection) private bool _isLoading = true; private bool _isReadonly = true; private string _locale = "en_US"; - private string _theme = "Default"; - private string _themeOverrides = string.Empty; + private ThemeOverrides _selectedTheme = new("Default"); + private static List _themes = new() { new ThemeOverrides("Default"), new ThemeOverrides("Dark"), new ThemeOverrides("Light") }; private string _defaultFontFamily = string.Empty; private string _monospaceFontFamily = string.Empty; private double _defaultFontSize = 13; diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index 407ba4a04..3e677959c 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -193,19 +193,31 @@ Text="{DynamicResource Text.Preferences.Appearance.Theme}" HorizontalAlignment="Right" Margin="0,0,16,0"/> - - - Default - Dark - Light - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - -