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
@@ -1,14 +1,14 @@
|
||||
import { Graphics } from 'pixi.js';
|
||||
import { RenameProjectCommand } from '../../core/commands/projectCommands';
|
||||
import { listScenes } from '../../core/model/Project';
|
||||
import { Button, HStack, Label, Spacer, TextField, Theme, type Widget } from '../../ui';
|
||||
import { Button, Dropdown, HStack, Label, Spacer, TextField, Theme, type Widget } from '../../ui';
|
||||
import type { EditorState, SaveStatus } from '../state';
|
||||
|
||||
/** What the toolbar buttons trigger; the editor supplies the implementations. */
|
||||
export interface ToolbarActions {
|
||||
/** "Project ▾": list, create and delete projects. `anchor` is the button to open the menu under. */
|
||||
/** The "Project" dropdown: list, create and delete projects. `anchor` is the button to open the menu under. */
|
||||
openProjectMenu(anchor: Widget): void;
|
||||
/** "<scene> ▾": switch the active scene. */
|
||||
/** The scene dropdown: switch the active scene. */
|
||||
openSceneMenu(anchor: Widget): void;
|
||||
/** Play in edit mode, Stop in play mode. */
|
||||
togglePlay(): void;
|
||||
@@ -26,7 +26,7 @@ const SAVE_LABELS: Record<SaveStatus, string> = {
|
||||
export class Toolbar extends HStack {
|
||||
private readonly bg = new Graphics();
|
||||
private readonly projectName: TextField;
|
||||
private readonly sceneButton: Button;
|
||||
private readonly sceneButton: Dropdown;
|
||||
private readonly undoButton: Button;
|
||||
private readonly redoButton: Button;
|
||||
private readonly playButton: Button;
|
||||
@@ -42,7 +42,7 @@ export class Toolbar extends HStack {
|
||||
this.minHeight = Theme.controlHeight + Theme.padding * 2;
|
||||
this.addChildAt(this.bg, 0);
|
||||
|
||||
const projectButton = new Button('Project ▾', { onClick: () => actions.openProjectMenu(projectButton) });
|
||||
const projectButton = new Dropdown('Project', { onClick: () => actions.openProjectMenu(projectButton) });
|
||||
this.add(projectButton);
|
||||
this.projectName = new TextField('', {
|
||||
width: 180,
|
||||
@@ -56,7 +56,7 @@ export class Toolbar extends HStack {
|
||||
this.add(new Spacer(Theme.padding));
|
||||
|
||||
this.add(new Label('Scene', { style: 'muted' }));
|
||||
this.sceneButton = new Button('—', {
|
||||
this.sceneButton = new Dropdown('—', {
|
||||
width: 160,
|
||||
onClick: () => actions.openSceneMenu(this.sceneButton),
|
||||
});
|
||||
@@ -105,7 +105,7 @@ export class Toolbar extends HStack {
|
||||
this.projectName.setValue(state.project.name);
|
||||
const scene = state.activeScene;
|
||||
const scenes = listScenes(state.project);
|
||||
this.sceneButton.setText(scene ? `${scene.name} ▾` : scenes.length ? 'Select scene ▾' : 'No scene ▾');
|
||||
this.sceneButton.setText(scene ? scene.name : scenes.length ? 'Select scene' : 'No scene');
|
||||
const editing = state.mode === 'edit';
|
||||
// Undo/redo work in play mode too – on the runtime copy's own history.
|
||||
this.undoButton.setEnabled(state.commands.canUndo);
|
||||
|
||||
Reference in new issue
Block a user