diff --git a/langs/en-ca.json b/langs/en-ca.json index 99de0b3..de83d02 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -13,6 +13,24 @@ }, "sheet-names": { "PlayerSheet": "Player Sheet" + }, + "Apps": { + "TAFDocumentSheetConfig": { + "Sizing": "Sizing", + "Width": { + "label": "Width" + }, + "Height": { + "label": "Height" + }, + "Resizable": { + "label": "Resizable" + }, + "tabs": { + "foundry": "Foundry", + "system": "Text-Based Actors" + } + } } } } diff --git a/module/apps/PlayerSheet.mjs b/module/apps/PlayerSheet.mjs index e9caf53..257182a 100644 --- a/module/apps/PlayerSheet.mjs +++ b/module/apps/PlayerSheet.mjs @@ -1,7 +1,7 @@ import { __ID__, filePath } from "../consts.mjs"; import { AttributeManager } from "./AttributeManager.mjs"; import { attributeSorter } from "../utils/attributeSort.mjs"; -import { ResizeControlManager } from "./ResizeControlManager.mjs"; +import { TAFDocumentSheetConfig } from "./TAFDocumentSheetConfig.mjs"; const { HandlebarsApplicationMixin } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets; @@ -28,7 +28,7 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }, actions: { manageAttributes: this.#manageAttributes, - sizeSettings: this.#configureSizeSettings, + configureSheet: this.#configureSheet, }, }; @@ -78,15 +78,6 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) { return isGM || (allowPlayerEdits && editable); }, }); - controls.push({ - icon: `fa-solid fa-crop-simple`, - label: `Configure Size`, - action: `sizeSettings`, - visible: () => { - const isGM = game.user.isGM; - return isGM; - }, - }); return controls; }; @@ -156,15 +147,18 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) { }; }; - #sizeSettings = null; - /** @this {PlayerSheet} */ - static async #configureSizeSettings() { - this.#sizeSettings ??= new ResizeControlManager({ document: this.actor }); - if (this.#sizeSettings.rendered) { - await this.#sizeSettings.bringToFront(); - } else { - await this.#sizeSettings.render({ force: true }); - }; + static async #configureSheet(event) { + event.stopPropagation(); + if ( event.detail > 1 ) { return } + + // const docSheetConfigWidth = TAFDocumentSheetConfig.DEFAULT_OPTIONS.position.width; + new TAFDocumentSheetConfig({ + document: this.document, + position: { + top: this.position.top + 40, + left: this.position.left + ((this.position.width - 60) / 2), + }, + }).render({ force: true }); }; // #endregion Actions }; diff --git a/module/apps/ResizeControlManager.mjs b/module/apps/ResizeControlManager.mjs deleted file mode 100644 index 3bd2b08..0000000 --- a/module/apps/ResizeControlManager.mjs +++ /dev/null @@ -1,61 +0,0 @@ -import { __ID__, filePath } from "../consts.mjs"; - -const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api; -const { getProperty } = foundry.utils; - -export class ResizeControlManager extends HandlebarsApplicationMixin(DocumentSheetV2) { - - // #region Options - static DEFAULT_OPTIONS = { - classes: [ - __ID__, - `ResizeControlManager`, - ], - position: { - width: 400, - height: `auto`, - }, - window: { - resizable: true, - }, - form: { - submitOnChange: false, - closeOnSubmit: true, - }, - actions: {}, - }; - - static PARTS = { - settings: { template: filePath(`templates/ResizeControlManager/settings.hbs`) }, - controls: { template: filePath(`templates/ResizeControlManager/controls.hbs`) }, - }; - // #endregion Options - - // #region Instance Data - get title() { - return `Sizing Settings For : ${this.document.name}`; - }; - // #endregion Instance Data - - // #region Data Prep - async _prepareContext() { - const sizing = getProperty(this.document, `flags.${__ID__}.PlayerSheet.size`) ?? {}; - - const ctx = { - meta: { - idp: this.id, - }, - width: sizing.width, - height: sizing.height, - resizable: sizing.resizable, - resizeOptions: [ - { label: `Default`, value: `` }, - { label: `Resizable`, value: `true` }, - { label: `No Resizing`, value: `false` }, - ], - }; - - return ctx; - }; - // #endregion Data Prep -}; diff --git a/module/apps/TAFDocumentSheetConfig.mjs b/module/apps/TAFDocumentSheetConfig.mjs new file mode 100644 index 0000000..760e462 --- /dev/null +++ b/module/apps/TAFDocumentSheetConfig.mjs @@ -0,0 +1,171 @@ +import { __ID__, filePath } from "../consts.mjs"; +import { getDefaultSizing } from "../utils/getSizing.mjs"; + +const { diffObject, expandObject, flattenObject } = foundry.utils; +const { DocumentSheetConfig } = foundry.applications.apps; +const { CONST } = foundry; + +export class TAFDocumentSheetConfig extends DocumentSheetConfig { + + // #region Options + static DEFAULT_OPTIONS = { + classes: [`taf`], + form: { + handler: this.#onSubmit, + }, + }; + + static get PARTS() { + const { form, footer } = super.PARTS; + return { + tabs: { template: `templates/generic/tab-navigation.hbs` }, + foundryTab: { + ...form, + template: filePath(`templates/TAFDocumentSheetConfig/foundry.hbs`), + templates: [ `templates/sheets/document-sheet-config.hbs` ], + }, + systemTab: { + template: filePath(`templates/TAFDocumentSheetConfig/system.hbs`), + classes: [`standard-form`], + }, + footer, + }; + }; + + static TABS = { + main: { + initial: `system`, + labelPrefix: `taf.Apps.TAFDocumentSheetConfig.tabs`, + tabs: [ + { id: `system` }, + { id: `foundry` }, + ], + }, + }; + // #endregion Options + + // #region Data Prep + async _preparePartContext(partID, context, options) { + this._prepareTabs(`main`); + + context.meta = { + idp: this.id, + }; + + switch (partID) { + case `foundryTab`: { + await this._prepareFormContext(context, options); + break; + }; + case `systemTab`: { + await this._prepareSystemSettingsContext(context, options); + break; + }; + case `footer`: { + await this._prepareFooterContext(context, options); + break; + }; + }; + return context; + }; + + async _prepareSystemSettingsContext(context, _options) { + // Inherited values for placeholders + const defaults = getDefaultSizing(); + context.placeholders = { + ...defaults, + resizable: defaults.resizable ? `Resizable` : `Not Resizable`, + }; + + // Custom values from document itself + const sheetConfig = this.document.getFlag(__ID__, `PlayerSheet`) ?? {}; + const sizing = sheetConfig.size ?? {}; + context.values = { + width: sizing.width, + height: sizing.height, + resizable: sizing.resizable ?? ``, + }; + + // Static prep + context.resizeOptions = [ + { label: `Default (${context.placeholders.resizable})`, value: `` }, + { label: `Resizable`, value: `true` }, + { label: `No Resizing`, value: `false` }, + ]; + }; + // #endregion Data Prep + + // #region Actions + /** @this {TAFDocumentSheetConfig} */ + static async #onSubmit(event, form, formData) { + const foundryReopen = await TAFDocumentSheetConfig.#submitFoundry.call(this, event, form, formData); + const systemReopen = await TAFDocumentSheetConfig.#submitSystem.call(this, event, form, formData); + if (foundryReopen || systemReopen) { + this.document._onSheetChange({ sheetOpen: true }); + }; + }; + + /** + * This method is mostly the form submission handler that foundry uses in + * DocumentSheetConfig, however because we clobber that in order to save our + * own config stuff as well, we need to duplicate Foundry's handling and tweak + * it a bit to make it work nicely with our custom saving. + * + * @this {TAFDocumentSheetConfig} + */ + static async #submitFoundry(_event, _form, formData) { + const { object } = formData; + const { documentName, type = CONST.BASE_DOCUMENT_TYPE } = this.document; + + // Update themes. + const themes = game.settings.get(`core`, `sheetThemes`); + const defaultTheme = foundry.utils.getProperty(themes, `defaults.${documentName}.${type}`); + const documentTheme = themes.documents?.[this.document.uuid]; + const themeChanged = (object.defaultTheme !== defaultTheme) || (object.theme !== documentTheme); + if (themeChanged) { + foundry.utils.setProperty(themes, `defaults.${documentName}.${type}`, object.defaultTheme); + themes.documents ??= {}; + themes.documents[this.document.uuid] = object.theme; + await game.settings.set(`core`, `sheetThemes`, themes); + } + + // Update sheets. + const { defaultClass } = this.constructor.getSheetClassesForSubType(documentName, type); + const sheetClass = this.document.getFlag(`core`, `sheetClass`) ?? ``; + const defaultSheetChanged = object.defaultClass !== defaultClass; + const documentSheetChanged = object.sheetClass !== sheetClass; + + if (themeChanged || (game.user.isGM && defaultSheetChanged)) { + if (game.user.isGM && defaultSheetChanged) { + const setting = game.settings.get(`core`, `sheetClasses`); + foundry.utils.setProperty(setting, `${documentName}.${type}`, object.defaultClass); + await game.settings.set(`core`, `sheetClasses`, setting); + } + + // This causes us to manually rerender the sheet due to the theme or default + // sheet class changing resulting in no update making it to the client-document's + // _onUpdate handling + if (!documentSheetChanged) { + return true; + } + } + + // Update the document-specific override. + if (documentSheetChanged) { + this.document.setFlag(`core`, `sheetClass`, object.sheetClass); + }; + return false; + }; + + /** @this {TAFDocumentSheetConfig} */ + static async #submitSystem(_event, _form, formData) { + const { FLAGS: flags } = expandObject(formData.object); + const diff = flattenObject(diffObject(this.document.flags, flags)); + const hasChanges = Object.keys(diff).length > 0; + if (hasChanges) { + await this.document.update({ flags }); + }; + return hasChanges; + }; + // #endregion Actions +}; diff --git a/module/utils/getSizing.mjs b/module/utils/getSizing.mjs new file mode 100644 index 0000000..63e6822 --- /dev/null +++ b/module/utils/getSizing.mjs @@ -0,0 +1,32 @@ +import { PlayerSheet } from "../apps/PlayerSheet.mjs"; + +/** + * @typedef SheetSizing + * @property {number} width The initial width of the application + * @property {number} height The initial height of the application + * @property {boolean} resizable Whether or not the application + * is able to be resized with a drag handle. + */ + +/** + * Retrieves the computed default sizing data based on world settings + * and the sheet class' DEFAULT_OPTIONS + * @returns {SheetSizing} + */ +export function getDefaultSizing() { + /** @type {SheetSizing} */ + const sizing = { + width: undefined, + height: undefined, + resizable: undefined, + }; + + // TODO: defaults from world settings + + // Defaults from the sheet class itself + sizing.height ||= PlayerSheet.DEFAULT_OPTIONS.position.height; + sizing.width ||= PlayerSheet.DEFAULT_OPTIONS.position.width; + sizing.resizable ||= PlayerSheet.DEFAULT_OPTIONS.window.resizable; + + return sizing; +}; diff --git a/styles/Apps/ResizeControlManager.css b/styles/Apps/ResizeControlManager.css deleted file mode 100644 index 7783d40..0000000 --- a/styles/Apps/ResizeControlManager.css +++ /dev/null @@ -1,20 +0,0 @@ -.taf.ResizeControlManager { - fieldset { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); - align-items: center; - gap: 8px; - border: 1px solid rebeccapurple; - border-radius: 4px; - } - - .controls { - display: flex; - flex-direction: row; - gap: 8px; - - button { - flex-grow: 1; - } - } -} diff --git a/styles/Apps/TAFDocumentSheetConfig.css b/styles/Apps/TAFDocumentSheetConfig.css new file mode 100644 index 0000000..247c8fd --- /dev/null +++ b/styles/Apps/TAFDocumentSheetConfig.css @@ -0,0 +1,15 @@ +.taf.sheet-config { + + section { + display: flex; + flex-direction: column; + gap: 1rem; + } + + .tab { + display: none; + } + .tab.active { + display: unset; + } +} diff --git a/styles/main.css b/styles/main.css index bc257ab..10ea6ec 100644 --- a/styles/main.css +++ b/styles/main.css @@ -20,4 +20,4 @@ @import url("./Apps/Ask.css") layer(apps); @import url("./Apps/AttributeManager.css") layer(apps); @import url("./Apps/PlayerSheet.css") layer(apps); -@import url("./Apps/ResizeControlManager.css") layer(apps); +@import url("./Apps/TAFDocumentSheetConfig.css") layer(apps); diff --git a/system.json b/system.json index 0f4d84a..1aaa3a5 100644 --- a/system.json +++ b/system.json @@ -44,7 +44,7 @@ "flags": { "hotReload": { "extensions": ["css", "hbs", "json", "js", "mjs", "svg"], - "paths": ["templates", "langs", ".styles", "module", "assets"] + "paths": ["templates", "langs", "styles", "module", "assets"] } } } diff --git a/templates/ResizeControlManager/controls.hbs b/templates/ResizeControlManager/controls.hbs deleted file mode 100644 index 6f62afb..0000000 --- a/templates/ResizeControlManager/controls.hbs +++ /dev/null @@ -1,7 +0,0 @@ -
- Changes to these settings will only take effect after a reload of Foundry. -
- -