All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
7.0 KiB

  1. import { insertArtSelectButton } from '../../applications/artSelect.js';
  2. import { showArtSelect } from '../../token-variants.mjs';
  3. import { TVA_CONFIG } from '../settings.js';
  4. import { SEARCH_TYPE, updateActorImage } from '../utils.js';
  5. import { registerHook, unregisterHook } from './hooks.js';
  6. const feature_id = 'ArtSelect';
  7. export function registerArtSelectButtonHooks() {
  8. // Insert right-click listeners to open up ArtSelect forms from various contexts
  9. if (TVA_CONFIG.permissions.portrait_right_click[game.user.role]) {
  10. registerHook(feature_id, 'renderActorSheet', _modActorSheet);
  11. registerHook(feature_id, 'renderItemSheet', _modItemSheet);
  12. registerHook(feature_id, 'renderItemActionSheet', _modItemSheet);
  13. registerHook(feature_id, 'renderJournalSheet', _modJournalSheet);
  14. registerHook(feature_id, 'renderRollTableConfig', _modRollTableSheet);
  15. } else {
  16. [
  17. 'renderActorSheet',
  18. 'renderItemSheet',
  19. 'renderItemActionSheet',
  20. 'renderJournalSheet',
  21. 'renderRollTableConfig',
  22. ].forEach((name) => unregisterHook(feature_id, name));
  23. }
  24. // Insert buttons
  25. if (TVA_CONFIG.permissions.image_path_button[game.user.role]) {
  26. registerHook(feature_id, 'renderTileConfig', _modTileConfig);
  27. registerHook(feature_id, 'renderMeasuredTemplateConfig', _modTemplateConfig);
  28. registerHook(feature_id, 'renderTokenConfig', _modTokenConfig);
  29. registerHook(feature_id, 'renderDrawingConfig', _modDrawingConfig);
  30. registerHook(feature_id, 'renderNoteConfig', _modNoteConfig);
  31. registerHook(feature_id, 'renderSceneConfig', _modSceneConfig);
  32. registerHook(feature_id, 'renderMacroConfig', _modMacroConfig);
  33. registerHook(feature_id, 'renderActiveEffectConfig', _modActiveEffectConfig);
  34. } else {
  35. [
  36. 'renderTileConfig',
  37. 'renderMeasuredTemplateConfig',
  38. 'renderTokenConfig',
  39. 'renderDrawingConfig',
  40. 'renderNoteConfig',
  41. 'renderSceneConfig',
  42. `renderActiveEffectConfig`,
  43. ].forEach((name) => unregisterHook(feature_id, name));
  44. }
  45. }
  46. function _modTokenConfig(config, html) {
  47. insertArtSelectButton(html, 'texture.src', {
  48. search: config.object.name,
  49. searchType: SEARCH_TYPE.TOKEN,
  50. });
  51. }
  52. function _modTemplateConfig(config, html) {
  53. insertArtSelectButton(html, 'texture', { search: 'Template', searchType: SEARCH_TYPE.TILE });
  54. }
  55. function _modDrawingConfig(config, html) {
  56. insertArtSelectButton(html, 'texture', {
  57. search: 'Drawing',
  58. searchType: TVA_CONFIG.customImageCategories.includes('Drawing') ? 'Drawing' : SEARCH_TYPE.TILE,
  59. });
  60. }
  61. function _modNoteConfig(config, html) {
  62. insertArtSelectButton(html, 'icon.custom', {
  63. search: 'Note',
  64. searchType: TVA_CONFIG.customImageCategories.includes('Note') ? 'Note' : SEARCH_TYPE.ITEM,
  65. });
  66. }
  67. function _modSceneConfig(config, html) {
  68. insertArtSelectButton(html, 'background.src', {
  69. search: config.object.name,
  70. searchType: TVA_CONFIG.customImageCategories.includes('Scene') ? 'Scene' : SEARCH_TYPE.TILE,
  71. });
  72. insertArtSelectButton(html, 'foreground', {
  73. search: config.object.name,
  74. searchType: TVA_CONFIG.customImageCategories.includes('Scene') ? 'Scene' : SEARCH_TYPE.TILE,
  75. });
  76. insertArtSelectButton(html, 'fogOverlay', {
  77. search: config.object.name,
  78. searchType: TVA_CONFIG.customImageCategories.includes('Fog') ? 'Fog' : SEARCH_TYPE.TILE,
  79. });
  80. }
  81. function _modTileConfig(tileConfig, html) {
  82. insertArtSelectButton(html, 'texture.src', {
  83. search: tileConfig.object.getFlag('token-variants', 'tileName') || 'Tile',
  84. searchType: SEARCH_TYPE.TILE,
  85. });
  86. }
  87. function _modActiveEffectConfig(effectConfig, html) {
  88. const inserted = insertArtSelectButton(html, 'icon', {
  89. search: effectConfig.object.name || 'Active Effect',
  90. searchType: TVA_CONFIG.customImageCategories.includes('Active Effect') ? 'Active Effect' : SEARCH_TYPE.ITEM,
  91. });
  92. if (!inserted) {
  93. const img = $(html).find('.effect-icon');
  94. img.on('contextmenu', () => {
  95. showArtSelect(effectConfig.object?.name ?? 'Active Effect', {
  96. searchType: SEARCH_TYPE.ITEM,
  97. callback: (imgSrc) => img.attr('src', imgSrc),
  98. });
  99. });
  100. }
  101. }
  102. function _modItemSheet(itemSheet, html, options) {
  103. $(html)
  104. .find('img.profile, .profile-img, [data-edit="img"]')
  105. .on('contextmenu', () => {
  106. const item = itemSheet.object;
  107. if (!item) return;
  108. showArtSelect(item.name, {
  109. searchType: SEARCH_TYPE.ITEM,
  110. callback: (imgSrc) => item.update({ img: imgSrc }),
  111. });
  112. });
  113. }
  114. function _modMacroConfig(macroConfig, html, options) {
  115. const img = $(html).find('.sheet-header > img');
  116. img.on('contextmenu', () => {
  117. showArtSelect(macroConfig.object?.name ?? 'Macro', {
  118. searchType: SEARCH_TYPE.MACRO,
  119. callback: (imgSrc) => img.attr('src', imgSrc),
  120. });
  121. });
  122. }
  123. function _modJournalSheet(journalSheet, html, options) {
  124. $(html)
  125. .find('.header-button.entry-image')
  126. .on('contextmenu', () => {
  127. const journal = journalSheet.object;
  128. if (!journal) return;
  129. showArtSelect(journal.name, {
  130. searchType: SEARCH_TYPE.JOURNAL,
  131. callback: (imgSrc) => journal.update({ img: imgSrc }),
  132. });
  133. });
  134. }
  135. function _modRollTableSheet(sheet, html) {
  136. $(html)
  137. .find('.result-image')
  138. .on('contextmenu', (event) => {
  139. const table = sheet.object;
  140. if (!table) return;
  141. const img = $(event.target).closest('.result-image').find('img');
  142. showArtSelect(table.name, {
  143. searchType: TVA_CONFIG.customImageCategories.includes('RollTable') ? 'RollTable' : SEARCH_TYPE.ITEM,
  144. callback: (imgSrc) => {
  145. img.attr('src', imgSrc);
  146. sheet._onSubmit(event);
  147. },
  148. });
  149. });
  150. }
  151. /**
  152. * Adds right-click listener to Actor Sheet profile image to open up
  153. * the 'Art Select' screen.
  154. */
  155. function _modActorSheet(actorSheet, html, options) {
  156. if (options.editable && TVA_CONFIG.permissions.portrait_right_click[game.user.role]) {
  157. let profile = null;
  158. let profileQueries = {
  159. all: ['.profile', '.profile-img', '.profile-image'],
  160. pf2e: ['.player-image', '.actor-icon', '.sheet-header img', '.actor-image'],
  161. };
  162. for (let query of profileQueries.all) {
  163. profile = html[0].querySelector(query);
  164. if (profile) break;
  165. }
  166. if (!profile && game.system.id in profileQueries) {
  167. for (let query of profileQueries[game.system.id]) {
  168. profile = html[0].querySelector(query);
  169. if (profile) break;
  170. }
  171. }
  172. if (!profile) {
  173. console.warn('TVA |', game.i18n.localize('token-variants.notifications.warn.profile-image-not-found'));
  174. return;
  175. }
  176. profile.addEventListener(
  177. 'contextmenu',
  178. function (ev) {
  179. showArtSelect(actorSheet.object.name, {
  180. callback: (imgSrc, name) => updateActorImage(actorSheet.object, imgSrc),
  181. searchType: SEARCH_TYPE.PORTRAIT,
  182. object: actorSheet.object,
  183. });
  184. },
  185. false
  186. );
  187. }
  188. }