From 8d94d5f2ea49bbe55bbd9c91a93b793679923921 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 23 Jul 2026 03:52:08 -0400 Subject: [PATCH] Fix top center and top right positioning in some situations (#2108) --- daggerheart.d.ts | 13 +++++++-- module/applications/ui/chatLog.mjs | 3 +-- module/applications/ui/effectsDisplay.mjs | 2 ++ module/applications/ui/fearTracker.mjs | 32 ++++++++++++++++++----- module/data/settings/Appearance.mjs | 10 ++++--- styles/less/ui/countdown/countdown.less | 2 +- styles/less/ui/resources/resources.less | 10 ++++--- 7 files changed, 53 insertions(+), 19 deletions(-) diff --git a/daggerheart.d.ts b/daggerheart.d.ts index 02abf063..8dd0734f 100644 --- a/daggerheart.d.ts +++ b/daggerheart.d.ts @@ -12,8 +12,10 @@ import { macros } from './module/_module.mjs'; import * as dice from './module/dice/_module.mjs'; import * as fields from './module/data/fields/_module.mjs'; import { gameSettings } from './module/config/settingsConfig.mjs'; -import DhCountdowns from './module/data/countdowns.mjs'; import DhAutomation from './module/data/settings/Automation.mjs'; +import FearTracker from './module/applications/ui/fearTracker.mjs'; +import DhCountdowns from './module/applications/ui/countdowns.mjs'; +import DhEffectsDisplay from './module/applications/ui/effectsDisplay.mjs'; // Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such @@ -115,4 +117,11 @@ declare module '@client/helpers/client-settings.mjs' { get(namespace: 'daggerheart', key: typeof gameSettings.Countdowns): DhCountdowns; get(namespace: 'daggerheart', key: string): unknown; } -} \ No newline at end of file +} + +// Add to global ui object +declare module '@client/ui.mjs' { + const countdowns: DhCountdowns; + const resources: FearTracker; + const effectsDisplay: DhEffectsDisplay; +} diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index fcef7786..1b75f96f 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,7 +1,6 @@ import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs'; import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs'; import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs'; -import FearTracker from './fearTracker.mjs'; export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { @@ -284,7 +283,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo _toggleNotifications({ closing = false } = {}) { super._toggleNotifications(closing) - FearTracker.handleOffSet(); + ui.resources.handleOffset(); } async onRollReloadCheck(_event, messageData) { diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index c9739f56..9cfedeaf 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -130,5 +130,7 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica if (options?.force) { document.getElementById('ui-right-column-1')?.appendChild(this.element); } + + ui.resources.handleOffset(); } } diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index d1813736..696f7a84 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -92,7 +92,7 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV const fearPosition = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).fearPosition; - if (options.isFirstRender) FearTracker.handleOffSet(); + if (options.isFirstRender) this.handleOffset(); if (!options.force) return; this.handleStyleElement(fearPosition); @@ -134,17 +134,37 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV this.element.classList.add(fearPosition); } - static handleOffSet() { + handleOffset() { const fearTracker = document.getElementById('resources'); const hotbar = document.getElementById('hotbar'); - + const countdowns = document.getElementById('countdowns'); if (!fearTracker) return; - const offset = Number(hotbar.style.getPropertyValue('--offset').replace(/px$/, '')) || 0; + let offset = Math.max(0, Number(hotbar.style.getPropertyValue('--offset').replace(/px$/, '')) || 0); + offset -= 13; - if (offset > 0) return; + // If the countdowns overlaps the fear, offset some more. Countdown based offsets only need to be halved (due to centering) + if (this.fearPosition === 'topCenter') { + const effectsDisplay = document.getElementById('effects-display'); + const top = document.getElementById('ui-top'); - fearTracker.style.setProperty('--offset', `${offset - 13}px`); + // Logic derived from the internal code's ChatLog#offsetHotbar(). Since the sidebar might be mid animation, we can't really check directly. + const { uiScale } = game.settings.get('core', 'uiConfig'); + const sidebarWidth = (348 * ui.sidebar.expanded) / uiScale; + const countdownsWidth = countdowns.getBoundingClientRect().width; + const effectsWidth = effectsDisplay.getBoundingClientRect().width; + const rightWidth = sidebarWidth + countdownsWidth + 16 + (effectsWidth ? 62 : 0); + const countdownLeftEdge = window.innerWidth - rightWidth - 16; // w/ extra padding + + const topBounds = top.getBoundingClientRect(); + const fearTrackerWidth = fearTracker.clientWidth; + const currentRightEdge = topBounds.right - (topBounds.width - fearTrackerWidth) / 2; + const countdownShift = countdownLeftEdge < currentRightEdge ? countdownLeftEdge - currentRightEdge : null + + offset = Math.min(offset, countdownShift ?? Infinity); + } + + fearTracker.style.setProperty('--offset', `${offset}px`); } _onPosition(position) { diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index 16f5fac0..7fe3e90a 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -123,10 +123,12 @@ export default class DhAppearance extends foundry.abstract.DataModel { /** Invoked by the setting when data changes */ handleChange() { - if (this.displayFear) { - if (ui.resources) { - if (this.displayFear === 'hide') ui.resources.close({ allowed: true }); - else ui.resources.render({ force: true }); + if (ui.resources) { + if (this.displayFear === 'hide') { + ui.resources.close({ allowed: true }); + } else { + ui.resources.render({ force: true }); + ui.resources.handleOffset(); } } diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 4e64d50a..c6e79448 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -44,7 +44,7 @@ transition: opacity var(--ui-fade-duration); } - #ui-right:has(#effects-display .effect-container) & { + #ui-right:has(#effects-display):has(.effect-container) & { right: 62px; } diff --git a/styles/less/ui/resources/resources.less b/styles/less/ui/resources/resources.less index 27f11942..8aac2ba2 100644 --- a/styles/less/ui/resources/resources.less +++ b/styles/less/ui/resources/resources.less @@ -65,8 +65,14 @@ body.theme-light { } &.rightTop { + position: relative; width: 300px !important; max-width: 300px; + left: unset!important; + top: unset!important; + #ui-right:has(#effects-display):has(.effect-container) & { + right: 62px; + } } &.leftBottom { @@ -79,10 +85,6 @@ body.theme-light { backdrop-filter: blur(5px); } - #ui-right:has(#effects-display .effect-container) & { - right: 62px; - } - #resource-fear { position: relative;