Show only the game view in fullscreen, in the editor and the link (#267)
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>
This commit is contained in:
1 parent
24d360aeec
commit
cab1c0177f
17 files changed
+356
-8
No files matched your search
@@ -33,6 +33,7 @@ import { GamePane } from './viewport/GamePane';
|
||||
import { ScenePane } from './viewport/ScenePane';
|
||||
import { GameViewport } from './viewport/GameViewport';
|
||||
import { SceneViewport } from './viewport/SceneViewport';
|
||||
import { leaveFullscreen, onFullscreenChange } from '../viewport/fullscreen';
|
||||
import { TAB_OF_ASSET } from './assetTabs';
|
||||
import { attachAutosave } from './autosave';
|
||||
import { PasswordDialog } from './auth/PasswordDialog';
|
||||
@@ -463,6 +464,8 @@ export class Editor implements PlayHost {
|
||||
const onTick = (ticker: Ticker) => this.tick(ticker.deltaMS / 1000);
|
||||
app.ticker.add(onTick);
|
||||
this.cleanups.push(() => app.ticker.remove(onTick));
|
||||
// Only the game tab's button asks for fullscreen, so every change is the game view's (#267).
|
||||
this.cleanups.push(onFullscreenChange((on) => this.showGameFullscreen(on)));
|
||||
// In the game view Tab is a game key (an inventory, a target switch) – like every other key there.
|
||||
const focus = ui().focus;
|
||||
focus.tabBelongsElsewhere = () => this.tab === 'game';
|
||||
@@ -690,6 +693,28 @@ export class Editor implements PlayHost {
|
||||
else this.showTab(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* In fullscreen the game view is all the page shows: it moves from its tab to the root, and the
|
||||
* toolbar and the workspace step aside. Leaving puts it back where it was, below the tab header.
|
||||
*/
|
||||
private showGameFullscreen(on: boolean): void {
|
||||
if (on === this.gameView.isFullscreen) return;
|
||||
this.toolbar.visible = !on;
|
||||
this.workspace.visible = !on;
|
||||
if (on) this.root.add(this.gameView, 1);
|
||||
else this.gamePane.add(this.gameView, 1);
|
||||
this.gameView.setFullscreen(on);
|
||||
this.root.relayout();
|
||||
// The pane may come back at the size it had, and a resize to the same size lays nothing out.
|
||||
this.gamePane.relayout();
|
||||
}
|
||||
|
||||
/** Back to the editor now, not when the browser reports it: the caller goes on with the layout. */
|
||||
private endGameFullscreen(): void {
|
||||
leaveFullscreen();
|
||||
this.showGameFullscreen(false);
|
||||
}
|
||||
|
||||
private kindOf(doc: DocumentId): CentreTab | null {
|
||||
if (doc.startsWith('values:')) {
|
||||
const target = this.valueTargets.get(doc);
|
||||
@@ -754,6 +779,8 @@ export class Editor implements PlayHost {
|
||||
|
||||
/** Shows the pane for `tab`, with `strip` lit in the row – none for an empty kind reached by `showTab`. */
|
||||
private display(tab: TabId, strip: StripTab | null): void {
|
||||
// Fullscreen is the game view's; a tab that takes its place (Stop goes back to the scene) ends it.
|
||||
if (tab !== 'game') this.endGameFullscreen();
|
||||
this.tab = tab;
|
||||
this.documents = { docs: this.documents.docs, active: strip && isDocument(strip) ? strip : null };
|
||||
// The hierarchy lists what the visible tab edits; every other tab is about the scene.
|
||||
@@ -817,6 +844,7 @@ export class Editor implements PlayHost {
|
||||
async dispose(): Promise<void> {
|
||||
if (this.disposed) return;
|
||||
this.disposed = true;
|
||||
this.endGameFullscreen();
|
||||
await this.autosave.flush();
|
||||
this.autosave.stop();
|
||||
if (this.play.isPlaying) this.play.stop();
|
||||
|
||||
Reference in new issue
Block a user