HostExtensionServices.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. //
  2. // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. import * as EditorUI from "../ui/EditorUI";
  23. import MainFrame = require("../ui/frames/MainFrame");
  24. import InspectorFrame = require("../ui/frames/inspector/InspectorFrame");
  25. import ModalOps = require("../ui/modal/ModalOps");
  26. import ResourceOps = require("../resources/ResourceOps");
  27. /**
  28. * Generic registry for storing Editor Extension Services
  29. */
  30. export class ServicesProvider<T extends Editor.Extensions.ServiceEventListener> implements Editor.Extensions.ServicesProvider<T> {
  31. registeredServices: T[] = [];
  32. /**
  33. * Adds a service to the registered services list for this type of service
  34. * @param {T} service the service to register
  35. */
  36. register(service: T) {
  37. this.registeredServices.push(service);
  38. }
  39. unregister(service: T) {
  40. var index = this.registeredServices.indexOf(service, 0);
  41. if (index > -1) {
  42. this.registeredServices.splice(index, 1);
  43. }
  44. }
  45. }
  46. export interface ServiceEventSubscriber {
  47. /**
  48. * Allow this service registry to subscribe to events that it is interested in
  49. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  50. */
  51. subscribeToEvents(topLevelWindow: Atomic.UIWidget);
  52. }
  53. /**
  54. * Registry for service extensions that are concerned about project events
  55. */
  56. export class ProjectServicesProvider extends ServicesProvider<Editor.HostExtensions.ProjectServicesEventListener> implements Editor.HostExtensions.ProjectServicesProvider {
  57. constructor() {
  58. super();
  59. }
  60. /**
  61. * Allow this service registry to subscribe to events that it is interested in
  62. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  63. */
  64. subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
  65. eventDispatcher.subscribeToEvent(Editor.LoadProjectNotificationEvent((ev) => this.projectLoaded(ev)));
  66. eventDispatcher.subscribeToEvent(Editor.EditorCloseProjectEvent((ev) => this.projectUnloaded(ev)));
  67. eventDispatcher.subscribeToEvent(Editor.EditorPlayRequestEvent(() => this.playerStarted()));
  68. }
  69. /**
  70. * Called when the project is unloaded
  71. * @param {[type]} data Event info from the project unloaded event
  72. */
  73. projectUnloaded(data) {
  74. // Need to use a for loop for length down to 0 because extensions *could* delete themselves from the list on projectUnloaded
  75. for (let i = this.registeredServices.length - 1; i >= 0; i--) {
  76. let service = this.registeredServices[i];
  77. // Notify services that the project has been unloaded
  78. try {
  79. if (service.projectUnloaded) {
  80. service.projectUnloaded();
  81. }
  82. } catch (e) {
  83. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e} \n\n ${e.stack}`);
  84. }
  85. };
  86. }
  87. /**
  88. * Called when the project is loaded
  89. * @param {[type]} data Event info from the project unloaded event
  90. */
  91. projectLoaded(ev: Editor.EditorLoadProjectEvent) {
  92. // Need to use a for loop and don't cache the length because the list of services *may* change while processing. Extensions could be appended to the end
  93. for (let i = 0; i < this.registeredServices.length; i++) {
  94. let service = this.registeredServices[i];
  95. try {
  96. // Notify services that the project has just been loaded
  97. if (service.projectLoaded) {
  98. service.projectLoaded(ev);
  99. }
  100. } catch (e) {
  101. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  102. }
  103. };
  104. }
  105. playerStarted() {
  106. this.registeredServices.forEach((service) => {
  107. try {
  108. // Notify services that the project has just been loaded
  109. if (service.playerStarted) {
  110. service.playerStarted();
  111. }
  112. } catch (e) {
  113. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
  114. }
  115. });
  116. }
  117. /**
  118. * Return a preference value or the provided default from the user settings file
  119. * @param {string} extensionName name of the extension the preference lives under
  120. * @param {string} preferenceName name of the preference to retrieve
  121. * @param {number | boolean | string} defaultValue value to return if pref doesn't exist
  122. * @return {number|boolean|string}
  123. */
  124. getUserPreference(settingsGroup: string, preferenceName: string, defaultValue?: number): number;
  125. getUserPreference(settingsGroup: string, preferenceName: string, defaultValue?: string): string;
  126. getUserPreference(settingsGroup: string, preferenceName: string, defaultValue?: boolean): boolean;
  127. getUserPreference(extensionName: string, preferenceName: string, defaultValue?: any): any {
  128. return EditorUI.getEditor().getUserPreference(extensionName, preferenceName, defaultValue);
  129. }
  130. /**
  131. * Return a preference value or the provided default from the global user settings file
  132. * @param {string} groupName name of the section the preference lives under
  133. * @param {string} preferenceName name of the preference to retrieve
  134. * @param {number | boolean | string} defaultValue value to return if pref doesn't exist
  135. * @return {number|boolean|string}
  136. */
  137. getApplicationPreference(settingsGroup: string, preferenceName: string, defaultValue?: number): number;
  138. getApplicationPreference(settingsGroup: string, preferenceName: string, defaultValue?: string): string;
  139. getApplicationPreference(settingsGroup: string, preferenceName: string, defaultValue?: boolean): boolean;
  140. getApplicationPreference(settingsGroup: string, preferenceName: string, defaultValue?: any): any {
  141. return EditorUI.getEditor().getApplicationPreference(settingsGroup, preferenceName, defaultValue);
  142. }
  143. /**
  144. * Sets a user preference value in the project user settings file
  145. * @param {string} extensionName name of the extension the preference lives under
  146. * @param {string} preferenceName name of the preference to set
  147. * @param {number | boolean | string} value value to set
  148. */
  149. setUserPreference(extensionName: string, preferenceName: string, value: number | boolean | string) {
  150. EditorUI.getEditor().setUserPreference(extensionName, preferenceName, value);
  151. }
  152. /**
  153. * Sets an editor preference value in the global application settings file
  154. * @param {string} extensionName name of the extension the preference lives under
  155. * @param {string} preferenceName name of the preference to set
  156. * @param {number | boolean | string} value value to set
  157. */
  158. setApplicationPreference(extensionName: string, preferenceName: string, value: number | boolean | string) {
  159. EditorUI.getEditor().setApplicationPreference(extensionName, preferenceName, value);
  160. }
  161. /**
  162. * Sets a group of user preference values in the user settings file located in the project. Elements in the
  163. * group will merge in with existing group preferences. Use this method if setting a bunch of settings
  164. * at once.
  165. * @param {string} extensionName name of the group the preference lives under
  166. * @param {string} groupPreferenceValues an object literal containing all of the preferences for the group.
  167. */
  168. setUserPreferenceGroup(extensionName: string, groupPreferenceValues: Object) {
  169. EditorUI.getEditor().setUserPreferenceGroup(extensionName, groupPreferenceValues);
  170. }
  171. }
  172. /**
  173. * Registry for service extensions that are concerned about Resources
  174. */
  175. export class ResourceServicesProvider extends ServicesProvider<Editor.HostExtensions.ResourceServicesEventListener> implements Editor.HostExtensions.ResourceServicesProvider {
  176. constructor() {
  177. super();
  178. }
  179. /**
  180. * Allow this service registry to subscribe to events that it is interested in
  181. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  182. */
  183. subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
  184. eventDispatcher.subscribeToEvent(Editor.EditorSaveResourceNotificationEvent((ev) => this.saveResource(ev)));
  185. eventDispatcher.subscribeToEvent(Editor.EditorDeleteResourceNotificationEvent((ev) => this.deleteResource(ev)));
  186. eventDispatcher.subscribeToEvent(Editor.EditorRenameResourceNotificationEvent((ev) => this.renameResource(ev)));
  187. eventDispatcher.subscribeToEvent(Editor.EditorEditResourceEvent((ev) => this.editResource(ev)));
  188. }
  189. /**
  190. * Called after a resource has been saved
  191. * @param {Editor.EditorEvents.SaveResourceEvent} ev
  192. */
  193. saveResource(ev: Editor.EditorSaveResourceEvent) {
  194. // run through and find any services that can handle this.
  195. this.registeredServices.forEach((service) => {
  196. try {
  197. // Verify that the service contains the appropriate methods and that it can save
  198. if (service.save) {
  199. service.save(ev);
  200. }
  201. } catch (e) {
  202. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  203. }
  204. });
  205. }
  206. /**
  207. * Called when a resource has been deleted
  208. */
  209. deleteResource(ev: Editor.EditorDeleteResourceEvent) {
  210. this.registeredServices.forEach((service) => {
  211. try {
  212. // Verify that the service contains the appropriate methods and that it can delete
  213. if (service.delete) {
  214. service.delete(ev);
  215. }
  216. } catch (e) {
  217. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  218. }
  219. });
  220. }
  221. /**
  222. * Called when a resource has been renamed
  223. * @param ev
  224. */
  225. renameResource(ev: Editor.EditorRenameResourceNotificationEvent) {
  226. this.registeredServices.forEach((service) => {
  227. try {
  228. // Verify that the service contains the appropriate methods and that it can handle the rename
  229. if (service.rename) {
  230. service.rename(ev);
  231. }
  232. } catch (e) {
  233. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  234. }
  235. });
  236. }
  237. /**
  238. * Called when a resource is about to be edited
  239. * @param {Editor.EditorEvents.EditResourceEvent} ev
  240. */
  241. editResource(ev: Editor.EditorEditResourceEvent) {
  242. this.registeredServices.forEach((service) => {
  243. try {
  244. // Verify that the service contains the appropriate methods and that it can handle the edit
  245. if (service.edit) {
  246. service.edit(ev);
  247. }
  248. } catch (e) {
  249. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  250. }
  251. });
  252. }
  253. /**
  254. * Create New Material
  255. * @param {string} resourcePath
  256. * @param {string} materialName
  257. * @param {boolean} reportError
  258. */
  259. createMaterial(resourcePath: string, materialName: string, reportError: boolean): boolean {
  260. return ResourceOps.CreateNewMaterial(resourcePath, materialName, reportError);
  261. }
  262. }
  263. /**
  264. * Registry for service extensions that are concerned about Scenes
  265. */
  266. export class SceneServicesProvider extends ServicesProvider<Editor.HostExtensions.SceneServicesEventListener> implements Editor.HostExtensions.SceneServicesProvider {
  267. constructor() {
  268. super();
  269. }
  270. /**
  271. * Allow this service registry to subscribe to events that it is interested in
  272. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  273. */
  274. subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
  275. eventDispatcher.subscribeToEvent(Editor.EditorActiveSceneEditorChangeEvent((ev) => this.activeSceneEditorChange(ev)));
  276. eventDispatcher.subscribeToEvent(Editor.EditorSceneClosedEvent((ev) => this.sceneClosed(ev)));
  277. }
  278. /**
  279. * Called after an active scene editor change
  280. * @param {Editor.EditorEvents.ActiveSceneEditorChangeEvent} ev
  281. */
  282. activeSceneEditorChange(ev: Editor.EditorActiveSceneEditorChangeEvent) {
  283. // run through and find any services that can handle this.
  284. this.registeredServices.forEach((service) => {
  285. try {
  286. // Verify that the service contains the appropriate methods and that it can save
  287. if (service.activeSceneEditorChanged) {
  288. service.activeSceneEditorChanged(ev);
  289. }
  290. } catch (e) {
  291. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  292. }
  293. });
  294. }
  295. /**
  296. * Called after a scene is closed
  297. * @param {Editor.EditorEvents.SceneClosedEvent} ev
  298. */
  299. sceneClosed(ev: Editor.EditorSceneClosedEvent) {
  300. // run through and find any services that can handle this.
  301. this.registeredServices.forEach((service) => {
  302. try {
  303. // Verify that the service contains the appropriate methods and that it can save
  304. if (service.editorSceneClosed) {
  305. service.editorSceneClosed(ev);
  306. }
  307. } catch (e) {
  308. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  309. }
  310. });
  311. }
  312. }
  313. /**
  314. * Registry for service extensions that are concerned about and need access to parts of the editor user interface
  315. * Note: we may want to move this out into it's own file since it has a bunch of editor dependencies
  316. */
  317. export class UIServicesProvider extends ServicesProvider<Editor.HostExtensions.UIServicesEventListener> implements Editor.HostExtensions.UIServicesProvider {
  318. constructor() {
  319. super();
  320. }
  321. private mainFrame: MainFrame = null;
  322. private inspectorFrame: InspectorFrame = null;
  323. private modalOps: ModalOps;
  324. init(mainFrame: MainFrame, modalOps: ModalOps) {
  325. // Only set these once
  326. if (this.mainFrame == null) {
  327. this.mainFrame = mainFrame;
  328. this.inspectorFrame = this.mainFrame.inspectorframe;
  329. }
  330. if (this.modalOps == null) {
  331. this.modalOps = modalOps;
  332. }
  333. }
  334. /**
  335. * Adds a new menu to the plugin menu
  336. * @param {string} id
  337. * @param {any} items
  338. * @return {Atomic.UIMenuItemSource}
  339. */
  340. createPluginMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
  341. return this.mainFrame.menu.createPluginMenuItemSource(id, items);
  342. }
  343. /**
  344. * Removes a previously added menu from the plugin menu
  345. * @param {string} id
  346. */
  347. removePluginMenuItemSource(id: string) {
  348. this.mainFrame.menu.removePluginMenuItemSource(id);
  349. }
  350. /**
  351. * Returns the currently active resource editor or null
  352. * @return {Editor.ResourceEditor}
  353. */
  354. getCurrentResourceEditor(): Editor.ResourceEditor {
  355. return this.mainFrame.resourceframe.currentResourceEditor;
  356. }
  357. /**
  358. * Will load a resource editor or navigate to an already loaded resource editor by path
  359. * @param resourcePath full path to resource to load
  360. * @param lineNumber optional line number to navigate to
  361. * @return {Editor.ResourceEditor}
  362. */
  363. loadResourceEditor(resourcePath: string, lineNumber?: number): Editor.ResourceEditor {
  364. this.mainFrame.resourceframe.sendEvent(Editor.EditorEditResourceEventData({path: resourcePath, lineNumber: lineNumber}));
  365. return this.mainFrame.resourceframe.currentResourceEditor;
  366. }
  367. /**
  368. * Adds a new menu to the hierarchy context menu
  369. * @param {string} id
  370. * @param {any} items
  371. * @return {Atomic.UIMenuItemSource}
  372. */
  373. createHierarchyContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
  374. return this.mainFrame.hierarchyFrame.menu.createPluginItemSource(id, items);
  375. }
  376. /**
  377. * Removes a previously added menu from the hierarchy context menu
  378. * @param {string} id
  379. */
  380. removeHierarchyContextMenuItemSource(id: string) {
  381. this.mainFrame.hierarchyFrame.menu.removePluginItemSource(id);
  382. }
  383. /**
  384. * Adds a new menu to the project context menu
  385. * @param {string} id
  386. * @param {any} items
  387. * @return {Atomic.UIMenuItemSource}
  388. */
  389. createProjectContextMenuItemSource(id: string, items: any): Atomic.UIMenuItemSource {
  390. return this.mainFrame.projectframe.menu.createPluginItemSource(id, items);
  391. }
  392. /**
  393. * Removes a previously added menu from the project context menu
  394. * @param {string} id
  395. */
  396. removeProjectContextMenuItemSource(id: string) {
  397. this.mainFrame.projectframe.menu.removePluginItemSource(id);
  398. }
  399. /**
  400. * Refreshes the hierarchy frame
  401. */
  402. refreshHierarchyFrame() {
  403. this.mainFrame.hierarchyFrame.populate();
  404. }
  405. /**
  406. * Loads Custom Inspector Widget
  407. * @param {Atomic.UIWidget} customInspector
  408. */
  409. loadCustomInspector(customInspector: Atomic.UIWidget) {
  410. if (this.inspectorFrame) {
  411. this.inspectorFrame.loadCustomInspectorWidget(customInspector);
  412. }
  413. }
  414. /**
  415. * Disaplays a modal window
  416. * @param {Editor.Modal.ModalWindow} window
  417. */
  418. showModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
  419. return this.modalOps.showExtensionWindow(windowText, uifilename, handleWidgetEventCB, true);
  420. }
  421. /**
  422. * Disaplays a modal window
  423. * @param {Editor.Modal.ModalWindow} window
  424. */
  425. showNonModalWindow(windowText: string, uifilename: string, handleWidgetEventCB: (ev: Atomic.UIWidgetEvent) => void): Editor.Modal.ExtensionWindow {
  426. return this.modalOps.showExtensionWindow(windowText, uifilename, handleWidgetEventCB, false);
  427. }
  428. /**
  429. * Disaplays a modal error window
  430. * @param {string} windowText
  431. * @param {string} message
  432. */
  433. showModalError(windowText: string, message: string): Atomic.UIMessageWindow {
  434. return this.modalOps.showError(windowText, message);
  435. }
  436. /**
  437. * Displays a resource slection window
  438. * @param {string} windowText
  439. * @param {string} importerType
  440. * @param {string} resourceType
  441. * @param {function} callback
  442. * @param {any} retObject
  443. * @param {any} args
  444. */
  445. showResourceSelection(windowText: string, importerType: string, resourceType: string, callback: (retObject: any, args: any) => void, args: any = undefined) {
  446. this.modalOps.showResourceSelection(windowText, importerType, resourceType, callback);
  447. }
  448. /**
  449. * Will register a custom editor for a particular file type.
  450. * @param {Editor.Extensions.ResourceEditorBuilder} editorBuilder
  451. */
  452. registerCustomEditor(editorBuilder: Editor.Extensions.ResourceEditorBuilder) {
  453. this.mainFrame.resourceframe.resourceEditorProvider.registerCustomEditor(editorBuilder);
  454. }
  455. /**
  456. * Will unregister a previously registered editor builder
  457. * @param {Editor.Extensions.ResourceEditorBuilder} editorBuilder
  458. */
  459. unregisterCustomEditor(editorBuilder: Editor.Extensions.ResourceEditorBuilder) {
  460. this.mainFrame.resourceframe.resourceEditorProvider.unregisterCustomEditor(editorBuilder);
  461. }
  462. /**
  463. * Called when a menu item has been clicked
  464. * @param {string} refId
  465. * @type {boolean} return true if handled
  466. */
  467. menuItemClicked(refid: string): boolean {
  468. // run through and find any services that can handle this.
  469. return this.registeredServices.some((service) => {
  470. try {
  471. // Verify that the service contains the appropriate methods and that it can handle it
  472. if (service.menuItemClicked) {
  473. if (service.menuItemClicked(refid)) {
  474. return true;
  475. }
  476. }
  477. } catch (e) {
  478. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  479. }
  480. });
  481. }
  482. /**
  483. * Called when a context menu item in the hierarchy pane has been clicked
  484. * @param {Atomic.Node} node
  485. * @param {string} refId
  486. * @type {boolean} return true if handled
  487. */
  488. hierarchyContextItemClicked(node: Atomic.Node, refid: string): boolean {
  489. if (!node)
  490. return false;
  491. // run through and find any services that can handle this.
  492. return this.registeredServices.some((service) => {
  493. try {
  494. // Verify that the service contains the appropriate methods and that it can handle it
  495. if (service.hierarchyContextItemClicked) {
  496. if (service.hierarchyContextItemClicked(node, refid)) {
  497. return true;
  498. }
  499. }
  500. } catch (e) {
  501. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  502. }
  503. });
  504. }
  505. /**
  506. * Called when a context menu item in the hierarchy pane has been clicked
  507. * @param {ToolCore.Asset} asset
  508. * @param {string} refId
  509. * @type {boolean} return true if handled
  510. */
  511. projectContextItemClicked(asset: ToolCore.Asset, refid: string): boolean {
  512. if (!asset)
  513. return false;
  514. // run through and find any services that can handle this.
  515. return this.registeredServices.some((service) => {
  516. try {
  517. // Verify that the service contains the appropriate methods and that it can handle it
  518. if (service.projectContextItemClicked) {
  519. if (service.projectContextItemClicked(asset, refid)) {
  520. return true;
  521. }
  522. }
  523. } catch (e) {
  524. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  525. }
  526. });
  527. }
  528. /**
  529. * Called when a project asset in the hierarchy pane has been clicked
  530. * @param {ToolCore.Asset} asset
  531. * @type {boolean} return true if handled
  532. */
  533. projectAssetClicked(asset: ToolCore.Asset):boolean {
  534. if (!asset) {
  535. return false;
  536. }
  537. // run through and find any services that can handle this.
  538. return this.registeredServices.some((service) => {
  539. try {
  540. // Verify that the service contains the appropriate methods and that it can handle it
  541. if (service.projectAssetClicked) {
  542. if (service.projectAssetClicked(asset)) {
  543. return true;
  544. }
  545. }
  546. } catch (e) {
  547. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  548. }
  549. });
  550. }
  551. /**
  552. * Hooks into web messages coming in from web views
  553. * @param {[String|Object]} data
  554. */
  555. handleWebMessage(data: WebView.WebMessageEvent) {
  556. let messageType;
  557. let messageObject;
  558. try {
  559. messageObject = JSON.parse(data.request);
  560. messageType = messageObject.message;
  561. } catch (e) {
  562. // not JSON, we are just getting a notification message of some sort
  563. messageType = data.request;
  564. }
  565. // run through and find any services that can handle this.
  566. this.registeredServices.forEach((service) => {
  567. try {
  568. // Verify that the service contains the appropriate methods and that it can save
  569. if (service.handleWebMessage) {
  570. service.handleWebMessage(messageType, messageObject);
  571. }
  572. } catch (e) {
  573. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}:\n${e}\n\n ${e.stack}`);
  574. }
  575. });
  576. }
  577. /**
  578. * Allow this service registry to subscribe to events that it is interested in
  579. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  580. */
  581. subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
  582. // Placeholder for when UI events published by the editor need to be listened for
  583. eventDispatcher.subscribeToEvent(WebView.WebMessageEvent((ev) => this.handleWebMessage(ev)));
  584. }
  585. }