From 4954e41b02cd2d09caed5d49ccff380edede6c10 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 25 Nov 2025 17:13:13 +0100 Subject: [PATCH] Added the display --- module/applications/ui/countdowns.mjs | 11 +++- module/applications/ui/effectsDisplay.mjs | 53 +++++++++----------- module/config/_module.mjs | 1 + module/config/hooksConfig.mjs | 5 ++ module/config/system.mjs | 4 +- module/documents/tooltipManager.mjs | 26 ++++++++++ styles/less/ui/countdown/countdown.less | 3 +- styles/less/ui/effects-display/sheet.less | 4 ++ styles/less/ui/sidebar/tabs.less | 18 ++++--- styles/less/ux/index.less | 1 + styles/less/ux/tooltip/bordered-tooltip.less | 19 +++++++ templates/ui/effects-display.hbs | 14 +++--- templates/ui/tooltip/effect-display.hbs | 9 ++++ 13 files changed, 125 insertions(+), 43 deletions(-) create mode 100644 module/config/hooksConfig.mjs create mode 100644 styles/less/ux/tooltip/bordered-tooltip.less create mode 100644 templates/ui/tooltip/effect-display.hbs diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 55e1843f..4fc6b7c7 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -60,7 +60,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application async _renderFrame(options) { const frame = await super._renderFrame(options); - frame.classList.add('effects-present'); + if (game.system.api.applications.ui.DhEffectsDisplay.getTokenEffects().length > 0) { + frame.classList.add('effects-present'); + } const iconOnly = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === @@ -152,6 +154,11 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application if (refreshType === RefreshType.Countdown) this.render(); }; + effectDisplayToggle = (hidden, _token) => { + if (hidden) this.element.classList.remove('effects-present'); + else this.element.classList.add('effects-present'); + }; + static canPerformEdit() { if (game.user.isGM) return true; @@ -236,6 +243,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application } setupHooks() { + Hooks.on(CONFIG.DH.HOOKS.effectDisplayToggle, this.effectDisplayToggle.bind()); Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind()); } @@ -243,6 +251,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */ if (options.closeKey) return; + Hooks.off(CONFIG.DH.HOOKS.effectDisplayToggle, this.effectDisplayToggle); Hooks.off(socketEvent.Refresh, this.cooldownRefresh); return super.close(options); } diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index 6f7ca669..7ac3d964 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -1,5 +1,3 @@ -import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; - const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; /** @@ -46,35 +44,25 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica return this.element.classList.contains('hidden'); } - // /**@inheritdoc */ - // async _renderFrame(options) { - // const frame = await super._renderFrame(options); + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); - // const header = frame.querySelector('.window-header'); - // header.querySelector('button[data-action="close"]').remove(); - - // if (game.user.isGM) { - // const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle'); - // const editButton = ``; - // header.insertAdjacentHTML('beforeEnd', editButton); - // } - - // const minimizeTooltip = game.i18n.localize('DAGGERHEART.UI.Countdowns.toggleIconMode'); - // const minimizeButton = ``; - // header.insertAdjacentHTML('beforeEnd', minimizeButton); - - // return frame; - // } + if (this.element) { + this.element.querySelectorAll('.effect-container').forEach(element => { + element.addEventListener('contextmenu', this.removeEffect.bind(this)); + }); + } + } /** @override */ async _prepareContext(options) { const context = await super._prepareContext(options); - context.effects = this.getTokenEffects(); + context.effects = DhEffectsDisplay.getTokenEffects(); return context; } - getTokenEffects = token => { + static getTokenEffects = token => { const actor = token ? token.actor : canvas.tokens.controlled.length === 0 @@ -82,18 +70,28 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica ? game.user.character : null : canvas.tokens.controlled[0].actor; - return actor?.effects ?? []; + return actor?.effects ? Array.from(actor.effects) : []; }; toggleHidden(token, focused) { - const effects = this.getTokenEffects(focused ? token : null); - this.element.hidden = !focused || effects.size === 0; - if (effects.size > 0) this.render(); + const effects = DhEffectsDisplay.getTokenEffects(focused ? token : null); + this.element.hidden = effects.length === 0; + + Hooks.callAll(CONFIG.DH.HOOKS.effectDisplayToggle, this.element.hidden, token); + + if (effects.length > 0) this.render(); + } + + async removeEffect(event) { + const element = event.target.closest('.effect-container'); + const effects = DhEffectsDisplay.getTokenEffects(); + const effect = effects.find(x => x.id === element.id); + await effect.delete(); + this.render(); } setupHooks() { Hooks.on('controlToken', this.toggleHidden.bind(this)); - // Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind()); } async close(options) { @@ -101,7 +99,6 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica if (options.closeKey) return; Hooks.off('controlToken', this.toggleHidden); - // Hooks.off(socketEvent.Refresh, this.cooldownRefresh); return super.close(options); } diff --git a/module/config/_module.mjs b/module/config/_module.mjs index 63797607..8631ab32 100644 --- a/module/config/_module.mjs +++ b/module/config/_module.mjs @@ -4,6 +4,7 @@ export * as domainConfig from './domainConfig.mjs'; export * as effectConfig from './effectConfig.mjs'; export * as flagsConfig from './flagsConfig.mjs'; export * as generalConfig from './generalConfig.mjs'; +export * as hooksConfig from './hooksConfig.mjs'; export * as itemConfig from './itemConfig.mjs'; export * as settingsConfig from './settingsConfig.mjs'; export * as systemConfig from './system.mjs'; diff --git a/module/config/hooksConfig.mjs b/module/config/hooksConfig.mjs new file mode 100644 index 00000000..d316c4d4 --- /dev/null +++ b/module/config/hooksConfig.mjs @@ -0,0 +1,5 @@ +const hooksConfig = { + effectDisplayToggle: 'DHEffectDisplayToggle' +}; + +export default hooksConfig; diff --git a/module/config/system.mjs b/module/config/system.mjs index 374fd58c..c2c84509 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -6,7 +6,8 @@ import * as SETTINGS from './settingsConfig.mjs'; import * as EFFECTS from './effectConfig.mjs'; import * as ACTIONS from './actionConfig.mjs'; import * as FLAGS from './flagsConfig.mjs'; -import * as ITEMBROWSER from './itemBrowserConfig.mjs' +import HOOKS from './hooksConfig.mjs'; +import * as ITEMBROWSER from './itemBrowserConfig.mjs'; export const SYSTEM_ID = 'daggerheart'; @@ -20,5 +21,6 @@ export const SYSTEM = { EFFECTS, ACTIONS, FLAGS, + HOOKS, ITEMBROWSER }; diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 95621441..fd95d61e 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -1,8 +1,26 @@ export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager { + #bordered = false; + async activate(element, options = {}) { const { TextEditor } = foundry.applications.ux; let html = options.html; + if (element.dataset.tooltip === '#effect-display#') { + this.#bordered = true; + const effect = await foundry.utils.fromUuid(element.dataset.uuid); + html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/effect-display.hbs`, + { + effect + } + ); + + this.tooltip.innerHTML = html; + options.direction = this._determineItemTooltipDirection(element); + } else { + this.#bordered = false; + } + if (element.dataset.tooltip?.startsWith('#item#')) { const itemUuid = element.dataset.tooltip.slice(6); const item = await foundry.utils.fromUuid(itemUuid); @@ -112,6 +130,14 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti super.activate(element, { ...options, html: html }); } + _setStyle(position = {}) { + super._setStyle(position); + + if (this.#bordered) { + this.tooltip.classList.add('bordered-tooltip'); + } + } + _determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) { const pos = element.getBoundingClientRect(); const dirs = this.constructor.TOOLTIP_DIRECTIONS; diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 87f1e8fe..4adf1ba2 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -19,13 +19,14 @@ width: 300px; pointer-events: all; align-self: flex-end; + transition: 0.3s right ease-in-out; .window-title { font-family: @font-body; } &.effects-present { - right: 180px; + right: 84px; } &.icon-only { diff --git a/styles/less/ui/effects-display/sheet.less b/styles/less/ui/effects-display/sheet.less index db15365e..d5dea9fa 100644 --- a/styles/less/ui/effects-display/sheet.less +++ b/styles/less/ui/effects-display/sheet.less @@ -1,5 +1,6 @@ .daggerheart.dh-style.effects-display { position: absolute; + right: 0; width: 68px; max-height: 300px; overflow: hidden; @@ -16,7 +17,10 @@ gap: 8px; .effect-container { + cursor: pointer; + img { + pointer-events: all; } } } diff --git a/styles/less/ui/sidebar/tabs.less b/styles/less/ui/sidebar/tabs.less index ec4bbe9f..57d9bdd4 100644 --- a/styles/less/ui/sidebar/tabs.less +++ b/styles/less/ui/sidebar/tabs.less @@ -4,12 +4,18 @@ } } -#interface #ui-right #sidebar { - menu li button { - img { - width: 22px; - max-width: unset; - filter: @beige-filter; +#interface #ui-right { + #ui-right-column-1 { + position: relative; + } + + #sidebar { + menu li button { + img { + width: 22px; + max-width: unset; + filter: @beige-filter; + } } } } diff --git a/styles/less/ux/index.less b/styles/less/ux/index.less index 68cfc7e5..a5e507ea 100644 --- a/styles/less/ux/index.less +++ b/styles/less/ux/index.less @@ -1,2 +1,3 @@ @import './tooltip/tooltip.less'; +@import './tooltip/bordered-tooltip.less'; @import './autocomplete/autocomplete.less'; diff --git a/styles/less/ux/tooltip/bordered-tooltip.less b/styles/less/ux/tooltip/bordered-tooltip.less new file mode 100644 index 00000000..34c978d4 --- /dev/null +++ b/styles/less/ux/tooltip/bordered-tooltip.less @@ -0,0 +1,19 @@ +#tooltip.bordered-tooltip { + width: 320px; + border: 1px solid light-dark(@dark-blue, @golden); + + .daggerheart.dh-style.tooltip { + color: @beige; + + .tooltip-header { + display: flex; + flex-direction: column; + text-align: center; + + .helper { + font-size: 12px; + font-style: italic; + } + } + } +} diff --git a/templates/ui/effects-display.hbs b/templates/ui/effects-display.hbs index c4eb5d42..6da7f9db 100644 --- a/templates/ui/effects-display.hbs +++ b/templates/ui/effects-display.hbs @@ -1,7 +1,9 @@ -
- {{#each effects as | effect |}} -
- -
- {{/each}} +
+
+ {{#each effects as | effect |}} +
+ +
+ {{/each}} +
\ No newline at end of file diff --git a/templates/ui/tooltip/effect-display.hbs b/templates/ui/tooltip/effect-display.hbs new file mode 100644 index 00000000..31117257 --- /dev/null +++ b/templates/ui/tooltip/effect-display.hbs @@ -0,0 +1,9 @@ +
+
+

{{effect.name}}

+
{{localize "[Right Click] Remove Effect"}}
+
+
+ {{#if effect.description}}{{{effect.description}}}{{else}}{{{effect.parent.system.description}}}{{/if}} +
+
\ No newline at end of file