A game is easier to judge and to play without the editor around it. The Game tab and the game link now both have a button that puts the game view itself into fullscreen, not just the browser window. - The Game tab header has a fullscreen button on the right. It hides toolbar, panels, tab headers and console and keeps the zoom from the picker. A running game keeps running. - A corner button at the top right, or Esc, leaves fullscreen and puts the view back into the tab where it was. - The game link shows the same corner button. Over a running game it hides after 2 s without mouse movement and returns on the next move. - A click on the corner button never reaches the game. - Stop ends fullscreen. Stop switches back to the Scene tab, and any switch away from the Game tab ends fullscreen, so no empty screen is left behind. - src/viewport/fullscreen.ts wraps the Fullscreen API and holds the rule for when the button shows. FullscreenCorner.ts is the button used by both pages. Two SVG icons are new. - Editor.showGameFullscreen moves the GameViewport into the root while fullscreen is on. Tests: - ./check.sh: 195 files, 2815 tests, exit 0 (3 new unit tests). - e2e 60: 2 new checks for the corner button on the game link. - e2e 61 (new): 4 checks for fullscreen in the editor. - Full e2e run (local): ALL PASSED, 1580 checks. - Counter-checks fail as expected: rule without "no game running", no stopPropagation (60h), no end on tab switch (61c), no reparenting of the view (61a). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.2 KiB
TypeScript
47 lines
1.2 KiB
TypeScript
import type { Application, Container } from 'pixi.js';
|
|
import { ui } from '../../ui';
|
|
import type { Editor } from '../Editor';
|
|
import type { SnapToggle } from '../graph/SnapToggle';
|
|
import type { EditorState } from '../state';
|
|
|
|
/** What every group of test hooks is built from. */
|
|
export interface HookContext {
|
|
app: Application;
|
|
state: EditorState;
|
|
editor: Editor;
|
|
}
|
|
|
|
export function centreOf(widget: { getGlobalPosition(): { x: number; y: number }; w: number; h: number }): {
|
|
x: number;
|
|
y: number;
|
|
} {
|
|
const p = widget.getGlobalPosition();
|
|
return { x: p.x + widget.w / 2, y: p.y + widget.h / 2 };
|
|
}
|
|
|
|
/** Whether `node` and everything it hangs from are visible, up to a stage. */
|
|
export function shownOnStage(node: Container): boolean {
|
|
let current = node;
|
|
while (current.visible) {
|
|
if (!current.parent) return current === ui().app.stage;
|
|
current = current.parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** A tab's magnet (#194): where to click it, how big it is drawn, and whether it is on. */
|
|
export function snapButton(snap: SnapToggle): {
|
|
x: number;
|
|
y: number;
|
|
w: number;
|
|
h: number;
|
|
toggled: boolean;
|
|
} {
|
|
return {
|
|
...centreOf(snap.button),
|
|
w: snap.button.w,
|
|
h: snap.button.h,
|
|
toggled: snap.button.toggled,
|
|
};
|
|
}
|