diff --git a/js/ui/overlay.js b/js/ui/overlay.js index 26f436f65..f195c90df 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -218,6 +218,31 @@ Overlay.prototype = { //// Public methods //// + // Returns the scale the overlay has when we just start zooming out + // to overview mode. That is, when just the active workspace is showing. + getZoomedInScale : function() { + return 1 / this._workspaces.getScale(); + }, + + // Returns the position the overlay has when we just start zooming out + // to overview mode. That is, when just the active workspace is showing. + getZoomedInPosition : function() { + let [posX, posY] = this._workspaces.getActiveWorkspacePosition(); + let scale = this.getZoomedInScale(); + + return [- posX * scale, - posY * scale]; + }, + + // Returns the current scale of the overlay. + getScale : function() { + return this._group.scaleX; + }, + + // Returns the current position of the overlay. + getPosition : function() { + return [this._group.x, this._group.y]; + }, + show : function() { if (this.visible) return; @@ -262,26 +287,29 @@ Overlay.prototype = { global.window_group.hide(); this._group.show(); - // Try to make the menu not too visible behind the empty space between - // the workspace previews by sliding in its clipping rectangle. - // We want to finish drawing the Dash just before the top workspace fully - // slides in on the top. Which means that we have more time to wait before - // drawing the dash if the active workspace is displayed on the bottom of - // the workspaces grid, and almost no time to wait if it is displayed in the top - // row of the workspaces grid. The calculations used below try to roughly - // capture the animation ratio for when workspaces are covering the top of the overlay - // vs. when workspaces are already below the top of the overlay, and apply it - // to clipping the dash. The clipping is removed in this._showDone(). - this._dash.actor.set_clip(0, 0, - this._workspaces.getFullSizeX(), - this._dash.actor.height); - Tweener.addTween(this._dash.actor, - { clipWidthRight: this._dash._width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(), + // Create a zoom out effect. First scale the overlay group up and + // position it so that the active workspace fills up the whole screen, + // then transform the group to its normal dimensions and position. + // The opposite transition is used in hide(). + this._group.scaleX = this._group.scaleY = this.getZoomedInScale(); + [this._group.x, this._group.y] = this.getZoomedInPosition(); + Tweener.addTween(this._group, + { x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + transition: 'easeOutQuad', time: ANIMATION_TIME, - transition: "easeOutQuad", onComplete: this._showDone, onCompleteScope: this + }); + // Make Dash fade in so that it doesn't appear to big. + this._dash.actor.opacity = 0; + Tweener.addTween(this._dash.actor, + { opacity: 255, + transition: 'easeOutQuad', + time: ANIMATION_TIME }); this.emit('showing'); @@ -297,27 +325,29 @@ Overlay.prototype = { this._hideInProgress = true; if (this._activeDisplayPane != null) this._activeDisplayPane.close(); - // lower the panes, so that workspaces display is on top while sliding out - this._dash.actor.lower(this._workspaces.actor); this._workspaces.hide(); - // Try to make the menu not too visible behind the empty space between - // the workspace previews by sliding in its clipping rectangle. - // The logic used is the same as described in this.show(). If the active workspace - // is displayed in the top row, than almost full animation time is needed for it - // to reach the top of the overlay and cover the Dash fully, while if the - // active workspace is in the lower row, than the top left workspace reaches the - // top of the overlay sooner as it is moving out of the way. - // The clipping is removed in this._hideDone(). - this._dash.actor.set_clip(0, 0, - this._dash.actor.width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(), - this._dash.actor.height); - Tweener.addTween(this._dash.actor, - { clipWidthRight: this._workspaces.getFullSizeX() + this._workspaces.getWidthToTopActiveWorkspace() - global.screen_width, + // Create a zoom in effect by transforming the overlay group so that + // the active workspace fills up the whole screen. The opposite + // transition is used in show(). + let scale = this.getZoomedInScale(); + let [posX, posY] = this.getZoomedInPosition(); + Tweener.addTween(this._group, + { x: posX, + y: posY, + scaleX: scale, + scaleY: scale, + transition: 'easeOutQuad', time: ANIMATION_TIME, - transition: "easeOutQuad", onComplete: this._hideDone, onCompleteScope: this + }); + + // Make Dash fade out so that it doesn't appear to big. + Tweener.addTween(this._dash.actor, + { opacity: 0, + transition: 'easeOutQuad', + time: ANIMATION_TIME }); this.emit('hiding'); @@ -347,21 +377,10 @@ Overlay.prototype = { //// Private methods //// - // Raises the Dash to the top, so that we can tell if the pointer is above one of its items. - // We need to do this once the workspaces are shown because the workspaces actor currently covers - // the whole screen, regardless of where the workspaces are actually displayed. - // - // Once we rework the workspaces actor to only cover the area it actually needs, we can - // remove this workaround. Also http://bugzilla.openedhand.com/show_bug.cgi?id=1513 requests being - // able to pick only a reactive actor at a certain position, rather than any actor. Being able - // to do that would allow us to not have to raise the Dash. _showDone: function() { if (this._hideInProgress) return; - this._dash.actor.raise_top(); - this._dash.actor.remove_clip(); - this.animationInProgress = false; this.emit('shown'); @@ -375,7 +394,6 @@ Overlay.prototype = { this._workspaces.destroy(); this._workspaces = null; - this._dash.actor.remove_clip(); this._dash.hide(); this._group.hide(); @@ -388,36 +406,3 @@ Overlay.prototype = { } }; Signals.addSignalMethods(Overlay.prototype); - -Tweener.registerSpecialProperty("clipHeightBottom", _clipHeightBottomGet, _clipHeightBottomSet); - -function _clipHeightBottomGet(actor) { - let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip(); - return clipHeight; -} - -function _clipHeightBottomSet(actor, clipHeight) { - actor.set_clip(0, 0, actor.width, clipHeight); -} - -Tweener.registerSpecialProperty("clipHeightTop", _clipHeightTopGet, _clipHeightTopSet); - -function _clipHeightTopGet(actor) { - let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip(); - return clipHeight; -} - -function _clipHeightTopSet(actor, clipHeight) { - actor.set_clip(0, actor.height - clipHeight, actor.width, clipHeight); -} - -Tweener.registerSpecialProperty("clipWidthRight", _clipWidthRightGet, _clipWidthRightSet); - -function _clipWidthRightGet(actor) { - let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip(); - return clipWidth; -} - -function _clipWidthRightSet(actor, clipWidth) { - actor.set_clip(0, 0, clipWidth, actor.height); -} diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js index 82b7bcc2f..80ad99303 100644 --- a/js/ui/workspaces.js +++ b/js/ui/workspaces.js @@ -590,46 +590,48 @@ Workspace.prototype = { // Animate the full-screen to overlay transition. zoomToOverlay : function() { - // Move the workspace into size/position - this.actor.set_position(this.fullSizeX, this.fullSizeY); - - this.updateInOverlay(); + this.actor.set_position(this.gridX, this.gridY); + this.actor.set_scale(this.scale, this.scale); + + // Position and scale the windows. + this.positionWindows(true); + + // Fade in the remove button if available, so that it doesn't appear + // too abrubtly and doesn't start at a too big size. + if (this._removeButton) { + Tweener.removeTweens(this._removeButton); + this._removeButton.opacity = 0; + Tweener.addTween(this._removeButton, + { opacity: 255, + time: Overlay.ANIMATION_TIME, + transition: 'easeOutQuad' + }); + } this._visible = true; }, - // Animates the display of a workspace and its windows to have the current dimensions and position. - updateInOverlay : function() { - Tweener.addTween(this.actor, - { x: this.gridX, - y: this.gridY, - scale_x: this.scale, - scale_y: this.scale, - time: Overlay.ANIMATION_TIME, - transition: "easeOutQuad" - }); - - // Likewise for each of the windows in the workspace. - this.positionWindows(true); - }, - // Animates the return from overlay mode zoomFromOverlay : function() { this.leavingOverlay = true; this._hideAllIcons(); - Tweener.addTween(this.actor, - { x: this.fullSizeX, - y: this.fullSizeY, - scale_x: 1.0, - scale_y: 1.0, - time: Overlay.ANIMATION_TIME, - transition: "easeOutQuad", - onComplete: this._doneLeavingOverlay, - onCompleteScope: this - }); + Main.overlay.connect('hidden', Lang.bind(this, + this._doneLeavingOverlay)); + // Fade out the remove button if available, so that it doesn't + // disappear too abrubtly and doesn't become too big. + if (this._removeButton) { + Tweener.removeTweens(this._removeButton); + Tweener.addTween(this._removeButton, + { opacity: 0, + time: Overlay.ANIMATION_TIME, + transition: 'easeOutQuad' + }); + } + + // Position and scale the windows. for (let i = 1; i < this._windows.length; i++) { let clone = this._windows[i]; Tweener.addTween(clone.actor, @@ -906,9 +908,15 @@ Workspaces.prototype = { let lastWorkspace = this._workspaces[this._workspaces.length - 1]; lastWorkspace.updateRemovable(true); - // Position/scale the desktop windows and their children - for (let w = 0; w < this._workspaces.length; w++) - this._workspaces[w].zoomToOverlay(); + // Position/scale the desktop windows and their children after the + // workspaces have been created. This cannot be done first because + // window movement depends on the Workspaces object being accessible + // as an Overlay member. + Main.overlay.connect('showing', + Lang.bind(this, function() { + for (let w = 0; w < this._workspaces.length; w++) + this._workspaces[w].zoomToOverlay(); + })); // Track changes to the number of workspaces this._nWorkspacesNotifyId = @@ -945,17 +953,6 @@ Workspaces.prototype = { } }, - // Updates position of the workspaces display based on the new coordinates. - // Preserves the old value for the coordinate, if the passed value is null. - updatePosition : function(x, y) { - if (x != null) - this._x = x; - if (y != null) - this._y = y; - - this._updateInOverlay(); - }, - hide : function() { let global = Shell.Global.get(); let activeWorkspaceIndex = global.screen.get_active_workspace_index(); @@ -982,35 +979,17 @@ Workspaces.prototype = { global.window_manager.disconnect(this._switchWorkspaceNotifyId); }, - getFullSizeX : function() { - return this._workspaces[0].fullSizeX; + getScale : function() { + return this._workspaces[0].scale; }, - // If j-th workspace in the i-th row is active, returns the full width - // of j workspaces including empty space if i = 1, or the width of one - // workspace. - // Used in overlay.js to determine when it is ok to remove the sideshow - // during animations for entering and leaving the overlay. - getWidthToTopActiveWorkspace : function() { + // Get the grid position of the active workspace. + getActiveWorkspacePosition : function() { let global = Shell.Global.get(); let activeWorkspaceIndex = global.screen.get_active_workspace_index(); let activeWorkspace = this._workspaces[activeWorkspaceIndex]; - if (activeWorkspace.gridRow == 0) - return (activeWorkspace.gridCol + 1) * global.screen_width + activeWorkspace.gridCol * GRID_SPACING; - else - return global.screen_width; - }, - - // Updates the workspaces display based on the current dimensions and position. - _updateInOverlay : function() { - let global = Shell.Global.get(); - - this._positionWorkspaces(global); - - // Position/scale the desktop windows and their children - for (let w = 0; w < this._workspaces.length; w++) - this._workspaces[w].updateInOverlay(); + return [activeWorkspace.gridX, activeWorkspace.gridY]; }, // Assign grid positions to workspaces. We can't just do a simple @@ -1063,14 +1042,6 @@ Workspaces.prototype = { } } } - - // Now figure out their full-size coordinates - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - - workspace.fullSizeX = (workspace.gridCol - activeWorkspace.gridCol) * (global.screen_width + GRID_SPACING); - workspace.fullSizeY = (workspace.gridRow - activeWorkspace.gridRow) * (global.screen_height + GRID_SPACING); - } }, _workspacesChanged : function() { @@ -1167,28 +1138,49 @@ Workspaces.prototype = { Tweener.registerSpecialPropertyModifier("workspace_relative", _workspaceRelativeModifier, _workspaceRelativeGet); function _workspaceRelativeModifier(workspace) { - let endX, endY; + let [startX, startY] = Main.overlay.getPosition(); + let overlayPosX, overlayPosY, overlayScale; if (!workspace) return []; if (workspace.leavingOverlay) { - endX = workspace.fullSizeX; - endY = workspace.fullSizeY; + let [zoomedInX, zoomedInY] = Main.overlay.getZoomedInPosition(); + overlayPosX = { begin: startX, end: zoomedInX }; + overlayPosY = { begin: startY, end: zoomedInY }; + overlayScale = { begin: Main.overlay.getScale(), + end: Main.overlay.getZoomedInScale() }; } else { - endX = workspace.gridX; - endY = workspace.gridY; + overlayPosX = { begin: startX, end: 0 }; + overlayPosY = { begin: startY, end: 0 }; + overlayScale = { begin: Main.overlay.getScale(), end: 1 }; } return [ { name: "x", - parameters: { begin: workspace.actor.x, end: endX, - cur: function() { return workspace.actor.x; } } }, + parameters: { workspacePos: workspace.gridX, + overlayPos: overlayPosX, + overlayScale: overlayScale } }, { name: "y", - parameters: { begin: workspace.actor.y, end: endY, - cur: function() { return workspace.actor.y; } } } + parameters: { workspacePos: workspace.gridY, + overlayPos: overlayPosY, + overlayScale: overlayScale } } ]; } function _workspaceRelativeGet(begin, end, time, params) { - return (begin + params.begin) + time * (end + params.end - (begin + params.begin)) - params.cur(); + let curOverlayPos = (1 - time) * params.overlayPos.begin + + time * params.overlayPos.end; + let curOverlayScale = (1 - time) * params.overlayScale.begin + + time * params.overlayScale.end; + + // Calculate the screen position of the window. + let screen = (1 - time) * + ((begin + params.workspacePos) * params.overlayScale.begin + + params.overlayPos.begin) + + time * + ((end + params.workspacePos) * params.overlayScale.end + + params.overlayPos.end); + + // Return the workspace coordinates. + return (screen - curOverlayPos) / curOverlayScale - params.workspacePos; }