From 62507c975919fc9344077cfda8baee8849b44dca Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Wed, 5 Jan 2011 15:39:36 +0100 Subject: [PATCH] Add a facility to show the stage without grabbing When the user is doing a drag-and-drop, we want to temporarily show the stage to allow them to drag to a different window. But we're not "really" in the overview, and getting a grab would conflict with the X client doing the drag and drop. So add a showTemporarily()/hideTemporarily() pair of methods that show the overview without grabbing. This adds a lot more possibilities for asynchronous race conditions, so rework the code to be more robust against multiple calls to show*() and hide*(). The interpretation is now that all calls to show*() and hide*() affect the state, but if we have conflicting calls to show and hide we wait until the current animation is finished before correcting to the right visual state. https://bugzilla.gnome.org/show_bug.cgi?id=601731 --- js/ui/overview.js | 151 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 123 insertions(+), 28 deletions(-) diff --git a/js/ui/overview.js b/js/ui/overview.js index 4c3e21cb1..56da40adb 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -6,6 +6,7 @@ const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; const St = imports.gi.St; +const Shell = imports.gi.Shell; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; @@ -135,7 +136,10 @@ Overview.prototype = { this._workspacesDisplay = null; - this.visible = false; + this.visible = false; // animating to overview, in overview, animating out + this._shown = false; // show() and not hide() + this._shownTemporarily = false; // showTemporarily() and not hideTemporarily() + this._modal = false; // have a modal grab this.animationInProgress = false; this._hideInProgress = false; @@ -267,11 +271,23 @@ Overview.prototype = { return [this.workspaces.actor.x, this.workspaces.actor.y]; }, + // show: + // + // Animates the overview visible and grabs mouse and keyboard input show : function() { - if (this.visible) + if (this._shown) return; + // Do this manually instead of using _syncInputMode, to handle failure if (!Main.pushModal(this.viewSelector.actor)) return; + this._modal = true; + this._animateVisible(); + this._shown = true; + }, + + _animateVisible: function() { + if (this.visible || this.animationInProgress) + return; this.visible = true; this.animationInProgress = true; @@ -337,8 +353,102 @@ Overview.prototype = { this.emit('showing'); }, + // showTemporarily: + // + // Animates the overview visible without grabbing mouse and keyboard input; + // if show() has already been called, this has no immediate effect, but + // will result in the overview not being hidden until hideTemporarily() is + // called. + showTemporarily: function() { + if (this._shownTemporarily) + return; + + this._syncInputMode(); + this._animateVisible(); + this._shownTemporarily = true; + }, + + // hide: + // + // Reverses the effect of show() hide: function() { - if (!this.visible || this._hideInProgress) + if (!this._shown) + return; + + if (!this._shownTemporarily) + this._animateNotVisible(); + + this._shown = false; + this._syncInputMode(); + }, + + // hideTemporarily: + // + // Reverses the effect of showTemporarily() + hideTemporarily: function() { + if (!this._shownTemporarily) + return; + + if (!this._shown) + this._animateNotVisible(); + + this._shownTemporarily = false; + this._syncInputMode(); + }, + + toggle: function() { + if (this._shown) + this.hide(); + else + this.show(); + }, + + /** + * getWorkspacesForWindow: + * @metaWindow: A #MetaWindow + * + * Returns the Workspaces object associated with the given window. + * This method is not be accessible if the overview is not open + * and will return %null. + */ + getWorkspacesForWindow: function(metaWindow) { + return this.workspaces; + }, + + //// Private methods //// + + _syncInputMode: function() { + // We delay input mode changes during animation so that when removing the + // overview we don't have a problem with the release of a press/release + // going to an application. + if (this.animationInProgress) + return; + + if (this._shown) { + if (!this._modal) { + if (Main.pushModal(this._dash.actor)) + this._modal = true; + else + this.hide(); + } + } else if (this._shownTemporarily) { + if (this._modal) { + Main.popModal(this._dash.actor); + this._modal = false; + } + global.stage_input_mode = Shell.StageInputMode.FULLSCREEN; + } else { + if (this._modal) { + Main.popModal(this._dash.actor); + this._modal = false; + } + else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) + global.stage_input_mode = Shell.StageInputMode.NORMAL; + } + }, + + _animateNotVisible: function() { + if (!this.visible || this.animationInProgress) return; this.animationInProgress = true; @@ -382,36 +492,17 @@ Overview.prototype = { this.emit('hiding'); }, - toggle: function() { - if (this.visible) - this.hide(); - else - this.show(); - }, - - /** - * getWorkspacesForWindow: - * @metaWindow: A #MetaWindow - * - * Returns the Workspaces object associated with the given window. - * This method is not be accessible if the overview is not open - * and will return %null. - */ - getWorkspacesForWindow: function(metaWindow) { - return this.workspaces; - }, - - //// Private methods //// - _showDone: function() { - if (this._hideInProgress) - return; - this.animationInProgress = false; this._desktopFade.hide(); this._coverPane.lower_bottom(); this.emit('shown'); + // Handle any calls to hide* while we were showing + if (!this._shown && !this._shownTemporarily) + this._animateNotVisible(); + + this._syncInputMode(); }, _hideDone: function() { @@ -434,8 +525,12 @@ Overview.prototype = { this._coverPane.lower_bottom(); - Main.popModal(this.viewSelector.actor); this.emit('hidden'); + // Handle any calls to show* while we were hiding + if (this._shown || this._shownTemporarily) + this._animateVisible(); + + this._syncInputMode(); } }; Signals.addSignalMethods(Overview.prototype);