From 72ed457d107405ae5c67cbf906165a84756f5862 Mon Sep 17 00:00:00 2001 From: ChrisB85 Date: Thu, 27 Aug 2026 10:29:27 +0200 Subject: [PATCH 1/4] grouped-window-list: keep a gap between the label and the button edge allocate() set childBox.x2 = box.x2, so the label ended flush with the button edge and an ellipsized title touched the border. On a horizontal panel there is no slack there, because setIconPadding() zeroes the actor padding. Reserve the same 6 px that getPreferredWidth() already adds to natural_size, mirrored for RTL. --- .../applets/grouped-window-list@cinnamon.org/appGroup.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js index a9e20e400c..fbe2959bd1 100644 --- a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js +++ b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js @@ -472,11 +472,15 @@ var AppGroup = class AppGroup { childBox.y1 = box.y1 + labelYPadding; childBox.y2 = childBox.y1 + Math.min(labelNaturalHeight, allocHeight); + // The same 6 px getPreferredWidth adds to natural_size - without it the label ends flush + // with the button edge and the ellipsis touches the border. + const labelPadding = 6 * global.ui_scale; + if (direction === Clutter.TextDirection.LTR) { childBox.x1 = Math.min(this.iconBox.x + this.iconBox.width, box.x2); - childBox.x2 = box.x2; + childBox.x2 = Math.max(childBox.x1, box.x2 - labelPadding); } else { - childBox.x1 = box.x1; + childBox.x1 = Math.min(box.x1 + labelPadding, this.iconBox.x); childBox.x2 = this.iconBox.x; } From 545875afa8e38c42868623e246a41ade8e20194c Mon Sep 17 00:00:00 2001 From: ChrisB85 Date: Thu, 27 Aug 2026 10:29:39 +0200 Subject: [PATCH 2/4] grouped-window-list: show the title when a pinned app opens a window showLabel() bailed out on !this.label.realized, and a hidden label (hideLabel() calls label.hide()) is exactly that. A pinned app with no windows was therefore stuck at labelVisiblePref=false: opening a window set the text but the button kept its icon-only width and showed no title. Drop realized from the early return and skip the animation for an unrealized label instead. --- .../grouped-window-list@cinnamon.org/appGroup.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js index fbe2959bd1..39ece801ad 100644 --- a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js +++ b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js @@ -517,11 +517,17 @@ var AppGroup = class AppGroup { if (this.labelVisiblePref || !this.label || !this.state.isHorizontal - || this.label.is_finalized() - || !this.label.realized) { + || this.label.is_finalized()) { return; } + // hideLabel() hides the label, so unrealized is the normal state of a pinned app with no + // windows. Bailing out here left such a button titleless once a window was opened. + // An unrealized actor is shown right away instead of being animated. + if (!this.label.realized) { + animate = false; + } + const width = MAX_BUTTON_WIDTH * global.ui_scale; this.labelVisiblePref = true; From a5fa2290d96319cd91773e95b87564c8aaa31f97 Mon Sep 17 00:00:00 2001 From: ChrisB85 Date: Thu, 27 Aug 2026 10:29:53 +0200 Subject: [PATCH 3/4] grouped-window-list: equal width for buttons without a label The allocateForLabel branch counted labelNaturalSize even with no windows open. An empty label has a non-zero, state-dependent width (1 px or 5 px), so two pinned apps without windows ended up 30 px and 29 px wide. With no window there is nothing to label, so such a button now takes the same path as with labels turned off. That path also uses the panel icon size rather than the icon actor's natural width, which depends on the artwork a given app ships. --- .../grouped-window-list@cinnamon.org/appGroup.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js index 39ece801ad..dcfbc67840 100644 --- a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js +++ b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js @@ -382,12 +382,18 @@ var AppGroup = class AppGroup { this.workspaceState.lastFocusedApp === appId); if (this.state.orientation === St.Side.TOP || this.state.orientation === St.Side.BOTTOM) { - if (allocateForLabel) { - const max = this.labelVisiblePref && this.groupState.metaWindows.length > 0 ? - labelNaturalSize + iconNaturalSize + 6 : 0; + // With no open window there is nothing to label (allocate() draws the label only when + // metaWindows.length > 0), so the button is icon only. Counting labelNaturalSize in here + // gave pinned apps arbitrarily different widths: an empty label reports 1 px or 5 px + // depending on its state. + if (allocateForLabel && this.groupState.metaWindows.length > 0) { + const max = this.labelVisiblePref ? labelNaturalSize + iconNaturalSize + 6 : 0; alloc.natural_size = Math.min(iconNaturalSize + Math.max(max, labelNaturalSize), MAX_BUTTON_WIDTH * global.ui_scale); } else { - alloc.natural_size = iconNaturalSize + 6 * global.ui_scale; + // Width from the panel icon size instead of the actor's natural width, so buttons with + // no label stay equal no matter what a particular image reports. + const iconWidth = this.iconSize ? this.iconSize * global.ui_scale : iconNaturalSize; + alloc.natural_size = iconWidth + 6 * global.ui_scale; } alloc.min_size = alloc.natural_size; } else { From 12f3f677253e388f43258789455776205fe1ee8d Mon Sep 17 00:00:00 2001 From: ChrisB85 Date: Thu, 27 Aug 2026 10:47:51 +0200 Subject: [PATCH 4/4] grouped-window-list: stop painting the label after the button shrinks allocate() only calls label.allocate() when drawLabel is true. Clutter keeps the previous allocation of an actor that is skipped during a layout pass and still paints it, so a button that loses its last window - and with it the room for a title - leaves the old, wide label box in place. The text then spills out of the button and over its neighbours. Allocate a collapsed box in the else branch instead of skipping the label. --- .../applets/grouped-window-list@cinnamon.org/appGroup.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js index dcfbc67840..1ec699a6c6 100644 --- a/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js +++ b/files/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/appGroup.js @@ -502,6 +502,13 @@ var AppGroup = class AppGroup { this.label.set_style('text-align: right;'); this.label.allocate(childBox); + } else { + // An actor that is not allocated keeps its previous allocation and is still painted. + // A button that shrinks to icon size therefore left the old, wide label box behind and + // the text spilled over the neighbouring buttons. Collapse the box instead of skipping it. + childBox.x1 = childBox.x2 = box.x1; + childBox.y1 = childBox.y2 = box.y1; + this.label.allocate(childBox); } // Call set_icon_geometry for support of Cinnamon's minimize animation