From 6432de95ccd1cd2b6576b4a75eb53e4df9abf098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 15 Jul 2010 16:21:32 +0200 Subject: [PATCH] overview: Do not zoom the desktop background While scaling the desktop background with the window previews represents workspaces quite intuitively, the approach is not without problems. As window previews in the overview behave quite differently to "real" windows, the representation of workspaces as miniature versions of "real" workspaces is flawed. The scaling also makes the transitions to and from the overview much more visually expensive, without adding much benefit. Leaving the background in place provides more visual stability to the transitions and emphasizes the distinctive behavior of elements in the overview. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 4 - js/ui/overview.js | 62 +++++++++++--- js/ui/workspace.js | 166 ++++++++----------------------------- js/ui/workspacesView.js | 10 ++- 4 files changed, 97 insertions(+), 145 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 0a62039c8..8c5c0d2f0 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,10 +257,6 @@ StTooltip StLabel { /* Overview */ -.overview { - background-color: #111; -} - .new-workspace-area { border: 2px solid rgba(255, 255, 255, 0.8); border-radius: 10px; diff --git a/js/ui/overview.js b/js/ui/overview.js index bec99c707..3c398d0e8 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -1,6 +1,7 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const Clutter = imports.gi.Clutter; +const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; @@ -172,7 +173,16 @@ function Overview() { Overview.prototype = { _init : function() { - this._group = new St.Group({ style_class: 'overview' }); + this._desktopFade = new St.Bin(); + global.overlay_group.add_actor(this._desktopFade); + + // The actual global.background_actor is inside global.window_group, + // which is hidden when displaying the overview, so we display a clone. + this._background = new Clutter.Clone({ source: global.background_actor }); + this._background.hide(); + global.overlay_group.add_actor(this._background); + + this._group = new St.Group({ name: 'overview' }); this._group._delegate = this; this._group.connect('destroy', Lang.bind(this, function() { @@ -209,10 +219,6 @@ Overview.prototype = { reactive: true }); this._group.add_actor(this._transparentBackground); - // Background color for the Overview - this._backOver = new St.Label(); - this._group.add_actor(this._backOver); - this._group.hide(); global.overlay_group.add_actor(this._group); @@ -238,6 +244,20 @@ Overview.prototype = { this.workspaces = null; }, + _getDesktopClone: function() { + let windows = global.get_window_actors().filter(function(w) { + return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP; + }); + if (windows.length == 0) + return null; + + let clone = new Clutter.Clone({ source: windows[0].get_texture() }); + clone.source.connect('destroy', Lang.bind(this, function() { + clone.destroy(); + })); + return clone; + }, + _onViewChanged: function() { if (!this.visible) return; @@ -314,11 +334,6 @@ Overview.prototype = { this._workspacesBarWidth = this._workspacesWidth; this._workspacesBarY = primary.height - displayGridRowHeight; - // The parent (this._group) is positioned at the top left of the primary monitor - // while this._backOver occupies the entire screen. - this._backOver.set_position(- primary.x, - primary.y); - this._backOver.set_size(global.screen_width, global.screen_height); - this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, this._workspacesY); // Dynamic width @@ -456,6 +471,19 @@ Overview.prototype = { this._group.add_actor(this._workspacesBar); this._workspacesBar.raise(this.workspaces.actor); + if (!this._desktopFade.child) + this._desktopFade.child = this._getDesktopClone(); + + if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) { + this._desktopFade.opacity = 255; + this._desktopFade.show(); + Tweener.addTween(this._desktopFade, + { opacity: 0, + time: ANIMATION_TIME, + transition: 'easeOutQuad' + }); + } + // All the the actors in the window group are completely obscured, // hiding the group holding them while the Overview is displayed greatly // increases performance of the Overview especially when there are many @@ -465,6 +493,7 @@ Overview.prototype = { // clones of them, this would obviously no longer be necessary. global.window_group.hide(); this._group.show(); + this._background.show(); // Create a zoom out effect. First scale the Overview group up and // position it so that the active workspace fills up the whole screen, @@ -502,6 +531,16 @@ Overview.prototype = { this.animationInProgress = true; this._hideInProgress = true; + + if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) { + this._desktopFade.opacity = 0; + this._desktopFade.show(); + Tweener.addTween(this._desktopFade, + { opacity: 255, + time: ANIMATION_TIME, + transition: 'easeOutQuad' }); + } + if (this._activeDisplayPane != null) this._activeDisplayPane.close(); this.workspaces.hide(); @@ -559,6 +598,7 @@ Overview.prototype = { return; this.animationInProgress = false; + this._desktopFade.hide(); this._coverPane.lower_bottom(); this.emit('shown'); @@ -576,6 +616,8 @@ Overview.prototype = { this._workspacesManager = null; this._dash.hide(); + this._desktopFade.hide(); + this._background.hide(); this._group.hide(); this.visible = false; diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 349c87b5e..200217570 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -289,76 +289,9 @@ WindowClone.prototype = { this.emit('drag-end'); } }; - Signals.addSignalMethods(WindowClone.prototype); -function DesktopClone(window) { - this._init(window); -} - -DesktopClone.prototype = { - _init : function(window) { - this.actor = new Clutter.Group({ reactive: true }); - - let background = new Clutter.Clone({ source: global.background_actor }); - this.actor.add_actor(background); - - if (window) { - this._desktop = new Clutter.Clone({ source: window.get_texture() }); - this.actor.add_actor(this._desktop); - this._desktop.hide(); - } else { - this._desktop = null; - } - - this.actor.connect('button-release-event', - Lang.bind(this, this._onButtonRelease)); - }, - - zoomFromOverview: function(fadeInIcons) { - if (this._desktop == null) - return; - - if (fadeInIcons) { - this._desktop.opacity = 0; - this._desktop.show(); - Tweener.addTween(this._desktop, - { opacity: 255, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad' }); - } - }, - - zoomToOverview: function(fadeOutIcons) { - if (this._desktop == null) - return; - - if (fadeOutIcons) { - this._desktop.opacity = 255; - this._desktop.show(); - Tweener.addTween(this._desktop, - { opacity: 0, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, - function() { - this._desktop.hide(); - }) - }); - } else { - this._desktop.hide(); - } - }, - - _onButtonRelease : function (actor, event) { - this.emit('selected', event.get_time()); - } -}; - -Signals.addSignalMethods(DesktopClone.prototype); - - /** * @windowClone: Corresponding window clone * @parentActor: The actor which will be the parent of all overlay items @@ -561,7 +494,6 @@ WindowOverlay.prototype = { this._parentActor.queue_relayout(); } }; - Signals.addSignalMethods(WindowOverlay.prototype); const WindowPositionFlags = { @@ -585,10 +517,20 @@ Workspace.prototype = { // Without this the drop area will be overlapped. this._windowOverlaysGroup.set_size(0, 0); - this.actor = new Clutter.Group(); + this.actor = new Clutter.Group({ reactive: true }); this.actor._delegate = this; this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + this.actor.connect('button-release-event', Lang.bind(this, + function(actor, event) { + // Only switch to the workspace when there's no application + // windows open. The problem is that it's too easy to miss + // an app window and get the wrong one focused. + if (this._windows.length == 0) { + this.metaWorkspace.activate(event.get_time()); + Main.overview.hide(); + } + })); // Items in _windowOverlaysGroup should not be scaled, so we don't // add them to this.actor, but to its parent whenever it changes @@ -604,35 +546,10 @@ Workspace.prototype = { let windows = global.get_window_actors().filter(this._isMyWindow, this); - // Find the desktop window - for (let i = 0; i < windows.length; i++) { - if (windows[i].meta_window.get_window_type() == Meta.WindowType.DESKTOP) { - this._desktop = new DesktopClone(windows[i]); - break; - } - } - // If there wasn't one, fake it - if (!this._desktop) - this._desktop = new DesktopClone(); - - this._desktop.connect('selected', - Lang.bind(this, - function(clone, time) { - // Only switch to the workspace when there's no application windows - // open (we always have one window for the desktop). The problem - // is that it's too easy to miss an app window and get the wrong - // one focused. - if (this._windows.length == 1) { - this.metaWorkspace.activate(time); - Main.overview.hide(); - } - })); - this.actor.add_actor(this._desktop.actor); - // Create clones for remaining windows that should be // visible in the Overview - this._windows = [this._desktop]; - this._windowOverlays = [ null ]; + this._windows = []; + this._windowOverlays = []; for (let i = 0; i < windows.length; i++) { if (this._isOverviewWindow(windows[i])) { this._addWindowClone(windows[i]); @@ -747,10 +664,10 @@ Workspace.prototype = { // FIXME: do something cooler-looking using clutter-cairo this._frame = new Clutter.Rectangle({ color: FRAME_COLOR }); this.actor.add_actor(this._frame); - this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); + this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, + - FRAME_SIZE / this.actor.scale_y); + this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, + this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); this._frame.lower_bottom(); this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition)); @@ -770,14 +687,14 @@ Workspace.prototype = { * Set the workspace (desktop) reactive **/ setReactive: function(reactive) { - this._desktop.actor.reactive = reactive; + this.actor.reactive = reactive; }, _updateFramePosition : function() { - this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); + this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, + - FRAME_SIZE / this.actor.scale_y); + this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, + this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); }, _isCloneVisible: function(clone) { @@ -788,7 +705,7 @@ Workspace.prototype = { * _getVisibleClones: * * Returns a list WindowClone objects where the clone isn't filtered - * out by any application filter. The clone for the desktop is excluded. + * out by any application filter. * The returned array will always be newly allocated; it is not in any * defined order, and thus it's convenient to call .sort() with your * choice of sorting function. @@ -796,7 +713,7 @@ Workspace.prototype = { _getVisibleClones: function() { let visible = []; - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; if (!this._isCloneVisible(clone)) @@ -808,7 +725,7 @@ Workspace.prototype = { }, _resetCloneVisibility: function () { - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; let overlay = this._windowOverlays[i]; @@ -1007,9 +924,9 @@ Workspace.prototype = { let buttonOuterHeight, captionHeight; let buttonOuterWidth = 0; - if (this._windowOverlays[1]) { - [buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); - buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; + if (this._windowOverlays[0]) { + [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights(); + buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale; } else [buttonOuterHeight, captionHeight] = [0, 0]; buttonOuterHeight /= this.scale; @@ -1172,7 +1089,7 @@ Workspace.prototype = { }, _fadeInAllOverlays: function() { - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; let overlay = this._windowOverlays[i]; if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) @@ -1182,7 +1099,7 @@ Workspace.prototype = { }, _hideAllOverlays: function() { - for (let i = 1; i< this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let overlay = this._windowOverlays[i]; overlay.hide(); } @@ -1309,8 +1226,8 @@ Workspace.prototype = { }, // check for maximized windows on the workspace - _haveMaximizedWindows: function() { - for (let i = 1; i < this._windows.length; i++) { + hasMaximizedWindows: function() { + for (let i = 0; i < this._windows.length; i++) { let metaWindow = this._windows[i].metaWindow; if (metaWindow.showing_on_its_workspace() && metaWindow.maximized_horizontally && @@ -1331,12 +1248,6 @@ Workspace.prototype = { else this.positionWindows(WindowPositionFlags.ZOOM); - let active = global.screen.get_active_workspace(); - let fadeInIcons = (Main.overview.animationInProgress && - active == this.metaWorkspace && - !this._haveMaximizedWindows()); - this._desktop.zoomToOverview(fadeInIcons); - this._visible = true; }, @@ -1354,7 +1265,7 @@ Workspace.prototype = { this._doneLeavingOverview)); // Position and scale the windows. - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; clone.zoomFromOverview(); @@ -1383,11 +1294,6 @@ Workspace.prototype = { } } - let active = global.screen.get_active_workspace(); - let fadeOutIcons = (active == this.metaWorkspace && - !this._haveMaximizedWindows()); - this._desktop.zoomFromOverview(fadeOutIcons); - this._visible = false; }, @@ -1451,7 +1357,7 @@ Workspace.prototype = { // Don't let the user try to select this workspace as it's // making its exit. - this._desktop.reactive = false; + this.actor.reactive = false; }, destroy : function() { @@ -1475,7 +1381,7 @@ Workspace.prototype = { // their parent (this.actor), but we might have a zoomed window // which has been reparented to the stage - _windows[0] holds // the desktop window, which is never reparented - for (let w = 1; w < this._windows.length; w++) + for (let w = 0; w < this._windows.length; w++) this._windows[w].destroy(); this._windows = []; }, @@ -1534,7 +1440,7 @@ Workspace.prototype = { }, _onShowOverlayClose: function (windowOverlay) { - for (let i = 1; i < this._windowOverlays.length; i++) { + for (let i = 0; i < this._windowOverlays.length; i++) { let overlay = this._windowOverlays[i]; if (overlay == windowOverlay) continue; diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 34945d776..4a7edc9c3 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -117,6 +117,11 @@ GenericWorkspacesView.prototype = { } }, + getActiveWorkspace: function() { + let active = global.screen.get_active_workspace_index(); + return this._workspaces[active]; + }, + _clearApplicationWindowSelection: function(reposition) { if (this._windowSelectionAppId == null) return; @@ -826,7 +831,7 @@ SingleView.prototype = { if (index < 0 || index >= global.n_workspaces) return; - let dragActor = this._workspaces[index]._desktop.actor; + let dragActor = this._workspaces[index].actor; if (draggable) { this._workspaces[index].actor.reactive = true; @@ -856,6 +861,9 @@ SingleView.prototype = { // start dragging the active workspace _onButtonPress: function(actor, event) { + if (actor != event.get_source()) + return; + if (this._dragIndex == -1) return;