[gnome-shell-extensions/wip/rstrode/heads-up-display: 4/62] Add panel-favorites extension




commit a6bb0ef5c5b042345928829ac4e29e359f9dfc1f
Author: Florian Müllner <fmuellner gnome org>
Date:   Wed May 20 18:55:47 2015 +0200

    Add panel-favorites extension

 extensions/panel-favorites/extension.js     | 267 ++++++++++++++++++++++++++++
 extensions/panel-favorites/meson.build      |   5 +
 extensions/panel-favorites/metadata.json.in |  10 ++
 extensions/panel-favorites/stylesheet.css   |  14 ++
 meson.build                                 |   1 +
 5 files changed, 297 insertions(+)
---
diff --git a/extensions/panel-favorites/extension.js b/extensions/panel-favorites/extension.js
new file mode 100644
index 0000000..b817dbb
--- /dev/null
+++ b/extensions/panel-favorites/extension.js
@@ -0,0 +1,267 @@
+// Copyright (C) 2011-2013 R M Yorston
+// Licence: GPLv2+
+
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const St = imports.gi.St;
+const Mainloop = imports.mainloop;
+
+const AppFavorites = imports.ui.appFavorites;
+const Main = imports.ui.main;
+const Panel = imports.ui.panel;
+const Tweener = imports.ui.tweener;
+
+const PANEL_LAUNCHER_LABEL_SHOW_TIME = 0.15;
+const PANEL_LAUNCHER_LABEL_HIDE_TIME = 0.1;
+const PANEL_LAUNCHER_HOVER_TIMEOUT = 300;
+
+const PanelLauncher = new Lang.Class({
+    Name: 'PanelLauncher',
+
+    _init: function(app) {
+        this.actor = new St.Button({ style_class: 'panel-button',
+                                     reactive: true });
+        this.iconSize = 24;
+        let icon = app.create_icon_texture(this.iconSize);
+        this.actor.set_child(icon);
+        this.actor._delegate = this;
+        let text = app.get_name();
+        if ( app.get_description() ) {
+            text += '\n' + app.get_description();
+        }
+
+        this.label = new St.Label({ style_class: 'panel-launcher-label'});
+        this.label.set_text(text);
+        Main.layoutManager.addChrome(this.label);
+        this.label.hide();
+        this.actor.label_actor = this.label;
+
+        this._app = app;
+        this.actor.connect('clicked', Lang.bind(this, function() {
+            this._app.open_new_window(-1);
+        }));
+        this.actor.connect('notify::hover',
+                Lang.bind(this, this._onHoverChanged));
+        this.actor.opacity = 207;
+
+        this.actor.connect('notify::allocation', Lang.bind(this, this._alloc));
+    },
+
+    _onHoverChanged: function(actor) {
+        actor.opacity = actor.hover ? 255 : 207;
+    },
+
+    _alloc: function() {
+        let size = this.actor.allocation.y2 - this.actor.allocation.y1 - 3;
+        if ( size >= 24 && size != this.iconSize ) {
+            this.actor.get_child().destroy();
+            this.iconSize = size;
+            let icon = this._app.create_icon_texture(this.iconSize);
+            this.actor.set_child(icon);
+        }
+    },
+
+    showLabel: function() {
+        this.label.opacity = 0;
+        this.label.show();
+
+        let [stageX, stageY] = this.actor.get_transformed_position();
+
+        let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
+        let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
+        let labelWidth = this.label.get_width();
+
+        let node = this.label.get_theme_node();
+        let yOffset = node.get_length('-y-offset');
+
+        let y = stageY + itemHeight + yOffset;
+        let x = Math.floor(stageX + itemWidth/2 - labelWidth/2);
+
+        let parent = this.label.get_parent();
+        let parentWidth = parent.allocation.x2 - parent.allocation.x1;
+
+        if ( Clutter.get_default_text_direction() == Clutter.TextDirection.LTR ) {
+            // stop long tooltips falling off the right of the screen
+            x = Math.min(x, parentWidth-labelWidth-6);
+            // but whatever happens don't let them fall of the left
+            x = Math.max(x, 6);
+        }
+        else {
+            x = Math.max(x, 6);
+            x = Math.min(x, parentWidth-labelWidth-6);
+        }
+
+        this.label.set_position(x, y);
+        Tweener.addTween(this.label,
+                         { opacity: 255,
+                           time: PANEL_LAUNCHER_LABEL_SHOW_TIME,
+                           transition: 'easeOutQuad',
+                         });
+    },
+
+    hideLabel: function() {
+        this.label.opacity = 255;
+        Tweener.addTween(this.label,
+                         { opacity: 0,
+                           time: PANEL_LAUNCHER_LABEL_HIDE_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this, function() {
+                               this.label.hide();
+                           })
+                         });
+    },
+
+    destroy: function() {
+        this.label.destroy();
+        this.actor.destroy();
+    }
+});
+
+const PanelFavorites = new Lang.Class({
+    Name: 'PanelFavorites',
+
+    _init: function() {
+        this._showLabelTimeoutId = 0;
+        this._resetHoverTimeoutId = 0;
+        this._labelShowing = false;
+
+        this.actor = new St.BoxLayout({ name: 'panelFavorites',
+                                        x_expand: true, y_expand: true,
+                                        style_class: 'panel-favorites' });
+        this._display();
+
+        this.container = new St.Bin({ y_fill: true,
+                                      x_fill: true,
+                                      child: this.actor });
+
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+        this._installChangedId = Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, 
this._redisplay));
+        this._changedId = AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, 
this._redisplay));
+    },
+
+    _redisplay: function() {
+        for ( let i=0; i<this._buttons.length; ++i ) {
+            this._buttons[i].destroy();
+        }
+
+        this._display();
+    },
+
+    _display: function() {
+        let launchers = global.settings.get_strv(AppFavorites.getAppFavorites().FAVORITE_APPS_KEY);
+
+        this._buttons = [];
+        let j = 0;
+        for ( let i=0; i<launchers.length; ++i ) {
+            let app = Shell.AppSystem.get_default().lookup_app(launchers[i]);
+
+            if ( app == null ) {
+                continue;
+            }
+
+            let launcher = new PanelLauncher(app);
+            this.actor.add(launcher.actor);
+            launcher.actor.connect('notify::hover',
+                        Lang.bind(this, function() {
+                            this._onHover(launcher);
+                        }));
+            this._buttons[j] = launcher;
+            ++j;
+        }
+    },
+
+    // this routine stolen from dash.js
+    _onHover: function(launcher) {
+        if ( launcher.actor.hover ) {
+            if (this._showLabelTimeoutId == 0) {
+                let timeout = this._labelShowing ?
+                                0 : PANEL_LAUNCHER_HOVER_TIMEOUT;
+                this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
+                    Lang.bind(this, function() {
+                        this._labelShowing = true;
+                        launcher.showLabel();
+                        this._showLabelTimeoutId = 0;
+                        return GLib.SOURCE_REMOVE;
+                    }));
+                if (this._resetHoverTimeoutId > 0) {
+                    Mainloop.source_remove(this._resetHoverTimeoutId);
+                    this._resetHoverTimeoutId = 0;
+                }
+            }
+        } else {
+            if (this._showLabelTimeoutId > 0) {
+                Mainloop.source_remove(this._showLabelTimeoutId);
+                this._showLabelTimeoutId = 0;
+            }
+            launcher.hideLabel();
+            if (this._labelShowing) {
+                this._resetHoverTimeoutId = Mainloop.timeout_add(
+                    PANEL_LAUNCHER_HOVER_TIMEOUT,
+                    Lang.bind(this, function() {
+                        this._labelShowing = false;
+                        this._resetHoverTimeoutId = 0;
+                        return GLib.SOURCE_REMOVE;
+                    }));
+            }
+        }
+    },
+
+    _onDestroy: function() {
+        if ( this._installChangedId != 0 ) {
+            Shell.AppSystem.get_default().disconnect(this._installChangedId);
+            this._installChangedId = 0;
+        }
+
+        if ( this._changedId != 0 ) {
+            AppFavorites.getAppFavorites().disconnect(this._changedId);
+            this._changedId = 0;
+        }
+    }
+});
+Signals.addSignalMethods(PanelFavorites.prototype);
+
+let myAddToStatusArea;
+let panelFavorites;
+
+function enable() {
+    Panel.Panel.prototype.myAddToStatusArea = myAddToStatusArea;
+
+    // place panel to left of app menu, or failing that at right end of box
+    let siblings = Main.panel._leftBox.get_children();
+    let appMenu = Main.panel.statusArea['appMenu'];
+    let pos = appMenu ? siblings.indexOf(appMenu.container) : siblings.length;
+
+    panelFavorites = new PanelFavorites();
+    Main.panel.myAddToStatusArea('panel-favorites', panelFavorites,
+                                pos, 'left');
+}
+
+function disable() {
+    delete Panel.Panel.prototype.myAddToStatusArea;
+
+    panelFavorites.actor.destroy();
+    panelFavorites.emit('destroy');
+    panelFavorites = null;
+}
+
+function init() {
+    myAddToStatusArea = function(role, indicator, position, box) {
+        if (this.statusArea[role])
+            throw new Error('Extension point conflict: there is already a status indicator for role ' + 
role);
+
+        position = position || 0;
+        let boxes = {
+            left: this._leftBox,
+            center: this._centerBox,
+            right: this._rightBox
+        };
+        let boxContainer = boxes[box] || this._rightBox;
+        this.statusArea[role] = indicator;
+        this._addToPanelBox(role, indicator, position, boxContainer);
+        return indicator;
+    };
+}
diff --git a/extensions/panel-favorites/meson.build b/extensions/panel-favorites/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/panel-favorites/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+  input: metadata_name + '.in',
+  output: metadata_name,
+  configuration: metadata_conf
+)
diff --git a/extensions/panel-favorites/metadata.json.in b/extensions/panel-favorites/metadata.json.in
new file mode 100644
index 0000000..037f281
--- /dev/null
+++ b/extensions/panel-favorites/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Frippery Panel Favorites",
+"description": "Add launchers for Favorites to the panel",
+"shell-version": [ "@shell_current@" ],
+"url": "http://intgat.tigress.co.uk/rmy/extensions/index.html";
+}
diff --git a/extensions/panel-favorites/stylesheet.css b/extensions/panel-favorites/stylesheet.css
new file mode 100644
index 0000000..120adac
--- /dev/null
+++ b/extensions/panel-favorites/stylesheet.css
@@ -0,0 +1,14 @@
+.panel-favorites {
+    spacing: 6px;
+}
+
+.panel-launcher-label {
+    border-radius: 7px;
+    padding: 4px 12px;
+    background-color: rgba(0,0,0,0.9);
+    color: white;
+    text-align: center;
+    font-size: 9pt;
+    font-weight: bold;
+    -y-offset: 6px;
+}
diff --git a/meson.build b/meson.build
index 2909135..e8e00dc 100644
--- a/meson.build
+++ b/meson.build
@@ -51,6 +51,7 @@ all_extensions += [
   'auto-move-windows',
   'dash-to-dock',
   'native-window-placement',
+  'panel-favorites',
   'top-icons',
   'user-theme'
 ]


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