[gnome-todo] Add a dark / light theme switcher



commit ba6d0315f2b0ce8fe4655f2e14f2bad4c602a791
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri Jul 2 14:07:19 2021 -0300

    Add a dark / light theme switcher
    
    https://gitlab.gnome.org/GNOME/gnome-todo/-/issues/397

 data/org.gnome.todo.gschema.xml      |   9 ++
 src/gui/gtd-theme-selector.c         | 214 +++++++++++++++++++++++++++++++++++
 src/gui/gtd-theme-selector.h         |  37 ++++++
 src/gui/gtd-theme-selector.ui        |  42 +++++++
 src/gui/gtd-window.c                 |   6 +
 src/gui/gui.gresource.xml            |   1 +
 src/gui/menus.ui                     |   6 +
 src/meson.build                      |   1 +
 src/themes/Adwaita-themeselector.css |  25 ++++
 src/themes/Adwaita.css               |   1 +
 src/todo.gresource.xml               |   1 +
 11 files changed, 343 insertions(+)
---
diff --git a/data/org.gnome.todo.gschema.xml b/data/org.gnome.todo.gschema.xml
index 1daa7410..52119669 100644
--- a/data/org.gnome.todo.gschema.xml
+++ b/data/org.gnome.todo.gschema.xml
@@ -31,5 +31,14 @@
             <summary>Sidebar revealed</summary>
             <description>Whether the sidebar is revealed or not</description>
         </key>
+        <key name="style-variant" type="s">
+          <choices>
+            <choice value="light"/>
+            <choice value="dark"/>
+          </choices>
+          <default>'light'</default>
+          <summary>Style Variant</summary>
+          <description>Use the light or dark variant of the GTK theme and/or GtkSourceView style 
scheme.</description>
+        </key>
     </schema>
 </schemalist>
diff --git a/src/gui/gtd-theme-selector.c b/src/gui/gtd-theme-selector.c
new file mode 100644
index 00000000..dd95df4c
--- /dev/null
+++ b/src/gui/gtd-theme-selector.c
@@ -0,0 +1,214 @@
+/* gtd-theme-selector.c
+ *
+ * Copyright 2021 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "gtd-manager.h"
+#include "gtd-theme-selector.h"
+
+struct _GtdThemeSelector
+{
+  GtkWidget           parent;
+
+  GtkToggleButton    *dark;
+  GtkToggleButton    *light;
+
+  gchar              *theme;
+};
+
+G_DEFINE_TYPE (GtdThemeSelector, gtd_theme_selector, GTK_TYPE_WIDGET)
+
+enum
+{
+  PROP_0,
+  PROP_THEME,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/*
+ * Auxiliary methods
+ */
+
+static gboolean
+style_variant_to_boolean (GValue   *value,
+                          GVariant *variant,
+                          gpointer  user_data)
+{
+  gboolean is_dark = g_strcmp0 (g_variant_get_string (variant, NULL), "dark") == 0;
+
+  g_value_set_boolean (value, is_dark);
+
+  return TRUE;
+}
+
+static void
+setup_action (GtdThemeSelector *self)
+{
+  g_autoptr (GSimpleActionGroup) group = NULL;
+  g_autoptr (GAction) action = NULL;
+  GtkSettings *gtk_settings;
+  GtdManager *manager;
+  GSettings *settings;
+  gboolean is_dark;
+
+  manager = gtd_manager_get_default ();
+  settings = gtd_manager_get_settings (manager);
+
+  self->theme = g_settings_get_string (settings, "style-variant");
+  is_dark = g_strcmp0 (self->theme, "dark") == 0;
+  gtk_settings = gtk_settings_get_default ();
+  g_object_set (gtk_settings,
+                "gtk-application-prefer-dark-theme", is_dark,
+                NULL);
+
+  group = g_simple_action_group_new ();
+  action = g_settings_create_action (settings, "style-variant");
+  g_action_map_add_action (G_ACTION_MAP (group), action);
+
+  gtk_widget_insert_action_group (GTK_WIDGET (self),
+                                  "settings",
+                                  G_ACTION_GROUP (group));
+
+  g_settings_bind_with_mapping (settings,
+                                "style-variant",
+                                gtk_settings,
+                                "gtk-application-prefer-dark-theme",
+                                G_SETTINGS_BIND_GET,
+                                style_variant_to_boolean,
+                                NULL, NULL, NULL);
+}
+
+/*
+ * GObject overrides
+ */
+static void
+gtd_theme_selector_dispose (GObject *object)
+{
+  GtdThemeSelector *self = (GtdThemeSelector *)object;
+
+  g_clear_pointer (&self->theme, g_free);
+
+  G_OBJECT_CLASS (gtd_theme_selector_parent_class)->dispose (object);
+}
+
+static void
+gtd_theme_selector_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GtdThemeSelector *self = GTD_THEME_SELECTOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_THEME:
+      g_value_set_string (value, gtd_theme_selector_get_theme (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_theme_selector_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GtdThemeSelector *self = GTD_THEME_SELECTOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_THEME:
+      gtd_theme_selector_set_theme (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_theme_selector_class_init (GtdThemeSelectorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = gtd_theme_selector_dispose;
+  object_class->get_property = gtd_theme_selector_get_property;
+  object_class->set_property = gtd_theme_selector_set_property;
+
+  properties [PROP_THEME] =
+    g_param_spec_string ("theme",
+                         "Theme",
+                         "Theme",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_install_property_action (widget_class, "theme.mode", "theme");
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/gtd-theme-selector.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GtdThemeSelector, dark);
+  gtk_widget_class_bind_template_child (widget_class, GtdThemeSelector, light);
+
+  gtk_widget_class_set_css_name (widget_class, "themeselector");
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static void
+gtd_theme_selector_init (GtdThemeSelector *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  setup_action (self);
+}
+
+GtkWidget *
+gtd_theme_selector_new (void)
+{
+  return g_object_new (GTD_TYPE_THEME_SELECTOR, NULL);
+}
+
+const gchar *
+gtd_theme_selector_get_theme (GtdThemeSelector *self)
+{
+  g_return_val_if_fail (GTD_IS_THEME_SELECTOR (self), NULL);
+
+  return self->theme;
+}
+
+void
+gtd_theme_selector_set_theme (GtdThemeSelector *self,
+                              const gchar      *theme)
+{
+  g_return_if_fail (GTD_IS_THEME_SELECTOR (self));
+
+  if (g_strcmp0 (theme, self->theme) == 0)
+    return;
+
+  g_clear_pointer (&self->theme, g_free);
+  self->theme = g_strdup (theme);
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_THEME]);
+}
diff --git a/src/gui/gtd-theme-selector.h b/src/gui/gtd-theme-selector.h
new file mode 100644
index 00000000..0a731d6f
--- /dev/null
+++ b/src/gui/gtd-theme-selector.h
@@ -0,0 +1,37 @@
+/* gtd-theme-selector.h
+ *
+ * Copyright 2021 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_THEME_SELECTOR (gtd_theme_selector_get_type())
+G_DECLARE_FINAL_TYPE (GtdThemeSelector, gtd_theme_selector, GTD, THEME_SELECTOR, GtkWidget)
+
+GtkWidget *          gtd_theme_selector_new                      (void);
+
+const gchar *        gtd_theme_selector_get_theme                (GtdThemeSelector   *self);
+
+void                 gtd_theme_selector_set_theme                (GtdThemeSelector   *self,
+                                                                  const gchar        *theme);
+
+G_END_DECLS
diff --git a/src/gui/gtd-theme-selector.ui b/src/gui/gtd-theme-selector.ui
new file mode 100644
index 00000000..364a2463
--- /dev/null
+++ b/src/gui/gtd-theme-selector.ui
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtdThemeSelector" parent="GtkWidget">
+
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="halign">center</property>
+        <property name="orientation">horizontal</property>
+        <property name="margin-start">24</property>
+        <property name="margin-end">24</property>
+        <property name="spacing">18</property>
+
+        <child>
+          <object class="GtkCheckButton" id="light">
+            <style>
+              <class name="light"/>
+            </style>
+            <property name="focus-on-click">false</property>
+            <property name="action-name">settings.style-variant</property>
+            <property name="action-target">'light'</property>
+            <property name="tooltip-text" translatable="yes">Light style</property>
+          </object>
+        </child>
+
+        <child>
+          <object class="GtkCheckButton" id="dark">
+            <style>
+              <class name="dark"/>
+            </style>
+            <property name="group">light</property>
+            <property name="focus-on-click">false</property>
+            <property name="action-name">settings.style-variant</property>
+            <property name="action-target">'dark'</property>
+            <property name="tooltip-text" translatable="yes">Dark style</property>
+          </object>
+        </child>
+
+      </object>
+    </child>
+
+  </template>
+</interface>
diff --git a/src/gui/gtd-window.c b/src/gui/gtd-window.c
index 96f337a6..cd9dcff5 100644
--- a/src/gui/gtd-window.c
+++ b/src/gui/gtd-window.c
@@ -35,6 +35,7 @@
 #include "gtd-panel.h"
 #include "gtd-task.h"
 #include "gtd-task-list.h"
+#include "gtd-theme-selector.h"
 #include "gtd-window.h"
 #include "gtd-workspace.h"
 
@@ -577,6 +578,7 @@ static void
 gtd_window_init (GtdWindow *self)
 {
   GtkApplication *application;
+  GtkPopover *popover;
   GMenu *primary_menu;
 
   static const GActionEntry entries[] = {
@@ -604,6 +606,10 @@ gtd_window_init (GtdWindow *self)
   primary_menu = gtk_application_get_menu_by_id (application, "primary-menu");
   gtk_menu_button_set_menu_model (self->primary_menu_button, G_MENU_MODEL (primary_menu));
 
+  popover = gtk_menu_button_get_popover (self->primary_menu_button);
+  gtk_popover_menu_add_child (GTK_POPOVER_MENU (popover),
+                              gtd_theme_selector_new (),
+                              "theme");
   /* Development build */
   if (is_development_build ())
     setup_development_build (self);
diff --git a/src/gui/gui.gresource.xml b/src/gui/gui.gresource.xml
index bb7161f7..60ddb00a 100644
--- a/src/gui/gui.gresource.xml
+++ b/src/gui/gui.gresource.xml
@@ -13,6 +13,7 @@
     <file compressed="true" preprocess="xml-stripblanks">gtd-task-list-popover.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">gtd-task-list-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">gtd-task-row.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">gtd-theme-selector.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">gtd-window.ui</file>
 
     <!-- Assets -->
diff --git a/src/gui/menus.ui b/src/gui/menus.ui
index f90d756b..18e57ee0 100644
--- a/src/gui/menus.ui
+++ b/src/gui/menus.ui
@@ -2,6 +2,12 @@
 <interface>
   <menu id="primary-menu">
 
+    <section>
+      <item>
+        <attribute name="custom">theme</attribute>
+      </item>
+    </section>
+
     <section>
       <item>
         <attribute name="label" translatable="yes">_About To Do</attribute>
diff --git a/src/meson.build b/src/meson.build
index 9ab95215..ebafc5fd 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -152,6 +152,7 @@ sources += files(
   'gui/gtd-omni-area.c',
   'gui/gtd-omni-area-addin.c',
   'gui/gtd-theme-manager.c',
+  'gui/gtd-theme-selector.c',
   'gui/gtd-widget.c',
   'gui/gtd-window.c',
   'models/gtd-list-model-filter.c',
diff --git a/src/themes/Adwaita-themeselector.css b/src/themes/Adwaita-themeselector.css
new file mode 100644
index 00000000..a7676b6e
--- /dev/null
+++ b/src/themes/Adwaita-themeselector.css
@@ -0,0 +1,25 @@
+themeselector checkbutton radio {
+  -gtk-icon-source: none;
+  background: none;
+  box-shadow: none;
+  padding: 12px;
+  min-height: 24px;
+  min-width: 24px;
+  border-width: 3px;
+  border-style: solid;
+  border-color: transparent;
+}
+
+themeselector checkbutton radio:checked {
+  border-color: @theme_selected_bg_color;
+  -gtk-icon-source: -gtk-scaled(-gtk-icontheme("object-select-symbolic"));
+}
+
+themeselector checkbutton.light radio {
+  color: #2e3436;
+  background-color: #fff;
+}
+themeselector checkbutton.dark radio {
+  color: #fff;
+  background-color: #2e3436;
+}
diff --git a/src/themes/Adwaita.css b/src/themes/Adwaita.css
index c2d8c904..5552c06e 100644
--- a/src/themes/Adwaita.css
+++ b/src/themes/Adwaita.css
@@ -1,6 +1,7 @@
 @import url("resource:///org/gnome/todo/themes/Adwaita-omniarea.css");
 @import url("resource:///org/gnome/todo/themes/Adwaita-tasklistview.css");
 @import url("resource:///org/gnome/todo/themes/Adwaita-taskrow.css");
+@import url("resource:///org/gnome/todo/themes/Adwaita-themeselector.css");
 @import url("resource:///org/gnome/todo/themes/Adwaita-widgets.css");
 
 .transparent {
diff --git a/src/todo.gresource.xml b/src/todo.gresource.xml
index a754744c..98085beb 100644
--- a/src/todo.gresource.xml
+++ b/src/todo.gresource.xml
@@ -6,6 +6,7 @@
     <file compressed="true">themes/Adwaita-omniarea.css</file>
     <file compressed="true">themes/Adwaita-tasklistview.css</file>
     <file compressed="true">themes/Adwaita-taskrow.css</file>
+    <file compressed="true">themes/Adwaita-themeselector.css</file>
     <file compressed="true">themes/Adwaita-widgets.css</file>
   </gresource>
 </gresources>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]