EditorExtensionServices.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. // LICENSE: Atomic Game Engine Editor and Tools EULA
  4. // Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
  5. // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
  6. //
  7. import * as EditorEvents from "../editor/EditorEvents";
  8. import * as EditorUI from "../ui/EditorUI";
  9. /**
  10. * Base interface for any editor services.
  11. */
  12. export interface EditorService {
  13. /**
  14. * Called by the service locator at load time
  15. */
  16. initialize(serviceLocator: ServiceLocatorType);
  17. /**
  18. * Unique name of this service
  19. * @type {string}
  20. */
  21. name: string;
  22. /**
  23. * Description of this service
  24. * @type {string}
  25. */
  26. description: string;
  27. }
  28. export interface ResourceService extends EditorService {
  29. save?(ev: EditorEvents.SaveResourceEvent);
  30. canSave?(ev: EditorEvents.SaveResourceEvent);
  31. canDelete?(ev: EditorEvents.DeleteResourceEvent);
  32. delete?(ev: EditorEvents.DeleteResourceEvent);
  33. canRename?(ev: EditorEvents.RenameResourceEvent);
  34. rename?(ev: EditorEvents.RenameResourceEvent);
  35. }
  36. export interface ProjectService extends EditorService {
  37. projectUnloaded?();
  38. projectLoaded?(ev: EditorEvents.LoadProjectEvent);
  39. playerStarted?();
  40. }
  41. interface ServiceEventSubscriber {
  42. /**
  43. * Allow this service registry to subscribe to events that it is interested in
  44. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  45. */
  46. subscribeToEvents(topLevelWindow: Atomic.UIWidget);
  47. }
  48. /**
  49. * Generic registry for storing Editor Extension Services
  50. */
  51. class ServiceRegistry<T extends EditorService> {
  52. registeredServices: T[] = [];
  53. /**
  54. * Adds a service to the registered services list for this type of service
  55. * @param {T} service the service to register
  56. */
  57. register(service: T) {
  58. this.registeredServices.push(service);
  59. }
  60. }
  61. /**
  62. * Registry for service extensions that are concerned about project events
  63. */
  64. class ProjectServiceRegistry extends ServiceRegistry<ProjectService> implements ServiceEventSubscriber {
  65. constructor() {
  66. super();
  67. }
  68. /**
  69. * Allow this service registry to subscribe to events that it is interested in
  70. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  71. */
  72. subscribeToEvents(topLevelWindow: Atomic.UIWidget) {
  73. topLevelWindow.subscribeToEvent(EditorEvents.LoadProjectNotification, (ev) => this.projectLoaded(ev));
  74. topLevelWindow.subscribeToEvent(EditorEvents.CloseProject, (ev) => this.projectUnloaded(ev));
  75. topLevelWindow.subscribeToEvent(EditorEvents.PlayerStartRequest, () => this.playerStarted());
  76. }
  77. /**
  78. * Called when the project is unloaded
  79. * @param {[type]} data Event info from the project unloaded event
  80. */
  81. projectUnloaded(data) {
  82. this.registeredServices.forEach((service) => {
  83. // Notify services that the project has been unloaded
  84. try {
  85. if (service.projectUnloaded) {
  86. service.projectUnloaded();
  87. }
  88. } catch (e) {
  89. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  90. }
  91. });
  92. }
  93. /**
  94. * Called when the project is loaded
  95. * @param {[type]} data Event info from the project unloaded event
  96. */
  97. projectLoaded(ev: EditorEvents.LoadProjectEvent) {
  98. this.registeredServices.forEach((service) => {
  99. try {
  100. // Notify services that the project has just been loaded
  101. if (service.projectLoaded) {
  102. service.projectLoaded(ev);
  103. }
  104. } catch (e) {
  105. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  106. }
  107. });
  108. }
  109. playerStarted() {
  110. this.registeredServices.forEach((service) => {
  111. try {
  112. // Notify services that the project has just been loaded
  113. if (service.playerStarted) {
  114. service.playerStarted();
  115. }
  116. } catch (e) {
  117. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  118. }
  119. });
  120. }
  121. }
  122. /**
  123. * Registry for service extensions that are concerned about Resources
  124. */
  125. class ResourceServiceRegistry extends ServiceRegistry<ResourceService> {
  126. constructor() {
  127. super();
  128. }
  129. /**
  130. * Allow this service registry to subscribe to events that it is interested in
  131. * @param {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
  132. */
  133. subscribeToEvents(topLevelWindow: Atomic.UIWidget) {
  134. topLevelWindow.subscribeToEvent(EditorEvents.SaveResourceNotification, (ev) => this.saveResource(ev));
  135. topLevelWindow.subscribeToEvent(EditorEvents.DeleteResourceNotification, (ev) => this.deleteResource(ev));
  136. topLevelWindow.subscribeToEvent(EditorEvents.RenameResourceNotification, (ev) => this.renameResource(ev));
  137. }
  138. /**
  139. * Called after a resource has been saved
  140. * @param {EditorEvents.SaveResourceEvent} ev
  141. */
  142. saveResource(ev: EditorEvents.SaveResourceEvent) {
  143. // run through and find any services that can handle this.
  144. this.registeredServices.forEach((service) => {
  145. try {
  146. // Verify that the service contains the appropriate methods and that it can save
  147. if (service.canSave && service.save && service.canSave(ev)) {
  148. service.save(ev);
  149. }
  150. } catch (e) {
  151. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  152. }
  153. });
  154. }
  155. /**
  156. * Called when a resource has been deleted
  157. * @return {[type]} [description]
  158. */
  159. deleteResource(ev: EditorEvents.DeleteResourceEvent) {
  160. this.registeredServices.forEach((service) => {
  161. try {
  162. // Verify that the service contains the appropriate methods and that it can delete
  163. if (service.canDelete && service.delete && service.canDelete(ev)) {
  164. service.delete(ev);
  165. }
  166. } catch (e) {
  167. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  168. }
  169. });
  170. }
  171. /**
  172. * Called when a resource has been renamed
  173. * @param {EditorEvents.RenameResourceEvent} ev
  174. */
  175. renameResource(ev: EditorEvents.RenameResourceEvent) {
  176. this.registeredServices.forEach((service) => {
  177. try {
  178. // Verify that the service contains the appropriate methods and that it can handle the rename
  179. if (service.canRename && service.rename && service.canRename(ev)) {
  180. service.rename(ev);
  181. }
  182. } catch (e) {
  183. EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
  184. }
  185. });
  186. }
  187. }
  188. /**
  189. * Generic service locator of editor services that may be injected by either a plugin
  190. * or by the editor itself.
  191. */
  192. export class ServiceLocatorType {
  193. constructor() {
  194. this.resourceServices = new ResourceServiceRegistry();
  195. this.projectServices = new ProjectServiceRegistry();
  196. }
  197. private eventDispatcher: Atomic.UIWidget = null;
  198. resourceServices: ResourceServiceRegistry;
  199. projectServices: ProjectServiceRegistry;
  200. loadService(service: EditorService) {
  201. service.initialize(this);
  202. }
  203. /**
  204. * This is where the top level window will allow the service locator to listen for events and act on them.
  205. * @param {Atomic.UIWidget} frame
  206. */
  207. subscribeToEvents(frame: Atomic.UIWidget) {
  208. this.eventDispatcher = frame;
  209. this.resourceServices.subscribeToEvents(this.eventDispatcher);
  210. this.projectServices.subscribeToEvents(this.eventDispatcher);
  211. }
  212. /**
  213. * Send a custom event. This can be used by services to publish custom events
  214. * @param {string} eventType
  215. * @param {any} data
  216. */
  217. sendEvent(eventType: string, data: any) {
  218. if (this.eventDispatcher) {
  219. this.eventDispatcher.sendEvent(eventType, data);
  220. }
  221. }
  222. /**
  223. * Subscribe to an event and provide a callback. This can be used by services to subscribe to custom events
  224. * @param {string} eventType
  225. * @param {any} callback
  226. */
  227. subscribeToEvent(eventType: string, callback: (data: any) => void) {
  228. if (this.eventDispatcher) {
  229. this.eventDispatcher.subscribeToEvent(eventType, callback);
  230. }
  231. }
  232. }