Skip to content

Commit 73db471

Browse files
authored
Fix combat hud bottom button drag (#246)
1 parent e10a985 commit 73db471

File tree

1 file changed

+77
-58
lines changed

1 file changed

+77
-58
lines changed

module/ui/combat-hud.mjs

+77-58
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ export class CombatHUD extends Application {
2323
this._emptyImage = document.createElement('img');
2424
this._emptyImage.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
2525

26+
// Drag trackers
27+
this.dragInitialX = 0;
28+
this.dragInitialY = 0;
29+
this.dragInitialTop = 0;
30+
this.dragInitialLeft = 0;
31+
this.firefoxDragX = 0;
32+
this.firefoxDragY = 0;
33+
34+
// TODO: Move such browser checks to a higher scope
35+
this.isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
36+
2637
console.debug(`Combat HUD: Constructing`);
2738
Hooks.callAll('combatHudInit', this);
2839
this.#hooks.push({ hook: 'createCombatant', func: this._onUpdateHUD.bind(this) });
@@ -193,10 +204,8 @@ export class CombatHUD extends Application {
193204
actor: combatant.actor,
194205
token: combatant.token,
195206
effects: activeEffects,
196-
img: game.settings.get(SYSTEM, SETTINGS.optionCombatHudPortrait) === 'token' ?
197-
// token._source should contain the most current version of the token's texture.
198-
combatant.token._source.texture.src :
199-
combatant.actor.img,
207+
// token._source should contain the most current version of the token's texture.
208+
img: game.settings.get(SYSTEM, SETTINGS.optionCombatHudPortrait) === 'token' ? combatant.token._source.texture.src : combatant.actor.img,
200209
trackedResourcePart1: trackedResourcePart1,
201210
trackedResourcePart2: trackedResourcePart2,
202211
trackedResourcePart3: trackedResourcePart3,
@@ -332,7 +341,7 @@ export class CombatHUD extends Application {
332341
dragButton.on('drag', this._doHudDrag.bind(this));
333342
dragButton.on('dragend', this._doHudDrop.bind(this));
334343

335-
if(navigator.userAgent.toLowerCase().includes('firefox')) {
344+
if (this.isFirefox) {
336345
$(window.document).on('dragover', this._fireFoxDragWorkaround.bind(this));
337346
}
338347

@@ -356,32 +365,66 @@ export class CombatHUD extends Application {
356365

357366
_doHudDragStart(event) {
358367
event.originalEvent.dataTransfer.setDragImage(this._emptyImage, 0, 0);
368+
369+
// Set drag tracking vars
370+
const nativeEvent = event.originalEvent;
371+
const elementPos = this.element.position();
372+
this.dragInitialLeft = elementPos.left;
373+
this.dragInitialTop = elementPos.top;
374+
this.dragInitialX = nativeEvent.clientX;
375+
this.dragInitialY = nativeEvent.clientY;
376+
this.firefoxDragX = 0;
377+
this.firefoxDragY = 0;
359378
}
360379

380+
// FireFox does not populate event.clientX or event.clientY
381+
// during drag events. A workaround is to bind a seperate handler
382+
// to the dragover event instead, which gets processed directly
383+
// before any drag event and allows us to record the current drag position.
361384
_fireFoxDragWorkaround(event) {
362-
// FireFox does not populate event.clientX or event.clientY
363-
// during drag events. A workaround is to bind a seperate handler
364-
// to the dragover event instead, which gets processed directly
365-
// before any drag event and allows us to record the current drag position.
366-
this._dragX = event.clientX;
367-
this._dragY = event.clientY;
385+
// Keep this check; drag events can trigger with (0,0) when outside the window or target
386+
// and they should be treated as invalid
387+
if (event.clientX <= 0 || event.clientY <= 0) return;
388+
389+
// These need to be tracked separately
390+
// The listener is listening to *any* drag on window, and may not be relevant to the combatHUD
391+
// So the actual update should be deferred to _doHudDrag which is bound specifically to combatHUD
392+
this.firefoxDragX = event.clientX;
393+
this.firefoxDragY = event.clientY;
368394
}
369395

370396
_doHudDrag(event) {
371-
let dragPosition;
397+
event.originalEvent.dataTransfer.dropEffect = 'move';
372398

373-
if (navigator.userAgent.toLowerCase().includes('firefox')) {
374-
// Workaround: FireFox doesn't populate event.<clientX/clientY> during drag events
375-
dragPosition = { x: this._dragX, y: this._dragY };
399+
// Firefox doesn't handle drag events the same as other browsers
400+
// We use the 'dragOver' event for that
401+
let dragPosition;
402+
if (this.isFirefox) {
403+
dragPosition = { x: this.firefoxDragX, y: this.firefoxDragY };
376404
} else {
377405
dragPosition = { x: event.clientX, y: event.clientY };
378406
}
379-
event.originalEvent.dataTransfer.dropEffect = 'move';
380407

381-
if (this._dragAnimationFrame) cancelAnimationFrame(this._dragAnimationFrame);
408+
// Keep this check; drag events can trigger with (0,0) when outside the window or target
409+
// and they should be treated as invalid
410+
if (dragPosition.x <= 0 || dragPosition.y <= 0) return;
411+
412+
// Update
413+
if (this._dragAnimationFrame) {
414+
cancelAnimationFrame(this._dragAnimationFrame);
415+
}
382416
this._dragAnimationFrame = requestAnimationFrame(() => {
383-
this.element.css('left', this._dragOffsetX + dragPosition.x);
384-
this.element.css('top', this._dragOffsetY + dragPosition.y);
417+
// Calculate deltas
418+
const deltaX = dragPosition.x - this.dragInitialX;
419+
const deltaY = dragPosition.y - this.dragInitialY;
420+
421+
// Calculate final values
422+
const newLeft = this.dragInitialLeft + deltaX;
423+
const newTop = this.dragInitialTop + deltaY;
424+
425+
// Apply
426+
this.element.css('left', newLeft);
427+
this.element.css('top', newTop);
385428
if (this.element.css('bottom') !== 'initial') {
386429
this.element.css('bottom', 'initial');
387430
}
@@ -395,7 +438,7 @@ export class CombatHUD extends Application {
395438

396439
const draggedPosition = {
397440
x: offset.left,
398-
y: positionFromTop ? offset.top : $(window).height() - offset.top - height
441+
y: positionFromTop ? offset.top : $(window).height() - offset.top - height,
399442
};
400443
game.settings.set(SYSTEM, SETTINGS.optionCombatHudDraggedPosition, draggedPosition);
401444
}
@@ -739,33 +782,28 @@ export class CombatHUD extends Application {
739782

740783
_onUpdateToken(token, changes) {
741784
// Is the updated token in the current combat?
742-
if(!game.combat?.combatants.some(c => c.token.uuid === token.uuid)) {
785+
if (!game.combat?.combatants.some((c) => c.token.uuid === token.uuid)) {
743786
return;
744787
}
745788

746789
// Are any of the changes relevant to the Combat HUD?
747790
if (
748791
foundry.utils.hasProperty(changes, 'name') ||
749-
foundry.utils.hasProperty(changes, 'actorId') ||
750-
foundry.utils.hasProperty(changes, 'disposition') ||
751-
(
752-
game.settings.get(SYSTEM, 'optionCombatHudPortrait') === 'token' &&
753-
foundry.utils.hasProperty(changes, 'texture.src')
754-
)
792+
foundry.utils.hasProperty(changes, 'actorId') ||
793+
foundry.utils.hasProperty(changes, 'disposition') ||
794+
(game.settings.get(SYSTEM, 'optionCombatHudPortrait') === 'token' && foundry.utils.hasProperty(changes, 'texture.src'))
755795
) {
756796
this._onUpdateHUD();
757797
}
758798
}
759799

760800
_onUpdateActor(actor, changes) {
761801
// Is the updated actor in the current combat?
762-
if(!game.combat?.combatants.some(c => c.actor.uuid === actor.uuid)) {
802+
if (!game.combat?.combatants.some((c) => c.actor.uuid === actor.uuid)) {
763803
return;
764804
}
765805

766-
const systemResources = [
767-
'hp', 'mp', 'ip', 'fp', 'exp', 'zenit'
768-
];
806+
const systemResources = ['hp', 'mp', 'ip', 'fp', 'exp', 'zenit'];
769807

770808
const trackedResources = [
771809
game.settings.get(SYSTEM, SETTINGS.optionCombatHudTrackedResource1),
@@ -775,10 +813,7 @@ export class CombatHUD extends Application {
775813
];
776814

777815
// Are any of the changes relevant to the Combat HUD?
778-
if (
779-
trackedResources.filter(r => systemResources.includes(r))
780-
.some(r => foundry.utils.hasProperty(changes, `system.resources.${r}`))
781-
) {
816+
if (trackedResources.filter((r) => systemResources.includes(r)).some((r) => foundry.utils.hasProperty(changes, `system.resources.${r}`))) {
782817
this._onUpdateHUD();
783818
}
784819
}
@@ -793,10 +828,7 @@ export class CombatHUD extends Application {
793828

794829
_onModifyItem(item, changes) {
795830
// Is the item owned by an actor in the current combat?
796-
if (
797-
item.parent?.documentName !== 'Actor' ||
798-
!game.combat?.combatants.some(c => c.actor.uuid === item.parent.uuid)
799-
) {
831+
if (item.parent?.documentName !== 'Actor' || !game.combat?.combatants.some((c) => c.actor.uuid === item.parent.uuid)) {
800832
return;
801833
}
802834

@@ -809,20 +841,11 @@ export class CombatHUD extends Application {
809841

810842
// Are any of the changes relevant to the combat HUD?
811843
if (
812-
trackedResources.includes('zeropower') &&
813-
item.type === 'optionalFeature' &&
814-
(
815-
item.system.optionalType === 'projectfu.zeroPower') ||
816-
// The optionalType can theoretically change, so make sure to check for it.
817-
foundry.utils.hasProperty(changes, 'system.optionalType'
818-
)
819-
&&
820-
(
844+
(trackedResources.includes('zeropower') && item.type === 'optionalFeature' && item.system.optionalType === 'projectfu.zeroPower') ||
845+
// The optionalType can theoretically change, so make sure to check for it.
846+
(foundry.utils.hasProperty(changes, 'system.optionalType') &&
821847
// Progress changes aren't relevant during create/delete hooks.
822-
!changes ||
823-
foundry.utils.hasProperty(changes, 'system.data.progress.current') ||
824-
foundry.utils.hasProperty(changes, 'system.data.progress.max')
825-
)
848+
(!changes || foundry.utils.hasProperty(changes, 'system.data.progress.current') || foundry.utils.hasProperty(changes, 'system.data.progress.max')))
826849
) {
827850
this._onUpdateHUD();
828851
}
@@ -831,17 +854,13 @@ export class CombatHUD extends Application {
831854
_onModifyActiveEffect(activeEffect, changes) {
832855
if (
833856
// Is the active effect targeting an actor in the current combat?
834-
!(
835-
activeEffect.target &&
836-
game.combat?.combatants.some(c => c.actor.uuid === activeEffect.target.uuid)
837-
)
838-
&&
857+
!(activeEffect.target && game.combat?.combatants.some((c) => c.actor.uuid === activeEffect.target.uuid)) &&
839858
// Did the transfer property change on an effect on an item owned by an actor in the current combat?
840859
!(
841860
activeEffect.parent?.documentName === 'Item' &&
842861
activeEffect.parent.parent?.documentName === 'Actor' &&
843862
foundry.utils.hasProperty(changes, 'transfer') &&
844-
game.combat?.combatants.some(c => c.actor.uuid === activeEffect.parent.parent.uuid)
863+
game.combat?.combatants.some((c) => c.actor.uuid === activeEffect.parent.parent.uuid)
845864
)
846865
) {
847866
return;

0 commit comments

Comments
 (0)