From bc5aa0d9dea67fb909d2e5715368551e5b32a66b Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 26 Dec 2025 12:03:49 -0500 Subject: [PATCH 1/7] workspace: Don't freeze layout when closing overview This fixes a bug where windows would retrieve and interpolate the wrong dimensions when closing the overview, resulting in translating the wrong direction. --- js/ui/workspace.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index ccd23ae6b1..fa01d2a117 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -1297,7 +1297,6 @@ class Workspace extends St.Widget { this._layoutFrozenId = 0; } - this._container.layout_manager.layout_frozen = true; Main.overview.connectObject( 'hidden', this._doneLeavingOverview.bind(this), this); } -- GitLab From 3a64863c2096c462827a6f9872a5fc5f51d253cf Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 19 Dec 2025 10:42:28 -0500 Subject: [PATCH 2/7] workspace: Translate origins of larger window dimensions Currently, the allocation uses larger values for the dimensions of the windows, which amplifies their curved movement when animated. This new method shifts the origin of the larger dimensions so that the centers of both dimensions are identical. Related: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2968 --- js/ui/workspace.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index fa01d2a117..85b4358943 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -724,8 +724,14 @@ export const WorkspaceLayout = GObject.registerClass({ // actually larger than the target layout size, that is while // animating between the session and the window picker. if (inSessionTransition) { - workspaceBoxWidth = Math.max(workspaceBoxWidth, width); - workspaceBoxHeight = Math.max(workspaceBoxHeight, height); + const finalWidth = Math.max(workspaceBoxWidth, width); + const finalHeight = Math.max(workspaceBoxHeight, height); + + workspaceBoxX += (workspaceBoxWidth - finalWidth) / 2; + workspaceBoxY += (workspaceBoxHeight - finalHeight) / 2; + + workspaceBoxWidth = finalWidth; + workspaceBoxHeight = finalHeight; } x = Util.lerp(workspaceBoxX, x, stateAdjustementValue); -- GitLab From e22036389924e095d9ddee2f837b39aab0b2083c Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sat, 27 Dec 2025 22:13:07 -0500 Subject: [PATCH 3/7] workspace: Make `computeWindowScale` a public method This will be needed to interpolate window dimensions during allocation. --- js/ui/workspace.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 85b4358943..d1924913bd 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -63,7 +63,7 @@ const BACKGROUND_CORNER_RADIUS_PIXELS = 30; // computing an optimal scale for each window that fits the constraints, // and doesn't make the thumbnail too small to see. There are two factors // involved in thumbnail scale to make sure that these two goals are met: -// the window scale (calculated by _computeWindowScale) and the layout +// the window scale (calculated by computeWindowScale) and the layout // scale (calculated by computeSizeAndScale). // // The calculation logic becomes slightly more complicated because row @@ -163,24 +163,6 @@ class UnalignedLayoutStrategy extends LayoutStrategy { }; } - // Computes and returns an individual scaling factor for @window, - // to be applied in addition to the overall layout scale. - _computeWindowScale(window) { - // Since we align windows next to each other, the height of the - // thumbnails is much more important to preserve than the width of - // them, so two windows with equal height, but maybe differering - // widths line up. - const ratio = window.boundingBox.height / this._monitor.height; - - // The purpose of this manipulation here is to prevent windows - // from getting too small. For something like a calculator window, - // we need to bump up the size just a bit to make sure it looks - // good. We'll use a multiplier of 1.5 for this. - - // Map from [0, 1] to [1.5, 1] - return Util.lerp(1.5, 1, ratio); - } - _computeRowSizes(layout) { const {rows, scale} = layout; for (let i = 0; i < rows.length; i++) { @@ -223,7 +205,7 @@ class UnalignedLayoutStrategy extends LayoutStrategy { let totalWidth = 0; for (let i = 0; i < windows.length; i++) { const window = windows[i]; - const s = this._computeWindowScale(window); + const s = this.computeWindowScale(window); totalWidth += window.boundingBox.width * s; } @@ -241,7 +223,7 @@ class UnalignedLayoutStrategy extends LayoutStrategy { for (; windowIdx < sortedWindows.length; windowIdx++) { const window = sortedWindows[windowIdx]; - const s = this._computeWindowScale(window); + const s = this.computeWindowScale(window); const width = window.boundingBox.width * s; const height = window.boundingBox.height * s; row.fullHeight = Math.max(row.fullHeight, height); @@ -299,6 +281,24 @@ class UnalignedLayoutStrategy extends LayoutStrategy { return [scale, space]; } + // Computes and returns an individual scaling factor for @window, + // to be applied in addition to the overall layout scale. + computeWindowScale(window) { + // Since we align windows next to each other, the height of the + // thumbnails is much more important to preserve than the width of + // them, so two windows with equal height, but maybe differering + // widths line up. + const ratio = window.boundingBox.height / this._monitor.height; + + // The purpose of this manipulation here is to prevent windows + // from getting too small. For something like a calculator window, + // we need to bump up the size just a bit to make sure it looks + // good. We'll use a multiplier of 1.5 for this. + + // Map from [0, 1] to [1.5, 1] + return Util.lerp(1.5, 1, ratio); + } + computeWindowSlots(layout, area) { this._computeRowSizes(layout); @@ -356,7 +356,7 @@ class UnalignedLayoutStrategy extends LayoutStrategy { for (let j = 0; j < row.windows.length; j++) { const window = row.windows[j]; - let s = scale * this._computeWindowScale(window) * row.additionalScale; + let s = scale * this.computeWindowScale(window) * row.additionalScale; const cellWidth = window.boundingBox.width * s; const cellHeight = window.boundingBox.height * s; -- GitLab From 7fd8c5be030b2928bf590706f35f3bc4bfca773b Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sat, 27 Dec 2025 18:18:53 -0500 Subject: [PATCH 4/7] workspace: Interpolate window dimensions during transition Currently, when transitioning to/from the overview, the dimension of windows that shrink to WINDOW_PREVIEW_MAXIMUM_SCALE (0.95) will exhibit an abrupt transition, where it will jump from 100% of the size to 95% instantly. This new method changes the way windows resize during transition by interpolating the sizes of windows. Related: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2968 --- js/ui/workspace.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index d1924913bd..ee7f44222d 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -724,14 +724,20 @@ export const WorkspaceLayout = GObject.registerClass({ // actually larger than the target layout size, that is while // animating between the session and the window picker. if (inSessionTransition) { - const finalWidth = Math.max(workspaceBoxWidth, width); - const finalHeight = Math.max(workspaceBoxHeight, height); + const scale = Math.min( + this._layoutStrategy.computeWindowScale(child), WINDOW_PREVIEW_MAXIMUM_SCALE); - workspaceBoxX += (workspaceBoxWidth - finalWidth) / 2; - workspaceBoxY += (workspaceBoxHeight - finalHeight) / 2; + const finalWidth = child.boundingBox.width * scale; + const finalHeight = child.boundingBox.height * scale; - workspaceBoxWidth = finalWidth; - workspaceBoxHeight = finalHeight; + const currentWidth = Util.lerp(child.boundingBox.width, finalWidth, stateAdjustementValue); + const currentHeight = Util.lerp(child.boundingBox.height, finalHeight, stateAdjustementValue); + + workspaceBoxX += (workspaceBoxWidth - currentWidth) / 2; + workspaceBoxY += (workspaceBoxHeight - currentHeight) / 2; + + workspaceBoxWidth = currentWidth; + workspaceBoxHeight = currentHeight; } x = Util.lerp(workspaceBoxX, x, stateAdjustementValue); -- GitLab From ee8af26cd9f8efc926377a7e8b2c884e085658fc Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Fri, 26 Dec 2025 17:33:44 -0500 Subject: [PATCH 5/7] workspace: Lerp as low as WINDOW_PREVIEW_MAXIMUM_SCALE When lerping, the target is now being set to WINDOW_PREVIEW_MAXIMUM_SCALE (0.95), which will shrink windows as low as the value more naturally. --- js/ui/workspace.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index ee7f44222d..b8e7c99e99 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -295,8 +295,8 @@ class UnalignedLayoutStrategy extends LayoutStrategy { // we need to bump up the size just a bit to make sure it looks // good. We'll use a multiplier of 1.5 for this. - // Map from [0, 1] to [1.5, 1] - return Util.lerp(1.5, 1, ratio); + // Map from [0, 1] to [1.5, WINDOW_PREVIEW_MAXIMUM_SCALE] + return Util.lerp(1.5, WINDOW_PREVIEW_MAXIMUM_SCALE, ratio); } computeWindowSlots(layout, area) { -- GitLab From 09942e31e1c3f6fd3714e680dd98440bd92b1a2f Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sun, 28 Dec 2025 10:59:19 -0500 Subject: [PATCH 6/7] overviewControls: Use ease-in-out-quad when transitioning into overview Same reason as https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4000. The first quarter of transitioning into the overview changes the most compared to the rest, so it's best to start with a slow curve at the beginning without compromising too much at the end. Ease-in-out-quad seems to provide the best compromise. --- js/ui/overviewControls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js index 135168194c..07560faec2 100644 --- a/js/ui/overviewControls.js +++ b/js/ui/overviewControls.js @@ -724,7 +724,7 @@ class ControlsManager extends St.Widget { this._stateAdjustment.value = ControlsState.HIDDEN; this._stateAdjustment.ease(state, { duration: Overview.ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_SINE, + mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, onStopped: () => { if (callback) callback(); -- GitLab From 9571963d83015a309f41cf16ded7894163de980c Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Mon, 5 Jan 2026 20:19:21 -0500 Subject: [PATCH 7/7] workspace: Avoid manipulating size when not transitioning into overview This fixes a bug which made windows overshoot when transitioning between the app grid and window manager. See: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4010#note_2644855 --- js/ui/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index b8e7c99e99..d3ea85fd82 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -723,7 +723,7 @@ export const WorkspaceLayout = GObject.registerClass({ // We only want to apply this when the scaled floating size is // actually larger than the target layout size, that is while // animating between the session and the window picker. - if (inSessionTransition) { + if (inSessionTransition && stateAdjustementValue > 0) { const scale = Math.min( this._layoutStrategy.computeWindowScale(child), WINDOW_PREVIEW_MAXIMUM_SCALE); -- GitLab