Seven characters of an app name, and the cell was never the problem
The app grid on this e-ink tablet showed `Document ...`, `Byobu Ter...`, `ImageMagi...`, `Midnight C...`. About seven characters and then an ellipsis, on a 10.3" display with a great deal of white space on it. Four of twenty-six apps were unreadable, and two of them were distinguishable only by their first word.
The obvious move is to make the cells wider. It does not work, and the reason it cannot work is the interesting part.
The cell is square, and that is not a style choice
GNOME's icon grid decides in this order:
- Pick a grid mode — a rows × columns pair — by comparing the page's aspect ratio to each mode's.
- Pick an icon size: walk a fixed list (96, 64, 48, 32, 24, 16) and take the first that fits.
- Compute the cell size as `Math.max(minWidth, minHeight)` over every item.
That third line is the one that matters. One number comes out, and it is used for both the width and the height of every cell. The cells are square, and the side is whatever the tallest tile needs — an icon, a line of label, and padding.
The allocation that consumes it is a `vfunc`, which in a GNOME extension means it cannot be patched from JavaScript at all: Clutter calls the pointer bound when the class was registered, and overriding the prototype changes nothing. Ordinary methods on the same object patch fine. Both were true in the same file on the same evening, which is a confusing thing to learn by experiment.
So: **the cell is square, height decides the side, and width has nothing to spend its surplus on.** Measured on this panel, the vertical budget per cell was 123px against a cell of 119 — four pixels of slack — while horizontally there were 21. The extra width went into the gaps until it hit `max-column-spacing: 36px`, and the remainder, 57 pixels of it, became dead margin at the edges.
That is why the gaps looked wrong. The horizontal gap was 36 and the vertical was 18, and the 36 was not a designed value. It was the ceiling.
GNOME can already show the whole name
`AppIcon._updateMultiline()` switches a label to `line_wrap: true, ellipsize: NONE`. The full name, wrapped, no ellipsis. It runs on hover and on keyboard focus.
This device has a touchscreen and nothing else. There is no hover state, and with no keyboard there is no way to move focus onto an icon. Tapping launches the app; a long press opens the context menu. Neither reveals the name.
So the names are not designed to be clipped here. They are clipped by a condition that cannot become true on this hardware. Filed upstream, narrowly: keyboard navigation does expand the label, so this is specifically about devices with neither a pointer nor a keyboard.
Turning it on permanently is not enough on its own, and the failure is instructive. The expanded label is meant to *overflow and cover its neighbours* — that is the hover affordance. The allocation hands each tile `Math.max(childSize, naturalWidth)`, and a wrapping label's natural width is the entire string on one line. Switch it on for every icon and they overlap.
What the labels needed was a width to wrap *inside*. And a height, pinned to two lines — because `_getChildrenMaxSize` asks for preferred height at width `-1`, and an unconstrained wrapping label is one line tall. Nothing reserves room for the second, and a three-line name (there is exactly one: `ImageMagick (color depth=q16)`) runs off the bottom of the screen. Two lines is the cap. The outlier gets an ellipsis rather than setting the height of every cell in the grid.
Then stop handing it the whole screen
With the names fixed, the shape was still wrong, and the wrongness had one source: we were giving the grid the entire display and then complaining about how it filled it.
The chain only runs one way. Height is fixed, so height decides the cell size. Cell size and row count decide the vertical gap. And the only free variable left is the width — which means the width is not an input, it is the answer to a question:
How wide does the canvas have to be for the horizontal gap to equal the vertical one?
Computed and applied: **874px wide, gaps of 14 and 14, and zero dead margin.** The ceilings that were clamping the answer got raised out of the way, and the app display box is now centred at the width the arithmetic asks for rather than stretched to the panel.
Most names fit outright now — `Contacts`, `Weather`, `Calendar`, `Extensions`, `LibreOffice` were all truncated before. The two-line ones are legible in full.
Two things got measured wrong on the way there
Both are the same mistake wearing different clothes, and both were caught by the numbers rather than by thinking harder.
**The canvas was sized from the previous frame.** `childSize` is cached, and the label heights land after the canvas is computed, so the first version solved for a cell size that was one frame old. Invalidate before measuring.
**The formula was right and the input was wrong.** The gap came out 19.4 against 14 with correct arithmetic. The height being fed in was the box I hand to the app display; the height the vertical spacing is actually computed from is the layout's own `_pageHeight`, 16 pixels smaller. Two quantities that are the same thing, taken from two sources, one of which is not the one doing the work.
That second one showed up again later the same evening, in a different component, and it is the durable lesson from this piece of work: **when a number appears twice, exactly one of the two places is where it comes from.** Centring on the container rather than the visible box inside it is the same error, and it cost another round.
The generalisable part
If a layout has a constraint you cannot change — square cells, an allocation you cannot patch, a fixed list of sizes — then the surplus in the unconstrained direction cannot be spent on the thing you want. It will turn into gap, or into margin, or into a ceiling being hit. Widening the cell was never going to widen the label, because the cell was never the variable.
The move that worked was to stop treating the canvas as a given. Everything downstream of it was already determined; the only real decision left was how big to make the thing everything else derives from.
The boring footnote
The extension, the measurements, and the reasoning are in CVERInc/pinenote, MIT. It is device-specific by design — the icon size, the two-line cap and the canvas width are all derived from this panel, not typed in.
Earlier from the same tablet: the picture that outlived the battery, and the file the driver never read.
Keep reading
-
I could measure everything except where the finger landed
The on-screen keyboard would not come up in a save dialog. I ruled out three causes with real measurements, all of them wrong, and the answer turned out to be a sentence I have no way to produce: I tapped the text that was already selected.
-
The keyboard had an Escape key. The rename box could not reach it.
Renaming a folder on a touch-only tablet brought up a keyboard with no way to cancel. Our own keyboard has an Escape key and a digit row — and a condition, written for a good reason, that had outlived the reason by about a year.
-
It only just fits, which is why it broke
A folder dialog whose icons appeared large and then shrank. Five changes had accumulated on it, and the one that caused the jump was not the one that looked suspicious — it was the one that crossed a threshold nobody knew was there, five pixels away.