-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#4876] Add support for modifier keys during drag operations
Adds the ability to use the OS-defined modifier key (usually Alt on Windows and Option on Mac) to toggle between the default drop behavior and the opposite behavior. So if dragging within the same actor this changes from the default move behaior to copy behavior and the opposite when dragging between different actors or to the sidebar. Enabling this required access to the current drag payload during the `ondragover` event, so this extends the `DragDrop` class provided by core to store that information during the `ondragstart` event and adds a new handler for the `ondragend` event to clear the stored payload. Currently this only covers dragging items, with some minor improvements to dragging favorites on the character sheet. This framework could be expanded in the future to support dragging actors into the bastion tab as well as dragging active effects, advancements, or activities. Closes #4876
- Loading branch information
Showing
10 changed files
with
348 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,85 @@ | ||
import { parseInputDelta } from "../../utils.mjs"; | ||
import DragDropApplicationMixin from "../mixins/drag-drop-mixin.mjs"; | ||
|
||
/** | ||
* Mixin method for common uses between all actor sheets. | ||
* @param {typeof Application} Base Application class being extended. | ||
* @returns {class} | ||
* @mixin | ||
*/ | ||
export default Base => class extends Base { | ||
/** | ||
* Handle input changes to numeric form fields, allowing them to accept delta-typed inputs. | ||
* @param {Event} event Triggering event. | ||
* @protected | ||
*/ | ||
_onChangeInputDelta(event) { | ||
const input = event.target; | ||
const target = this.actor.items.get(input.closest("[data-item-id]")?.dataset.itemId) ?? this.actor; | ||
const { activityId } = input.closest("[data-activity-id]")?.dataset ?? {}; | ||
const activity = target?.system.activities?.get(activityId); | ||
const result = parseInputDelta(input, activity ?? target); | ||
if ( result !== undefined ) { | ||
// Special case handling for Item uses. | ||
if ( input.dataset.name === "system.uses.value" ) { | ||
target.update({ "system.uses.spent": target.system.uses.max - result }); | ||
} else if ( activity && (input.dataset.name === "uses.value") ) { | ||
target.updateActivity(activityId, { "uses.spent": activity.uses.max - result }); | ||
export default function ActorSheetMixin(Base) { | ||
return class ActorSheet extends DragDropApplicationMixin(Base) { | ||
|
||
/** | ||
* Handle input changes to numeric form fields, allowing them to accept delta-typed inputs. | ||
* @param {Event} event Triggering event. | ||
* @protected | ||
*/ | ||
_onChangeInputDelta(event) { | ||
const input = event.target; | ||
const target = this.actor.items.get(input.closest("[data-item-id]")?.dataset.itemId) ?? this.actor; | ||
const { activityId } = input.closest("[data-activity-id]")?.dataset ?? {}; | ||
const activity = target?.system.activities?.get(activityId); | ||
const result = parseInputDelta(input, activity ?? target); | ||
if ( result !== undefined ) { | ||
// Special case handling for Item uses. | ||
if ( input.dataset.name === "system.uses.value" ) { | ||
target.update({ "system.uses.spent": target.system.uses.max - result }); | ||
} else if ( activity && (input.dataset.name === "uses.value") ) { | ||
target.updateActivity(activityId, { "uses.spent": activity.uses.max - result }); | ||
} | ||
else target.update({ [input.dataset.name]: result }); | ||
} | ||
else target.update({ [input.dataset.name]: result }); | ||
} | ||
} | ||
|
||
/* -------------------------------------------- */ | ||
|
||
/** | ||
* Stack identical consumables when a new one is dropped rather than creating a duplicate item. | ||
* @param {object} itemData The item data requested for creation. | ||
* @param {object} [options={}] | ||
* @param {string} [options.container=null] ID of the container into which this item is being dropped. | ||
* @returns {Promise<Item5e>|null} If a duplicate was found, returns the adjusted item stack. | ||
*/ | ||
_onDropStackConsumables(itemData, { container=null }={}) { | ||
const droppedSourceId = itemData._stats?.compendiumSource ?? itemData.flags.core?.sourceId; | ||
if ( itemData.type !== "consumable" || !droppedSourceId ) return null; | ||
const similarItem = this.actor.sourcedItems.get(droppedSourceId, { legacy: false }) | ||
?.filter(i => (i.system.container === container) && (i.name === itemData.name))?.first(); | ||
if ( !similarItem ) return null; | ||
return similarItem.update({ | ||
"system.quantity": similarItem.system.quantity + Math.max(itemData.system.quantity, 1) | ||
}); | ||
} | ||
}; | ||
|
||
/* -------------------------------------------- */ | ||
|
||
/** | ||
* Stack identical consumables when a new one is dropped rather than creating a duplicate item. | ||
* @param {object} itemData The item data requested for creation. | ||
* @param {object} [options={}] | ||
* @param {string} [options.container=null] ID of the container into which this item is being dropped. | ||
* @returns {Promise<Item5e>|null} If a duplicate was found, returns the adjusted item stack. | ||
*/ | ||
_onDropStackConsumables(itemData, { container=null }={}) { | ||
const droppedSourceId = itemData._stats?.compendiumSource ?? itemData.flags.core?.sourceId; | ||
if ( itemData.type !== "consumable" || !droppedSourceId ) return null; | ||
const similarItem = this.actor.sourcedItems.get(droppedSourceId, { legacy: false }) | ||
?.filter(i => (i.system.container === container) && (i.name === itemData.name))?.first(); | ||
if ( !similarItem ) return null; | ||
return similarItem.update({ | ||
"system.quantity": similarItem.system.quantity + Math.max(itemData.system.quantity, 1) | ||
}); | ||
} | ||
|
||
/* -------------------------------------------- */ | ||
/* Drag & Drop */ | ||
/* -------------------------------------------- */ | ||
|
||
/** @override */ | ||
_allowedDropBehaviors(event, data) { | ||
if ( !data.uuid ) return new Set(["copy"]); | ||
const allowed = new Set(["copy", "move"]); | ||
const s = foundry.utils.parseUuid(data.uuid); | ||
const t = foundry.utils.parseUuid(this.document.uuid); | ||
const sCompendium = s.collection instanceof CompendiumCollection; | ||
const tCompendium = t.collection instanceof CompendiumCollection; | ||
|
||
// If either source or target are within a compendium, but not inside the same compendium, move not allowed | ||
if ( (sCompendium || tCompendium) && (s.collection !== t.collection) ) allowed.delete("move"); | ||
|
||
return allowed; | ||
} | ||
|
||
/* -------------------------------------------- */ | ||
|
||
/** @override */ | ||
_defaultDropBehavior(event, data) { | ||
if ( !data.uuid ) return "copy"; | ||
const d = foundry.utils.parseUuid(data.uuid); | ||
const t = foundry.utils.parseUuid(this.document.uuid); | ||
return (d.collection === t.collection) && (d.documentId === t.documentId) && (d.documentType === t.documentType) | ||
? "move" : "copy"; | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.