Przeglądaj źródła

Changed: update `LiveSelection` range fixing algorithms after changes in `LiveRange` and `RemoveOperation`.

Szymon Cofalik 8 lat temu
rodzic
commit
3fba25e149
1 zmienionych plików z 31 dodań i 17 usunięć
  1. 31 17
      packages/ckeditor5-engine/src/model/liveselection.js

+ 31 - 17
packages/ckeditor5-engine/src/model/liveselection.js

@@ -302,11 +302,18 @@ export default class LiveSelection extends Selection {
 		this._checkRange( range );
 
 		const liveRange = LiveRange.createFromRange( range );
-		this.listenTo( liveRange, 'change', ( evt, oldRange ) => {
+
+		this.listenTo( liveRange, 'change', ( evt, oldRange, data ) => {
+			// If `LiveRange` is in whole moved to the graveyard, fix that range.
 			if ( liveRange.root == this._document.graveyard ) {
-				this._fixGraveyardSelection( liveRange, oldRange );
+				const sourceStart = data.sourcePosition;
+				const howMany = data.range.end.offset - data.range.start.offset;
+				const sourceRange = Range.createFromPositionAndShift( sourceStart, howMany );
+
+				this._fixGraveyardSelection( liveRange, sourceRange );
 			}
 
+			// Whenever a live range from selection changes, fire an event informing about that change.
 			this.fire( 'change:range', { directChange: false } );
 		} );
 
@@ -624,29 +631,36 @@ export default class LiveSelection extends Selection {
 	 * Fixes a selection range after it ends up in graveyard root.
 	 *
 	 * @private
-	 * @param {module:engine/model/liverange~LiveRange} gyRange The range added in selection, that ended up in graveyard root.
-	 * @param {module:engine/model/range~Range} oldRange The state of that range before it was added to graveyard root.
+	 * @param {module:engine/model/liverange~LiveRange} liveRange The range from selection, that ended up in the graveyard root.
+	 * @param {module:engine/model/range~Range} removedRange Range which removing had caused moving `liveRange` to the graveyard root.
 	 */
-	_fixGraveyardSelection( gyRange, oldRange ) {
-		const gyPath = gyRange.start.path;
-
-		const newPathLength = oldRange.start.path.length - ( gyPath.length - 2 );
-		const newPath = oldRange.start.path.slice( 0, newPathLength );
-		newPath[ newPath.length - 1 ] -= gyPath[ 1 ];
+	_fixGraveyardSelection( liveRange, removedRange ) {
+		// The start of the removed range is the closest position to the `liveRange` - the original selection range.
+		// This is a good candidate for a fixed selection range.
+		const positionCandidate = Position.createFromPosition( removedRange.start );
 
-		const newPosition = new Position( oldRange.root, newPath );
-		let selectionRange = this._document.getNearestSelectionRange( newPosition );
+		// Find a range that is a correct selection range and is closest to the start of removed range.
+		let selectionRange = this._document.getNearestSelectionRange( positionCandidate );
 
-		// If nearest valid selection range cannot be found - use one created at root beginning.
+		// If nearest valid selection range cannot be found - use one created at the beginning of the root.
 		if ( !selectionRange ) {
-			selectionRange = new Range( new Position( newPosition.root, [ 0 ] ) );
+			selectionRange = new Range( new Position( positionCandidate.root, [ 0 ] ) );
 		}
 
+		// Remove the old selection range before preparing and adding new selection range. This order is important,
+		// because new range, in some cases, may intersect with old range (it depends on `getNearestSelectionRange()` result).
+		const index = this._ranges.indexOf( liveRange );
+		this._ranges.splice( index, 1 );
+		liveRange.detach();
+
+		// Check the range, convert it to live range, bind events, etc.
 		const newRange = this._prepareRange( selectionRange );
-		const index = this._ranges.indexOf( gyRange );
-		gyRange.detach();
 
-		this._ranges.splice( index, 1, newRange );
+		// Add new range in the place of old range.
+		this._ranges.splice( index, 0, newRange );
+
+		// Fire an event informing about selection change.
+		this.fire( 'change:range', { directChange: false } );
 	}
 }