import type { ResizeAnchor } from '../../core/model/pixelDraw'; import { PIXEL_CANVAS_MAX } from '../../core/model/pixelCel'; import { Button, HStack, NumberField, VStack } from '../../ui'; import { dialogSlot, FormDialog } from '../FormDialog'; const FIELD_WIDTH = 76; const ANCHOR_SIZE = 24; const STEPS = [0, 0.5, 1] as const; const slot = dialogSlot(); /** The open resize dialog, for the check scripts. */ export const currentResizeDialog = slot.current; export const openResizeDialog = slot.open; /** * Asks for the new canvas size and where the old picture stays in it (answer 3): the nine anchor * buttons are the corners, edges and centre. What sticks out on the far side is cut off. */ export class ResizeCanvasDialog extends FormDialog { private newWidth: number; private newHeight: number; private anchor: ResizeAnchor = { x: 0, y: 0 }; private readonly anchors: { anchor: ResizeAnchor; button: Button }[] = []; readonly widthField: NumberField; readonly heightField: NumberField; /** Public for the check scripts. */ readonly confirm: Button; constructor( width: number, height: number, onConfirm: (w: number, h: number, anchor: ResizeAnchor) => void, ) { super(300, 'Resize canvas'); this.newWidth = width; this.newHeight = height; const field = (value: number, set: (value: number) => void) => { const number = new NumberField(value, { min: 1, max: PIXEL_CANVAS_MAX, step: 1, decimals: 0, onInput: set, onChange: set, }); number.minWidth = FIELD_WIDTH; return number; }; this.widthField = field(width, (value) => (this.newWidth = Math.round(value))); this.heightField = field(height, (value) => (this.newHeight = Math.round(value))); this.content.add(this.formRow(this.fieldLabel('Width'), this.widthField)); this.content.add(this.formRow(this.fieldLabel('Height'), this.heightField)); const grid = new VStack({ gap: 2 }); for (const y of STEPS) { const row = new HStack({ gap: 2 }); for (const x of STEPS) { const anchor: ResizeAnchor = { x, y }; const button = new Button('', { width: ANCHOR_SIZE, toggle: true, onClick: () => this.setAnchor(anchor), }); this.anchors.push({ anchor, button }); row.add(button); } grid.add(row); } this.content.add(this.formRow(this.fieldLabel('Anchor'), grid)); this.setAnchor(this.anchor); this.confirm = this.addButtons('Resize', 'primary', () => onConfirm(this.newWidth, this.newHeight, this.anchor), ); } setAnchor(anchor: ResizeAnchor): void { this.anchor = anchor; for (const { anchor: a, button } of this.anchors) button.setToggled(a.x === anchor.x && a.y === anchor.y); } /** What the dialog would resize to – the check scripts read this. */ debugInfo(): { width: number; height: number; anchor: ResizeAnchor } { return { width: this.newWidth, height: this.newHeight, anchor: { ...this.anchor } }; } /** Where the anchor button for (x, y) sits on screen – the check scripts click it. */ anchorButton(x: 0 | 0.5 | 1, y: 0 | 0.5 | 1): { x: number; y: number } | null { const found = this.anchors.find((a) => a.anchor.x === x && a.anchor.y === y); if (!found) return null; const p = found.button.getGlobalPosition(); return { x: p.x + found.button.w / 2, y: p.y + found.button.h / 2 }; } }