Added the display

This commit is contained in:
WBHarry 2025-11-25 17:13:13 +01:00
parent 01dd5ced94
commit 4954e41b02
13 changed files with 125 additions and 43 deletions

View file

@ -60,7 +60,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
async _renderFrame(options) { async _renderFrame(options) {
const frame = await super._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 = const iconOnly =
game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode) === 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(); 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() { static canPerformEdit() {
if (game.user.isGM) return true; if (game.user.isGM) return true;
@ -236,6 +243,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
} }
setupHooks() { setupHooks() {
Hooks.on(CONFIG.DH.HOOKS.effectDisplayToggle, this.effectDisplayToggle.bind());
Hooks.on(socketEvent.Refresh, this.cooldownRefresh.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 */ /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */
if (options.closeKey) return; if (options.closeKey) return;
Hooks.off(CONFIG.DH.HOOKS.effectDisplayToggle, this.effectDisplayToggle);
Hooks.off(socketEvent.Refresh, this.cooldownRefresh); Hooks.off(socketEvent.Refresh, this.cooldownRefresh);
return super.close(options); return super.close(options);
} }

View file

@ -1,5 +1,3 @@
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
/** /**
@ -46,35 +44,25 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
return this.element.classList.contains('hidden'); return this.element.classList.contains('hidden');
} }
// /**@inheritdoc */ _attachPartListeners(partId, htmlElement, options) {
// async _renderFrame(options) { super._attachPartListeners(partId, htmlElement, options);
// const frame = await super._renderFrame(options);
// const header = frame.querySelector('.window-header'); if (this.element) {
// header.querySelector('button[data-action="close"]').remove(); this.element.querySelectorAll('.effect-container').forEach(element => {
element.addEventListener('contextmenu', this.removeEffect.bind(this));
// if (game.user.isGM) { });
// const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle'); }
// const editButton = `<a style="margin-right: 8px;" class="header-control" data-tooltip="${editTooltip}" aria-label="${editTooltip}" data-action="editCountdowns"><i class="fa-solid fa-wrench"></i></a>`; }
// header.insertAdjacentHTML('beforeEnd', editButton);
// }
// const minimizeTooltip = game.i18n.localize('DAGGERHEART.UI.Countdowns.toggleIconMode');
// const minimizeButton = `<a class="header-control" data-tooltip="${minimizeTooltip}" aria-label="${minimizeTooltip}" data-action="toggleViewMode"><i class="fa-solid fa-down-left-and-up-right-to-center"></i></a>`;
// header.insertAdjacentHTML('beforeEnd', minimizeButton);
// return frame;
// }
/** @override */ /** @override */
async _prepareContext(options) { async _prepareContext(options) {
const context = await super._prepareContext(options); const context = await super._prepareContext(options);
context.effects = this.getTokenEffects(); context.effects = DhEffectsDisplay.getTokenEffects();
return context; return context;
} }
getTokenEffects = token => { static getTokenEffects = token => {
const actor = token const actor = token
? token.actor ? token.actor
: canvas.tokens.controlled.length === 0 : canvas.tokens.controlled.length === 0
@ -82,18 +70,28 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
? game.user.character ? game.user.character
: null : null
: canvas.tokens.controlled[0].actor; : canvas.tokens.controlled[0].actor;
return actor?.effects ?? []; return actor?.effects ? Array.from(actor.effects) : [];
}; };
toggleHidden(token, focused) { toggleHidden(token, focused) {
const effects = this.getTokenEffects(focused ? token : null); const effects = DhEffectsDisplay.getTokenEffects(focused ? token : null);
this.element.hidden = !focused || effects.size === 0; this.element.hidden = effects.length === 0;
if (effects.size > 0) this.render();
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() { setupHooks() {
Hooks.on('controlToken', this.toggleHidden.bind(this)); Hooks.on('controlToken', this.toggleHidden.bind(this));
// Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind());
} }
async close(options) { async close(options) {
@ -101,7 +99,6 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
if (options.closeKey) return; if (options.closeKey) return;
Hooks.off('controlToken', this.toggleHidden); Hooks.off('controlToken', this.toggleHidden);
// Hooks.off(socketEvent.Refresh, this.cooldownRefresh);
return super.close(options); return super.close(options);
} }

View file

@ -4,6 +4,7 @@ export * as domainConfig from './domainConfig.mjs';
export * as effectConfig from './effectConfig.mjs'; export * as effectConfig from './effectConfig.mjs';
export * as flagsConfig from './flagsConfig.mjs'; export * as flagsConfig from './flagsConfig.mjs';
export * as generalConfig from './generalConfig.mjs'; export * as generalConfig from './generalConfig.mjs';
export * as hooksConfig from './hooksConfig.mjs';
export * as itemConfig from './itemConfig.mjs'; export * as itemConfig from './itemConfig.mjs';
export * as settingsConfig from './settingsConfig.mjs'; export * as settingsConfig from './settingsConfig.mjs';
export * as systemConfig from './system.mjs'; export * as systemConfig from './system.mjs';

View file

@ -0,0 +1,5 @@
const hooksConfig = {
effectDisplayToggle: 'DHEffectDisplayToggle'
};
export default hooksConfig;

View file

@ -6,7 +6,8 @@ import * as SETTINGS from './settingsConfig.mjs';
import * as EFFECTS from './effectConfig.mjs'; import * as EFFECTS from './effectConfig.mjs';
import * as ACTIONS from './actionConfig.mjs'; import * as ACTIONS from './actionConfig.mjs';
import * as FLAGS from './flagsConfig.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'; export const SYSTEM_ID = 'daggerheart';
@ -20,5 +21,6 @@ export const SYSTEM = {
EFFECTS, EFFECTS,
ACTIONS, ACTIONS,
FLAGS, FLAGS,
HOOKS,
ITEMBROWSER ITEMBROWSER
}; };

View file

@ -1,8 +1,26 @@
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager { export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
#bordered = false;
async activate(element, options = {}) { async activate(element, options = {}) {
const { TextEditor } = foundry.applications.ux; const { TextEditor } = foundry.applications.ux;
let html = options.html; 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#')) { if (element.dataset.tooltip?.startsWith('#item#')) {
const itemUuid = element.dataset.tooltip.slice(6); const itemUuid = element.dataset.tooltip.slice(6);
const item = await foundry.utils.fromUuid(itemUuid); 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 }); 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) { _determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
const pos = element.getBoundingClientRect(); const pos = element.getBoundingClientRect();
const dirs = this.constructor.TOOLTIP_DIRECTIONS; const dirs = this.constructor.TOOLTIP_DIRECTIONS;

View file

@ -19,13 +19,14 @@
width: 300px; width: 300px;
pointer-events: all; pointer-events: all;
align-self: flex-end; align-self: flex-end;
transition: 0.3s right ease-in-out;
.window-title { .window-title {
font-family: @font-body; font-family: @font-body;
} }
&.effects-present { &.effects-present {
right: 180px; right: 84px;
} }
&.icon-only { &.icon-only {

View file

@ -1,5 +1,6 @@
.daggerheart.dh-style.effects-display { .daggerheart.dh-style.effects-display {
position: absolute; position: absolute;
right: 0;
width: 68px; width: 68px;
max-height: 300px; max-height: 300px;
overflow: hidden; overflow: hidden;
@ -16,7 +17,10 @@
gap: 8px; gap: 8px;
.effect-container { .effect-container {
cursor: pointer;
img { img {
pointer-events: all;
} }
} }
} }

View file

@ -4,12 +4,18 @@
} }
} }
#interface #ui-right #sidebar { #interface #ui-right {
menu li button { #ui-right-column-1 {
img { position: relative;
width: 22px; }
max-width: unset;
filter: @beige-filter; #sidebar {
menu li button {
img {
width: 22px;
max-width: unset;
filter: @beige-filter;
}
} }
} }
} }

View file

@ -1,2 +1,3 @@
@import './tooltip/tooltip.less'; @import './tooltip/tooltip.less';
@import './tooltip/bordered-tooltip.less';
@import './autocomplete/autocomplete.less'; @import './autocomplete/autocomplete.less';

View file

@ -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;
}
}
}
}

View file

@ -1,7 +1,9 @@
<div class="effects-display-container"> <div>
{{#each effects as | effect |}} <div class="effects-display-container">
<div class="effect-container"> {{#each effects as | effect |}}
<img src="{{effect.img}}" /> <div class="effect-container" data-tooltip="#effect-display#" data-uuid="{{effect.uuid}}" id="{{effect.id}}">
</div> <img src="{{effect.img}}" />
{{/each}} </div>
{{/each}}
</div>
</div> </div>

View file

@ -0,0 +1,9 @@
<div class="daggerheart dh-style tooltip">
<div class="tooltip-header">
<h3>{{effect.name}}</h3>
<div class="helper">{{localize "[Right Click] Remove Effect"}}</div>
</div>
<div>
{{#if effect.description}}{{{effect.description}}}{{else}}{{{effect.parent.system.description}}}{{/if}}
</div>
</div>