[gnome-shell] workspace: Use the new WorkspaceLayout for allocating window clones



commit 751189253a25b81faa97551e4d3d5c11e1022f61
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Wed Jun 3 18:17:54 2020 +0200

    workspace: Use the new WorkspaceLayout for allocating window clones
    
    Switch to the new WorkspaceLayout layout manager to allocate the window
    clones of the overview properly using Clutters layouting mechanisms.
    
    Since we now no longer make use of the fullGeometry, we can remove the
    setFullGeometry() function from the Workspace class. Also we can stop
    setting the actualGeometry on the Workspaces and WorkspaceViews and
    instead just set the fixed position and size of the views to their
    full or actual geometry. This also has the benefit that we no longer
    have to set a custom clip, but can simply enable clip_to_allocation.
    
    The geometry needs to be set inside a BEFORE_REDRAW later because
    _updateWorkspacesActualGeometry() is called from a notify::allocation
    handler.
    
    This isn't doing any animations when showing/hiding the overview yet,
    we'll add that in the next commit.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1305

 .../gnome-shell-sass/widgets/_window-picker.scss   |   8 +-
 js/ui/windowPreview.js                             |   2 -
 js/ui/workspace.js                                 | 548 +++------------------
 js/ui/workspacesView.js                            |  70 ++-
 4 files changed, 113 insertions(+), 515 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_window-picker.scss 
b/data/theme/gnome-shell-sass/widgets/_window-picker.scss
index 48d6f73f56..1b93218517 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 b7fc5efa5a..69e3ee5748 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 ef4fc3a492..182f350ae9 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.leavingOverview = false;
-
-        this._positionWindowsFlags = 0;
-        this._positionWindowsId = 0;
-    }
+        this._layoutFrozenId = 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();
-
-                metaWin._overviewHint = {
-                    x: stageX,
-                    y: stageY,
-                    width: transformedWidth,
-                    height: transformedHeight,
-                };
-            }
-            clone.destroy();
-        }
+        if (!clone)
+            return;
+
+        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._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._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.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);
-
-        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;
+        this._addWindowClone(metaWin);
 
-            clone.positioned = true;
+        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.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;
 
-        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;
-    }
+        this.layout_manager.removeWindow(this._windows[index]);
 
-    _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];
+        return this._windows.splice(index, 1).pop();
     }
 
-    _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 591c2b46f0..9335da625f 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 });
         }
     }
 


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