Left-align menu entries, add a Dropdown field, scrollable and searchable menus
Menu entries are left-aligned and live in a ScrollContainer capped at 320 px, so a
long list (the parent picker holds every object of the scene) scrolls instead of
running off the screen. `Menu(items, { searchable: true })` puts a filter box above
the scroll area; Enter takes the first match. Filtering forces a relayout because
two queries with the same hit count leave the height unchanged, which would keep
the stale row positions.
Fields that open a menu are a new `Dropdown` widget: the value sits flush left and
the chevron is drawn flush right instead of being written into the label. A menu
opened below a field is never narrower than that field.
New debug hooks `overlayTexts()` / `overlayRect()` make the popup contents
assertable; e2e step 11 covers the cap, the scrolling and the search box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d54a620ebd
commit
15ffcd5739
16 files changed
+297
-39
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import { Graphics } from 'pixi.js';
|
||||
import { Theme } from '../Theme';
|
||||
import type { Size } from '../Widget';
|
||||
import { Button, type ButtonOptions } from './Button';
|
||||
|
||||
/** Space reserved at the right edge for the chevron. */
|
||||
const CHEVRON_SLOT = 16;
|
||||
|
||||
/**
|
||||
* A button that opens a menu: value flush left, chevron flush right. The chevron is drawn
|
||||
* rather than written into the label, so the label stays the plain value – which keeps it
|
||||
* readable, lets it be compared against the menu entries, and stays crisp at any size.
|
||||
*/
|
||||
export class Dropdown extends Button {
|
||||
private readonly chevron = new Graphics();
|
||||
|
||||
constructor(label: string, options: ButtonOptions = {}) {
|
||||
super(label, { ...options, align: 'left' });
|
||||
this.addChild(this.chevron);
|
||||
}
|
||||
|
||||
override measure(): Size {
|
||||
const size = super.measure();
|
||||
return { w: size.w + CHEVRON_SLOT, h: size.h };
|
||||
}
|
||||
|
||||
override redraw(): void {
|
||||
super.redraw();
|
||||
const x = this.w - CHEVRON_SLOT / 2 - Theme.padding / 2;
|
||||
const y = this.h / 2;
|
||||
this.chevron
|
||||
.clear()
|
||||
.moveTo(x - 4, y - 2)
|
||||
.lineTo(x + 4, y - 2)
|
||||
.lineTo(x, y + 3)
|
||||
.fill(this.onAccent ? Theme.colors.accentText : Theme.colors.textMuted);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user