feat: Introduce customizable visual progress indicators for countdowns, update module metadata, and enhance control button tooltips.
This commit is contained in:
parent
988f2c3011
commit
02a3855ea1
5 changed files with 216 additions and 17 deletions
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "dh-improved-countdowns",
|
"id": "dh-improved-countdowns",
|
||||||
"name": "dh-improved-countdowns",
|
|
||||||
"title": "Improved Countdowns",
|
"title": "Improved Countdowns",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13"
|
"verified": "13"
|
||||||
|
|
@ -38,5 +37,8 @@
|
||||||
"path": "lang/en.json"
|
"path": "lang/en.json"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A modern, draggable countdown tracker for the Daggerheart system."
|
"description": "A modern, draggable countdown tracker for the Daggerheart system.",
|
||||||
|
"url": "https://github.com/cptn-cosmo/dh-improved-countdowns",
|
||||||
|
"manifest": "https://github.com/cptn-cosmo/dh-improved-countdowns/releases/latest/download/module.json",
|
||||||
|
"download": "https://github.com/cptn-cosmo/dh-improved-countdowns/releases/download/1.1.0/dh-improved-countdowns.zip"
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +51,15 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV
|
||||||
const isGM = game.user.isGM;
|
const isGM = game.user.isGM;
|
||||||
const isMinimized = game.settings.get("dh-improved-countdowns", "minimized");
|
const isMinimized = game.settings.get("dh-improved-countdowns", "minimized");
|
||||||
const isLocked = game.settings.get("dh-improved-countdowns", "locked");
|
const isLocked = game.settings.get("dh-improved-countdowns", "locked");
|
||||||
|
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 enableVisualBorder = game.settings.get("dh-improved-countdowns", "enableVisualBorder");
|
||||||
|
const gmAlwaysShowNumbers = game.settings.get("dh-improved-countdowns", "gmAlwaysShowNumbers");
|
||||||
|
|
||||||
|
const showNumbers = (isGM && gmAlwaysShowNumbers) || displayMode === "number";
|
||||||
|
|
||||||
// Fetch countdowns from system settings
|
// Fetch countdowns from system settings
|
||||||
const systemCountdownSetting = game.settings.get("daggerheart", "Countdowns");
|
const systemCountdownSetting = game.settings.get("daggerheart", "Countdowns");
|
||||||
|
|
@ -60,9 +69,15 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV
|
||||||
for (const [id, countdown] of Object.entries(systemCountdownSetting.countdowns)) {
|
for (const [id, countdown] of Object.entries(systemCountdownSetting.countdowns)) {
|
||||||
const ownership = this.#getPlayerOwnership(game.user, systemCountdownSetting, countdown);
|
const ownership = this.#getPlayerOwnership(game.user, systemCountdownSetting, countdown);
|
||||||
if (ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) {
|
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));
|
||||||
|
|
||||||
countdowns[id] = {
|
countdowns[id] = {
|
||||||
...countdown,
|
...countdown,
|
||||||
editable: isGM || ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
|
editable: isGM || ownership === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
|
||||||
|
percentage,
|
||||||
|
cssClass: `shape-${iconShape}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +87,13 @@ export class CountdownTrackerApp extends HandlebarsApplicationMixin(ApplicationV
|
||||||
countdowns,
|
countdowns,
|
||||||
isGM,
|
isGM,
|
||||||
isMinimized,
|
isMinimized,
|
||||||
isLocked
|
isLocked,
|
||||||
|
showNumbers,
|
||||||
|
iconShape,
|
||||||
|
barOrientation,
|
||||||
|
visualColor,
|
||||||
|
enableVisualOverlay,
|
||||||
|
enableVisualBorder
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,88 @@ Hooks.once('init', () => {
|
||||||
default: false,
|
default: false,
|
||||||
onChange: () => CountdownTrackerApp.instance?.render()
|
onChange: () => CountdownTrackerApp.instance?.render()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
game.settings.register("dh-improved-countdowns", "iconShape", {
|
||||||
|
name: "Icon Shape",
|
||||||
|
hint: "Choose the shape of the countdown icons.",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: String,
|
||||||
|
choices: {
|
||||||
|
"rounded": "Rounded Square",
|
||||||
|
"circle": "Circle"
|
||||||
|
},
|
||||||
|
default: "rounded",
|
||||||
|
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",
|
||||||
|
"visual": "Visual (Bar/Clock)"
|
||||||
|
},
|
||||||
|
default: "number",
|
||||||
|
onChange: () => CountdownTrackerApp.instance?.render()
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("dh-improved-countdowns", "barOrientation", {
|
||||||
|
name: "Bar Orientation",
|
||||||
|
hint: "Choose the orientation of the progress bar (for square icons).",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: String,
|
||||||
|
choices: {
|
||||||
|
"vertical": "Vertical",
|
||||||
|
"horizontal": "Horizontal"
|
||||||
|
},
|
||||||
|
default: "vertical",
|
||||||
|
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).",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
onChange: () => CountdownTrackerApp.instance?.render()
|
||||||
|
});
|
||||||
|
|
||||||
|
game.settings.register("dh-improved-countdowns", "enableVisualBorder", {
|
||||||
|
name: "Enable Border Progress",
|
||||||
|
hint: "Show a progress border around the icon.",
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
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.",
|
||||||
|
scope: "client", // This should be client-scoped as it's a preference for the GM user
|
||||||
|
config: true,
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
onChange: () => CountdownTrackerApp.instance?.render()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.once('ready', () => {
|
Hooks.once('ready', () => {
|
||||||
|
|
|
||||||
|
|
@ -141,10 +141,18 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
transition: border-radius 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container.shape-circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container.shape-rounded {
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-icon {
|
.countdown-icon {
|
||||||
|
|
@ -156,14 +164,64 @@
|
||||||
|
|
||||||
.value-overlay {
|
.value-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 0;
|
||||||
left: 50%;
|
left: 0;
|
||||||
transform: translate(-50%, -50%);
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-overlay.number {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.9), 0 0 8px rgba(0, 0, 0, 0.9);
|
text-shadow: 0 0 4px rgba(0, 0, 0, 0.9), 0 0 8px rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-overlay.visual {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clock Visual */
|
||||||
|
.clock-visual {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
/* Opacity and Color handled inline via template */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bar Visual */
|
||||||
|
.bar-visual {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
/* Box shadow removed as it might interfere with custom colors/transparency */
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
/* Color handled inline via template */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVG Border Visual */
|
||||||
|
.progress-border-svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
/* Rotation handled inline to start at top */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-visual.vertical {
|
||||||
|
width: 100%;
|
||||||
|
/* height is set inline */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-visual.horizontal {
|
||||||
|
height: 100%;
|
||||||
|
/* width is set inline */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* +/- Controls */
|
/* +/- Controls */
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,12 @@
|
||||||
<i class="fa-solid fa-plus-circle"></i>
|
<i class="fa-solid fa-plus-circle"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<a class="control-btn" data-action="toggleLock" data-tooltip="{{#if isLocked}}Unlock Window{{else}}Lock Window{{/if}}">
|
<a class="control-btn" data-action="toggleLock"
|
||||||
|
data-tooltip="{{#if isLocked}}Unlock Window{{else}}Lock Window{{/if}}">
|
||||||
<i class="fa-solid {{#if isLocked}}fa-lock{{else}}fa-lock-open{{/if}}"></i>
|
<i class="fa-solid {{#if isLocked}}fa-lock{{else}}fa-lock-open{{/if}}"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="control-btn" data-action="toggleViewMode" data-tooltip="{{#if isMinimized}}Maximize{{else}}Minimize{{/if}}">
|
<a class="control-btn" data-action="toggleViewMode"
|
||||||
|
data-tooltip="{{#if isMinimized}}Maximize{{else}}Minimize{{/if}}">
|
||||||
<i class="fa-solid {{#if isMinimized}}fa-expand-alt{{else}}fa-compress-alt{{/if}}"></i>
|
<i class="fa-solid {{#if isMinimized}}fa-expand-alt{{else}}fa-compress-alt{{/if}}"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,11 +34,45 @@
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="icon-container" {{#if ../isMinimized}}data-tooltip="{{countdown.name}}"{{/if}}>
|
<div class="icon-container {{countdown.cssClass}}" {{#if
|
||||||
|
../isMinimized}}data-tooltip="{{countdown.name}}" {{/if}}>
|
||||||
<img src="{{countdown.img}}" class="countdown-icon" />
|
<img src="{{countdown.img}}" class="countdown-icon" />
|
||||||
<div class="value-overlay">
|
|
||||||
{{countdown.progress.current}}{{#unless ../isMinimized}}<span class="max-value">/{{countdown.progress.start}}</span>{{/unless}}
|
{{#if ../showNumbers}}
|
||||||
|
<div class="value-overlay number">
|
||||||
|
{{countdown.progress.current}}{{#unless ../isMinimized}}<span
|
||||||
|
class="max-value">/{{countdown.progress.start}}</span>{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="value-overlay visual">
|
||||||
|
{{#if ../enableVisualOverlay}}
|
||||||
|
{{#if (eq ../iconShape "circle")}}
|
||||||
|
<div class="clock-visual"
|
||||||
|
style="background: conic-gradient({{../visualColor}} {{countdown.percentage}}%, transparent {{countdown.percentage}}%); opacity: 0.4;">
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="bar-visual {{../barOrientation}}"
|
||||||
|
style="background-color: {{../visualColor}}; opacity: 0.4; {{#if (eq ../barOrientation 'horizontal')}}width: {{countdown.percentage}}%;{{else}}height: {{countdown.percentage}}%;{{/if}}">
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if ../enableVisualBorder}}
|
||||||
|
<svg class="progress-border-svg" viewBox="0 0 48 48" style="transform: rotate(-90deg);">
|
||||||
|
{{#if (eq ../iconShape "circle")}}
|
||||||
|
<circle cx="24" cy="24" r="23" fill="none" stroke="{{../visualColor}}" stroke-width="2"
|
||||||
|
pathLength="100" stroke-dasharray="{{countdown.percentage}} 100"
|
||||||
|
style="transition: stroke-dasharray 0.3s ease; opacity: 1;"></circle>
|
||||||
|
{{else}}
|
||||||
|
<rect x="1" y="1" width="46" height="46" rx="8" ry="8" fill="none"
|
||||||
|
stroke="{{../visualColor}}" stroke-width="2" pathLength="100"
|
||||||
|
stroke-dasharray="{{countdown.percentage}} 100"
|
||||||
|
style="transition: stroke-dasharray 0.3s ease; opacity: 1;"></rect>
|
||||||
|
{{/if}}
|
||||||
|
</svg>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if countdown.editable}}
|
{{#if countdown.editable}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue