/** * @typedef {Object} SceneControlTool * @property {string} name * @property {string} title * @property {string} icon * @property {boolean} visible * @property {boolean} toggle * @property {boolean} active * @property {boolean} button * @property {Function} onClick * @property {ToolclipConfiguration} toolclip Configuration for rendering the tool's toolclip. */ /** * @typedef {Object} SceneControl * @property {string} name * @property {string} title * @property {string} layer * @property {string} icon * @property {boolean} visible * @property {SceneControlTool[]} tools * @property {string} activeTool */ /** * @typedef {object} ToolclipConfiguration * @property {string} src The filename of the toolclip video. * @property {string} heading The heading string. * @property {ToolclipConfigurationItem[]} items The items in the toolclip body. */ /** * @typedef {object} ToolclipConfigurationItem * @property {string} [paragraph] A plain paragraph of content for this item. * @property {string} [heading] A heading for the item. * @property {string} [content] Content for the item. * @property {string} [reference] If the item is a single key reference, use this instead of content. */ /** * Scene controls navigation menu * @extends {Application} */ class SceneControls extends Application { /** @inheritdoc */ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { width: 100, id: "controls", template: "templates/hud/controls.html", popOut: false }); } /* -------------------------------------------- */ /** * The Array of Scene Control buttons which are currently rendered * @type {SceneControl[]} */ controls = this._getControlButtons(); /* -------------------------------------------- */ /** * The currently active control set * @type {string} */ get activeControl() { return this.#control; } #control = "token"; /* -------------------------------------------- */ /** * The currently active tool in the control palette * @type {string} */ get activeTool() { return this.#tools[this.#control]; } /** * Track which tool is active within each control set * @type {Record} */ #tools = {}; /* -------------------------------------------- */ /** * Return the active control set * @type {SceneControl|null} */ get control() { if ( !this.controls ) return null; return this.controls.find(c => c.name === this.#control) || null; } /* -------------------------------------------- */ /** * Return the actively controlled tool * @type {SceneControlTool|null} */ get tool() { const control = this.control; if ( !control ) return null; return this.#tools[control.name] || null; } /* -------------------------------------------- */ /* Methods */ /* -------------------------------------------- */ /** * Initialize the Scene Controls by obtaining the set of control buttons and rendering the HTML * @param {object} options Options which modify how the controls UI is initialized * @param {string} [options.control] An optional control set to set as active * @param {string} [options.layer] An optional layer name to target as the active control * @param {string} [options.tool] A specific named tool to set as active for the palette */ initialize({control, layer, tool} = {}) { // Determine the control set to activate let controlSet = control ? this.controls.find(c => c.name === control) : null; if ( !controlSet && layer ) controlSet = this.controls.find(c => c.layer === layer); if ( !controlSet ) controlSet = this.control; // Determine the tool to activate tool ||= this.#tools[controlSet.name] || controlSet.activeTool || null; // Activate the new control scheme this.#control = controlSet?.name || null; if ( controlSet && (this.#tools[this.#control] !== tool) ) { this.#tools[this.#control] = tool; // Refresh placeable states if the active tool changed if ( canvas.ready ) { // TODO: Perhaps replace this with a CanvasLayer#_onToolChanged callback const layer = canvas[controlSet.layer]; if ( layer instanceof PlaceablesLayer ) { for ( const placeable of layer.placeables ) placeable.renderFlags.set({refreshState: true}); } } } this.controls = this._getControlButtons(); // Render the UI this.render(true); } /* -------------------------------------------- */ /** @inheritdoc */ async getData(options={}) { const showToolclips = game.settings.get("core", "showToolclips"); const canvasActive = !!canvas.scene; const controls = []; const isMac = navigator.appVersion.includes("Mac"); const mod = isMac ? "⌘" : game.i18n.localize("CONTROLS.CtrlAbbr"); const alt = isMac ? "⌥" : game.i18n.localize("CONTROLS.Alt"); for ( const c of this.controls ) { if ( c.visible === false ) continue; const control = foundry.utils.deepClone(c); control.isActive = canvasActive && (this.#control === control.name); control.css = control.isActive ? "active" : ""; control.tools = []; for ( const t of c.tools ) { if ( t.visible === false ) continue; const tool = foundry.utils.deepClone(t); tool.isActive = canvasActive && ((this.#tools[control.name] === tool.name) || (tool.toggle && tool.active)); tool.css = [ tool.toggle ? "toggle" : null, tool.isActive ? "active" : null ].filterJoin(" "); tool.tooltip = showToolclips && tool.toolclip ? await renderTemplate("templates/hud/toolclip.html", { ...tool.toolclip, alt, mod }) : tool.title; control.tools.push(tool); } if ( control.tools.length ) controls.push(control); } // Return data for rendering return { controls, active: canvasActive, cssClass: canvasActive ? "" : "disabled" }; } /* -------------------------------------------- */ /* Event Listeners and Handlers */ /* -------------------------------------------- */ /** @inheritdoc */ activateListeners(html) { html.find(".scene-control").click(this._onClickLayer.bind(this)); html.find(".control-tool").click(this._onClickTool.bind(this)); canvas.notes?.hintMapNotes(); } /* -------------------------------------------- */ /** * Handle click events on a Control set * @param {Event} event A click event on a tool control * @private */ _onClickLayer(event) { event.preventDefault(); if ( !canvas.ready ) return; const li = event.currentTarget; const controlName = li.dataset.control; if ( this.#control === controlName ) return; this.#control = controlName; const control = this.controls.find(c => c.name === controlName); if ( control ) canvas[control.layer].activate(); } /* -------------------------------------------- */ /** * Handle click events on Tool controls * @param {Event} event A click event on a tool control * @private */ _onClickTool(event) { event.preventDefault(); if ( !canvas.ready ) return; const li = event.currentTarget; const control = this.control; const toolName = li.dataset.tool; const tool = control.tools.find(t => t.name === toolName); // Handle Toggles if ( tool.toggle ) { tool.active = !tool.active; if ( tool.onClick instanceof Function ) tool.onClick(tool.active); } // Handle Buttons else if ( tool.button ) { if ( tool.onClick instanceof Function ) tool.onClick(); } // Handle Tools else if ( this.#tools[control.name] !== toolName ) { this.#tools[control.name] = toolName; const layer = canvas[control.layer]; if ( layer instanceof PlaceablesLayer ) { for ( const placeable of layer.placeables ) placeable.renderFlags.set({refreshState: true}); } if ( tool.onClick instanceof Function ) tool.onClick(); } // Render the controls this.render(); } /* -------------------------------------------- */ /** * Get the set of Control sets and tools that are rendered as the Scene Controls. * These controls may be extended using the "getSceneControlButtons" Hook. * @returns {SceneControl[]} * @private */ _getControlButtons() { const controls = []; const isGM = game.user.isGM; const clip = game.settings.get("core", "showToolclips") ? "Clip" : ""; const commonControls = { create: { heading: "CONTROLS.CommonCreate", reference: "CONTROLS.ClickDrag" }, move: { heading: "CONTROLS.CommonMove", reference: "CONTROLS.Drag" }, edit: { heading: "CONTROLS.CommonEdit", reference: "CONTROLS.DoubleClick" }, editAlt: { heading: "CONTROLS.CommonEdit", reference: "CONTROLS.DoubleRightClick" }, sheet: { heading: "CONTROLS.CommonOpenSheet", reference: "CONTROLS.DoubleClick" }, hide: { heading: "CONTROLS.CommonHide", reference: "CONTROLS.RightClick" }, delete: { heading: "CONTROLS.CommonDelete", reference: "CONTROLS.Delete" }, rotate: { heading: "CONTROLS.CommonRotate", content: "CONTROLS.ShiftOrCtrlScroll" }, select: { heading: "CONTROLS.CommonSelect", reference: "CONTROLS.Click" }, selectAlt: { heading: "CONTROLS.CommonSelect", content: "CONTROLS.ClickOrClickDrag" }, selectMultiple: { heading: "CONTROLS.CommonSelectMultiple", reference: "CONTROLS.ShiftClick" }, hud: { heading: "CONTROLS.CommonToggleHUD", reference: "CONTROLS.RightClick" }, draw: { heading: "CONTROLS.CommonDraw", reference: "CONTROLS.ClickDrag" }, drawProportionally: { heading: "CONTROLS.CommonDrawProportional", reference: "CONTROLS.AltClickDrag" }, place: { heading: "CONTROLS.CommonPlace", reference: "CONTROLS.ClickDrag" }, chain: { heading: "CONTROLS.CommonChain", content: "CONTROLS.ChainCtrlClick" }, movePoint: { heading: "CONTROLS.CommonMovePoint", reference: "CONTROLS.ClickDrag" }, openClose: { heading: "CONTROLS.CommonOpenClose", reference: "CONTROLS.Click" }, openCloseSilently: { heading: "CONTROLS.CommonOpenCloseSilently", reference: "CONTROLS.AltClick" }, lock: { heading: "CONTROLS.CommonLock", reference: "CONTROLS.RightClick" }, lockSilently: { heading: "CONTROLS.CommonLockSilently", reference: "CONTROLS.AltRightClick" }, onOff: { heading: "CONTROLS.CommonOnOff", reference: "CONTROLS.RightClick" } }; const buildItems = (...items) => items.map(item => commonControls[item]); // Token Controls controls.push({ name: "token", title: "CONTROLS.GroupToken", layer: "tokens", icon: "fa-solid fa-user-alt", tools: [ { name: "select", title: "CONTROLS.BasicSelect", icon: "fa-solid fa-expand", toolclip: { src: "toolclips/tools/token-select.webm", heading: "CONTROLS.BasicSelect", items: [ { paragraph: "CONTROLS.BasicSelectP" }, ...buildItems("selectAlt", "selectMultiple", "move", "rotate", "hud", "sheet"), ...(game.user.isGM ? buildItems("editAlt", "delete") : []), { heading: "CONTROLS.BasicMeasureStart", reference: "CONTROLS.CtrlClickDrag" }, { heading: "CONTROLS.BasicMeasureWaypoints", reference: "CONTROLS.CtrlClick" }, { heading: "CONTROLS.BasicMeasureFollow", reference: "CONTROLS.Spacebar" } ] } }, { name: "target", title: "CONTROLS.TargetSelect", icon: "fa-solid fa-bullseye", toolclip: { src: "toolclips/tools/token-target.webm", heading: "CONTROLS.TargetSelect", items: [ { paragraph: "CONTROLS.TargetSelectP" }, ...buildItems("selectAlt", "selectMultiple") ] } }, { name: "ruler", title: "CONTROLS.BasicMeasure", icon: "fa-solid fa-ruler", toolclip: { src: "toolclips/tools/token-measure.webm", heading: "CONTROLS.BasicMeasure", items: [ { heading: "CONTROLS.BasicMeasureStart", reference: "CONTROLS.ClickDrag" }, { heading: "CONTROLS.BasicMeasureWaypoints", reference: "CONTROLS.CtrlClick" }, { heading: "CONTROLS.BasicMeasureFollow", reference: "CONTROLS.Spacebar" } ] } } ], activeTool: "select" }); // Measurement Layer Tools controls.push({ name: "measure", title: "CONTROLS.GroupMeasure", layer: "templates", icon: "fa-solid fa-ruler-combined", visible: game.user.can("TEMPLATE_CREATE"), tools: [ { name: "circle", title: "CONTROLS.MeasureCircle", icon: "fa-regular fa-circle", toolclip: { src: "toolclips/tools/measure-circle.webm", heading: "CONTROLS.MeasureCircle", items: buildItems("create", "move", "edit", "hide", "delete") } }, { name: "cone", title: "CONTROLS.MeasureCone", icon: "fa-solid fa-angle-left", toolclip: { src: "toolclips/tools/measure-cone.webm", heading: "CONTROLS.MeasureCone", items: buildItems("create", "move", "edit", "hide", "delete", "rotate") } }, { name: "rect", title: "CONTROLS.MeasureRect", icon: "fa-regular fa-square", toolclip: { src: "toolclips/tools/measure-rect.webm", heading: "CONTROLS.MeasureRect", items: buildItems("create", "move", "edit", "hide", "delete", "rotate") } }, { name: "ray", title: "CONTROLS.MeasureRay", icon: "fa-solid fa-arrows-alt-v", toolclip: { src: "toolclips/tools/measure-ray.webm", heading: "CONTROLS.MeasureRay", items: buildItems("create", "move", "edit", "hide", "delete", "rotate") } }, { name: "clear", title: "CONTROLS.MeasureClear", icon: "fa-solid fa-trash", visible: isGM, onClick: () => canvas.templates.deleteAll(), button: true } ], activeTool: "circle" }); // Tiles Layer controls.push({ name: "tiles", title: "CONTROLS.GroupTile", layer: "tiles", icon: "fa-solid fa-cubes", visible: isGM, tools: [ { name: "select", title: "CONTROLS.TileSelect", icon: "fa-solid fa-expand", toolclip: { src: "toolclips/tools/tile-select.webm", heading: "CONTROLS.TileSelect", items: buildItems("selectAlt", "selectMultiple", "move", "rotate", "hud", "edit", "delete") } }, { name: "tile", title: "CONTROLS.TilePlace", icon: "fa-solid fa-cube", toolclip: { src: "toolclips/tools/tile-place.webm", heading: "CONTROLS.TilePlace", items: buildItems("create", "move", "rotate", "hud", "edit", "delete") } }, { name: "browse", title: "CONTROLS.TileBrowser", icon: "fa-solid fa-folder", button: true, onClick: () => { new FilePicker({ type: "imagevideo", displayMode: "tiles", tileSize: true }).render(true); }, toolclip: { src: "toolclips/tools/tile-browser.webm", heading: "CONTROLS.TileBrowser", items: buildItems("place", "move", "rotate", "hud", "edit", "delete") } }, { name: "foreground", title: "CONTROLS.TileForeground", icon: "fa-solid fa-home", toggle: true, active: false, onClick: active => { this.control.foreground = active; for ( const tile of canvas.tiles.placeables ) { tile.renderFlags.set({refreshState: true}); if ( tile.controlled ) tile.release(); } } }, { name: "snap", title: "CONTROLS.CommonForceSnap", icon: "fa-solid fa-plus", toggle: true, active: canvas.forceSnapVertices, onClick: toggled => canvas.forceSnapVertices = toggled } ], activeTool: "select" }); // Drawing Tools controls.push({ name: "drawings", title: "CONTROLS.GroupDrawing", layer: "drawings", icon: "fa-solid fa-pencil-alt", visible: game.user.can("DRAWING_CREATE"), tools: [ { name: "select", title: "CONTROLS.DrawingSelect", icon: "fa-solid fa-expand", toolclip: { src: "toolclips/tools/drawing-select.webm", heading: "CONTROLS.DrawingSelect", items: buildItems("selectAlt", "selectMultiple", "move", "hud", "edit", "delete", "rotate") } }, { name: "rect", title: "CONTROLS.DrawingRect", icon: "fa-solid fa-square", toolclip: { src: "toolclips/tools/drawing-rect.webm", heading: "CONTROLS.DrawingRect", items: buildItems("draw", "move", "hud", "edit", "delete", "rotate") } }, { name: "ellipse", title: "CONTROLS.DrawingEllipse", icon: "fa-solid fa-circle", toolclip: { src: "toolclips/tools/drawing-ellipse.webm", heading: "CONTROLS.DrawingEllipse", items: buildItems("draw", "move", "hud", "edit", "delete", "rotate") } }, { name: "polygon", title: "CONTROLS.DrawingPoly", icon: "fa-solid fa-draw-polygon", toolclip: { src: "toolclips/tools/drawing-polygon.webm", heading: "CONTROLS.DrawingPoly", items: [ { heading: "CONTROLS.CommonDraw", content: "CONTROLS.DrawingPolyP" }, ...buildItems("move", "hud", "edit", "delete", "rotate") ] } }, { name: "freehand", title: "CONTROLS.DrawingFree", icon: "fa-solid fa-signature", toolclip: { src: "toolclips/tools/drawing-free.webm", heading: "CONTROLS.DrawingFree", items: buildItems("draw", "move", "hud", "edit", "delete", "rotate") } }, { name: "text", title: "CONTROLS.DrawingText", icon: "fa-solid fa-font", onClick: () => { const controlled = canvas.drawings.controlled; if ( controlled.length === 1 ) controlled[0].enableTextEditing(); }, toolclip: { src: "toolclips/tools/drawing-text.webm", heading: "CONTROLS.DrawingText", items: buildItems("draw", "move", "hud", "edit", "delete", "rotate") } }, { name: "role", title: "CONTROLS.DrawingRole", icon: "fa-solid fa-circle-info", toggle: true, active: false }, { name: "snap", title: "CONTROLS.CommonForceSnap", icon: "fa-solid fa-plus", toggle: true, active: canvas.forceSnapVertices, onClick: toggled => canvas.forceSnapVertices = toggled }, { name: "configure", title: "CONTROLS.DrawingConfig", icon: "fa-solid fa-cog", onClick: () => canvas.drawings.configureDefault(), button: true }, { name: "clear", title: "CONTROLS.DrawingClear", icon: "fa-solid fa-trash", visible: isGM, onClick: () => canvas.drawings.deleteAll(), button: true } ], activeTool: "select" }); // Walls Layer Tools controls.push({ name: "walls", title: "CONTROLS.GroupWall", layer: "walls", icon: "fa-solid fa-block-brick", visible: isGM, tools: [ { name: "select", title: "CONTROLS.WallSelect", icon: "fa-solid fa-expand", toolclip: { src: "toolclips/tools/wall-select.webm", heading: "CONTROLS.WallSelect", items: [ ...buildItems("selectAlt", "selectMultiple", "move"), { heading: "CONTROLS.CommonMoveWithoutSnapping", reference: "CONTROLS.ShiftDrag" }, { heading: "CONTROLS.CommonEdit", content: "CONTROLS.WallSelectEdit" }, ...buildItems("delete") ] } }, { name: "walls", title: "CONTROLS.WallDraw", icon: "fa-solid fa-bars", toolclip: { src: "toolclips/tools/wall-basic.webm", heading: "CONTROLS.WallBasic", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.WallBasicBlocks" }, ...buildItems("place", "chain", "movePoint", "edit", "delete") ] } }, { name: "terrain", title: "CONTROLS.WallTerrain", icon: "fa-solid fa-mountain", toolclip: { src: "toolclips/tools/wall-terrain.webm", heading: "CONTROLS.WallTerrain", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.WallTerrainBlocks" }, ...buildItems("place", "chain", "movePoint", "edit", "delete") ] } }, { name: "invisible", title: "CONTROLS.WallInvisible", icon: "fa-solid fa-eye-slash", toolclip: { src: "toolclips/tools/wall-invisible.webm", heading: "CONTROLS.WallInvisible", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.WallInvisibleBlocks" }, ...buildItems("place", "chain", "movePoint", "edit", "delete") ] } }, { name: "ethereal", title: "CONTROLS.WallEthereal", icon: "fa-solid fa-mask", toolclip: { src: "toolclips/tools/wall-ethereal.webm", heading: "CONTROLS.WallEthereal", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.WallEtherealBlocks" }, ...buildItems("place", "chain", "movePoint", "edit", "delete") ] } }, { name: "doors", title: "CONTROLS.WallDoors", icon: "fa-solid fa-door-open", toolclip: { src: "toolclips/tools/wall-door.webm", heading: "CONTROLS.WallDoors", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.DoorBlocks" }, ...buildItems("openClose", "openCloseSilently", "lock", "lockSilently", "place", "chain", "movePoint", "edit") ] } }, { name: "secret", title: "CONTROLS.WallSecret", icon: "fa-solid fa-user-secret", toolclip: { src: "toolclips/tools/wall-secret-door.webm", heading: "CONTROLS.WallSecret", items: [ { heading: "CONTROLS.WallSecretHidden", content: "CONTROLS.WallSecretHiddenP" }, { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.DoorBlocks" }, ...buildItems("openClose", "openCloseSilently", "lock", "lockSilently", "place", "chain", "movePoint", "edit") ] } }, { name: "window", title: "CONTROLS.WallWindow", icon: "fa-solid fa-window-frame", toolclip: { src: "toolclips/tools/wall-window.webm", heading: "CONTROLS.WallWindow", items: [ { heading: "CONTROLS.CommonBlocks", content: "CONTROLS.WallWindowBlocks" }, ...buildItems("place", "chain", "movePoint", "edit", "delete") ] } }, { name: "clone", title: "CONTROLS.WallClone", icon: "fa-regular fa-clone" }, { name: "snap", title: "CONTROLS.CommonForceSnap", icon: "fa-solid fa-plus", toggle: true, active: canvas.forceSnapVertices, onClick: toggled => canvas.forceSnapVertices = toggled, toolclip: { src: "toolclips/tools/wall-snap.webm", heading: "CONTROLS.CommonForceSnap", items: [{ paragraph: "CONTROLS.WallSnapP" }] } }, { name: "close-doors", title: "CONTROLS.WallCloseDoors", icon: "fa-regular fa-door-closed", onClick: () => { let updates = canvas.walls.placeables.reduce((arr, w) => { if ( w.isDoor && (w.document.ds === CONST.WALL_DOOR_STATES.OPEN) ) { arr.push({_id: w.id, ds: CONST.WALL_DOOR_STATES.CLOSED}); } return arr; }, []); if ( !updates.length ) return; canvas.scene.updateEmbeddedDocuments("Wall", updates, {sound: false}); ui.notifications.info(game.i18n.format("CONTROLS.WallDoorsClosed", {number: updates.length})); } }, { name: "clear", title: "CONTROLS.WallClear", icon: "fa-solid fa-trash", onClick: () => canvas.walls.deleteAll(), button: true } ], activeTool: "walls" }); // Lighting Layer Tools controls.push({ name: "lighting", title: "CONTROLS.GroupLighting", layer: "lighting", icon: "fa-regular fa-lightbulb", visible: isGM, tools: [ { name: "light", title: "CONTROLS.LightDraw", icon: "fa-solid fa-lightbulb", toolclip: { src: "toolclips/tools/light-draw.webm", heading: "CONTROLS.LightDraw", items: buildItems("create", "edit", "rotate", "onOff") } }, { name: "day", title: "CONTROLS.LightDay", icon: "fa-solid fa-sun", visible: !canvas.scene?.environment.darknessLock, onClick: () => canvas.scene.update( {environment: {darknessLevel: 0.0}}, {animateDarkness: CONFIG.Canvas.darknessToDaylightAnimationMS} ), button: true, toolclip: { src: "toolclips/tools/light-day.webm", heading: "CONTROLS.LightDay", items: [ {heading: "CONTROLS.MakeDayH", content: "CONTROLS.MakeDayP"}, {heading: "CONTROLS.AutoLightToggleH", content: "CONTROLS.AutoLightToggleP"} ] } }, { name: "night", title: "CONTROLS.LightNight", icon: "fa-solid fa-moon", visible: !canvas.scene?.environment.darknessLock, onClick: () => canvas.scene.update( {environment: {darknessLevel: 1.0}}, {animateDarkness: CONFIG.Canvas.daylightToDarknessAnimationMS} ), button: true, toolclip: { src: "toolclips/tools/light-night.webm", heading: "CONTROLS.LightNight", items: [ {heading: "CONTROLS.MakeNightH", content: "CONTROLS.MakeNightP"}, {heading: "CONTROLS.AutoLightToggleH", content: "CONTROLS.AutoLightToggleP"} ] } }, { name: "reset", title: "CONTROLS.LightReset", icon: "fa-solid fa-cloud", onClick: () => { new Dialog({ title: game.i18n.localize("CONTROLS.FOWResetTitle"), content: `

${game.i18n.localize("CONTROLS.FOWResetDesc")}

`, buttons: { yes: { icon: '', label: "Yes", callback: () => canvas.fog.reset() }, no: { icon: '', label: "No" } } }).render(true); }, button: true, toolclip: { src: "toolclips/tools/light-reset.webm", heading: "CONTROLS.LightReset", items: [{ paragraph: "CONTROLS.LightResetP" }] } }, { name: "clear", title: "CONTROLS.LightClear", icon: "fa-solid fa-trash", onClick: () => canvas.lighting.deleteAll(), button: true } ], activeTool: "light" }); // Sounds Layer Tools controls.push({ name: "sounds", title: "CONTROLS.GroupSound", layer: "sounds", icon: "fa-solid fa-music", visible: isGM, tools: [ { name: "sound", title: "CONTROLS.SoundDraw", icon: "fa-solid fa-volume-up", toolclip: { src: "toolclips/tools/sound-draw.webm", heading: "CONTROLS.SoundDraw", items: buildItems("create", "edit", "rotate", "onOff") } }, { name: "preview", title: `CONTROLS.SoundPreview${clip}`, icon: "fa-solid fa-headphones", toggle: true, active: canvas.sounds?.livePreview ?? false, onClick: toggled => { canvas.sounds.livePreview = toggled; canvas.sounds.refresh(); }, toolclip: { src: "toolclips/tools/sound-preview.webm", heading: "CONTROLS.SoundPreview", items: [{ paragraph: "CONTROLS.SoundPreviewP" }] } }, { name: "clear", title: "CONTROLS.SoundClear", icon: "fa-solid fa-trash", onClick: () => canvas.sounds.deleteAll(), button: true } ], activeTool: "sound" }); // Regions Layer Tools controls.push({ name: "regions", title: "CONTROLS.GroupRegion", layer: "regions", icon: "fa-regular fa-game-board", visible: game.user.isGM, tools: [ { name: "select", title: "CONTROLS.RegionSelect", icon: "fa-solid fa-expand", toolclip: { src: "toolclips/tools/region-select.webm", heading: "CONTROLS.RegionSelect", items: [ { paragraph: "CONTROLS.RegionSelectP" }, ...buildItems("selectAlt", "selectMultiple", "edit", "delete") ] } }, { name: "rectangle", title: "CONTROLS.RegionRectangle", icon: "fa-solid fa-square", toolclip: { src: "toolclips/tools/region-rectangle.webm", heading: "CONTROLS.RegionRectangle", items: [ { paragraph: "CONTROLS.RegionShape" }, ...buildItems("draw", "drawProportionally"), { paragraph: "CONTROLS.RegionPerformance" }, ] } }, { name: "ellipse", title: "CONTROLS.RegionEllipse", icon: "fa-solid fa-circle", toolclip: { src: "toolclips/tools/region-ellipse.webm", heading: "CONTROLS.RegionEllipse", items: [ { paragraph: "CONTROLS.RegionShape" }, ...buildItems("draw", "drawProportionally"), { paragraph: "CONTROLS.RegionPerformance" }, ] } }, { name: "polygon", title: "CONTROLS.RegionPolygon", icon: "fa-solid fa-draw-polygon", toolclip: { src: "toolclips/tools/region-polygon.webm", heading: "CONTROLS.RegionPolygon", items: [ { paragraph: "CONTROLS.RegionShape" }, ...buildItems("draw", "drawProportionally"), { paragraph: "CONTROLS.RegionPerformance" }, ] } }, { name: "hole", title: "CONTROLS.RegionHole", icon: "fa-duotone fa-object-subtract", toggle: true, active: canvas.regions?._holeMode ?? false, onClick: toggled => canvas.regions._holeMode = toggled, toolclip: { src: "toolclips/tools/region-hole.webm", heading: "CONTROLS.RegionHole", items: [ { paragraph: "CONTROLS.RegionHoleP" }, ...buildItems("draw", "drawProportionally") ] } }, { name: "snap", title: "CONTROLS.CommonForceSnap", icon: "fa-solid fa-plus", toggle: true, active: canvas.forceSnapVertices, onClick: toggled => canvas.forceSnapVertices = toggled, toolclip: { src: "toolclips/tools/region-snap.webm", heading: "CONTROLS.CommonForceSnap", items: [ { paragraph: "CONTROLS.RegionSnap" }, ...buildItems("draw", "drawProportionally") ] } }, { name: "clear", title: "CONTROLS.RegionClear", icon: "fa-solid fa-trash", onClick: () => canvas.regions.deleteAll(), button: true } ], activeTool: "select" }); // Notes Layer Tools controls.push({ name: "notes", title: "CONTROLS.GroupNotes", layer: "notes", icon: "fa-solid fa-bookmark", tools: [ { name: "select", title: "CONTROLS.NoteSelect", icon: "fa-solid fa-expand" }, { name: "journal", title: "NOTE.Create", visible: game.user.hasPermission("NOTE_CREATE"), icon: CONFIG.JournalEntry.sidebarIcon }, { name: "toggle", title: "CONTROLS.NoteToggle", icon: "fa-solid fa-map-pin", toggle: true, active: game.settings.get("core", NotesLayer.TOGGLE_SETTING), onClick: toggled => game.settings.set("core", NotesLayer.TOGGLE_SETTING, toggled) }, { name: "clear", title: "CONTROLS.NoteClear", icon: "fa-solid fa-trash", visible: isGM, onClick: () => canvas.notes.deleteAll(), button: true } ], activeTool: "select" }); // Pass the Scene Controls to a hook function to allow overrides or changes Hooks.callAll("getSceneControlButtons", controls); return controls; } }