Explorar o código

Added `selectionChangeDone` event to FakeSelectionObserver.

Oskar Wróbel %!s(int64=9) %!d(string=hai) anos
pai
achega
b1d13596c9

+ 33 - 4
packages/ckeditor5-engine/src/view/observer/fakeselectionobserver.js

@@ -10,6 +10,7 @@
 import Observer from './observer';
 import ViewSelection from '../selection';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import debounce from '@ckeditor/ckeditor5-utils/src/lib/lodash/debounce';
 
 /**
  * Fake selection observer class. If view selection is fake it is placed in dummy DOM container. This observer listens
@@ -28,6 +29,15 @@ export default class FakeSelectionObserver extends Observer {
 	 */
 	constructor( document ) {
 		super( document );
+
+		/**
+		 * Fires `selectionChangeDone` event debounced. It use `lodash#debounce` method to delay function call.
+		 *
+		 * @private
+		 * @param {Object} data Selection change data.
+		 * @method #_fireSelectionChangeDoneDebounced
+		 */
+		this._fireSelectionChangeDoneDebounced = debounce( data => this.document.fire( 'selectionChangeDone', data ), 200 );
 	}
 
 	/**
@@ -48,16 +58,27 @@ export default class FakeSelectionObserver extends Observer {
 		}, { priority: 'lowest' } );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
+	destroy() {
+		super.destroy();
+
+		this._fireSelectionChangeDoneDebounced.cancel();
+	}
+
 	/**
 	 * Handles collapsing view selection according to given key code. If left or up key is provided - new selection will be
 	 * collapsed to left. If right or down key is pressed - new selection will be collapsed to right.
 	 *
-	 * This method fires {@link module:engine/view/document~Document#event:selectionChange} event imitating behaviour of
+	 * This method fires {@link module:engine/view/document~Document#event:selectionChange} and
+	 * {@link module:engine/view/document~Document#event:selectionChangeDone} events imitating behaviour of
 	 * {@link module:engine/view/observer/selectionobserver~SelectionObserver}.
 	 *
 	 * @private
 	 * @param {Number} keyCode
 	 * @fires module:engine/view/document~Document#event:selectionChange
+	 * @fires module:engine/view/document~Document#event:selectionChangeDone
 	 */
 	_handleSelectionMove( keyCode ) {
 		const selection = this.document.selection;
@@ -74,12 +95,20 @@ export default class FakeSelectionObserver extends Observer {
 			newSelection.collapseToEnd();
 		}
 
-		// Fire dummy selection change event.
-		this.document.fire( 'selectionChange', {
+		const data = {
 			oldSelection: selection,
 			newSelection: newSelection,
 			domSelection: null
-		} );
+		};
+
+		// Fire dummy selection change event.
+		this.document.fire( 'selectionChange', data );
+
+		// Call` #_fireSelectionChangeDoneDebounced` every time when `selectionChange` event is fired.
+		// This function is debounced what means that `selectionChangeDone` event will be fired only when
+		// defined int the function time will elapse since the last time the function was called.
+		// So `selectionChangeDone` will be fired when selection will stop changing.
+		this._fireSelectionChangeDoneDebounced( data );
 	}
 }
 

+ 69 - 8
packages/ckeditor5-engine/tests/view/observer/fakeselectionobserver.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* globals document */
+/* globals document, setTimeout */
 
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
 import FakeSelectionObserver from '../../../src/view/observer/fakeselectionobserver';
@@ -100,6 +100,60 @@ describe( 'FakeSelectionObserver', () => {
 		);
 	} );
 
+	it( 'should fire `selectionChangeDone` event after selection stop changing', ( done ) => {
+		// Note that it's difficult to test lodash#debounce with sinon fake timers.
+		// See: https://github.com/lodash/lodash/issues/304
+
+		const spy = sinon.spy();
+
+		viewDocument.on( 'selectionChangeDone', spy );
+
+		// Change selection.
+		changeFakeSelectionPressing( keyCodes.arrowdown );
+
+		// Wait 100ms.
+		setTimeout( () => {
+			// Check if spy was called.
+			expect( spy.notCalled ).to.true;
+
+			// Change selection one more time.
+			changeFakeSelectionPressing( keyCodes.arrowdown );
+
+			// Wait 210ms (debounced function should be called).
+			setTimeout( () => {
+				expect( spy.calledOnce ).to.true;
+
+				done();
+			}, 210 );
+		}, 100 );
+	} );
+
+	it( 'should not fire `selectionChangeDone` event when observer will be destroyed', ( done ) => {
+		// Note that it's difficult to test lodash#debounce with sinon fake timers.
+		// See: https://github.com/lodash/lodash/issues/304
+
+		const spy = sinon.spy();
+
+		viewDocument.on( 'selectionChangeDone', spy );
+
+		// Change selection.
+		changeFakeSelectionPressing( keyCodes.arrowdown );
+
+		// Wait 100ms.
+		setTimeout( () => {
+			// And destroy observer.
+			observer.destroy();
+
+			// Wait another 110ms.
+			setTimeout( () => {
+				// Check that event won't be called.
+				expect( spy.notCalled ).to.true;
+
+				done();
+			}, 110 );
+		}, 100 );
+	} );
+
 	// Checks if preventDefault method was called by FakeSelectionObserver for specified key code.
 	//
 	// @param {Number} keyCode
@@ -140,14 +194,21 @@ describe( 'FakeSelectionObserver', () => {
 			} );
 
 			setData( viewDocument, initialData );
-			viewDocument.selection.setFake();
+			changeFakeSelectionPressing( keyCode );
+		} );
+	}
 
-			const data = {
-				keyCode,
-				preventDefault: sinon.spy(),
-			};
+	// Sets fake selection to the document and fire `keydown` event what cause `selectionChange` event.
+	//
+	// @param {Number} keyCode
+	function changeFakeSelectionPressing( keyCode ) {
+		viewDocument.selection.setFake();
 
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, { target: document.body }, data ) );
-		} );
+		const data = {
+			keyCode,
+			preventDefault: sinon.spy(),
+		};
+
+		viewDocument.fire( 'keydown', new DomEventData( viewDocument, { target: document.body }, data ) );
 	}
 } );