[gnome-shell] [MessageTray] pop out the last notification when mousing over the summary



commit ac54fed8d48102cd0fd2cf1701cb3efcdcd77465
Author: Dan Winship <danw gnome org>
Date:   Thu Feb 25 14:42:18 2010 -0500

    [MessageTray] pop out the last notification when mousing over the summary
    
    https://bugzilla.gnome.org/show_bug.cgi?id=610726

 data/theme/gnome-shell.css |   26 +++++++-
 js/ui/messageTray.js       |  144 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 161 insertions(+), 9 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 4dc3a2e..6d1a5d1 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -677,6 +677,11 @@ StTooltip {
     max-width: 40em;
 }
 
+#summary-notification-bin #notification {
+    /* message-tray.height + notification.padding-bottom */
+    padding-bottom: 38px;
+}
+
 #notification-actions {
     spacing: 5px;
 }
@@ -699,9 +704,26 @@ StTooltip {
     background: #808080;
 }
 
+/* The spacing and padding on the summary is tricky; we want to keep
+ * the icons from touching each other or the edges of the screen, but
+ * we also want them to be "Fitts"-y with respect to the edges, so the
+ * summary area's bottom and right padding must actually be part of
+ * the icons. However, we can't put *all* of the padding into the
+ * icons, because then the summary would be 0x0 when there were no
+ * icons in it, and so you wouldn't be able to hover over it to
+ * activate it.
+ *
+ * The padding-right on the non-rightmost icons is noticeable and
+ * slightly annoying. If StBoxLayout implemented the ":last-child"
+ * pseudo-class we could fix that...
+ */
 #summary-mode {
-    spacing: 10px;
-    padding: 2px 4px;
+    spacing: 6px;
+    padding: 2px 0px 0px 4px;
+}
+
+.summary-icon  {
+    padding: 0px 4px 2px 0px;
 }
 
 /* App Switcher */
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 69231c0..fefc9ee 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -15,7 +15,7 @@ const ANIMATION_TIME = 0.2;
 const NOTIFICATION_TIMEOUT = 4;
 const SUMMARY_TIMEOUT = 1;
 
-const MESSAGE_TRAY_TIMEOUT = 0.2;
+const HIDE_TIMEOUT = 0.2;
 
 const ICON_SIZE = 24;
 
@@ -339,6 +339,19 @@ Source.prototype = {
     },
 
     notify: function(notification) {
+        if (this.notification)
+            this.notification.disconnect(this._notificationDestroyedId);
+
+        this.notification = notification;
+
+        this._notificationDestroyedId = notification.connect('destroy', Lang.bind(this,
+            function () {
+                if (this.notification == notification) {
+                    this.notification = null;
+                    this._notificationDestroyedId = 0;
+                }
+            }));
+
         this.emit('notify', notification);
     },
 
@@ -379,6 +392,17 @@ MessageTray.prototype = {
         this._summaryBin.child = this._summary;
         this._summaryBin.opacity = 0;
 
+        this._summaryNotificationBin = new St.Bin({ name: 'summary-notification-bin',
+                                                    x_align: St.Align.END,
+                                                    reactive: true,
+                                                    track_hover: true });
+        this.actor.add(this._summaryNotificationBin);
+        this._summaryNotificationBin.lower_bottom();
+        this._summaryNotificationBin.hide();
+        this._summaryNotificationBin.connect('notify::hover', Lang.bind(this, this._onSummaryNotificationHoverChanged));
+        this._summaryNotification = null;
+        this._hoverSource = null;
+
         this._trayState = State.HIDDEN;
         this._trayLeftTimeoutId = 0;
         this._pointerInTray = false;
@@ -387,13 +411,15 @@ MessageTray.prototype = {
         this._pointerInSummary = false;
         this._notificationState = State.HIDDEN;
         this._notificationTimeoutId = 0;
+        this._summaryNotificationState = State.HIDDEN;
+        this._summaryNotificationTimeoutId = 0;
         this._overviewVisible = false;
         this._notificationRemoved = false;
 
-        this.actor.show();
         Main.chrome.addActor(this.actor, { affectsStruts: false,
                                            visibleInOverview: true });
         Main.chrome.trackActor(this._notificationBin, { affectsStruts: false });
+        Main.chrome.trackActor(this._summaryNotificationBin, { affectsStruts: false });
 
         global.connect('screen-size-changed',
                        Lang.bind(this, this._setSizePosition));
@@ -420,8 +446,8 @@ MessageTray.prototype = {
         this.actor.y = primary.y + primary.height - 1;
         this.actor.width = primary.width;
 
-        this._notificationBin.x = this._summaryBin.x = 0;
-        this._notificationBin.width = this._summaryBin.width = primary.width;
+        this._notificationBin.x = this._summaryBin.x = this._summaryNotificationBin.x = 0;
+        this._notificationBin.width = this._summaryBin.width = this._summaryNotificationBin.width = primary.width;
     },
 
     contains: function(source) {
@@ -434,7 +460,8 @@ MessageTray.prototype = {
             return;
         }
 
-        let iconBox = new St.Clickable({ reactive: true });
+        let iconBox = new St.Clickable({ style_class: 'summary-icon',
+                                         reactive: true });
         iconBox.child = source.createIcon(ICON_SIZE);
         this._summary.insert_actor(iconBox, 0);
         this._summaryNeedsToBeShown = true;
@@ -443,6 +470,10 @@ MessageTray.prototype = {
 
         source.connect('notify', Lang.bind(this, this._onNotify));
 
+        iconBox.connect('notify::hover', Lang.bind(this,
+            function () {
+                this._onSourceHoverChanged(source, iconBox.hover);
+            }));
         iconBox.connect('clicked', Lang.bind(this,
             function () {
                 source.clicked();
@@ -474,14 +505,23 @@ MessageTray.prototype = {
         delete this._icons[source.id];
         delete this._sources[source.id];
 
+        let needUpdate = false;
+
         if (this._notification && this._notification.source == source) {
             if (this._notificationTimeoutId) {
                 Mainloop.source_remove(this._notificationTimeoutId);
                 this._notificationTimeoutId = 0;
             }
             this._notificationRemoved = true;
-            this._updateState();
+            needUpdate = true;
         }
+        if (this._hoverSource == source) {
+            this._hoverSource = null;
+            needUpdate = true;
+        }
+
+        if (needUpdate);
+            this._updateState();
     },
 
     removeSourceByApp: function(app) {
@@ -532,6 +572,39 @@ MessageTray.prototype = {
         this._updateState();
     },
 
+    _onSourceHoverChanged: function(source, hover) {
+        if (!source.notification)
+            return;
+
+        if (this._summaryNotificationTimeoutId != 0) {
+            Mainloop.source_remove(this._summaryNotificationTimeoutId);
+            this._summaryNotificationTimeoutId = 0;
+        }
+
+        if (hover) {
+            this._hoverSource = source;
+            this._updateState();
+        } else if (this._hoverSource == source) {
+            let timeout = HIDE_TIMEOUT * 1000;
+            this._summaryNotificationTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onSourceHoverChangedTimeout, source));
+        }
+    },
+
+    _onSourceHoverChangedTimeout: function(source) {
+        this._summaryNotificationTimeoutId = 0;
+        if (this._hoverSource == source) {
+            this._hoverSource = null;
+            this._updateState();
+        }
+    },
+
+    _onSummaryNotificationHoverChanged: function() {
+        if (!this._summaryNotification)
+            return;
+        this._onSourceHoverChanged(this._summaryNotification.source,
+                                   this._summaryNotificationBin.hover);
+    },
+
     _onSummaryHoverChanged: function() {
         this._pointerInSummary = this._summary.hover;
         this._updateState();
@@ -550,7 +623,7 @@ MessageTray.prototype = {
         } else {
             // We wait just a little before hiding the message tray in case the
             // user quickly moves the mouse back into it.
-            let timeout = MESSAGE_TRAY_TIMEOUT * 1000;
+            let timeout = HIDE_TIMEOUT * 1000;
             this._trayLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onTrayLeftTimeout));
         }
     },
@@ -603,6 +676,22 @@ MessageTray.prototype = {
                 this._hideSummary();
         }
 
+        // Summary notification
+        let haveSummaryNotification = this._hoverSource != null;
+        let summaryNotificationIsMainNotification = (haveSummaryNotification &&
+                                                     this._hoverSource.notification == this._notification);
+        let canShowSummaryNotification = this._summaryState == State.SHOWN;
+        let wrongSummaryNotification = (haveSummaryNotification &&
+                                        this._summaryNotification != this._hoverSource.notification);
+
+        if (this._summaryNotificationState == State.HIDDEN) {
+            if (haveSummaryNotification && !summaryNotificationIsMainNotification && canShowSummaryNotification)
+                this._showSummaryNotification();
+        } else if (this._summaryNotificationState == State.SHOWN) {
+            if (!haveSummaryNotification || !canShowSummaryNotification || wrongSummaryNotification)
+                this._hideSummaryNotification();
+        }
+
         // Tray itself
         let trayIsVisible = (this._trayState == State.SHOWING ||
                              this._trayState == State.SHOWN);
@@ -753,5 +842,46 @@ MessageTray.prototype = {
                       transition: "easeOutQuad"
                     });
         this._summaryNeedsToBeShown = false;
+    },
+
+    _showSummaryNotification: function() {
+        this._summaryNotification = this._hoverSource.notification;
+
+        let index = this._notificationQueue.indexOf(this._summaryNotification);
+        if (index != -1)
+            this._notificationQueue.splice(index, 1);
+
+        this._summaryNotificationBin.child = this._summaryNotification.actor;
+        this._summaryNotification.popOut();
+
+        this._summaryNotificationBin.opacity = 0;
+        this._summaryNotificationBin.y = this.actor.height;
+        this._summaryNotificationBin.show();
+
+        this._tween(this._summaryNotificationBin, "_summaryNotificationState", State.SHOWN,
+                    { y: this.actor.height - this._summaryNotificationBin.height,
+                      opacity: 255,
+                      time: ANIMATION_TIME,
+                      transition: "easeOutQuad"
+                    });
+    },
+
+    _hideSummaryNotification: function() {
+        this._summaryNotification.popIn();
+
+        this._tween(this._summaryNotificationBin, "_summaryNotificationState", State.HIDDEN,
+                    { y: this.actor.height,
+                      opacity: 0,
+                      time: ANIMATION_TIME,
+                      transition: "easeOutQuad",
+                      onComplete: this._hideSummaryNotificationCompleted,
+                      onCompleteScope: this
+                    });
+    },
+
+    _hideSummaryNotificationCompleted: function() {
+        this._summaryNotificationBin.hide();
+        this._summaryNotificationBin.child = null;
+        this._summaryNotification = null;
     }
 };



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