import { findAsset, findAssetOf } from '../../core/model/Project'; import { previewObjectId } from '../../core/model/animationPreview'; import { currentFramesDialog } from '../animation/FramesDialog'; import { currentRowDialog } from '../animation/RowDialog'; import { centreOf, type HookContext } from './hookHelpers'; /** The Texture, Animation, Tile and List tabs. */ export function documentHooks({ state, editor }: HookContext) { const { textureEditor } = editor; return { texture: { current: () => textureEditor.currentAssetId, /** Where an image pixel of the shown sheet sits on screen (frames are dragged in image coordinates). */ pointAt: (x: number, y: number) => textureEditor.imageToScreen(x, y), rect: () => textureEditor.canvasRect(), /** Whether a press edits the selected frame's collision outline (#141). */ collisionMode: () => textureEditor.canvas.collisionMode, /** Rebuilds of the unselected frames' marks, their outlines, and where marks and names sit (#220). */ drawInfo: () => textureEditor.canvas.drawInfo, /** The open "Generate collision…" dialog (#161), or null. */ generateDialog: () => textureEditor.generateDialog.current()?.info ?? null, }, animation: { current: () => editor.animationEditor.currentAssetId, info: () => editor.animationEditor.shownInfo, rootPickerAt: () => centreOf(editor.animationEditor.rootPicker), addRowAt: () => centreOf(editor.animationEditor.addRowButton), playAt: () => ({ ...centreOf(editor.animationEditor.playButton), text: editor.animationEditor.playButton.text, }), /** Where a cell, a row's header and a frame on the ruler sit – no text names a cell. */ cellAt: (trackId: string, frame: number) => editor.animationEditor.canvas.cellGlobal(trackId, frame), headerAt: (trackId: string) => editor.animationEditor.canvas.headerGlobal(trackId), rulerAt: (frame: number) => editor.animationEditor.canvas.rulerGlobal(frame), keys: () => editor.animationEditor.canvas.keysDrawn(), rows: () => editor.animationEditor.canvas.rowsDrawn(), view: () => editor.animationEditor.canvas.viewInfo(), scrub: (frame: number) => editor.animationEditor.canvas.scrub(frame), /** The open row dialog: what it offers and holds, and a way to pick without clicking through menus. */ rowDialog: () => currentRowDialog()?.debugInfo() ?? null, rowDialogPick: (target: string, property: string) => currentRowDialog()?.pick(target, property), /** Copy/paste and frames (#102): what the clipboard holds, and the open frames dialog. */ clipboard: () => structuredClone(state.keyClipboard), framesDialog: () => currentFramesDialog()?.debugInfo() ?? null, framesDialogCount: (count: number) => currentFramesDialog()?.setCount(count), framesDialogConfirm: () => { const dialog = currentFramesDialog(); if (!dialog) return null; const g = dialog.confirm.getGlobalPosition(); return { x: g.x + dialog.confirm.w / 2, y: g.y + dialog.confirm.h / 2 }; }, /** The preview above the timeline (#99): where a row's target is drawn and with which values. */ preview: { /** The id the preview draws a row's target under (`''` = the root itself). */ objectId: (target: string) => { const asset = findAssetOf(state.project, editor.animationEditor.currentAssetId, 'animation'); return previewObjectId(asset && findAsset(state.project, asset.root), target); }, rect: (objectId: string) => editor.animationEditor.preview.objectRect(objectId), item: (objectId: string) => editor.animationEditor.preview.itemDrawn(objectId), info: () => editor.animationEditor.preview.debugInfo(), fitAt: () => centreOf(editor.animationEditor.fitButton), }, }, tile: { current: () => editor.tileEditor.currentAssetId, info: () => editor.tileEditor.shownInfo, /** What the board drew: tiles, the picture it is showing and what stands in the way. */ board: () => editor.tileEditor.canvas.debugInfo(), pictures: () => editor.tileEditor.strip.debugInfo(), /** A tile of the board: its quarters and where each of them is drawn. */ tile: (index: number) => editor.tileEditor.canvas.tileDrawn(index), /** Which board tile a cell of the sample map became – `-1` when that cell is empty. */ sampleTile: (row: number, column: number) => editor.tileEditor.canvas.sampleTile(row, column), /** The captions under the grid: size, text and whether the zoom reaches their raster. */ captions: () => editor.tileEditor.canvas.captionInfo(), kindPickerAt: () => centreOf(editor.tileEditor.kindPicker), addPictureAt: () => centreOf(editor.tileEditor.strip.addButton), removePictureAt: () => centreOf(editor.tileEditor.strip.removeButton), pictureRowAt: (index: number) => editor.tileEditor.strip.list.rowGlobal(String(index)), fitAt: () => centreOf(editor.tileEditor.fitButton), }, list: { info: () => editor.listEditor.debugInfo(), /** Where an item's widget sits, so a check can click it – a number field has no findable text. */ fieldAt: (index: number, field = '') => editor.listEditor.fieldAt(index, field), /** The kind mark of a row, or of one field of a struct item – both are pickers. */ markAt: (index: number, field = '') => editor.listEditor.markAt(index, field), nameAt: (index: number, field: string) => editor.listEditor.nameAt(index, field), removeAt: (index: number) => editor.listEditor.removeAt(index), }, }; }