fix(panel): make the panel's right padding match its left #24

Merged
yuris merged 1 commit from fix/right-padding into main 2026-09-06 08:52:36 +00:00
Owner

the right padding seems to be bigger than the left, and that's what I want to
fix. Reduce the right padding to match the left.

Correct, and #23 is what caused it. Measured on the running panel before this
change:

gap between content and panel border
left 18 px
right 36 px

Why #23 did that

#23 gave every block on the right one edge by widening the column with the
scrollbar's strip (RIGHT_W = CONTENT_W + 4 + 14 = 323) and paying for it in
plugin.toml's width (578 → 596). That put the strip in the padding.

Everything outside the column is already symmetric — the shell insets a
decorated panel by Style::panelPadding (14) a side, and the root row adds its
own 4 — so any width the column carries beyond its content lands in the
right-hand gap and doubles it.

The fix

The column is its content, and the strip comes out of the scroll:

CONTENT_W = BAR_W * DAYS + BAR_GAP * (DAYS - 1)             -- 305, the chart
RIGHT_W   = CONTENT_W                                       -- 305, was 323
CHROME_W  = CONTENT_W                                       -- 305
LIST_W    = RIGHT_W - 2 * VIEWPORT_INSET - SCROLLBAR_GUTTER -- 283, was 301

plugin.toml's width goes back to 578. The chart is untouched.

Verified on screen

Panel spans x=671..1249 (578 wide, as declared):

measured
left column's content starts x = 690 → 19 px inside the left border
search field's frame ends x = 1230 → 19 px inside the right border

Both guides in the screenshot sit the same distance from their borders: the left
one touches the title, the card and the chips; the right one touches the chart's
last bar, the search field and the scope total.

The one thing that does not reach the shared edge

The history rows. This is forced, not chosen — the strip has to come out of some
node's width and the scroll is the only candidate:

  • a row cannot be wider than the scroll that clips it
  • ScrollView::m_viewportPaddingH (4 px) is unreachable from the plugin whitelist
  • the gutter has to be reserved, or the duration column slides 14 px the instant
    the list starts scrolling (m_scrollbarShown flips at that exact row count,
    scroll_view.cpp:429)

So the list reads as indented under its own chrome, and the durations end 18 px
short of the total above them. The alternative is 18 px of dead air down the
panel's right edge, which is what this PR removes.

If you would rather the durations reached the edge, dropping
SCROLLBAR_GUTTER from LIST_W gets them to 4 px short instead of 18 — at the
cost of the scrollbar painting over the duration column whenever the list
scrolls. One-line change; say so and I will make it.

Suite

17 states, 2358 field comparisons / ok; lint 0 errors, 0 warnings.

The padding claim is now pinned directly: scrollNode().props.width == chromeW,
i.e. the content's right edge is the column's. The row's inset is pinned as a
decision (rowW == chromeW - 2 * INSET - GUTTER) rather than left as whatever
fell out.

10 mutations, no survivors — including P1, re-widening the column by the strip,
which is exactly the regression this fixes, and P4, dropping the gutter
reservation.

> the right padding seems to be bigger than the left, and that's what I want to > fix. Reduce the right padding to match the left. Correct, and #23 is what caused it. Measured on the running panel before this change: | | gap between content and panel border | |---|---| | left | 18 px | | right | **36 px** | ## Why #23 did that #23 gave every block on the right one edge by widening the column with the scrollbar's strip (`RIGHT_W = CONTENT_W + 4 + 14` = 323) and paying for it in `plugin.toml`'s `width` (578 → 596). That put the strip *in the padding*. Everything outside the column is already symmetric — the shell insets a decorated panel by `Style::panelPadding` (14) a side, and the root row adds its own 4 — so **any width the column carries beyond its content lands in the right-hand gap and doubles it.** ## The fix The column is its content, and the strip comes out of the scroll: ``` CONTENT_W = BAR_W * DAYS + BAR_GAP * (DAYS - 1) -- 305, the chart RIGHT_W = CONTENT_W -- 305, was 323 CHROME_W = CONTENT_W -- 305 LIST_W = RIGHT_W - 2 * VIEWPORT_INSET - SCROLLBAR_GUTTER -- 283, was 301 ``` `plugin.toml`'s `width` goes back to 578. The chart is untouched. ## Verified on screen Panel spans x=671..1249 (578 wide, as declared): | | measured | |---|---| | left column's content starts | x = 690 → **19 px** inside the left border | | search field's frame ends | x = 1230 → **19 px** inside the right border | Both guides in the screenshot sit the same distance from their borders: the left one touches the title, the card and the chips; the right one touches the chart's last bar, the search field and the scope total. ## The one thing that does not reach the shared edge The history rows. This is forced, not chosen — the strip has to come out of some node's width and the scroll is the only candidate: - a row cannot be wider than the scroll that clips it - `ScrollView::m_viewportPaddingH` (4 px) is unreachable from the plugin whitelist - the gutter has to be reserved, or the duration column slides 14 px the instant the list starts scrolling (`m_scrollbarShown` flips at that exact row count, `scroll_view.cpp:429`) So the list reads as indented under its own chrome, and the durations end 18 px short of the total above them. The alternative is 18 px of dead air down the panel's right edge, which is what this PR removes. **If you would rather the durations reached the edge**, dropping `SCROLLBAR_GUTTER` from `LIST_W` gets them to 4 px short instead of 18 — at the cost of the scrollbar painting over the duration column whenever the list scrolls. One-line change; say so and I will make it. ## Suite `17 states, 2358 field comparisons` / `ok`; lint `0 errors, 0 warnings`. The padding claim is now pinned directly: `scrollNode().props.width == chromeW`, i.e. the content's right edge *is* the column's. The row's inset is pinned as a decision (`rowW == chromeW - 2 * INSET - GUTTER`) rather than left as whatever fell out. 10 mutations, no survivors — including `P1`, re-widening the column by the strip, which is exactly the regression this fixes, and `P4`, dropping the gutter reservation.
The previous wave gave every block on the right one edge by widening the
column with the scrollbar's strip and paying for it in plugin.toml's
`width`. That put the strip in the panel's right padding: 36 px of air
between the content and the right border against 18 on the left.

Everything outside the column is symmetric already -- the shell insets a
decorated panel by Style::panelPadding (14) a side and the root row adds
its own 4 -- so any width the column carries beyond its content lands in
that gap and doubles it. The column is now its content:

  RIGHT_W  = CONTENT_W                                      -- 305
  CHROME_W = CONTENT_W
  LIST_W   = RIGHT_W - VIEWPORT_INSET * 2 - SCROLLBAR_GUTTER -- 283

and plugin.toml's width goes back to 578. Measured on a rendered panel:
the left column's content starts 19 px inside the left border and the
search field's frame ends 19 px inside the right one.

The strip has to come out of some node's width and the scroll is the only
candidate, so the history rows are the one block that cannot reach the
shared edge: a row cannot be wider than the scroll that clips it,
m_viewportPaddingH is unreachable from the plugin whitelist, and the
gutter has to be reserved or the duration column slides 14 px the moment
the list starts scrolling. The list reads as indented under its own
chrome, which is the cost of not having 18 px of dead air down the
panel's right edge.

The suite pins the padding claim directly -- the scroll's width equals
the chrome's, so the content's right edge IS the column's -- and pins the
row's inset as a decision rather than a leftover.
yuris merged commit b616843003 into main 2026-09-06 08:52:36 +00:00
yuris deleted branch fix/right-padding 2026-09-06 08:52:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/noctalia-timew!24
No description provided.