import type { Container, FederatedPointerEvent } from 'pixi.js'; import { Button, requestRender, Theme, UI_ICON_SVG } from '../ui'; import { CORNER_BUTTON_IDLE_SECONDS, isFullscreen, onFullscreenChange, showsCornerButton, toggleFullscreen, } from './fullscreen'; /** * The fullscreen button in the top right corner of a game on screen (#267): on the player page * always, in the editor while its game view is in fullscreen. Over a running game it steps aside * once the mouse rests (`showsCornerButton`). */ export class FullscreenCorner extends Button { private active = true; private running = false; private lastMove = performance.now(); private hideTimer = 0; private readonly stopChange: () => void; constructor(host: Container) { super('', { width: Theme.controlHeight, icon: isFullscreen() ? UI_ICON_SVG.fullscreenExit : UI_ICON_SVG.fullscreen, tooltip: () => (isFullscreen() ? 'Leave fullscreen' : 'Fullscreen'), onClick: () => void toggleFullscreen(), }); this.focusable = false; this.resize(Theme.controlHeight, Theme.controlHeight); // The button lies on the game: a press on it must neither start the game nor reach it as a click. const keep = (e: FederatedPointerEvent): void => e.stopPropagation(); this.on('pointerdown', keep); this.on('pointerup', keep); this.on('pointerover', () => this.refresh()); this.on('pointerout', () => this.refresh()); host.on('globalpointermove', () => { this.lastMove = performance.now(); this.refresh(); }); this.stopChange = onFullscreenChange((on) => this.setIcon(on ? UI_ICON_SVG.fullscreenExit : UI_ICON_SVG.fullscreen), ); } override destroy(options?: Parameters[0]): void { this.stopChange(); window.clearTimeout(this.hideTimer); super.destroy(options); } /** `active`: whether the host wants the button at all; `running`: whether a game is on screen. */ sync(active: boolean, running: boolean): void { this.active = active; this.running = running; this.refresh(); } /** Top right, a gap in from both edges of a host `w` wide. */ place(w: number): void { this.position.set(Math.round(w - this.w - Theme.gap * 2), Theme.gap * 2); } private refresh(): void { window.clearTimeout(this.hideTimer); const idleSeconds = (performance.now() - this.lastMove) / 1000; const show = this.active && showsCornerButton({ running: this.running, idleSeconds, overButton: this.hovered }); if (show !== this.visible) { this.visible = show; requestRender(); } if (show && this.running && !this.hovered) { const left = CORNER_BUTTON_IDLE_SECONDS - idleSeconds; this.hideTimer = window.setTimeout(() => this.refresh(), Math.max(0, left * 1000) + 20); } } }