diff --git a/data/theme/gnome-shell-sass/widgets/_window-picker.scss b/data/theme/gnome-shell-sass/widgets/_window-picker.scss index 48d6f73f5..1b9321851 100644 --- a/data/theme/gnome-shell-sass/widgets/_window-picker.scss +++ b/data/theme/gnome-shell-sass/widgets/_window-picker.scss @@ -1,7 +1,7 @@ /* Window Picker */ -$window_picker_spacing: $base_spacing * 2; // 16px -$window_picker_padding: $base_padding * 2; // 16px +$window_picker_spacing: $base_spacing; // 6px +$window_picker_padding: $base_padding * 2; // 12px $window_thumbnail_border_color:transparentize($selected_fg_color, 0.65); @@ -13,8 +13,8 @@ $window_clone_border_size: 6px; // Window picker .window-picker { // Space between window thumbnails - -horizontal-spacing: $window_picker_spacing; - -vertical-spacing: $window_picker_spacing; + spacing: $window_picker_spacing; + // Padding for container around window thumbnails padding: $window_picker_padding; diff --git a/js/ui/windowPreview.js b/js/ui/windowPreview.js index b7fc5efa5..69e3ee574 100644 --- a/js/ui/windowPreview.js +++ b/js/ui/windowPreview.js @@ -237,8 +237,6 @@ var WindowPreview = GObject.registerClass({ this._windowActor.connect('destroy', () => this.destroy()); this._updateAttachedDialogs(); - this.x = this.boundingBox.x; - this.y = this.boundingBox.y; let clickAction = new Clutter.ClickAction(); clickAction.connect('clicked', () => this._activate()); diff --git a/js/ui/workspace.js b/js/ui/workspace.js index ef4fc3a49..182f350ae 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -1,7 +1,7 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported Workspace */ -const { Clutter, GLib, GObject, Meta, St } = imports.gi; +const { Clutter, GLib, GObject, St } = imports.gi; const DND = imports.ui.dnd; const Main = imports.ui.main; @@ -26,12 +26,6 @@ function _interpolate(start, end, step) { return start + (end - start) * step; } -var WindowPositionFlags = { - NONE: 0, - INITIAL: 1 << 0, - ANIMATE: 1 << 1, -}; - // Window Thumbnail Layout Algorithm // ================================= // @@ -367,28 +361,6 @@ var UnalignedLayoutStrategy = class extends LayoutStrategy { } }; -function padArea(area, padding) { - return { - x: area.x + padding.left, - y: area.y + padding.top, - width: area.width - padding.left - padding.right, - height: area.height - padding.top - padding.bottom, - }; -} - -function rectEqual(one, two) { - if (one == two) - return true; - - if (!one || !two) - return false; - - return one.x == two.x && - one.y == two.y && - one.width == two.width && - one.height == two.height; -} - function animateAllocation(actor, box) { if (actor.allocation.equal(box) || actor.allocation.get_width() === 0 || @@ -815,39 +787,23 @@ var WorkspaceLayout = GObject.registerClass({ var Workspace = GObject.registerClass( class Workspace extends St.Widget { _init(metaWorkspace, monitorIndex) { - super._init({ style_class: 'window-picker' }); + super._init({ + style_class: 'window-picker', + layout_manager: new WorkspaceLayout(metaWorkspace, monitorIndex), + }); // When dragging a window, we use this slot for reserve space. this._reservedSlot = null; this._reservedSlotWindow = null; this.metaWorkspace = metaWorkspace; - // The full geometry is the geometry we should try and position - // windows for. The actual geometry we allocate may be less than - // this, like if the workspace switcher is slid out. - this._fullGeometry = null; - - // The actual geometry is the geometry we need to arrange windows - // in. If this is a smaller area than the full geometry, we'll - // do some simple aspect ratio like math to fit the layout calculated - // for the full geometry into this area. - this._actualGeometry = null; - this._actualGeometryLater = 0; - - this._currentLayout = null; - this.monitorIndex = monitorIndex; this._monitor = Main.layoutManager.monitors[this.monitorIndex]; if (monitorIndex != Main.layoutManager.primaryIndex) this.add_style_class_name('external-monitor'); - this.set_size(0, 0); - - this._dropRect = new Clutter.Actor({ opacity: 0 }); - this._dropRect._delegate = this; - - this.add_actor(this._dropRect); + this.connect('style-changed', this._onStyleChanged.bind(this)); this.connect('destroy', this._onDestroy.bind(this)); const windows = global.get_window_actors().map(a => a.meta_window) @@ -858,7 +814,7 @@ class Workspace extends St.Widget { this._windows = []; for (let i = 0; i < windows.length; i++) { if (this._isOverviewWindow(windows[i])) - this._addWindowClone(windows[i], true); + this._addWindowClone(windows[i]); } // Track window changes @@ -872,68 +828,14 @@ class Workspace extends St.Widget { this._windowEnteredMonitor.bind(this)); this._windowLeftMonitorId = global.display.connect('window-left-monitor', this._windowLeftMonitor.bind(this)); - this._repositionWindowsId = 0; + this._layoutFrozenId = 0; - this.leavingOverview = false; - - this._positionWindowsFlags = 0; - this._positionWindowsId = 0; - } - - vfunc_map() { - super.vfunc_map(); - this._syncActualGeometry(); + // DND requires this to be set + this._delegate = this; } vfunc_get_focus_chain() { - return this.get_children().filter(c => c.visible).sort((a, b) => { - if (a instanceof WindowPreview && b instanceof WindowPreview) - return a.slotId - b.slotId; - - return 0; - }); - } - - setFullGeometry(geom) { - if (rectEqual(this._fullGeometry, geom)) - return; - - this._fullGeometry = geom; - - if (this.mapped) - this._recalculateWindowPositions(WindowPositionFlags.NONE); - } - - setActualGeometry(geom) { - if (rectEqual(this._actualGeometry, geom)) - return; - - this._actualGeometry = geom; - this._actualGeometryDirty = true; - - if (this.mapped) - this._syncActualGeometry(); - } - - _syncActualGeometry() { - if (this._actualGeometryLater || !this._actualGeometryDirty) - return; - if (!this._actualGeometry) - return; - - this._actualGeometryLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._actualGeometryLater = 0; - if (!this.mapped) - return false; - - let geom = this._actualGeometry; - - this._dropRect.set_position(geom.x, geom.y); - this._dropRect.set_size(geom.width, geom.height); - this._updateWindowPositions(Main.overview.animationInProgress ? WindowPositionFlags.ANIMATE : WindowPositionFlags.NONE); - - return false; - }); + return this.layout_manager.getFocusChain(); } _lookupIndex(metaWindow) { @@ -959,232 +861,59 @@ class Workspace extends St.Widget { this._reservedSlotWindow = metaWindow; this._reservedSlot = this._windows[this._lookupIndex(metaWindow)]; } - - this._recalculateWindowPositions(WindowPositionFlags.ANIMATE); - } - - _recalculateWindowPositions(flags) { - this._positionWindowsFlags |= flags; - - if (this._positionWindowsId > 0) - return; - - this._positionWindowsId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._realRecalculateWindowPositions(this._positionWindowsFlags); - this._positionWindowsFlags = 0; - this._positionWindowsId = 0; - return false; - }); - } - - _realRecalculateWindowPositions(flags) { - if (this._repositionWindowsId > 0) { - GLib.source_remove(this._repositionWindowsId); - this._repositionWindowsId = 0; - } - - let clones = this._windows.slice(); - if (clones.length == 0) - return; - - clones.sort((a, b) => { - return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence(); - }); - - if (this._reservedSlot) - clones.push(this._reservedSlot); - - this._currentLayout = this._computeLayout(clones); - this._updateWindowPositions(flags); - } - - _updateWindowPositions(flags) { - if (this._currentLayout == null) { - this._recalculateWindowPositions(flags); - return; - } - - // We will reposition windows anyway when enter again overview or when ending the windows - // animations with fade animation. - // In this way we avoid unwanted animations of windows repositioning while - // animating overview. - if (this.leavingOverview || this._animatingWindowsFade) - return; - - let initialPositioning = flags & WindowPositionFlags.INITIAL; - let animate = flags & WindowPositionFlags.ANIMATE; - - let layout = this._currentLayout; - let strategy = layout.strategy; - - let [, , padding] = this._getSpacingAndPadding(); - let area = padArea(this._actualGeometry, padding); - let slots = strategy.computeWindowSlots(layout, area); - - const isOnCurrentWorkspace = - this.metaWorkspace === null || this.metaWorkspace.active; - - for (let i = 0; i < slots.length; i++) { - let slot = slots[i]; - const [x, y, cellWidth, cellHeight, clone] = slot; - - clone.slotId = i; - - // Positioning a window currently being dragged must be avoided; - // we'll just leave a blank spot in the layout for it. - if (clone.inDrag) - continue; - - const cloneWidth = cellWidth; - const cloneHeight = cellHeight; - - if (!clone.positioned) { - // This window appeared after the overview was already up - // Grow the clone from the center of the slot - clone.x = x + cloneWidth / 2; - clone.y = y + cloneHeight / 2; - clone.width = 0; - clone.height = 0; - clone.positioned = true; - } - - if (animate && isOnCurrentWorkspace) { - if (!clone.metaWindow.showing_on_its_workspace()) { - /* Hidden windows should fade in and grow - * therefore we need to resize them now so they - * can be scaled up later */ - if (initialPositioning) { - clone.opacity = 0; - clone.x = x; - clone.y = y; - clone.width = cloneWidth; - clone.height = cloneHeight; - } - - clone.ease({ - opacity: 255, - mode: Clutter.AnimationMode.EASE_IN_QUAD, - duration: Overview.ANIMATION_TIME, - }); - } - - this._animateClone(clone, x, y, cloneWidth, cloneHeight); - } else { - // cancel any active tweens (otherwise they might override our changes) - clone.remove_all_transitions(); - clone.set_position(x, y); - clone.set_size(cloneWidth, cloneHeight); - clone.set_opacity(255); - } - } } syncStacking(stackIndices) { - let clones = this._windows.slice(); - clones.sort((a, b) => { - let indexA = stackIndices[a.metaWindow.get_stable_sequence()]; - let indexB = stackIndices[b.metaWindow.get_stable_sequence()]; - return indexA - indexB; - }); - - for (let i = 0; i < clones.length; i++) { - let clone = clones[i]; - if (i == 0) { - clone.setStackAbove(this._dropRect); - } else { - let previousClone = clones[i - 1]; - clone.setStackAbove(previousClone); - } - } - } - - _animateClone(clone, x, y, width, height) { - clone.ease({ - x, y, - width, height, - duration: Overview.ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - - _delayedWindowRepositioning() { - let [x, y] = global.get_pointer(); - - let pointerHasMoved = this._cursorX != x && this._cursorY != y; - let inWorkspace = this._fullGeometry.x < x && x < this._fullGeometry.x + this._fullGeometry.width && - this._fullGeometry.y < y && y < this._fullGeometry.y + this._fullGeometry.height; - - if (pointerHasMoved && inWorkspace) { - // store current cursor position - this._cursorX = x; - this._cursorY = y; - return GLib.SOURCE_CONTINUE; - } - - let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); - for (let i = 0; i < this._windows.length; i++) { - if (this._windows[i] == actorUnderPointer) - return GLib.SOURCE_CONTINUE; - } - - this._recalculateWindowPositions(WindowPositionFlags.ANIMATE); - this._repositionWindowsId = 0; - return GLib.SOURCE_REMOVE; + this.layout_manager.syncStacking(stackIndices); } _doRemoveWindow(metaWin) { - let win = metaWin.get_compositor_private(); - let clone = this._removeWindowClone(metaWin); - if (clone) { - // If metaWin.get_compositor_private() returned non-NULL, that - // means the window still exists (and is just being moved to - // another workspace or something), so set its overviewHint - // accordingly. (If it returned NULL, then the window is being - // destroyed; we'd like to animate this, but it's too late at - // this point.) - if (win) { - let [stageX, stageY] = clone.get_transformed_position(); - const [transformedWidth, transformedHeight] = - clone.get_transformed_size(); + if (!clone) + return; - metaWin._overviewHint = { - x: stageX, - y: stageY, - width: transformedWidth, - height: transformedHeight, - }; - } - clone.destroy(); - } + clone.destroy(); // We need to reposition the windows; to avoid shuffling windows // around while the user is interacting with the workspace, we delay // the positioning until the pointer remains still for at least 750 ms // or is moved outside the workspace + this.layout_manager.layout_frozen = true; - // remove old handler - if (this._repositionWindowsId > 0) { - GLib.source_remove(this._repositionWindowsId); - this._repositionWindowsId = 0; + if (this._layoutFrozenId > 0) { + GLib.source_remove(this._layoutFrozenId); + this._layoutFrozenId = 0; } - // setup new handler - let [x, y] = global.get_pointer(); - this._cursorX = x; - this._cursorY = y; + let [oldX, oldY] = global.get_pointer(); - this._currentLayout = null; - this._repositionWindowsId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, WINDOW_REPOSITIONING_DELAY, - this._delayedWindowRepositioning.bind(this)); - GLib.Source.set_name_by_id(this._repositionWindowsId, '[gnome-shell] this._delayedWindowRepositioning'); + this._layoutFrozenId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + WINDOW_REPOSITIONING_DELAY, + () => { + const [newX, newY] = global.get_pointer(); + const pointerHasMoved = oldX !== newX || oldY !== newY; + const actorUnderPointer = global.stage.get_actor_at_pos( + Clutter.PickMode.REACTIVE, newX, newY); + + if ((pointerHasMoved && this.contains(actorUnderPointer)) || + this._windows.some(w => w.contains(actorUnderPointer))) { + oldX = newX; + oldY = newY; + return GLib.SOURCE_CONTINUE; + } + + this.layout_manager.layout_frozen = false; + this._layoutFrozenId = 0; + return GLib.SOURCE_REMOVE; + }); + + GLib.Source.set_name_by_id(this._layoutFrozenId, + '[gnome-shell] this._layoutFrozenId'); } _doAddWindow(metaWin) { - if (this.leavingOverview) - return; - let win = metaWin.get_compositor_private(); if (!win) { @@ -1224,23 +953,16 @@ class Workspace extends St.Widget { return; } - let clone = this._addWindowClone(metaWin, false); + this._addWindowClone(metaWin); - if (metaWin._overviewHint) { - let x = metaWin._overviewHint.x - this.x; - let y = metaWin._overviewHint.y - this.y; - const width = metaWin._overviewHint.width; - const height = metaWin._overviewHint.height; - delete metaWin._overviewHint; + if (this._layoutFrozenId > 0) { + // If a window was closed before, unfreeze the layout to ensure + // the new window is immediately shown + this.layout_manager.layout_frozen = false; - clone.positioned = true; - - clone.set_position(x, y); - clone.set_size(width, height); + GLib.source_remove(this._layoutFrozenId); + this._layoutFrozenId = 0; } - - this._currentLayout = null; - this._recalculateWindowPositions(WindowPositionFlags.ANIMATE); } _windowAdded(metaWorkspace, metaWin) { @@ -1275,7 +997,7 @@ class Workspace extends St.Widget { fadeToOverview() { // We don't want to reposition windows while animating in this way. - this._animatingWindowsFade = true; + this.layout_manager.layout_frozen = true; this._overviewShownId = Main.overview.connect('shown', this._doneShowingOverview.bind(this)); if (this._windows.length == 0) return; @@ -1319,7 +1041,7 @@ class Workspace extends St.Widget { } fadeFromOverview() { - this.leavingOverview = true; + this.layout_manager.layout_frozen = true; this._overviewHiddenId = Main.overview.connect('hidden', this._doneLeavingOverview.bind(this)); if (this._windows.length == 0) return; @@ -1327,9 +1049,9 @@ class Workspace extends St.Widget { for (let i = 0; i < this._windows.length; i++) this._windows[i].remove_all_transitions(); - if (this._repositionWindowsId > 0) { - GLib.source_remove(this._repositionWindowsId); - this._repositionWindowsId = 0; + if (this._layoutFrozenId > 0) { + GLib.source_remove(this._layoutFrozenId); + this._layoutFrozenId = 0; } if (this.metaWorkspace !== null && !this.metaWorkspace.active) @@ -1375,10 +1097,6 @@ class Workspace extends St.Widget { clone.hideOverlay(false); if (clone.metaWindow.showing_on_its_workspace()) { - clone.x = clone.boundingBox.x; - clone.y = clone.boundingBox.y; - clone.width = clone.boundingBox.width; - clone.height = clone.boundingBox.height; clone.ease({ opacity, duration, @@ -1391,20 +1109,18 @@ class Workspace extends St.Widget { } zoomToOverview() { - // Position and scale the windows. - this._recalculateWindowPositions(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL); } zoomFromOverview() { - this.leavingOverview = true; - for (let i = 0; i < this._windows.length; i++) this._windows[i].remove_all_transitions(); - if (this._repositionWindowsId > 0) { - GLib.source_remove(this._repositionWindowsId); - this._repositionWindowsId = 0; + if (this._layoutFrozenId > 0) { + GLib.source_remove(this._layoutFrozenId); + this._layoutFrozenId = 0; } + + this.layout_manager.layout_frozen = true; this._overviewHiddenId = Main.overview.connect('hidden', this._doneLeavingOverview.bind(this)); if (this.metaWorkspace !== null && !this.metaWorkspace.active) @@ -1421,10 +1137,6 @@ class Workspace extends St.Widget { if (clone.metaWindow.showing_on_its_workspace()) { clone.ease({ - x: clone.boundingBox.x, - y: clone.boundingBox.y, - width: clone.boundingBox.width, - height: clone.boundingBox.height, opacity: 255, duration: Overview.ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, @@ -1432,8 +1144,6 @@ class Workspace extends St.Widget { } else { // The window is hidden, make it shrink and fade it out clone.ease({ - width: 0, - height: 0, opacity: 0, duration: Overview.ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, @@ -1447,6 +1157,11 @@ class Workspace extends St.Widget { this._overviewHiddenId = 0; } + if (this._overviewShownId) { + Main.overview.disconnect(this._overviewShownId); + this._overviewShownId = 0; + } + if (this.metaWorkspace) { this.metaWorkspace.disconnect(this._windowAddedId); this.metaWorkspace.disconnect(this._windowRemovedId); @@ -1454,32 +1169,20 @@ class Workspace extends St.Widget { global.display.disconnect(this._windowEnteredMonitorId); global.display.disconnect(this._windowLeftMonitorId); - if (this._repositionWindowsId > 0) { - GLib.source_remove(this._repositionWindowsId); - this._repositionWindowsId = 0; - } - - if (this._positionWindowsId > 0) { - Meta.later_remove(this._positionWindowsId); - this._positionWindowsId = 0; - } - - if (this._actualGeometryLater > 0) { - Meta.later_remove(this._actualGeometryLater); - this._actualGeometryLater = 0; + if (this._layoutFrozenId > 0) { + GLib.source_remove(this._layoutFrozenId); + this._layoutFrozenId = 0; } this._windows = []; } - // Sets this.leavingOverview flag to false. _doneLeavingOverview() { - this.leavingOverview = false; + this.layout_manager.layout_frozen = false; } _doneShowingOverview() { - this._animatingWindowsFade = false; - this._recalculateWindowPositions(WindowPositionFlags.INITIAL); + this.layout_manager.layout_frozen = false; } _isMyWindow(window) { @@ -1495,9 +1198,8 @@ class Workspace extends St.Widget { } // Create a clone of a (non-desktop) window and add it to the window list - _addWindowClone(metaWindow, positioned) { + _addWindowClone(metaWindow) { let clone = new WindowPreview(metaWindow, this); - clone.positioned = positioned; clone.connect('selected', this._onCloneSelected.bind(this)); @@ -1510,9 +1212,6 @@ class Workspace extends St.Widget { clone.connect('drag-end', () => { Main.overview.endWindowDrag(metaWindow); }); - clone.connect('size-changed', () => { - this._recalculateWindowPositions(WindowPositionFlags.NONE); - }); clone.connect('show-chrome', () => { let focus = global.stage.key_focus; if (focus == null || this.contains(focus)) @@ -1524,10 +1223,10 @@ class Workspace extends St.Widget { }); }); clone.connect('destroy', () => { - this._removeWindowClone(metaWindow); + this._doRemoveWindow(metaWindow); }); - this.add_child(clone); + this.layout_manager.addWindow(clone, metaWindow); if (this._windows.length == 0) clone.setStackAbove(null); @@ -1546,94 +1245,14 @@ class Workspace extends St.Widget { if (index == -1) return null; + this.layout_manager.removeWindow(this._windows[index]); + return this._windows.splice(index, 1).pop(); } - _isBetterLayout(oldLayout, newLayout) { - if (oldLayout.scale === undefined) - return true; - - let spacePower = (newLayout.space - oldLayout.space) * LAYOUT_SPACE_WEIGHT; - let scalePower = (newLayout.scale - oldLayout.scale) * LAYOUT_SCALE_WEIGHT; - - if (newLayout.scale > oldLayout.scale && newLayout.space > oldLayout.space) { - // Win win -- better scale and better space - return true; - } else if (newLayout.scale > oldLayout.scale && newLayout.space <= oldLayout.space) { - // Keep new layout only if scale gain outweighs aspect space loss - return scalePower > spacePower; - } else if (newLayout.scale <= oldLayout.scale && newLayout.space > oldLayout.space) { - // Keep new layout only if aspect space gain outweighs scale loss - return spacePower > scalePower; - } else { - // Lose -- worse scale and space - return false; - } - } - - _getBestLayout(windows, area, rowSpacing, columnSpacing) { - // We look for the largest scale that allows us to fit the - // largest row/tallest column on the workspace. - - let lastLayout = {}; - - let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing); - - for (let numRows = 1; ; numRows++) { - let numColumns = Math.ceil(windows.length / numRows); - - // If adding a new row does not change column count just stop - // (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows -> - // 3 columns as well => just use 3 rows then) - if (numColumns == lastLayout.numColumns) - break; - - let layout = { area, strategy, numRows, numColumns }; - strategy.computeLayout(windows, layout); - strategy.computeScaleAndSpace(layout); - - if (!this._isBetterLayout(lastLayout, layout)) - break; - - lastLayout = layout; - } - - return lastLayout; - } - - _getSpacingAndPadding() { - let node = this.get_theme_node(); - - // Window grid spacing - let columnSpacing = node.get_length('-horizontal-spacing'); - let rowSpacing = node.get_length('-vertical-spacing'); - let padding = { - left: node.get_padding(St.Side.LEFT), - top: node.get_padding(St.Side.TOP), - bottom: node.get_padding(St.Side.BOTTOM), - right: node.get_padding(St.Side.RIGHT), - }; - - // All of the overlays have the same chrome sizes, - // so just pick the first one. - let clone = this._windows[0]; - let [topBorder, bottomBorder] = clone.chromeHeights(); - let [leftBorder, rightBorder] = clone.chromeWidths(); - - rowSpacing += (topBorder + bottomBorder) / 2; - columnSpacing += (rightBorder + leftBorder) / 2; - padding.top += topBorder; - padding.bottom += bottomBorder; - padding.left += leftBorder; - padding.right += rightBorder; - - return [rowSpacing, columnSpacing, padding]; - } - - _computeLayout(windows) { - let [rowSpacing, columnSpacing, padding] = this._getSpacingAndPadding(); - let area = padArea(this._fullGeometry, padding); - return this._getBestLayout(windows, area, rowSpacing, columnSpacing); + _onStyleChanged() { + const themeNode = this.get_theme_node(); + this.layout_manager.spacing = themeNode.get_length('spacing'); } _onCloneSelected(clone, time) { @@ -1666,15 +1285,6 @@ class Workspace extends St.Widget { if (this._isMyWindow(window)) return false; - // Set a hint on the Mutter.Window so its initial position - // in the new workspace will be correct - window._overviewHint = { - x: actor.x, - y: actor.y, - width: actor.width, - heigth: actor.height, - }; - // We need to move the window before changing the workspace, because // the move itself could cause a workspace change if the window enters // the primary monitor diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 591c2b46f..9335da625 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -25,13 +25,9 @@ var WorkspacesViewBase = GObject.registerClass({ this.connect('destroy', this._onDestroy.bind(this)); global.focus_manager.add_group(this); - // The actor itself isn't a drop target, so we don't want to pick on its area - this.set_size(0, 0); - this._monitorIndex = monitorIndex; this._fullGeometry = null; - this._actualGeometry = null; this._inDrag = false; this._windowDragBeginId = Main.overview.connect('window-drag-begin', this._dragBegin.bind(this)); @@ -63,12 +59,13 @@ var WorkspacesViewBase = GObject.registerClass({ setFullGeometry(geom) { this._fullGeometry = geom; - this._syncFullGeometry(); } - setActualGeometry(geom) { - this._actualGeometry = geom; - this._syncActualGeometry(); + vfunc_allocate(box) { + this.set_allocation(box); + + for (const child of this) + child.allocate_available_size(0, 0, box.get_width(), box.get_height()); } }); @@ -100,12 +97,9 @@ class WorkspacesView extends WorkspacesViewBase { this._updateWorkspaceActors(false); }); - - this._overviewShownId = - Main.overview.connect('shown', () => { - this.set_clip(this._fullGeometry.x, this._fullGeometry.y, - this._fullGeometry.width, this._fullGeometry.height); - }); + this._overviewShownId = Main.overview.connect('shown', () => { + this.clip_to_allocation = true; + }); this._switchWorkspaceNotifyId = global.window_manager.connect('switch-workspace', @@ -117,16 +111,6 @@ class WorkspacesView extends WorkspacesViewBase { this._workspaces[i].setReservedSlot(window); } - _syncFullGeometry() { - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].setFullGeometry(this._fullGeometry); - } - - _syncActualGeometry() { - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].setActualGeometry(this._actualGeometry); - } - getActiveWorkspace() { let workspaceManager = global.workspace_manager; let active = workspaceManager.get_active_workspace_index(); @@ -144,7 +128,7 @@ class WorkspacesView extends WorkspacesViewBase { } animateFromOverview(animationType) { - this.remove_clip(); + this.clip_to_allocation = false; for (let w = 0; w < this._workspaces.length; w++) { if (animationType == AnimationType.ZOOM) @@ -242,12 +226,8 @@ class WorkspacesView extends WorkspacesViewBase { } } - if (this._fullGeometry) { + if (this._fullGeometry) this._updateWorkspaceActors(false); - this._syncFullGeometry(); - } - if (this._actualGeometry) - this._syncActualGeometry(); } _activeWorkspaceChanged(_wm, _from, _to, _direction) { @@ -353,14 +333,6 @@ class ExtraWorkspaceView extends WorkspacesViewBase { this._workspace.setReservedSlot(window); } - _syncFullGeometry() { - this._workspace.setFullGeometry(this._fullGeometry); - } - - _syncActualGeometry() { - this._workspace.setActualGeometry(this._actualGeometry); - } - getActiveWorkspace() { return this._workspace; } @@ -452,6 +424,7 @@ class WorkspacesDisplay extends St.Widget { this._scrollEventId = 0; this._keyPressEventId = 0; this._scrollTimeoutId = 0; + this._syncActualGeometryLater = 0; this._actualGeometry = null; this._fullGeometry = null; @@ -476,6 +449,11 @@ class WorkspacesDisplay extends St.Widget { this._parentSetLater = 0; } + if (this._syncActualGeometryLater) { + Meta.later_remove(this._syncActualGeometryLater); + this._syncActualGeometryLater = 0; + } + if (this._scrollTimeoutId !== 0) { GLib.source_remove(this._scrollTimeoutId); this._scrollTimeoutId = 0; @@ -755,7 +733,17 @@ class WorkspacesDisplay extends St.Widget { const height = this.allocation.get_height(); this._actualGeometry = { x, y, width, height }; - this._syncWorkspacesActualGeometry(); + + if (this._syncActualGeometryLater > 0) + return; + + this._syncActualGeometryLater = + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { + this._syncWorkspacesActualGeometry(); + + this._syncActualGeometryLater = 0; + return GLib.SOURCE_REMOVE; + }); } _syncWorkspacesActualGeometry() { @@ -765,7 +753,9 @@ class WorkspacesDisplay extends St.Widget { let monitors = Main.layoutManager.monitors; for (let i = 0; i < monitors.length; i++) { let geometry = i === this._primaryIndex ? this._actualGeometry : monitors[i]; - this._workspacesViews[i].setActualGeometry(geometry); + const { x, y, width, height } = geometry; + + this._workspacesViews[i].set({ x, y, width, height }); } }