viewModelEventDispatcher.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { Emitter } from '../../../base/common/event.js';
  6. import { Disposable } from '../../../base/common/lifecycle.js';
  7. export class ViewModelEventDispatcher extends Disposable {
  8. constructor() {
  9. super();
  10. this._onEvent = this._register(new Emitter());
  11. this.onEvent = this._onEvent.event;
  12. this._eventHandlers = [];
  13. this._viewEventQueue = null;
  14. this._isConsumingViewEventQueue = false;
  15. this._collector = null;
  16. this._collectorCnt = 0;
  17. this._outgoingEvents = [];
  18. }
  19. emitOutgoingEvent(e) {
  20. this._addOutgoingEvent(e);
  21. this._emitOutgoingEvents();
  22. }
  23. _addOutgoingEvent(e) {
  24. for (let i = 0, len = this._outgoingEvents.length; i < len; i++) {
  25. if (this._outgoingEvents[i].kind === e.kind) {
  26. this._outgoingEvents[i] = this._outgoingEvents[i].merge(e);
  27. return;
  28. }
  29. }
  30. // not merged
  31. this._outgoingEvents.push(e);
  32. }
  33. _emitOutgoingEvents() {
  34. while (this._outgoingEvents.length > 0) {
  35. if (this._collector || this._isConsumingViewEventQueue) {
  36. // right now collecting or emitting view events, so let's postpone emitting
  37. return;
  38. }
  39. const event = this._outgoingEvents.shift();
  40. if (event.isNoOp()) {
  41. continue;
  42. }
  43. this._onEvent.fire(event);
  44. }
  45. }
  46. addViewEventHandler(eventHandler) {
  47. for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
  48. if (this._eventHandlers[i] === eventHandler) {
  49. console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
  50. }
  51. }
  52. this._eventHandlers.push(eventHandler);
  53. }
  54. removeViewEventHandler(eventHandler) {
  55. for (let i = 0; i < this._eventHandlers.length; i++) {
  56. if (this._eventHandlers[i] === eventHandler) {
  57. this._eventHandlers.splice(i, 1);
  58. break;
  59. }
  60. }
  61. }
  62. beginEmitViewEvents() {
  63. this._collectorCnt++;
  64. if (this._collectorCnt === 1) {
  65. this._collector = new ViewModelEventsCollector();
  66. }
  67. return this._collector;
  68. }
  69. endEmitViewEvents() {
  70. this._collectorCnt--;
  71. if (this._collectorCnt === 0) {
  72. const outgoingEvents = this._collector.outgoingEvents;
  73. const viewEvents = this._collector.viewEvents;
  74. this._collector = null;
  75. for (const outgoingEvent of outgoingEvents) {
  76. this._addOutgoingEvent(outgoingEvent);
  77. }
  78. if (viewEvents.length > 0) {
  79. this._emitMany(viewEvents);
  80. }
  81. }
  82. this._emitOutgoingEvents();
  83. }
  84. emitSingleViewEvent(event) {
  85. try {
  86. const eventsCollector = this.beginEmitViewEvents();
  87. eventsCollector.emitViewEvent(event);
  88. }
  89. finally {
  90. this.endEmitViewEvents();
  91. }
  92. }
  93. _emitMany(events) {
  94. if (this._viewEventQueue) {
  95. this._viewEventQueue = this._viewEventQueue.concat(events);
  96. }
  97. else {
  98. this._viewEventQueue = events;
  99. }
  100. if (!this._isConsumingViewEventQueue) {
  101. this._consumeViewEventQueue();
  102. }
  103. }
  104. _consumeViewEventQueue() {
  105. try {
  106. this._isConsumingViewEventQueue = true;
  107. this._doConsumeQueue();
  108. }
  109. finally {
  110. this._isConsumingViewEventQueue = false;
  111. }
  112. }
  113. _doConsumeQueue() {
  114. while (this._viewEventQueue) {
  115. // Empty event queue, as events might come in while sending these off
  116. const events = this._viewEventQueue;
  117. this._viewEventQueue = null;
  118. // Use a clone of the event handlers list, as they might remove themselves
  119. const eventHandlers = this._eventHandlers.slice(0);
  120. for (const eventHandler of eventHandlers) {
  121. eventHandler.handleEvents(events);
  122. }
  123. }
  124. }
  125. }
  126. export class ViewModelEventsCollector {
  127. constructor() {
  128. this.viewEvents = [];
  129. this.outgoingEvents = [];
  130. }
  131. emitViewEvent(event) {
  132. this.viewEvents.push(event);
  133. }
  134. emitOutgoingEvent(e) {
  135. this.outgoingEvents.push(e);
  136. }
  137. }
  138. export class ContentSizeChangedEvent {
  139. constructor(oldContentWidth, oldContentHeight, contentWidth, contentHeight) {
  140. this.kind = 0 /* ContentSizeChanged */;
  141. this._oldContentWidth = oldContentWidth;
  142. this._oldContentHeight = oldContentHeight;
  143. this.contentWidth = contentWidth;
  144. this.contentHeight = contentHeight;
  145. this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth);
  146. this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight);
  147. }
  148. isNoOp() {
  149. return (!this.contentWidthChanged && !this.contentHeightChanged);
  150. }
  151. merge(other) {
  152. if (other.kind !== 0 /* ContentSizeChanged */) {
  153. return this;
  154. }
  155. return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight);
  156. }
  157. }
  158. export class FocusChangedEvent {
  159. constructor(oldHasFocus, hasFocus) {
  160. this.kind = 1 /* FocusChanged */;
  161. this.oldHasFocus = oldHasFocus;
  162. this.hasFocus = hasFocus;
  163. }
  164. isNoOp() {
  165. return (this.oldHasFocus === this.hasFocus);
  166. }
  167. merge(other) {
  168. if (other.kind !== 1 /* FocusChanged */) {
  169. return this;
  170. }
  171. return new FocusChangedEvent(this.oldHasFocus, other.hasFocus);
  172. }
  173. }
  174. export class ScrollChangedEvent {
  175. constructor(oldScrollWidth, oldScrollLeft, oldScrollHeight, oldScrollTop, scrollWidth, scrollLeft, scrollHeight, scrollTop) {
  176. this.kind = 2 /* ScrollChanged */;
  177. this._oldScrollWidth = oldScrollWidth;
  178. this._oldScrollLeft = oldScrollLeft;
  179. this._oldScrollHeight = oldScrollHeight;
  180. this._oldScrollTop = oldScrollTop;
  181. this.scrollWidth = scrollWidth;
  182. this.scrollLeft = scrollLeft;
  183. this.scrollHeight = scrollHeight;
  184. this.scrollTop = scrollTop;
  185. this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth);
  186. this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft);
  187. this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight);
  188. this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop);
  189. }
  190. isNoOp() {
  191. return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged);
  192. }
  193. merge(other) {
  194. if (other.kind !== 2 /* ScrollChanged */) {
  195. return this;
  196. }
  197. return new ScrollChangedEvent(this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop, other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop);
  198. }
  199. }
  200. export class ViewZonesChangedEvent {
  201. constructor() {
  202. this.kind = 3 /* ViewZonesChanged */;
  203. }
  204. isNoOp() {
  205. return false;
  206. }
  207. merge(other) {
  208. return this;
  209. }
  210. }
  211. export class HiddenAreasChangedEvent {
  212. constructor() {
  213. this.kind = 4 /* HiddenAreasChanged */;
  214. }
  215. isNoOp() {
  216. return false;
  217. }
  218. merge(other) {
  219. return this;
  220. }
  221. }
  222. export class CursorStateChangedEvent {
  223. constructor(oldSelections, selections, oldModelVersionId, modelVersionId, source, reason, reachedMaxCursorCount) {
  224. this.kind = 6 /* CursorStateChanged */;
  225. this.oldSelections = oldSelections;
  226. this.selections = selections;
  227. this.oldModelVersionId = oldModelVersionId;
  228. this.modelVersionId = modelVersionId;
  229. this.source = source;
  230. this.reason = reason;
  231. this.reachedMaxCursorCount = reachedMaxCursorCount;
  232. }
  233. static _selectionsAreEqual(a, b) {
  234. if (!a && !b) {
  235. return true;
  236. }
  237. if (!a || !b) {
  238. return false;
  239. }
  240. const aLen = a.length;
  241. const bLen = b.length;
  242. if (aLen !== bLen) {
  243. return false;
  244. }
  245. for (let i = 0; i < aLen; i++) {
  246. if (!a[i].equalsSelection(b[i])) {
  247. return false;
  248. }
  249. }
  250. return true;
  251. }
  252. isNoOp() {
  253. return (CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections)
  254. && this.oldModelVersionId === this.modelVersionId);
  255. }
  256. merge(other) {
  257. if (other.kind !== 6 /* CursorStateChanged */) {
  258. return this;
  259. }
  260. return new CursorStateChangedEvent(this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount);
  261. }
  262. }
  263. export class ReadOnlyEditAttemptEvent {
  264. constructor() {
  265. this.kind = 5 /* ReadOnlyEditAttempt */;
  266. }
  267. isNoOp() {
  268. return false;
  269. }
  270. merge(other) {
  271. return this;
  272. }
  273. }