Files
Foundry-VTT-Docker/resources/app/client-esm/applications/sheets/ambient-light-config.mjs
2025-01-04 00:34:03 +01:00

260 lines
7.7 KiB
JavaScript

import DocumentSheetV2 from "../api/document-sheet.mjs";
import HandlebarsApplicationMixin from "../api/handlebars-application.mjs";
/**
* The AmbientLight configuration application.
* @extends DocumentSheetV2
* @mixes HandlebarsApplication
* @alias AmbientLightConfig
*/
export default class AmbientLightConfig extends HandlebarsApplicationMixin(DocumentSheetV2) {
/** @inheritDoc */
static DEFAULT_OPTIONS = {
classes: ["ambient-light-config"],
window: {
contentClasses: ["standard-form"]
},
position: {
width: 560,
height: "auto"
},
form: {
handler: this.#onSubmit,
closeOnSubmit: true
},
actions:{
reset: this.#onReset
}
};
/** @override */
static PARTS = {
tabs: {
template: "templates/generic/tab-navigation.hbs"
},
basic: {
template: "templates/scene/parts/light-basic.hbs"
},
animation: {
template: "templates/scene/parts/light-animation.hbs"
},
advanced: {
template: "templates/scene/parts/light-advanced.hbs"
},
footer: {
template: "templates/generic/form-footer.hbs"
}
}
/**
* Maintain a copy of the original to show a real-time preview of changes.
* @type {AmbientLightDocument}
*/
preview;
/** @override */
tabGroups = {
sheet: "basic"
}
/* -------------------------------------------- */
/* Application Rendering */
/* -------------------------------------------- */
/** @inheritDoc */
async _preRender(context, options) {
await super._preRender(context, options);
if ( this.preview?.rendered ) {
await this.preview.object.draw();
this.document.object.initializeLightSource({deleted: true});
this.preview.object.layer.preview.addChild(this.preview.object);
this._previewChanges();
}
}
/* -------------------------------------------- */
/** @inheritDoc */
_onRender(context, options) {
super._onRender(context, options);
this.#toggleReset();
}
/* -------------------------------------------- */
/** @override */
_onClose(options) {
super._onClose(options);
if ( this.preview ) this._resetPreview();
if ( this.document.rendered ) this.document.object.initializeLightSource();
}
/* -------------------------------------------- */
/** @override */
async _prepareContext(options) {
// Create the preview on first render
if ( options.isFirstRender && this.document.object ) {
const clone = this.document.object.clone();
this.preview = clone.document;
}
// Prepare context
const document = this.preview ?? this.document;
const isDarkness = document.config.negative;
return {
light: document,
source: document.toObject(),
fields: document.schema.fields,
colorationTechniques: AdaptiveLightingShader.SHADER_TECHNIQUES,
gridUnits: document.parent.grid.units || game.i18n.localize("GridUnits"),
isDarkness,
lightAnimations: isDarkness ? CONFIG.Canvas.darknessAnimations : CONFIG.Canvas.lightAnimations,
tabs: this.#getTabs(),
buttons: [
{
type: "reset",
action: "reset",
icon: "fa-solid fa-undo",
label: "AMBIENT_LIGHT.ACTIONS.RESET"
},
{
type: "submit",
icon: "fa-solid fa-save",
label: this.document.id ? "AMBIENT_LIGHT.ACTIONS.UPDATE" : "AMBIENT_LIGHT.ACTIONS.CREATE"
}
]
}
}
/* -------------------------------------------- */
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
basic: {id: "basic", group: "sheet", icon: "fa-solid fa-lightbulb", label: "AMBIENT_LIGHT.SECTIONS.BASIC"},
animation: {id: "animation", group: "sheet", icon: "fa-solid fa-play", label: "AMBIENT_LIGHT.SECTIONS.ANIMATION"},
advanced: {id: "advanced", group: "sheet", icon: "fa-solid fa-cogs", label: "AMBIENT_LIGHT.SECTIONS.ADVANCED"}
}
for ( const v of Object.values(tabs) ) {
v.active = this.tabGroups[v.group] === v.id;
v.cssClass = v.active ? "active" : "";
}
return tabs;
}
/* -------------------------------------------- */
/**
* Toggle visibility of the reset button which is only visible on the advanced tab.
*/
#toggleReset() {
const reset = this.element.querySelector("button[data-action=reset]");
reset.classList.toggle("hidden", this.tabGroups.sheet !== "advanced");
}
/* -------------------------------------------- */
/** @inheritDoc */
changeTab(...args) {
super.changeTab(...args);
this.#toggleReset();
}
/* -------------------------------------------- */
/* Real-Time Preview */
/* -------------------------------------------- */
/** @inheritDoc */
_onChangeForm(formConfig, event) {
super._onChangeForm(formConfig, event);
const formData = new FormDataExtended(this.element);
this._previewChanges(formData.object);
// Special handling for darkness state change
if ( event.target.name === "config.negative") this.render({parts: ["animation", "advanced"]});
}
/* -------------------------------------------- */
/**
* Preview changes to the AmbientLight document as if they were true document updates.
* @param {object} [change] A change to preview.
* @protected
*/
_previewChanges(change) {
if ( !this.preview ) return;
if ( change ) this.preview.updateSource(change);
if ( this.preview?.rendered ) {
this.preview.object.renderFlags.set({refresh: true});
this.preview.object.initializeLightSource();
}
}
/* -------------------------------------------- */
/**
* Restore the true data for the AmbientLight document when the form is submitted or closed.
* @protected
*/
_resetPreview() {
if ( !this.preview ) return;
if ( this.preview.rendered ) {
this.preview.object.destroy({children: true});
}
this.preview = null;
if ( this.document.rendered ) {
const object = this.document.object;
object.renderable = true;
object.initializeLightSource();
object.renderFlags.set({refresh: true});
}
}
/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */
/**
* Process form submission for the sheet.
* @param {SubmitEvent} event The originating form submission event
* @param {HTMLFormElement} form The form element that was submitted
* @param {FormDataExtended} formData Processed data for the submitted form
* @this {AmbientLightConfig}
* @returns {Promise<void>}
*/
static async #onSubmit(event, form, formData) {
const submitData = this._prepareSubmitData(event, form, formData);
if ( this.document.id ) await this.document.update(submitData);
else await this.document.constructor.create(submitData, {parent: canvas.scene});
}
/* -------------------------------------------- */
/**
* Process reset button click
* @param {PointerEvent} event The originating button click
* @this {AmbientLightConfig}
* @returns {Promise<void>}
*/
static async #onReset(event) {
event.preventDefault();
const defaults = AmbientLightDocument.cleanData();
const keys = ["vision", "config"];
const configKeys = ["coloration", "contrast", "attenuation", "luminosity", "saturation", "shadows"];
for ( const k in defaults ) {
if ( !keys.includes(k) ) delete defaults[k];
}
for ( const k in defaults.config ) {
if ( !configKeys.includes(k) ) delete defaults.config[k];
}
this._previewChanges(defaults);
await this.render();
}
}