Wrap node descriptions and notes in the inspector
game-maker / gate (push) Successful in 3m23s
game-maker / suite (push) Successful in 16m24s

A node's description and every note in the inspector were one line
cut with an ellipsis. Stop pattern says what it does with no pattern
picked, but only past the cut, so nobody could read it.

- Label takes a wrap option. It breaks its text at the width it gets
  and keeps the margin a single line has, above and below all lines.
- Widget.heightFor(width) gives the height at a width. It is
  measure().h everywhere except where text wraps.
- VStack and SectionStack lay a column out with it, at the width
  each row will get.
- ScrollContainer measures again at the narrower width when its
  scrollbar shows, because the text then takes more lines.
- addNote and the empty inspector's hint wrap.
- The file list's own heightFor(count) is now heightOfRows, so the
  layout does not hand it a width as a row count.

Tests:
- Unit scroll-container: 7 green, 2 new (height for width, again
  beside the scrollbar)
- Unit ui + inspector: 17 files, 336 green (before the 2 new ones)
- e2e 02 (stein2, atx busy with suite 545): ALL PASSED, 29 checks
- Screenshot: Stop pattern shows its description in six lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-06 19:54:43 +02:00
1 parent 5fde3e975e
commit 32051b5097
11 files changed
+133 -22

No files matched your search

+38 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { ScrollContainer, Spacer, Theme, VStack } from '../../src/ui';
import { ScrollContainer, Spacer, Theme, VStack, Widget } from '../../src/ui';
/** Row tops after a layout; an unlaid-out stack leaves every child at 0. */
const tops = (stack: VStack) => stack.children.map((c) => c.y);
@@ -61,3 +61,40 @@ describe('a scroll container and its scrollbar', () => {
expect(rows.w).toBe(200);
});
});
/** Stands in for wrapped text: 20 px per line, a line per 100 px of text. */
class Wrapping extends Widget {
constructor(private readonly textWidth: number) {
super();
}
override heightFor(width: number): number {
return Math.ceil(this.textWidth / width) * 20;
}
}
describe('a scroll container with content that wraps', () => {
it('gives a row the height it needs at the width it gets', () => {
const scroll = new ScrollContainer();
scroll.resize(200, 300);
const rows = new VStack();
const text = rows.add(new Wrapping(500));
rows.add(new Spacer(20));
scroll.setContent(rows);
expect(text.h).toBe(60);
expect(tops(rows)).toEqual([0, 60]);
});
it('measures again once the scrollbar narrows the content', () => {
const scroll = new ScrollContainer();
scroll.resize(200, 100);
const rows = new VStack();
// 400 px of text is two lines at 200 px, but three beside the bar.
const text = rows.add(new Wrapping(400));
rows.add(new Spacer(70));
scroll.setContent(rows);
expect(rows.w).toBe(200 - Theme.scrollbarWidth);
expect(text.h).toBe(60);
expect(scroll.maxScroll).toBe(30);
});
});