[gnome-shell/T27795: 44/138] watermark: introduce a new Watermark feature in GNOME Shell



commit 3706ef9360406c77f3f363c72d5800ad29fbfaa8
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sun Jun 4 01:51:31 2017 -0300

    watermark: introduce a new Watermark feature in GNOME Shell
    
    This is not a simple copy-paste of the Watermark extension,
    but a fully-integrated watermark manager with verious settings
    to tweak the UI.
    
    Also, for backwards compabilitiy, we need to make sure that if we
    use a watermark specified by the image builder conf file, we treat
    it as always visible (regardless of the gsetting).
    
    https://phabricator.endlessm.com/T15839
    https://phabricator.endlessm.com/T18935

 data/org.gnome.shell.gschema.xml.in |  53 ++++++++
 js/js-resources.gresource.xml       |   1 +
 js/misc/config.js.in                |   2 +
 js/misc/meson.build                 |   1 +
 js/ui/main.js                       |   6 +
 js/ui/watermark.js                  | 257 ++++++++++++++++++++++++++++++++++++
 meson.build                         |   1 +
 7 files changed, 321 insertions(+)
---
diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in
index 79c0c2c784..829fe3f5ee 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -1,4 +1,15 @@
 <schemalist>
+  <!-- Keep Endless-specific enumerations at the top of the file -->
+
+  <enum id="org.gnome.shell.watermark.Position">
+    <value nick="center" value="0"/>
+    <value nick="bottom-left" value="1"/>
+    <value nick="bottom-center" value="2"/>
+    <value nick="bottom-right" value="3"/>
+  </enum>
+
+  <!-- End of Endless-specific enumerations -->
+
   <schema id="org.gnome.shell" path="/org/gnome/shell/"
           gettext-domain="@GETTEXT_PACKAGE@">
     <key name="development-tools" type="b">
@@ -453,4 +464,46 @@
       </description>
     </key>
   </schema>
+
+  <!-- Endless-specific schemas beyond this point -->
+
+  <schema id="org.gnome.shell.watermark" path="/org/gnome/shell/watermark/"
+         gettext-domain="@GETTEXT_PACKAGE@">
+    <key type="s" name="watermark-file">
+      <default>''</default>
+      <summary>Watermark file</summary>
+      <description>The full watermark file path</description>
+    </key>
+    <key name="watermark-position" enum="org.gnome.shell.watermark.Position">
+      <default>'bottom-right'</default>
+      <summary>Watermark position</summary>
+      <description>
+        The position of the watermark; valid values are 'center',
+        'bottom-left', 'bottom-center' and 'bottom-right'
+      </description>
+    </key>
+    <key type="d" name="watermark-size">
+      <range min="5.0" max="30.0"/>
+      <default>9.0</default>
+      <summary>Watermark size</summary>
+      <description>The watermark size in percent relative to the screen width</description>
+    </key>
+    <key type="u" name="watermark-border">
+      <range min="0" max="100"/>
+      <default>40</default>
+      <summary>Watermark border</summary>
+      <description>The watermark around the logo</description>
+    </key>
+    <key type="u" name="watermark-opacity">
+      <range min="0" max="255"/>
+      <default>255</default>
+      <summary>Watermark opacity</summary>
+      <description>The watermark of the logo</description>
+    </key>
+    <key type="b" name="watermark-always-visible">
+      <default>false</default>
+      <summary>Watermark always visible</summary>
+      <description>Show the watermark even on a non default background</description>
+    </key>
+  </schema>
 </schemalist>
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 48cb070d01..160791a8b6 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -150,5 +150,6 @@
     <file>ui/sideComponent.js</file>
     <file>ui/status/orientation.js</file>
     <file>ui/userMenu.js</file>
+    <file>ui/watermark.js</file>
   </gresource>
 </gresources>
diff --git a/js/misc/config.js.in b/js/misc/config.js.in
index 065d7a0a2a..d7f16e608f 100644
--- a/js/misc/config.js.in
+++ b/js/misc/config.js.in
@@ -18,3 +18,5 @@ var PKGDATADIR = '@datadir@/@PACKAGE_NAME@';
 var VPNDIR = '@vpndir@';
 /* g-i package versions */
 var LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@'
+/* used for the watermark feature */
+var LOCALSTATEDIR = '@localstatedir@';
diff --git a/js/misc/meson.build b/js/misc/meson.build
index 5a4871762a..980f1a5495 100644
--- a/js/misc/meson.build
+++ b/js/misc/meson.build
@@ -7,6 +7,7 @@ jsconf.set10('HAVE_BLUETOOTH', bt_dep.found())
 jsconf.set10('HAVE_NETWORKMANAGER', have_networkmanager)
 jsconf.set('datadir', datadir)
 jsconf.set('libexecdir', libexecdir)
+jsconf.set('localstatedir', localstatedir)
 jsconf.set('vpndir', vpndir)
 
 config_js = configure_file(
diff --git a/js/ui/main.js b/js/ui/main.js
index 0ca07052fa..f1652b43fc 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -47,6 +47,7 @@ const XdndHandler = imports.ui.xdndHandler;
 const KbdA11yDialog = imports.ui.kbdA11yDialog;
 const LocatePointer = imports.ui.locatePointer;
 const PointerA11yTimeout = imports.ui.pointerA11yTimeout;
+const Watermark = imports.ui.watermark;
 
 const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
 const STICKY_KEYS_ENABLE = 'stickykeys-enable';
@@ -95,6 +96,7 @@ let _a11ySettings = null;
 let _themeResource = null;
 let _oskResource = null;
 let _desktopAppClient = null;
+let _watermarkManager = null;
 
 function _sessionUpdated() {
     if (sessionMode.isPrimary)
@@ -273,6 +275,10 @@ function _initializeUI() {
             Scripting.runPerfScript(module, perfOutput);
         }
     });
+
+    /* Initialize watermarks */
+    _watermarkManager = new Watermark.WatermarkManager();
+    _watermarkManager.init();
 }
 
 function _getStylesheet(name) {
diff --git a/js/ui/watermark.js b/js/ui/watermark.js
new file mode 100644
index 0000000000..d81ea88b62
--- /dev/null
+++ b/js/ui/watermark.js
@@ -0,0 +1,257 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const { Clutter, Gio, GLib, St } = imports.gi;
+
+const Config = imports.misc.config;
+const Background = imports.ui.background;
+const Main = imports.ui.main;
+const Monitor = imports.ui.monitor;
+
+const WATERMARK_SCHEMA = 'org.gnome.shell.watermark';
+const WATERMARK_CUSTOM_BRANDING_FILE = Config.LOCALSTATEDIR + 
'/lib/eos-image-defaults/branding/gnome-shell.conf';
+
+var Watermark = class {
+    constructor(bgManager) {
+        this._bgManager = bgManager;
+
+        this._watermarkFile = null;
+        this._forceWatermarkVisible = false;
+
+        this._settings = new Gio.Settings({ schema_id: WATERMARK_SCHEMA });
+
+        this._settings.connect('changed::watermark-file',
+                               this._updateWatermark.bind(this));
+        this._settings.connect('changed::watermark-size',
+                               this._updateScale.bind(this));
+        this._settings.connect('changed::watermark-position',
+                               this._updatePosition.bind(this));
+        this._settings.connect('changed::watermark-border',
+                               this._updateBorder.bind(this));
+        this._settings.connect('changed::watermark-always-visible',
+                               this._updateVisibility.bind(this));
+
+        this._textureCache = St.TextureCache.get_default();
+        this._textureCache.connect('texture-file-changed', (cache, file) => {
+                if (!this._watermarkFile || !this._watermarkFile.equal(file))
+                    return;
+
+                this._updateWatermarkTexture();
+            });
+
+        this.actor = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            opacity: 0,
+        });
+        bgManager._container.add_actor(this.actor);
+
+        this.actor.connect('destroy', this._onDestroy.bind(this));
+
+        let monitorIndex = bgManager._monitorIndex;
+        let constraint = new Monitor.MonitorConstraint({
+            index: monitorIndex,
+            work_area: true,
+        });
+        this.actor.add_constraint(constraint);
+
+        this._bin = new St.Widget({ x_expand: true, y_expand: true });
+        this.actor.add_actor(this._bin);
+
+        this._settings.bind('watermark-opacity', this._bin, 'opacity',
+                            Gio.SettingsBindFlags.DEFAULT);
+
+        this._updateWatermark();
+        this._updatePosition();
+        this._updateBorder();
+
+        this._bgDestroyedId =
+            bgManager.backgroundActor.connect('destroy',
+                                              this._backgroundDestroyed.bind(this));
+
+        this._bgChangedId =
+            bgManager.connect('changed', this._updateVisibility.bind(this));
+
+        this._updateVisibility();
+    }
+
+    _loadBrandingFile() {
+        try {
+            let keyfile = new GLib.KeyFile();
+            keyfile.load_from_file(WATERMARK_CUSTOM_BRANDING_FILE, GLib.KeyFileFlags.NONE);
+            return keyfile.get_string('Watermark', 'logo');
+        } catch(e) {
+            return null;
+        }
+    }
+
+    _updateWatermark() {
+        let filename = this._settings.get_string('watermark-file');
+        let brandingFile = this._loadBrandingFile();
+
+        // If there's no GSettings file, but there is a custom file, use
+        // the custom file instead and make sure it is visible
+        if (!filename && brandingFile) {
+            filename = brandingFile;
+            this._forceWatermarkVisible = true;
+        } else {
+            this._forceWatermarkVisible = false;
+        }
+
+        let file = Gio.File.new_for_commandline_arg(filename);
+        if (this._watermarkFile && this._watermarkFile.equal(file))
+            return;
+
+        this._watermarkFile = file;
+
+        this._updateWatermarkTexture();
+    }
+
+    _updateWatermarkTexture() {
+        if (this._icon)
+            this._icon.destroy();
+
+        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+
+        this._icon = this._textureCache.load_file_async(
+            this._watermarkFile,
+            -1, -1,
+            scaleFactor,
+            this.actor.resource_scale);
+        this._icon.connect('allocation-changed', this._updateScale.bind(this));
+        this._bin.add_actor(this._icon);
+    }
+
+    _updateScale() {
+        if (this._icon.width == 0)
+            return;
+
+        let size = this._settings.get_double('watermark-size');
+        let width = this.actor.width * size / 100;
+        let height = this._icon.height * width / this._icon.width;
+        if (Math.abs(this._icon.height - height) < 1.0 &&
+            Math.abs(this._icon.width - width) < 1.0) {
+            // size of icon would not significantly change, so don't
+            // update the size to avoid recursion in case the
+            // manually set size differs just minimal from the eventually
+            // allocated size
+            return;
+        }
+        this._icon.set_size(width, height);
+    }
+
+    _updatePosition() {
+        let xAlign, yAlign;
+        switch (this._settings.get_string('watermark-position')) {
+            case 'center':
+                xAlign = Clutter.ActorAlign.CENTER;
+                yAlign = Clutter.ActorAlign.CENTER;
+                break;
+            case 'bottom-left':
+                xAlign = Clutter.ActorAlign.START;
+                yAlign = Clutter.ActorAlign.END;
+                break;
+            case 'bottom-center':
+                xAlign = Clutter.ActorAlign.CENTER;
+                yAlign = Clutter.ActorAlign.END;
+                break;
+            case 'bottom-right':
+                xAlign = Clutter.ActorAlign.END;
+                yAlign = Clutter.ActorAlign.END;
+                break;
+        }
+        this._bin.x_align = xAlign;
+        this._bin.y_align = yAlign;
+    }
+
+    _updateBorder() {
+        let border = this._settings.get_uint('watermark-border');
+        this.actor.style = 'padding: %dpx;'.format(border);
+    }
+
+    _updateVisibility() {
+        let background = this._bgManager.backgroundActor.background._delegate;
+        let defaultUri = background._settings.get_default_value('picture-uri');
+        let file = Gio.File.new_for_commandline_arg(defaultUri.deep_unpack());
+
+        let visible;
+        if (this._forceWatermarkVisible ||
+            this._settings.get_boolean('watermark-always-visible'))
+            visible = true;
+        else if (background._file)
+            visible = background._file.equal(file);
+        else // background == NONE
+            visible = false;
+
+        this.actor.ease({
+            opacity: visible ? 255 : 0,
+            duration: Background.FADE_ANIMATION_TIME,
+            mode: Clutter.Animation.EASE_OUT_QUAD,
+        });
+    }
+
+    _backgroundDestroyed() {
+        this._bgDestroyedId = 0;
+
+        if (this._bgManager._backgroundSource) // background swapped
+            this._bgDestroyedId =
+                this._bgManager.backgroundActor.connect('destroy',
+                                                        this._backgroundDestroyed.bind(this));
+        else // bgManager destroyed
+            this.actor.destroy();
+
+    }
+
+    _onDestroy() {
+        this._settings.run_dispose();
+        this._settings = null;
+
+        if (this._bgManager) {
+            if (this._bgDestroyedId)
+                this._bgManager.backgroundActor.disconnect(this._bgDestroyedId);
+
+            if (this._bgChangedId)
+                this._bgManager.disconnect(this._bgChangedId);
+        }
+
+        this._bgDestroyedId = 0;
+        this._bgChangedId = 0;
+
+        this._bgManager = null;
+        this._watermarkFile = null;
+        this.actor = null;
+    }
+};
+
+var WatermarkManager = class WatermarkManager {
+    constructor() {
+        this._watermarks = [];
+    }
+
+    init() {
+        Main.layoutManager.connect('monitors-changed', this._addWatermark.bind(this));
+        Main.layoutManager.connect('startup-prepared', this._addWatermark.bind(this));
+        this._addWatermark();
+    }
+
+    _addWatermark() {
+        this._destroyWatermark();
+        _forEachBackgroundManager((bgManager) => {
+            this._watermarks.push(new Watermark(bgManager));
+        });
+    }
+
+    _destroyWatermark() {
+        this._watermarks.forEach(l => {
+            if (l.actor)
+                l.actor.destroy();
+        });
+        this._watermarks = [];
+    }
+};
+
+function _forEachBackgroundManager(func) {
+    if (Main.overview._bgManagers)
+        Main.overview._bgManagers.forEach(func);
+
+    if (Main.layoutManager._bgManagers)
+        Main.layoutManager._bgManagers.forEach(func);
+}
diff --git a/meson.build b/meson.build
index b22f52af8f..b0e577abe3 100644
--- a/meson.build
+++ b/meson.build
@@ -46,6 +46,7 @@ bindir = join_paths(prefix, get_option('bindir'))
 datadir = join_paths(prefix, get_option('datadir'))
 libdir = join_paths(prefix, get_option('libdir'))
 libexecdir = join_paths(prefix, get_option('libexecdir'))
+localstatedir = join_paths(prefix, get_option('localstatedir'))
 mandir = join_paths(prefix, get_option('mandir'))
 sysconfdir = join_paths(prefix, get_option('sysconfdir'))
 


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