From d00248593a367f30b9418c19601d64b8768ec78a Mon Sep 17 00:00:00 2001 From: CPTN Cosmo Date: Mon, 22 Dec 2025 00:01:20 +0100 Subject: [PATCH] feat: Rework countdown visuals with new fill, border, and number color settings, including dynamic settings visibility and a "both" display mode. --- scripts/countdown-app.js | 34 ++++-- scripts/module.js | 200 ++++++++++++++++++++++++++++---- styles/countdown.css | 8 ++ templates/countdown-tracker.hbs | 77 ++++++++++-- 4 files changed, 282 insertions(+), 37 deletions(-) diff --git a/scripts/countdown-app.js b/scripts/countdown-app.js index 48745ca..5357482 100644 --- a/scripts/countdown-app.js +++ b/scripts/countdown-app.js @@ -54,12 +54,18 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV const iconShape = game.settings.get("dh-improved-countdowns", "iconShape"); const displayMode = game.settings.get("dh-improved-countdowns", "displayMode"); const barOrientation = game.settings.get("dh-improved-countdowns", "barOrientation"); - const visualColor = game.settings.get("dh-improved-countdowns", "visualColor"); const enableVisualOverlay = game.settings.get("dh-improved-countdowns", "enableVisualOverlay"); + const fillType = game.settings.get("dh-improved-countdowns", "fillType"); + const invertProgress = game.settings.get("dh-improved-countdowns", "invertProgress"); + const numberColor = game.settings.get("dh-improved-countdowns", "numberColor"); + const fillColor = game.settings.get("dh-improved-countdowns", "fillColor"); const enableVisualBorder = game.settings.get("dh-improved-countdowns", "enableVisualBorder"); + const invertBorder = game.settings.get("dh-improved-countdowns", "invertBorder"); + const borderColor = game.settings.get("dh-improved-countdowns", "borderColor"); const gmAlwaysShowNumbers = game.settings.get("dh-improved-countdowns", "gmAlwaysShowNumbers"); - const showNumbers = (isGM && gmAlwaysShowNumbers) || displayMode === "number"; + const showNumbers = (isGM && gmAlwaysShowNumbers) || displayMode === "number" || displayMode === "both"; + const showVisuals = displayMode === "visual" || displayMode === "both"; // Fetch countdowns from system settings const systemCountdownSetting = game.settings.get("daggerheart", "Countdowns"); @@ -71,12 +77,20 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV if (ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { const current = countdown.progress.current; const max = countdown.progress.start; - const percentage = Math.max(0, Math.min(100, (current / max) * 100)); + let percentage = Math.max(0, Math.min(100, (current / max) * 100)); + const pctRemaining = 100 - percentage; + + /* + if (invertProgress) { + // We handle inversion in template now to preserve position + } + */ countdowns[id] = { ...countdown, editable: isGM || ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, percentage, + pctRemaining, cssClass: `shape-${iconShape}` }; } @@ -89,11 +103,17 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV isMinimized, isLocked, showNumbers, + showVisuals, iconShape, barOrientation, - visualColor, enableVisualOverlay, - enableVisualBorder + fillType, + fillColor, + enableVisualBorder, + invertBorder, + borderColor, + invertProgress, + numberColor }; } @@ -130,13 +150,13 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV static async #onToggleView(event, target) { const current = game.settings.get("dh-improved-countdowns", "minimized"); await game.settings.set("dh-improved-countdowns", "minimized", !current); - this.instance.render(); + CountdownTrackerApp.instance?.render(); } static async #onToggleLock(event, target) { const current = game.settings.get("dh-improved-countdowns", "locked"); await game.settings.set("dh-improved-countdowns", "locked", !current); - this.instance.render(); + CountdownTrackerApp.instance?.render(); } _onRender(context, options) { diff --git a/scripts/module.js b/scripts/module.js index c36b4ab..50bb41f 100644 --- a/scripts/module.js +++ b/scripts/module.js @@ -14,7 +14,7 @@ Hooks.once('init', () => { name: "Lock Tracker Position", hint: "Prevents the countdown tracker from being dragged.", scope: "client", - config: true, + config: false, // Hidden from settings menu, toggled via UI type: Boolean, default: false, onChange: () => CountdownTrackerApp.instance?.render() @@ -29,6 +29,21 @@ Hooks.once('init', () => { onChange: () => CountdownTrackerApp.instance?.render() }); + game.settings.register("dh-improved-countdowns", "displayMode", { + name: "Display Mode", + hint: "Choose how the countdown value is displayed.", + scope: "world", + config: true, + type: String, + choices: { + "number": "Number Only", + "visual": "Visual Only", + "both": "Visual + Number" + }, + default: "number", + onChange: () => CountdownTrackerApp.instance?.render() + }); + game.settings.register("dh-improved-countdowns", "iconShape", { name: "Icon Shape", hint: "Choose the shape of the countdown icons.", @@ -43,17 +58,13 @@ Hooks.once('init', () => { onChange: () => CountdownTrackerApp.instance?.render() }); - game.settings.register("dh-improved-countdowns", "displayMode", { - name: "Display Mode", - hint: "Choose how the countdown value is displayed.", - scope: "world", + game.settings.register("dh-improved-countdowns", "numberColor", { + name: "Number Color", + hint: "Color for the numerical text.", + scope: "client", config: true, type: String, - choices: { - "number": "Number", - "visual": "Visual (Bar/Clock)" - }, - default: "number", + default: "#ffffff", onChange: () => CountdownTrackerApp.instance?.render() }); @@ -71,16 +82,6 @@ Hooks.once('init', () => { onChange: () => CountdownTrackerApp.instance?.render() }); - game.settings.register("dh-improved-countdowns", "visualColor", { - name: "Visual Color", - hint: "Choose the color for the progress overlay and border.", - scope: "client", - config: true, - type: String, - default: "#ffffff", - onChange: () => CountdownTrackerApp.instance?.render() - }); - game.settings.register("dh-improved-countdowns", "enableVisualOverlay", { name: "Enable Fill Overlay", hint: "Show the filled progress overlay (Bar or Clock).", @@ -91,6 +92,40 @@ Hooks.once('init', () => { onChange: () => CountdownTrackerApp.instance?.render() }); + game.settings.register("dh-improved-countdowns", "fillType", { + name: "Fill Type", + hint: "Choose between a color overlay or a grayscale filter method.", + scope: "client", + config: true, + type: String, + choices: { + "color": "Color Overlay", + "grayscale": "Grayscale Filter" + }, + default: "color", + onChange: () => CountdownTrackerApp.instance?.render() + }); + + game.settings.register("dh-improved-countdowns", "invertProgress", { + name: "Invert Fill Overlay", + hint: "Fill the empty space instead of the current value.", + scope: "client", + config: true, + type: Boolean, + default: false, + onChange: () => CountdownTrackerApp.instance?.render() + }); + + game.settings.register("dh-improved-countdowns", "fillColor", { + name: "Fill Overlay Color", + hint: "Color for the filled progress overlay.", + scope: "client", + config: true, + type: String, + default: "#ffffff", + onChange: () => CountdownTrackerApp.instance?.render() + }); + game.settings.register("dh-improved-countdowns", "enableVisualBorder", { name: "Enable Border Progress", hint: "Show a progress border around the icon.", @@ -101,6 +136,26 @@ Hooks.once('init', () => { onChange: () => CountdownTrackerApp.instance?.render() }); + game.settings.register("dh-improved-countdowns", "invertBorder", { + name: "Invert Border Progress", + hint: "Fill the empty space instead of the current value for the border.", + scope: "client", + config: true, + type: Boolean, + default: false, + onChange: () => CountdownTrackerApp.instance?.render() + }); + + game.settings.register("dh-improved-countdowns", "borderColor", { + name: "Border Color", + hint: "Color for the progress border.", + scope: "client", + config: true, + type: String, + default: "#ffffff", + onChange: () => CountdownTrackerApp.instance?.render() + }); + game.settings.register("dh-improved-countdowns", "gmAlwaysShowNumbers", { name: "GM Always Shows Numbers", hint: "If enabled, the GM will always see the numerical value even if Display Mode is set to Visual.", @@ -112,6 +167,111 @@ Hooks.once('init', () => { }); }); +Hooks.on('renderSettingsConfig', (app, html, data) => { + // Ensure html is a jQuery object + html = $(html); + + const moduleId = "dh-improved-countdowns"; + console.log("Improved Countdowns | Settings Config Rendered"); + + // Helper to find the form group for a setting + const getGroup = (settingName) => { + const input = html.find(`[name="${moduleId}.${settingName}"]`); + const group = input.closest(".form-group"); + if (!group.length) console.warn(`Improved Countdowns | Could not find form group for ${settingName}`); + return group; + }; + + const enableOverlayInput = html.find(`[name="${moduleId}.enableVisualOverlay"]`); + const enableBorderInput = html.find(`[name="${moduleId}.enableVisualBorder"]`); + const displayModeInput = html.find(`[name="${moduleId}.displayMode"]`); + + // Debug findings + if (!enableOverlayInput.length) console.warn("Improved Countdowns | Enable Overlay Input not found"); + // if (!displayModeInput.length) console.warn("Improved Countdowns | Display Mode Input not found"); + + const fillTypeGroup = getGroup("fillType"); + const invertProgressGroup = getGroup("invertProgress"); + const fillColorGroup = getGroup("fillColor"); + const invertBorderGroup = getGroup("invertBorder"); + const borderColorGroup = getGroup("borderColor"); + const barOrientationGroup = getGroup("barOrientation"); + + // Number specific groups + const numberColorGroup = getGroup("numberColor"); + // Visual specific groups - we already have them above + const enableOverlayGroup = getGroup("enableVisualOverlay"); + const enableBorderGroup = getGroup("enableVisualBorder"); + + const updateVisibility = () => { + const displayMode = displayModeInput.val(); + const showVisualSettings = displayMode === "visual" || displayMode === "both"; + const showNumberSettings = displayMode === "number" || displayMode === "both"; + + // Number Settings Visibility + if (showNumberSettings) { + numberColorGroup.show(); + } else { + numberColorGroup.hide(); + } + + // Visual Settings Visibility + if (showVisualSettings) { + enableOverlayGroup.show(); + enableBorderGroup.show(); + + // Nested visual settings logic + const overlayEnabled = enableOverlayInput.prop("checked"); + const borderEnabled = enableBorderInput.prop("checked"); + const fillType = html.find(`[name="${moduleId}.fillType"]`).val(); + + if (overlayEnabled) { + fillTypeGroup.show(); + invertProgressGroup.show(); + barOrientationGroup.show(); + + if (fillType === "grayscale") { + fillColorGroup.hide(); + } else { + fillColorGroup.show(); + } + } else { + fillTypeGroup.hide(); + invertProgressGroup.hide(); + fillColorGroup.hide(); + barOrientationGroup.hide(); + } + + if (borderEnabled) { + invertBorderGroup.show(); + borderColorGroup.show(); + } else { + invertBorderGroup.hide(); + borderColorGroup.hide(); + } + } else { + // Hide all visual settings + enableOverlayGroup.hide(); + enableBorderGroup.hide(); + fillTypeGroup.hide(); + invertProgressGroup.hide(); + fillColorGroup.hide(); + barOrientationGroup.hide(); + invertBorderGroup.hide(); + borderColorGroup.hide(); + } + }; + + // Listeners + displayModeInput.on("change", updateVisibility); + if (enableOverlayInput.length && enableBorderInput.length) { + html.find(`[name="${moduleId}.fillType"]`).on("change", updateVisibility); + enableOverlayInput.on("change", updateVisibility); + enableBorderInput.on("change", updateVisibility); + updateVisibility(); // Initial check + } +}); + Hooks.once('ready', () => { // Hide default countdown tracker via CSS (handled in countdown.css) diff --git a/styles/countdown.css b/styles/countdown.css index 118a6b8..b455568 100644 --- a/styles/countdown.css +++ b/styles/countdown.css @@ -214,6 +214,14 @@ /* Rotation handled inline to start at top */ } +/* Grayscale Filter */ +.grayscale-filter { + backdrop-filter: grayscale(100%) !important; + background: transparent !important; + opacity: 1 !important; + box-shadow: none !important; +} + .bar-visual.vertical { width: 100%; /* height is set inline */ diff --git a/templates/countdown-tracker.hbs b/templates/countdown-tracker.hbs index 57dfb98..88a8f1d 100644 --- a/templates/countdown-tracker.hbs +++ b/templates/countdown-tracker.hbs @@ -38,41 +38,98 @@ ../isMinimized}}data-tooltip="{{countdown.name}}" {{/if}}> - {{#if ../showNumbers}} -
- {{countdown.progress.current}}{{#unless ../isMinimized}}/{{countdown.progress.start}}{{/unless}} -
- {{else}} + {{#if ../showVisuals}}
{{#if ../enableVisualOverlay}} + {{#if (eq ../fillType "grayscale")}} + {{!-- Grayscale Filter Mode --}} {{#if (eq ../iconShape "circle")}} + {{#if ../invertProgress}} +
+
+ {{else}} +
+
+ {{/if}} + {{else}} + {{#if ../invertProgress}} +
+
+ {{else}} +
+
+ {{/if}} + {{/if}} + {{else}} + {{!-- Color Overlay Mode --}} + {{#if (eq ../iconShape "circle")}} + {{#if ../invertProgress}}
+ style="background: conic-gradient(transparent {{countdown.percentage}}%, {{../fillColor}} {{countdown.percentage}}%); opacity: 0.4;"> +
+ {{else}} +
+
+ {{/if}} + {{else}} + {{#if ../invertProgress}} +
{{else}}
+ style="background-color: {{../fillColor}}; opacity: 0.4; {{#if (eq ../barOrientation 'horizontal')}}width: {{countdown.percentage}}%;{{else}}height: {{countdown.percentage}}%;{{/if}}">
{{/if}} {{/if}} + {{/if}} + {{/if}} {{#if ../enableVisualBorder}} {{#if (eq ../iconShape "circle")}} - + + {{else}} + + {{/if}} + {{else}} + {{#if ../invertBorder}} + + {{else}} {{/if}} + {{/if}} {{/if}}
{{/if}} + + {{#if ../showNumbers}} +
+ {{countdown.progress.current}}{{#unless ../isMinimized}}/{{countdown.progress.start}}{{/unless}} +
+ {{/if}} {{#if countdown.editable}}