[dconf-editor] Add night mode.



commit 0069cd4679a8450e026de7113c60aa93e4fac131
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Mon Oct 1 15:19:26 2018 +0200

    Add night mode.

 editor/ca.desrt.dconf-editor.gschema.xml |   7 +
 editor/dconf-editor.vala                 |  30 ++++-
 editor/dconf-window.vala                 |  51 ++++++-
 editor/meson.build                       |   1 +
 editor/night-light-monitor.vala          | 222 +++++++++++++++++++++++++++++++
 5 files changed, 309 insertions(+), 2 deletions(-)
---
diff --git a/editor/ca.desrt.dconf-editor.gschema.xml b/editor/ca.desrt.dconf-editor.gschema.xml
index fbfe9ac..1f719ea 100644
--- a/editor/ca.desrt.dconf-editor.gschema.xml
+++ b/editor/ca.desrt.dconf-editor.gschema.xml
@@ -191,6 +191,13 @@
       <description>When a folder only contains one subfolder and no keys, Dconf Editor can jump to that 
subfolder directly. This flag enables that behaviour.</description>
     </key>
   </schema>
+  <schema id="ca.desrt.dconf-editor.NightLight">
+    <key name="automatic-night-mode" type="b">
+      <default>false</default>
+      <summary>Follow system night light</summary>
+      <description>Use GNOME night light setting to activate night-mode.</description>
+    </key>
+  </schema>
   <schema id="ca.desrt.dconf-editor.Bookmarks">
     <key name="bookmarks" type="as">
       <default>[]</default>
diff --git a/editor/dconf-editor.vala b/editor/dconf-editor.vala
index bca00bd..8df0916 100644
--- a/editor/dconf-editor.vala
+++ b/editor/dconf-editor.vala
@@ -20,6 +20,9 @@ private class ConfigurationEditor : Gtk.Application
     internal static string [,] internal_mappings = {
             {"ca.desrt.dconf-editor.Bookmarks",
                 "/ca/desrt/dconf-editor/"},
+            {"ca.desrt.dconf-editor.NightLight",
+                "/ca/desrt/dconf-editor/"},
+
             {"ca.desrt.dconf-editor.Demo.EmptyRelocatable",
                 "/ca/desrt/dconf-editor/Demo/EmptyRelocatable/"}
         };
@@ -149,6 +152,7 @@ private class ConfigurationEditor : Gtk.Application
     private const GLib.ActionEntry [] action_entries =
     {
         // generic
+        { "set-use-night-mode", set_use_night_mode, "b" },
         { "copy", copy_cb, "s" },   // TODO is that really the good way to do things? (see Taquin)
         { "about", about_cb },
 
@@ -320,6 +324,8 @@ private class ConfigurationEditor : Gtk.Application
         set_accels_for_action ("kbd.toggle-boolean",    { "<Primary>Return",
                                                           "<Primary>KP_Enter"   });
 
+        init_night_mode ();
+
         Gtk.CssProvider css_provider = new Gtk.CssProvider ();
         css_provider.load_from_resource ("/ca/desrt/dconf-editor/ui/dconf-editor.css");
         Gdk.Screen? screen = Gdk.Screen.get_default ();
@@ -345,6 +351,23 @@ private class ConfigurationEditor : Gtk.Application
             info (_("Looks like the DConf settings backend is used, all looks good."));
     } */
 
+    /*\
+    * * Night mode
+    \*/
+
+    NightLightMonitor night_light_monitor;  // keep it here or it is unrefed
+
+    private void init_night_mode ()
+    {
+        night_light_monitor = new NightLightMonitor ("/ca/desrt/dconf-editor/");
+    }
+
+    private void set_use_night_mode (SimpleAction action, Variant? gvariant)
+        requires (gvariant != null)
+    {
+        night_light_monitor.set_use_night_mode (((!) gvariant).get_boolean ());
+    }
+
     /*\
     * * Window activation
     \*/
@@ -480,8 +503,13 @@ private class ConfigurationEditor : Gtk.Application
 
     private Gtk.Window get_new_window (string? schema, string? path, string? key_name)
     {
-        DConfWindow window = new DConfWindow (disable_warning, schema, path, key_name);
+        DConfWindow window = new DConfWindow (disable_warning, schema, path, key_name, 
NightLightMonitor.NightTime.should_use_dark_theme (night_light_monitor.night_time), 
night_light_monitor.dark_theme, night_light_monitor.automatic_night_mode);
         add_window (window);
+
+        night_light_monitor.notify ["night-time"].connect (window.night_time_changed);
+        night_light_monitor.notify ["dark-theme"].connect (window.dark_theme_changed);
+        night_light_monitor.notify ["automatic-night-mode"].connect (window.automatic_night_mode_changed);
+
         return (Gtk.Window) window;
     }
 
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index c429878..3dfc853 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -120,8 +120,10 @@ private class DConfWindow : ApplicationWindow
     private ulong use_shortpaths_changed_handler = 0;
     private ulong behaviour_changed_handler = 0;
 
-    internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name)
+    internal DConfWindow (bool disable_warning, string? schema, string? path, string? key_name, bool 
_night_time, bool _dark_theme, bool _automatic_night_mode)
     {
+        init_night_mode (_night_time, _dark_theme, _automatic_night_mode);
+
         install_ui_action_entries ();
         install_kbd_action_entries ();
 
@@ -357,6 +359,52 @@ private class DConfWindow : ApplicationWindow
             get_style_context ().remove_class ("hc-theme");
     }
 
+    /*\
+    * * Night mode
+    \*/
+
+    private bool night_time = false;    // no need to use NightTime here (that allows an "Unknown" value)
+    private bool dark_theme = false;
+    private bool automatic_night_mode = false;
+
+    private void init_night_mode (bool _night_time, bool _dark_theme, bool _automatic_night_mode)
+    {
+        night_time = _night_time;
+        dark_theme = _dark_theme;
+        automatic_night_mode = _automatic_night_mode;
+    }
+
+    internal void night_time_changed (Object nlm, ParamSpec thing)
+    {
+        night_time = NightLightMonitor.NightTime.should_use_dark_theme (((NightLightMonitor) 
nlm).night_time);
+        update_hamburger_menu ();
+    }
+
+    internal void dark_theme_changed (Object nlm, ParamSpec thing)
+    {
+        dark_theme = ((NightLightMonitor) nlm).dark_theme;
+        update_hamburger_menu ();
+    }
+
+    internal void automatic_night_mode_changed (Object nlm, ParamSpec thing)
+    {
+        automatic_night_mode = ((NightLightMonitor) nlm).automatic_night_mode;
+        // update menu not needed
+    }
+
+    private void append_or_not_night_mode_entry (ref GLib.Menu section)
+    {
+        if (!night_time)
+            return;
+
+        if (dark_theme)
+            section.append (_("Pause night mode"), "app.set-use-night-mode(false)");
+        else if (automatic_night_mode)
+            section.append (_("Reuse night mode"), "app.set-use-night-mode(true)");
+        else
+            section.append (_("Use night mode"), "app.set-use-night-mode(true)");
+    }
+
     /*\
     * * Window management callbacks
     \*/
@@ -1062,6 +1110,7 @@ private class DConfWindow : ApplicationWindow
         }
 
         section = new GLib.Menu ();
+        append_or_not_night_mode_entry (ref section);
         section.append (_("Keyboard Shortcuts"), "win.show-help-overlay");
         section.append (_("About Dconf Editor"), "app.about");   // TODO move as "win."
         section.freeze ();
diff --git a/editor/meson.build b/editor/meson.build
index 2a8a404..47c45c8 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -79,6 +79,7 @@ sources = files(
   'model-utils.vala',
   'modifications-handler.vala',
   'modifications-revealer.vala',
+  'night-light-monitor.vala',
   'pathbar.vala',
   'pathentry.vala',
   'pathwidget.vala',
diff --git a/editor/night-light-monitor.vala b/editor/night-light-monitor.vala
new file mode 100644
index 0000000..565e113
--- /dev/null
+++ b/editor/night-light-monitor.vala
@@ -0,0 +1,222 @@
+/*
+  This file is part of Dconf Editor
+
+  Dconf Editor 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.
+
+  Dconf Editor 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 Dconf Editor.  If not, see <https://www.gnu.org/licenses/>.
+*/
+/*
+  This code has been inspired by the one for a similar function in Calendar
+  https://gitlab.gnome.org/GNOME/gnome-calendar commit 474ce8b9da4a3322f2fe
+  Copyright 2018 Georges Basile Stavracas Neto <georges stavracas gmail com>
+*/
+
+private class NightLightMonitor : Object
+{
+    // warnings callable during init
+    private const string warning_init_connection =
+    "Impossible to get connection for session bus, night-light mode disabled.";
+    private const string warning_init_dbus_proxy =
+    "Impossible to get dbus proxy for session bus, night-light mode disabled.";
+    private const string warning_cached_property =
+    "Impossible to get cached property from proxy, night-light mode disabled.";
+
+    // warnings callable when setting mode
+    private const string warning_check_your_logs =
+    "Something went wrong during the night mode support init, doing nothing.";
+    private const string warning_get_gtksettings =
+    "Something went wrong getting GtkSettings default object, doing nothing.";
+
+    // schema static things
+    private const string schema_name = "ca.desrt.dconf-editor.NightLight";  // TODO put in a library
+    private const string automatic_night_mode_key = "automatic-night-mode";
+
+    /*\
+    * * Public interface
+    \*/
+
+    public string schema_path { private get; internal construct; }
+
+    public enum NightTime {
+        UNKNOWN,
+        NIGHT,
+        DAY;
+
+        internal static bool should_use_dark_theme (NightTime state)
+        {
+            return state == NightTime.NIGHT;
+        }
+    }
+    public NightTime  night_time            { internal get; private construct set; default = 
NightTime.UNKNOWN; }
+    public bool       dark_theme            { internal get; private construct set; default = false; }
+    public bool       automatic_night_mode  { internal get; private construct set; default = false; }
+
+    internal NightLightMonitor (string _schema_path)
+    {
+        Object (schema_path: _schema_path);
+        if (night_time == NightTime.UNKNOWN)    // disables mode if night_time has not been set at first try 
   // TODO specific warning?
+            return;
+        connect_properties ();
+    }
+
+    internal void set_use_night_mode (bool night_mode_state)
+    {
+        if (night_time == NightTime.UNKNOWN)    // do nothing but warn
+            warning (warning_check_your_logs);
+        else
+        {
+            paused = !night_mode_state;
+            update ();
+        }
+    }
+
+    /*\
+    * * Init proxy
+    \*/
+
+    private GLib.Settings settings;
+    private DBusProxy proxy;
+
+    construct
+    {
+        settings = new GLib.Settings.with_path (schema_name, schema_path);
+        automatic_night_mode = settings.get_boolean (automatic_night_mode_key);
+        paused = !automatic_night_mode;
+
+        DBusConnection? connection;
+        init_connection (out connection);
+        if (connection == null)
+            return;
+
+        DBusProxy? nullable_proxy;
+        init_proxy ((!) connection, out nullable_proxy);
+        if (nullable_proxy == null)
+            return;
+
+        proxy = (!) nullable_proxy;
+        night_time = get_updated_night_time (ref proxy, automatic_night_mode);
+    }
+
+    private static void init_connection (out DBusConnection? connection)
+    {
+        try
+        {
+            connection = Bus.get_sync (BusType.SESSION, null);
+        }
+        catch (Error e)
+        {
+            warning (warning_init_connection);
+            warning (e.message);
+            connection = null;
+        }
+    }
+
+    private static void init_proxy (DBusConnection connection, out DBusProxy? proxy)
+    {
+        try
+        {
+            proxy = new DBusProxy.sync (connection,
+                                        DBusProxyFlags.GET_INVALIDATED_PROPERTIES,
+                                        null,
+                                        "org.gnome.SettingsDaemon.Color",
+                                        "/org/gnome/SettingsDaemon/Color",
+                                        "org.gnome.SettingsDaemon.Color",
+                                        null);
+        }
+        catch (Error e)
+        {
+            warning (warning_init_dbus_proxy);
+            warning (e.message);
+            proxy = null;
+        }
+    }
+
+    private static NightTime get_updated_night_time (ref DBusProxy proxy, bool automatic_night_mode)
+    {
+        Variant? variant_active = proxy.get_cached_property ("NightLightActive");
+        Variant? variant_paused = proxy.get_cached_property ("DisabledUntilTomorrow");
+        if (variant_active == null)
+        {
+            warning (warning_cached_property);
+            return NightTime.UNKNOWN;
+        }
+
+        bool night_time_is_night = ((!) variant_active).get_boolean ();
+        if (variant_paused != null)
+            night_time_is_night = night_time_is_night && !(((!) variant_paused).get_boolean () && 
automatic_night_mode);
+
+        if (night_time_is_night)
+            return NightTime.NIGHT;
+        else
+            return NightTime.DAY;
+    }
+
+    /*\
+    * * Private methods
+    \*/
+
+    private bool paused = false;
+
+    private void connect_properties ()
+    {
+        proxy.g_properties_changed.connect (() => {
+                night_time = get_updated_night_time (ref proxy, automatic_night_mode);
+                if (night_time != NightTime.NIGHT)
+                    paused = !automatic_night_mode;
+                update ();
+            });
+        settings.changed [automatic_night_mode_key].connect ((_settings, _key_name) => {
+                automatic_night_mode = _settings.get_boolean (_key_name);
+                paused = !automatic_night_mode;
+                update ();
+            });
+        update ();
+    }
+
+    private void update ()
+    {
+        if (automatic_night_mode)
+        {
+            if (paused)
+                set_dark_theme_real (false);
+            else
+                set_dark_theme_real (NightTime.should_use_dark_theme (night_time));
+        }
+        else if (night_time == NightTime.NIGHT)
+        {
+            if (paused)
+                set_dark_theme_real (false);
+            else
+                set_dark_theme_real (true);
+        }
+        else
+            set_dark_theme_real (false);
+    }
+    private void set_dark_theme_real (bool night_mode)
+    {
+        if (_set_dark_theme_real (night_mode))
+            dark_theme = night_mode;
+    }
+    private static bool _set_dark_theme_real (bool night_mode)
+    {
+        Gtk.Settings? gtk_settings = Gtk.Settings.get_default ();
+        if (gtk_settings == null)
+        {
+            warning (warning_get_gtksettings);
+            return false;
+        }
+
+        if (night_mode != ((!) gtk_settings).gtk_application_prefer_dark_theme)
+            ((!) gtk_settings).@set ("gtk-application-prefer-dark-theme", night_mode);
+        return true;
+    }
+}


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