[gnome-shell] [lightbox] Add lightbox.js and use it in overview, alt-tab, and run dialog



commit 3734479cbd47da87fcf1477f2cade64a774bce22
Author: Dan Winship <danw gnome org>
Date:   Tue Sep 22 15:24:14 2009 -0400

    [lightbox] Add lightbox.js and use it in overview, alt-tab, and run dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=596000

 js/ui/Makefile.am   |    1 +
 js/ui/altTab.js     |   47 +++-------------
 js/ui/lightbox.js   |  151 +++++++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/runDialog.js  |   12 +---
 js/ui/workspaces.js |   50 ++++-------------
 5 files changed, 176 insertions(+), 85 deletions(-)
---
diff --git a/js/ui/Makefile.am b/js/ui/Makefile.am
index 0d92e44..af5dca7 100644
--- a/js/ui/Makefile.am
+++ b/js/ui/Makefile.am
@@ -10,6 +10,7 @@ dist_jsui_DATA =		\
 	dnd.js			\
 	docDisplay.js		\
 	genericDisplay.js	\
+	lightbox.js		\
 	link.js			\
 	lookingGlass.js		\
 	main.js			\
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
index 206292a..fbc1268 100644
--- a/js/ui/altTab.js
+++ b/js/ui/altTab.js
@@ -7,6 +7,7 @@ const Meta = imports.gi.Meta;
 const Pango = imports.gi.Pango;
 const Shell = imports.gi.Shell;
 
+const Lightbox = imports.ui.lightbox;
 const Main = imports.ui.main;
 const Tweener = imports.ui.tweener;
 
@@ -24,10 +25,6 @@ const POPUP_NUM_COLUMNS = 5;
 
 const POPUP_LABEL_MAX_WIDTH = POPUP_NUM_COLUMNS * (POPUP_ICON_SIZE + POPUP_GRID_SPACING);
 
-const OVERLAY_COLOR = new Clutter.Color();
-OVERLAY_COLOR.from_pixel(0x00000044);
-
-const SHOW_TIME = 0.05;
 const SWITCH_TIME = 0.1;
 
 function AltTabPopup() {
@@ -73,19 +70,8 @@ AltTabPopup.prototype = {
         this.actor.append(this._indicator, Big.BoxPackFlags.FIXED);
 
         this._items = [];
-        this._toplevels = global.window_group.get_children();
 
         global.stage.add_actor(this.actor);
-
-        // Dark translucent window used to cover all but the
-        // currently-selected window while Alt-Tabbing.
-        this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR,
-                                                x: 0,
-                                                y: 0,
-                                                width: global.screen_width,
-                                                height: global.screen_height,
-                                                border_width: 0,
-                                                reactive: true });
     },
 
     addWindow : function(win) {
@@ -101,14 +87,6 @@ AltTabPopup.prototype = {
         item.box = new Big.Box({ padding: POPUP_INDICATOR_WIDTH * 2 });
         item.box.append(item.icon, Big.BoxPackFlags.NONE);
 
-        item.above = null;
-        for (let i = 1; i < this._toplevels.length; i++) {
-            if (this._toplevels[i] == win) {
-                item.above = this._toplevels[i - 1];
-                break;
-            }
-        }
-
         item.n = this._items.length;
         this._items.push(item);
 
@@ -122,13 +100,11 @@ AltTabPopup.prototype = {
     },
 
     show : function(initialSelection) {
-        global.window_group.add_actor(this._overlay);
-        this._overlay.raise_top();
-        this._overlay.show();
-        this.actor.opacity = 0;
-        Tweener.addTween(this.actor, { opacity: 255,
-                                       time: SHOW_TIME,
-                                       transition: "easeOutQuad" });
+        // Need to specify explicit width and height because the
+        // window_group may not actually cover the whole screen
+        this._lightbox = new Lightbox.Lightbox(global.window_group,
+                                               global.screen_width,
+                                               global.screen_height);
 
         this.actor.show_all();
         this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
@@ -139,7 +115,7 @@ AltTabPopup.prototype = {
 
     destroy : function() {
         this.actor.destroy();
-        this._overlay.destroy();
+        this._lightbox.destroy();
     },
 
     select : function(n) {
@@ -150,11 +126,6 @@ AltTabPopup.prototype = {
                 this._selected.box.disconnect(this._allocationChangedId);
                 delete this._allocationChangedId;
             }
-
-            if (this._selected.above)
-                this._selected.window.raise(this._selected.above);
-            else
-                this._selected.window.lower_bottom();
         }
 
         let item = this._items[n];
@@ -193,8 +164,7 @@ AltTabPopup.prototype = {
             }
             this._indicator.show();
 
-            if (this._overlay.visible)
-                this._selected.window.raise(this._overlay);
+            this._lightbox.highlight(this._selected.window);
 
             this._allocationChangedId =
                 this._selected.box.connect('notify::allocation',
@@ -202,6 +172,7 @@ AltTabPopup.prototype = {
         } else {
             this._label.text = "";
             this._indicator.hide();
+            this._lightbox.highlight(null);
         }
     },
 
diff --git a/js/ui/lightbox.js b/js/ui/lightbox.js
new file mode 100644
index 0000000..17ae9a7
--- /dev/null
+++ b/js/ui/lightbox.js
@@ -0,0 +1,151 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Lang = imports.lang;
+
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+
+const SHADE_COLOR = new Clutter.Color();
+SHADE_COLOR.from_pixel(0x00000044);
+
+/**
+ * Lightbox:
+ * @container: parent Clutter.Container
+ * @width: (optional) shade actor width
+ * @height: (optional) shade actor height
+ *
+ * Lightbox creates a dark translucent "shade" actor to hide the
+ * contents of @container, and allows you to specify particular actors
+ * in @container to highlight by bringing them above the shade. It
+ * tracks added and removed actors in @container while the lightboxing
+ * is active, and ensures that all actors are returned to their
+ * original stacking order when the lightboxing is removed. (However,
+ * if actors are restacked by outside code while the lightboxing is
+ * active, the lightbox may later revert them back to their original
+ * order.)
+ *
+ * By default, the shade window will have the height and width of
+ * @container and will track any changes in its size. You can override
+ * this by passing an explicit width and height
+ */
+function Lightbox(container, width, height) {
+    this._init(container, width, height);
+}
+
+Lightbox.prototype = {
+    _init : function(container, width, height) {
+        this._container = container;
+        this._children = container.get_children();
+        this.actor = new Clutter.Rectangle({ color: SHADE_COLOR,
+                                             x: 0,
+                                             y: 0,
+                                             border_width: 0,
+                                             reactive: true });
+
+        container.add_actor(this.actor);
+        this.actor.raise_top();
+
+        this._destroySignalId = this.actor.connect('destroy', Lang.bind(this, this.destroy));
+
+        if (width && height) {
+            this.actor.width = width;
+            this.actor.height = height;
+            this._allocationChangedSignalId = 0;
+        } else {
+            this.actor.width = container.width;
+            this.actor.height = container.height;
+            this._allocationChangedSignalId = container.connect('allocation-changed', Lang.bind(this, this._allocationChanged));
+        }
+
+        this._actorAddedSignalId = container.connect('actor-added', Lang.bind(this, this._actorAdded));
+        this._actorRemovedSignalId = container.connect('actor-removed', Lang.bind(this, this._actorRemoved));
+
+        this._highlighted = null;
+    },
+
+    _allocationChanged : function(container, box, flags) {
+        this.actor.width = this._container.width;
+        this.actor.height = this._container.height;
+    },
+
+    _actorAdded : function(container, newChild) {
+        let children = this._container.get_children();
+        let myIndex = children.indexOf(this.actor);
+        let newChildIndex = children.indexOf(newChild);
+
+        if (newChildIndex < myIndex) {
+            // The child was added above the shade (presumably it was
+            // made the new top-most child). Move it below the shade,
+            // and add it to this._children as the new topmost actor.
+            newChild.lower(this.actor);
+            this._children.unshift(newChild);
+        } else if (newChildIndex == children.length - 1) {
+            // Bottom of stack
+            this._children.push(newChild);
+        } else {
+            // Somewhere else; insert it into the correct spot
+            let nextChild = this._children.indexOf(children[newChildIndex + 1]);
+            if (nextChild != -1) // paranoia
+                this._children.splice(nextChild, 0, newChild);
+        }
+    },
+
+    _actorRemoved : function(container, child) {
+        let index = this._children.indexOf(child);
+        if (index != -1) // paranoia
+            this._children.splice(index, 1);
+
+        if (child == this._highlighted)
+            this._highlighted = null;
+    },
+
+    /**
+     * highlight:
+     * @window: actor to highlight
+     *
+     * Highlights the indicated actor and unhighlights any other
+     * currently-highlighted actor. With no arguments or a false/null
+     * argument, all actors will be unhighlighted.
+     */
+    highlight : function(window) {
+        if (this._highlighted == window)
+            return;
+
+        // Walk this._children raising and lowering actors as needed.
+        // Things get a little tricky if the to-be-raised and
+        // to-be-lowered actors were originally adjacent, in which
+        // case we may need to indicate some *other* actor as the new
+        // sibling of the to-be-lowered one.
+
+        let below = this.actor;
+        for (let i = 0; i < this._children.length; i++) {
+            if (this._children[i] == window)
+                this._children[i].raise_top();
+            else if (this._children[i] == this._highlighted)
+                this._children[i].lower(below);
+            else
+                below = this._children[i];
+        }
+
+        this._highlighted = window;
+    },
+
+    /**
+     * destroy:
+     *
+     * Destroys the lightbox. This is called automatically if the
+     * lightbox's container is destroyed.
+     */
+    destroy : function() {
+        if (this._allocationChangedSignalId != 0)
+            this._container.disconnect(this._allocationChangedSignalId);
+        this._container.disconnect(this._actorAddedSignalId);
+        this._container.disconnect(this._actorRemovedSignalId);
+
+        this.actor.disconnect(this._destroySignalId);
+
+        this.highlight(null);
+        this.actor.destroy();
+    }
+};
diff --git a/js/ui/runDialog.js b/js/ui/runDialog.js
index ca9f06e..d6d7994 100644
--- a/js/ui/runDialog.js
+++ b/js/ui/runDialog.js
@@ -11,11 +11,9 @@ const Signals = imports.signals;
 const Gettext = imports.gettext.domain('gnome-shell');
 const _ = Gettext.gettext;
 
+const Lightbox = imports.ui.lightbox;
 const Main = imports.ui.main;
 
-const OVERLAY_COLOR = new Clutter.Color();
-OVERLAY_COLOR.from_pixel(0x00000044);
-
 const BOX_BACKGROUND_COLOR = new Clutter.Color();
 BOX_BACKGROUND_COLOR.from_pixel(0x000000cc);
 
@@ -66,12 +64,7 @@ RunDialog.prototype = {
         this._group = new Clutter.Group({ visible: false });
         global.stage.add_actor(this._group);
 
-        this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR,
-                                                width: global.screen_width,
-                                                height: global.screen_height,
-                                                border_width: 0,
-                                                reactive: true });
-        this._group.add_actor(this._overlay);
+        this._lightbox = new Lightbox.Lightbox(this._group);
 
         let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
                                  x_align: Big.BoxAlignment.CENTER,
@@ -80,6 +73,7 @@ RunDialog.prototype = {
                                  height: global.screen_height });
 
         this._group.add_actor(boxH);
+        this._lightbox.highlight(boxH);
 
         let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
                                  y_align: Big.BoxAlignment.CENTER });
diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js
index de9e60f..cd7f6b0 100644
--- a/js/ui/workspaces.js
+++ b/js/ui/workspaces.js
@@ -12,6 +12,7 @@ const Shell = imports.gi.Shell;
 const Signals = imports.signals;
 
 const DND = imports.ui.dnd;
+const Lightbox = imports.ui.lightbox;
 const Main = imports.ui.main;
 const Overview = imports.ui.overview;
 const Panel = imports.ui.panel;
@@ -25,8 +26,6 @@ const WINDOWCLONE_TITLE_COLOR = new Clutter.Color();
 WINDOWCLONE_TITLE_COLOR.from_pixel(0xffffffff);
 const FRAME_COLOR = new Clutter.Color();
 FRAME_COLOR.from_pixel(0xffffffff);
-const LIGHTBOX_COLOR = new Clutter.Color();
-LIGHTBOX_COLOR.from_pixel(0x00000044);
 
 const SCROLL_SCALE_AMOUNT = 100 / 5;
 
@@ -206,21 +205,7 @@ WindowClone.prototype = {
     },
 
     _zoomStart : function () {
-        this._zoomOverlay = new Clutter.Rectangle({ reactive: true,
-                                                    color: LIGHTBOX_COLOR,
-                                                    border_width: 0,
-                                                    x: 0,
-                                                    y: 0,
-                                                    width: global.screen_width,
-                                                    height: global.screen_height,
-                                                    opacity: 0 });
-        this._zoomOverlay.show();
-        global.stage.add_actor(this._zoomOverlay);
-        Tweener.addTween(this._zoomOverlay,
-                         { opacity: 255,
-                           time: ZOOM_OVERLAY_FADE_TIME,
-                           transition: "easeOutQuad"
-                         });
+        this._zoomLightbox = new Lightbox.Lightbox(global.stage);
 
         this._zoomLocalOrig  = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
         this._zoomGlobalOrig = new ScaledPoint();
@@ -229,10 +214,8 @@ WindowClone.prototype = {
         this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position());
         this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height);
 
-        this._zoomOverlay.raise_top();
-        this._zoomOverlay.show();
-
         this.actor.reparent(global.stage);
+        this._zoomLightbox.highlight(this.actor);
 
         [this.actor.x, this.actor.y]             = this._zoomGlobalOrig.getPosition();
         [this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.getScale();
@@ -255,7 +238,7 @@ WindowClone.prototype = {
 
         this._adjustTitle();
 
-        this._zoomOverlay.destroy();
+        this._zoomLightbox.destroy();
         Main.overview.disconnect(this._hideEventId);
 
         this._zoomLocalPosition  = undefined;
@@ -263,8 +246,8 @@ WindowClone.prototype = {
         this._zoomGlobalPosition = undefined;
         this._zoomGlobalScale    = undefined;
         this._zoomTargetPosition = undefined;
-        this._zoomStep       = undefined;
-        this._zoomOverlay    = undefined;
+        this._zoomStep           = undefined;
+        this._zoomLightbox       = undefined;
     },
 
     _onButtonRelease : function (actor, event) {
@@ -431,14 +414,6 @@ Workspace.prototype = {
         this.actor.height = global.screen_height;
         this.scale = 1.0;
 
-        this._lightbox = new Clutter.Rectangle({ color: LIGHTBOX_COLOR });
-        this.actor.connect('notify::allocation', Lang.bind(this, function () {
-            let [width, height] = this.actor.get_size();
-            this._lightbox.set_size(width, height);
-        }));
-        this.actor.add_actor(this._lightbox);
-        this._lightbox.hide();
-
         let windows = global.get_windows().filter(this._isMyWindow, this);
 
         // Find the desktop window
@@ -573,10 +548,10 @@ Workspace.prototype = {
      */
     setLightboxMode: function (showLightbox) {
         if (showLightbox) {
-            this.setHighlightWindow(null);
-            this._lightbox.show();
+            this._lightbox = new Lightbox.Lightbox(this.actor);
         } else {
-            this._lightbox.hide();
+            this._lightbox.destroy();
+            this._lightbox = null;
         }
     },
 
@@ -587,13 +562,12 @@ Workspace.prototype = {
      * Draw the user's attention to the given window @metaWindow.
      */
     setHighlightWindow: function (metaWindow) {
-        for (let i = 0; i < this._windows.length; i++) {
-            this._windows[i].actor.lower(this._lightbox);
-        }
+        let actor;
         if (metaWindow != null) {
             let clone = this.lookupCloneForMetaWindow(metaWindow);
-            clone.actor.raise(this._lightbox);
+            actor = clone.actor;
         }
+        this._lightbox.highlight(actor);
     },
 
     _adjustRemoveButton : function() {



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