[gnome-shell] panel: make ActivitiesButton a PanelMenu.Button



commit 50f248ec5b2a0ac7d3b6d70b7844563bb34c620f
Author: Dan Winship <danw gnome org>
Date:   Thu Jul 14 08:56:14 2011 -0400

    panel: make ActivitiesButton a PanelMenu.Button
    
    The fact that everything in the top bar except the activities button
    was a menu made various things difficult. Simplify this by making the
    activities button be a menu too, but just hack it up a bit so that the
    menu associated with the button never actually appears.
    
    Fixes https://bugzilla.gnome.org/show_bug.cgi?id=645759 (Clicking on
    Activities with menu up leaves a funny state) and its semi-dup 641253
    (panel keynav between Activities and menus is quirky).

 data/theme/gnome-shell.css |    4 +-
 js/ui/panel.js             |  109 +++++++++++++++++++++++++++++++++----------
 2 files changed, 85 insertions(+), 28 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 98771db..edbd023 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -264,7 +264,7 @@ StTooltip StLabel {
 }
 
 .panel-corner:active,
-.panel-corner:checked,
+.panel-corner:overview,
 .panel-corner:focus {
     -panel-corner-inner-border-color: rgba(255,255,255,0.8);
 }
@@ -301,7 +301,7 @@ StTooltip StLabel {
 }
 
 .panel-button:active,
-.panel-button:checked,
+.panel-button:overview,
 .panel-button:focus {
     border-image: url("panel-button-border.svg") 10 10 0 2;
     background-image: url("panel-button-highlight-wide.svg");
diff --git a/js/ui/panel.js b/js/ui/panel.js
index d679cd6..86e83db 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -544,37 +544,41 @@ AppMenuButton.prototype = {
 
 Signals.addSignalMethods(AppMenuButton.prototype);
 
-// Activities button.
+// Activities button. Because everything else in the top bar is a
+// PanelMenu.Button, it simplifies some things to make this be one too.
+// We just hack it up to not actually have a menu attached to it.
 function ActivitiesButton() {
     this._init.apply(this, arguments);
 }
 
 ActivitiesButton.prototype = {
+    __proto__: PanelMenu.Button.prototype,
+
     _init: function() {
+        PanelMenu.Button.prototype._init.call(this, 0.0);
+
         /* Translators: If there is no suitable word for "Activities"
            in your language, you can use the word for "Overview". */
         let label = new St.Label({ text: _("Activities") });
-        this.actor = new St.Button({ name: 'panelActivities',
-                                     style_class: 'panel-button',
-                                     reactive: true,
-                                     can_focus: true });
-        this.actor.set_child(label);
-        this.actor._delegate = this;
+        this.actor.child = label;
+        this.actor.name = 'panelActivities';
 
-        this.actor.connect('clicked', Lang.bind(this, function(b) {
-            if (!Main.overview.animationInProgress) {
-                this._hotCorner.maybeToggleOverviewOnClick();
-                return true;
-            } else {
-                return false;
-            }
-        }));
+        // Hack up our menu...
+        this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
+        this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
+        this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
+
+        this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
+        this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
+        this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
 
         Main.overview.connect('showing', Lang.bind(this, function() {
-            this.actor.checked = true;
+            this.actor.add_style_pseudo_class('overview');
+            this._escapeMenuGrab();
         }));
         Main.overview.connect('hiding', Lang.bind(this, function() {
-            this.actor.checked = false;
+            this.actor.remove_style_pseudo_class('overview');
+            this._escapeMenuGrab();
         }));
 
         this._hotCorner = null;
@@ -595,6 +599,50 @@ ActivitiesButton.prototype = {
                                                  Lang.bind(this, this._xdndShowOverview, actor));
     },
 
+    _escapeMenuGrab: function() {
+        if (this.menu.isOpen)
+            this.menu.close();
+    },
+
+    _onCapturedEvent: function(actor, event) {
+        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
+            if (!this._hotCorner.shouldToggleOverviewOnClick())
+                return true;
+        }
+        return false;
+    },
+
+    _onMenuOpenRequest: function() {
+        this.menu.isOpen = true;
+        this.menu.emit('open-state-changed', true);
+    },
+
+    _onMenuCloseRequest: function() {
+        this.menu.isOpen = false;
+        this.menu.emit('open-state-changed', false);
+    },
+
+    _onMenuToggleRequest: function() {
+        this.menu.isOpen = !this.menu.isOpen;
+        this.menu.emit('open-state-changed', this.menu.isOpen);
+    },
+
+    _onButtonRelease: function() {
+        if (this.menu.isOpen) {
+            this.menu.close();
+            Main.overview.toggle();
+        }
+    },
+
+    _onKeyRelease: function(actor, event) {
+        let symbol = event.get_key_symbol();
+        if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
+            if (this.menu.isOpen)
+                this.menu.close();
+            Main.overview.toggle();
+        }
+    },
+
     _xdndShowOverview: function(actor) {
         let [x, y, mask] = global.get_pointer();
         let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
@@ -841,9 +889,9 @@ HotCorner.prototype = {
     },
 
     _onCornerClicked : function() {
-         if (!Main.overview.animationInProgress)
-             this.maybeToggleOverviewOnClick();
-         return true;
+        if (this.shouldToggleOverviewOnClick())
+            Main.overview.toggle();
+        return true;
     },
 
     _onCornerLeft : function(actor, event) {
@@ -862,13 +910,18 @@ HotCorner.prototype = {
         return false;
     },
 
-    // Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
-    // of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
-    // and clicked the Activities button.
-    maybeToggleOverviewOnClick: function() {
+    // Checks if the Activities button is currently sensitive to
+    // clicks. The first call to this function within the
+    // HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
+    // triggered will return false. This avoids opening and closing
+    // the overview if the user both triggered the hot corner and
+    // clicked the Activities button.
+    shouldToggleOverviewOnClick: function() {
+        if (Main.overview.animationInProgress)
+            return false;
         if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
-            Main.overview.toggle();
-        this._activationTime = 0;
+            return true;
+        return false;
     }
 }
 
@@ -980,6 +1033,10 @@ Panel.prototype = {
         this._activities = this.button = this._activitiesButton.actor;
         this._leftBox.add(this._activities);
 
+        // The activities button has a pretend menu, so as to integrate
+        // more cleanly with the rest of the panel
+        this._menus.addMenu(this._activitiesButton.menu);
+
         // Synchronize the button's pseudo classes with its corner
         this.button.connect('style-changed', Lang.bind(this,
             function(actor) {



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