[gnome-shell] osdWindow: Add a simple OSD popup



commit 19533f87e35764af384e4e47962859e016355cfb
Author: Florian Müllner <fmuellner gnome org>
Date:   Sat Mar 2 10:48:25 2013 +0100

    osdWindow: Add a simple OSD popup
    
    Implement a basic OSD popup that shows an icon and optionally a label
    and a fill level. It is based on the existing OSD implementation in
    gnome-settings-daemon, which it will replace.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=613543

 data/theme/gnome-shell.css |   14 ++++
 js/Makefile.am             |    1 +
 js/ui/main.js              |    3 +
 js/ui/osdWindow.js         |  170 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 188 insertions(+), 0 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 9e5c46d..1d92018 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1608,12 +1608,26 @@ StScrollBar StButton#vhandle:active {
     -shell-counter-overlap-y: 13px;
 }
 
+/* OSD */
+.osd-window {
+    text-align: center;
+    font-weight: bold;
+    spacing: 1em;
+}
+
+.osd-window .level {
+    height: 0.6em;
+    border-radius: 0.3em;
+    background-color: rgba(190,190,190,0.2);
+}
+
 /* App Switcher */
 .switcher-popup {
     padding: 8px;
     spacing: 16px;
 }
 
+.osd-window,
 .switcher-list {
     background: rgba(0,0,0,0.8);
     border: 1px solid rgba(128,128,128,0.40);
diff --git a/js/Makefile.am b/js/Makefile.am
index 908d515..62c8323 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -69,6 +69,7 @@ nobase_dist_js_DATA =         \
        ui/shellEntry.js        \
        ui/shellMountOperation.js \
        ui/notificationDaemon.js \
+       ui/osdWindow.js         \
        ui/overview.js          \
        ui/overviewControls.js  \
        ui/panel.js             \
diff --git a/js/ui/main.js b/js/ui/main.js
index 57b5a7a..3c4cba5 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -18,6 +18,7 @@ const ExtensionSystem = imports.ui.extensionSystem;
 const ExtensionDownloader = imports.ui.extensionDownloader;
 const Keyboard = imports.ui.keyboard;
 const MessageTray = imports.ui.messageTray;
+const OsdWindow = imports.ui.osdWindow;
 const Overview = imports.ui.overview;
 const Panel = imports.ui.panel;
 const Params = imports.misc.params;
@@ -51,6 +52,7 @@ let screenShield = null;
 let notificationDaemon = null;
 let windowAttentionHandler = null;
 let ctrlAltTabManager = null;
+let osdWindow = null;
 let sessionMode = null;
 let shellDBusService = null;
 let shellMountOpDBusService = null;
@@ -137,6 +139,7 @@ function startSession() {
 
     xdndHandler = new XdndHandler.XdndHandler();
     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
+    osdWindow = new OsdWindow.OsdWindow();
     overview = new Overview.Overview();
     wm = new WindowManager.WindowManager();
     magnifier = new Magnifier.Magnifier();
diff --git a/js/ui/osdWindow.js b/js/ui/osdWindow.js
new file mode 100644
index 0000000..fc90ed2
--- /dev/null
+++ b/js/ui/osdWindow.js
@@ -0,0 +1,170 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+
+const Lang = imports.lang;
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const Mainloop = imports.mainloop;
+const Tweener = imports.ui.tweener;
+
+const HIDE_TIMEOUT = 1500;
+const FADE_TIME = 0.1;
+const LEVEL_ANIMATION_TIME = 0.1;
+
+const LevelBar = new Lang.Class({
+    Name: 'LevelBar',
+
+    _init: function() {
+        this._level = 0;
+
+        this.actor = new St.Bin({ style_class: 'level',
+                                  x_fill: true, y_fill: true });
+        this._bar = new St.DrawingArea();
+        this._bar.connect('repaint', Lang.bind(this, this._repaint));
+
+        this.actor.set_child(this._bar);
+    },
+
+    get level() {
+        return this._level;
+    },
+
+    set level(value) {
+        let newValue = Math.max(0, Math.min(value, 100));
+        if (newValue == this._level)
+            return;
+        this._level = newValue;
+        this._bar.queue_repaint();
+    },
+
+    _repaint: function() {
+        let cr = this._bar.get_context();
+
+        let node = this.actor.get_theme_node();
+        let radius = node.get_border_radius(0); // assume same radius for all corners
+        Clutter.cairo_set_source_color(cr, node.get_foreground_color());
+
+        let [w, h] = this._bar.get_surface_size();
+        w *= (this._level / 100.);
+
+        if (w == 0)
+            return;
+
+        cr.moveTo(radius, 0);
+        if (w >= radius)
+            cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
+        else
+            cr.lineTo(w, 0);
+        if (w >= radius)
+            cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
+        else
+            cr.lineTo(w, h);
+        cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
+        cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
+        cr.fill();
+    }
+});
+
+const OsdWindow = new Lang.Class({
+    Name: 'OsdWindow',
+
+    _init: function() {
+        this.actor = new St.Widget({ x_expand: true,
+                                     y_expand: true,
+                                     x_align: Clutter.ActorAlign.CENTER,
+                                     y_align: Clutter.ActorAlign.CENTER });
+        this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
+        this._box = new St.BoxLayout({ style_class: 'osd-window',
+                                       vertical: true });
+        this.actor.add_actor(this._box);
+
+        this._icon = new St.Icon();
+        this._box.add(this._icon, { expand: true });
+
+        this._label = new St.Label();
+        this._box.add(this._label);
+
+        this._level = new LevelBar();
+        this._box.add(this._level.actor);
+
+        this._hideTimeoutId = 0;
+        this._reset();
+
+        Main.layoutManager.connect('monitors-changed',
+                                   Lang.bind(this, this._monitorsChanged));
+        this._monitorsChanged();
+
+        Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
+    },
+
+    setIcon: function(icon) {
+        this._icon.gicon = icon;
+    },
+
+    setLabel: function(label) {
+        this._label.visible = (label != undefined);
+        if (label)
+            this._label.text = label;
+    },
+
+    setLevel: function(level) {
+        this._level.actor.visible = (level != undefined);
+        if (this.actor.visible)
+            Tweener.addTween(this._level,
+                             { level: level,
+                               time: LEVEL_ANIMATION_TIME,
+                               transition: 'easeOutQuad' });
+        else
+            this._level.level = level;
+    },
+
+    show: function() {
+        if (!this._icon.gicon)
+            return;
+
+        if (!this.actor.visible) {
+            this.actor.show();
+            this.actor.opacity = 0;
+
+            Tweener.addTween(this.actor,
+                             { opacity: 255,
+                               time: FADE_TIME,
+                               transition: 'easeOutQuad' });
+        }
+
+        if (this._hideTimeoutId)
+            Mainloop.source_remove(this._hideTimeoutId);
+        this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
+                                                   Lang.bind(this, this._hide));
+    },
+
+    _hide: function() {
+        Tweener.addTween(this.actor,
+                         { opacity: 0,
+                           time: FADE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this, this._reset) });
+    },
+
+    _reset: function() {
+        this.actor.hide();
+        this.setLabel(null);
+        this.setLevel(null);
+    },
+
+    _monitorsChanged: function() {
+        /* assume 130x130 on a 640x480 display and scale from there */
+        let monitor = Main.layoutManager.primaryMonitor;
+        let scalew = monitor.width / 640.0;
+        let scaleh = monitor.height / 480.0;
+        let scale = Math.min(scalew, scaleh);
+        let size = 130 * Math.max(1, scale);
+
+        this._box.set_size(size, size);
+        this._box.translation_y = monitor.height / 4;
+
+        this._icon.icon_size = size / 2;
+    }
+});


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