From 34c078787764b53ec718ee759558cb938a8939cf Mon Sep 17 00:00:00 2001 From: Cameron White Date: Sun, 17 Dec 2023 14:29:43 -0500 Subject: [PATCH] Support switching between light and dark themes (#613) * Support switching between light and dark themes This uses AdwStyleManager's facilities for specifying whether the light or dark variant of the system theme should be used. If the system doesn't indicate a preference, the default is to use dark. * Save the color scheme preference to the settings. --- CHANGELOG.md | 1 + Pinta.Core/Actions/ViewActions.cs | 13 ++++++++ .../Actions/View/ColorSchemeChangedAction.cs | 33 +++++++++++++++++++ Pinta/DialogHandlers.cs | 1 + Pinta/Main.cs | 3 -- Pinta/MainWindow.cs | 6 +++- 6 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 Pinta/Actions/View/ColorSchemeChangedAction.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index c685d4d534..34c043b2c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Thanks to the following contributors who worked on this release: ### Added - Ported to GTK4 and libadwaita - Due to API changes in GTK4, the File -> New Screenshot option now invokes platform-specific tools (the XDG screenshot portal on Linux, and the screenshot tool on maCOS). This is currently unsupported on Windows + - Added a preference (in the `View` menu) for switching between a dark or light color scheme. - Upgraded to .NET 8 - Building against .NET 7 is still supported. When building from the tarball, .NET 7 will be used if .NET 8 is unavailable. - Restored support for add-ins, which had been disabled in Pinta 2.0 due to technical limitations diff --git a/Pinta.Core/Actions/ViewActions.cs b/Pinta.Core/Actions/ViewActions.cs index cb0962b6df..ab701625e3 100644 --- a/Pinta.Core/Actions/ViewActions.cs +++ b/Pinta.Core/Actions/ViewActions.cs @@ -45,6 +45,7 @@ public sealed class ViewActions public ToggleCommand ToolBox { get; } public ToggleCommand Rulers { get; } public Gio.SimpleAction RulerMetric { get; } + public Gio.SimpleAction ColorScheme { get; } public Command Fullscreen { get; } public ToolBarComboBox ZoomComboBox { get; } @@ -75,6 +76,7 @@ public ViewActions () ToolBox = new ToggleCommand ("ToolBox", Translations.GetString ("Tool Box"), null, null); Rulers = new ToggleCommand ("Rulers", Translations.GetString ("Rulers"), null, Resources.Icons.ViewRulers); RulerMetric = Gio.SimpleAction.NewStateful ("rulermetric", GtkExtensions.IntVariantType, GLib.Variant.NewInt32 (0)); + ColorScheme = Gio.SimpleAction.NewStateful ("colorscheme", GtkExtensions.IntVariantType, GLib.Variant.NewInt32 (0)); Fullscreen = new Command ("Fullscreen", Translations.GetString ("Fullscreen"), null, Resources.StandardIcons.DocumentNew); ZoomCollection = default_zoom_levels; @@ -169,6 +171,17 @@ public void RegisterActions (Gtk.Application app, Gio.Menu menu) app.AddAction (ImageTabs); show_hide_menu.AppendItem (ImageTabs.CreateMenuItem ()); + + var color_scheme_section = Gio.Menu.New (); + menu.AppendSection (null, color_scheme_section); + + var color_scheme_menu = Gio.Menu.New (); + color_scheme_section.AppendSubmenu (Translations.GetString ("Color Scheme"), color_scheme_menu); + + app.AddAction (ColorScheme); + color_scheme_menu.Append (Translations.GetString ("Default"), $"app.{ColorScheme.Name}(0)"); + color_scheme_menu.Append (Translations.GetString ("Light"), $"app.{ColorScheme.Name}(1)"); + color_scheme_menu.Append (Translations.GetString ("Dark"), $"app.{ColorScheme.Name}(2)"); } public void CreateStatusBar (Box statusbar) diff --git a/Pinta/Actions/View/ColorSchemeChangedAction.cs b/Pinta/Actions/View/ColorSchemeChangedAction.cs new file mode 100644 index 0000000000..644582a433 --- /dev/null +++ b/Pinta/Actions/View/ColorSchemeChangedAction.cs @@ -0,0 +1,33 @@ +using Gio; +using Pinta.Core; + +namespace Pinta.Actions; + +internal sealed class ColorSchemeChangedAction : IActionHandler +{ + #region IActionHandler Members + public void Initialize () + { + PintaCore.Actions.View.ColorScheme.OnActivate += Activated; + } + + public void Uninitialize () + { + PintaCore.Actions.View.ColorScheme.OnActivate -= Activated; + } + #endregion + + private void Activated (SimpleAction action, SimpleAction.ActivateSignalArgs args) + { + action.ChangeState (args.Parameter!); + + Adw.ColorScheme scheme = args.Parameter!.GetInt32 () switch { + 1 => Adw.ColorScheme.ForceLight, + 2 => Adw.ColorScheme.ForceDark, + _ => Adw.ColorScheme.PreferDark, // Use dark unless the system prefers light + }; + + Adw.StyleManager.GetDefault ().SetColorScheme (scheme); + } +} + diff --git a/Pinta/DialogHandlers.cs b/Pinta/DialogHandlers.cs index 66f04450d3..f1f1933070 100644 --- a/Pinta/DialogHandlers.cs +++ b/Pinta/DialogHandlers.cs @@ -72,6 +72,7 @@ public ActionHandlers () new ImageTabsToggledAction (), new StatusBarToggledAction (), new ToolBoxToggledAction (), + new ColorSchemeChangedAction (), // Window new CloseAllDocumentsAction (), diff --git a/Pinta/Main.cs b/Pinta/Main.cs index 1911401843..9424f33d71 100644 --- a/Pinta/Main.cs +++ b/Pinta/Main.cs @@ -76,9 +76,6 @@ private static void OpenMainWindow (int threads, IEnumerable files) { GLib.UnhandledException.SetHandler (OnUnhandledException); - // For testing a dark variant of the theme. - //Gtk.Settings.Default.SetProperty("gtk-application-prefer-dark-theme", new GLib.Value(true)); - Gsk.Module.Initialize (); Pango.Module.Initialize (); PangoCairo.Module.Initialize (); diff --git a/Pinta/MainWindow.cs b/Pinta/MainWindow.cs index 6614dceed6..c880b03700 100644 --- a/Pinta/MainWindow.cs +++ b/Pinta/MainWindow.cs @@ -217,7 +217,7 @@ private bool HandleGlobalKeyPress (Gtk.EventControllerKey controller, Gtk.EventC var canvas_window = ((PintaCanvas) PintaCore.Workspace.ActiveWorkspace.Canvas).CanvasWindow; if ((canvas_window.Canvas.HasFocus || canvas_window.IsMouseOnCanvas) && - canvas_window.Canvas.DoKeyPressEvent (controller, args)) { + canvas_window.Canvas.DoKeyPressEvent (controller, args)) { return true; } } @@ -458,6 +458,9 @@ private void LoadUserSettings () var ruler_metric = (MetricType) PintaCore.Settings.GetSetting ("ruler-metric", (int) MetricType.Pixels); PintaCore.Actions.View.RulerMetric.Activate (GLib.Variant.NewInt32 ((int) ruler_metric)); + + int color_scheme = PintaCore.Settings.GetSetting ("color-scheme", 0); + PintaCore.Actions.View.ColorScheme.Activate (GLib.Variant.NewInt32 (color_scheme)); } private void SaveUserSettings () @@ -471,6 +474,7 @@ private void SaveUserSettings () } PintaCore.Settings.PutSetting ("ruler-metric", (int) GetCurrentRulerMetric ()); + PintaCore.Settings.PutSetting ("color-scheme", PintaCore.Actions.View.ColorScheme.GetState ()!.GetInt32 ()); PintaCore.Settings.PutSetting ("window-maximized", window_shell.Window.IsMaximized ()); PintaCore.Settings.PutSetting ("ruler-shown", PintaCore.Actions.View.Rulers.Value); PintaCore.Settings.PutSetting ("image-tabs-shown", PintaCore.Actions.View.ImageTabs.Value);