Przeglądaj źródła

Intial implementation of converting markers to virtual selection.

Szymon Kupś 8 lat temu
rodzic
commit
df917fe317

+ 91 - 8
packages/ckeditor5-engine/src/conversion/buildmodelconverter.js

@@ -15,12 +15,12 @@ import {
 	removeUIElement,
 	wrapItem,
 	unwrapItem,
-	wrapRange,
-	unwrapRange
+	eventNameToConsumableType
 } from './model-to-view-converters';
 
 import { convertSelectionAttribute, convertSelectionMarker } from './model-selection-to-view-converters';
 
+import ModelRange from '../model/range';
 import ViewAttributeElement from '../view/attributeelement';
 import ViewContainerElement from '../view/containerelement';
 import ViewUIElement from '../view/uielement';
@@ -243,14 +243,97 @@ class ModelConverterBuilder {
 				dispatcher.on( 'removeAttribute:' + this._from.key, unwrapItem( element ), { priority } );
 
 				dispatcher.on( 'selectionAttribute:' + this._from.key, convertSelectionAttribute( element ), { priority } );
-			} else {
-				element = typeof element == 'string' ? new ViewAttributeElement( element ) : element;
+			}
+		}
+	}
 
-				dispatcher.on( 'addMarker:' + this._from.name, wrapRange( element ), { priority } );
-				dispatcher.on( 'removeMarker:' + this._from.name, unwrapRange( element ), { priority } );
+	toVirtualSelection( selectionInfo ) {
+		const priority = this._from.priority === null ? 'normal' : this._from.priority;
 
-				dispatcher.on( 'selectionMarker:' + this._from.name, convertSelectionMarker( element ), { priority } );
-			}
+		for ( const dispatcher of this._dispatchers ) {
+			dispatcher.on( 'selectionMarker:' + this._from.name, ( evt, data, consumable, api ) => {
+				const info = selectionInfo( data );
+				const wrapElement = new ViewAttributeElement( 'span', info.attributes );
+
+				if ( info.class ) {
+					wrapElement.addClass( info.class );
+				}
+
+				const wrap = convertSelectionMarker( wrapElement );
+
+				wrap( evt, data, consumable, api );
+			}, { priority } );
+
+			dispatcher.on( 'addMarker:' + this._from.name, ( evt, data, consumable, api ) => {
+				const modelItem = data.item;
+				const info = selectionInfo( data );
+
+				if ( modelItem.is( 'textProxy' ) ) {
+					const wrapElement = new ViewAttributeElement( 'span', info.attributes );
+
+					if ( info.class ) {
+						wrapElement.addClass( info.class );
+					}
+
+					const wrap = wrapItem( wrapElement );
+
+					wrap( evt, data, consumable, api );
+				}
+
+				if ( modelItem.is( 'element' ) ) {
+					const consumableType = eventNameToConsumableType( evt.name );
+					const viewElement = api.mapper.toViewElement( modelItem );
+
+					if ( !consumable.consume( modelItem, consumableType ) ) {
+						return;
+					}
+
+					if ( viewElement && viewElement.setVirtualSelection ) {
+						// Virtual selection will be handled by parent element - consume all children.
+						for ( const value of ModelRange.createIn( modelItem ) ) {
+							consumable.consume( value.item, consumableType );
+						}
+
+						viewElement.setVirtualSelection( info );
+					}
+				}
+			}, { priority } );
+
+			dispatcher.on( 'removeMarker:' + this._from.name, ( evt, data, consumable, api ) => {
+				const modelItem = data.item;
+				const info = selectionInfo( data );
+
+				if ( modelItem.is( 'textProxy' ) ) {
+					const info = selectionInfo( data );
+					const wrapElement = new ViewAttributeElement( 'span', info.attributes );
+
+					if ( info.class ) {
+						wrapElement.addClass( info.class );
+					}
+
+					const unwrap = unwrapItem( wrapElement );
+
+					unwrap( evt, data, consumable, api );
+				}
+
+				if ( modelItem.is( 'element' ) ) {
+					const consumableType = eventNameToConsumableType( evt.name );
+					const viewElement = api.mapper.toViewElement( modelItem );
+
+					if ( !consumable.consume( modelItem, consumableType ) ) {
+						return;
+					}
+
+					if ( viewElement && viewElement.setVirtualSelection ) {
+						// Virtual selection will be handled by parent element - consume all children.
+						for ( const value of ModelRange.createIn( modelItem ) ) {
+							consumable.consume( value.item, consumableType );
+						}
+
+						viewElement.removeVirtualSelection( info );
+					}
+				}
+			}, { priority } );
 		}
 	}
 

+ 15 - 3
packages/ckeditor5-engine/src/conversion/modelconversiondispatcher.js

@@ -399,10 +399,22 @@ export default class ModelConversionDispatcher {
 			return;
 		}
 
-		const consumable = this._createMarkerConsumable( type, range );
-		const data = { name, range };
+		// Create consumable for each item in range.
+		// TODO: better consumable name handling.
+		const consumable = this._createConsumableForRange( range, type + ':' + name.split( ':' )[ 0 ] );
 
-		this.fire( type + ':' + name, data, consumable, this.conversionApi );
+		// Create separate event for each node in the range.
+		for ( const value of range ) {
+			const item = value.item;
+			const itemRange = Range.createFromPositionAndShift( value.previousPosition, value.length );
+			const data = {
+				item,
+				name,
+				range: itemRange
+			};
+
+			this.fire( type + ':' + name, data, consumable, this.conversionApi );
+		}
 	}
 
 	/**

+ 7 - 4
packages/ckeditor5-engine/tests/manual/markers.js

@@ -18,7 +18,6 @@ import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import buildModelConverter from '../../src/conversion/buildmodelconverter';
 import Position from '../../src/model/position';
 import Range from '../../src/model/range';
-import ViewAttributeElement from '../../src/view/attributeelement';
 
 const markerNames = [];
 let model = null;
@@ -34,10 +33,14 @@ ClassicEditor.create( document.querySelector( '#editor' ), {
 
 	buildModelConverter().for( editor.editing.modelToView )
 		.fromMarker( 'highlight' )
-		.toElement( data => {
+		.toVirtualSelection( data => {
 			const color = data.name.split( ':' )[ 1 ];
 
-			return new ViewAttributeElement( 'span', { class: 'h-' + color } );
+			return {
+				class: 'h-' + color,
+				attributes: { 'data-id': 100 },
+				priority: 1000
+			};
 		} );
 
 	window.document.getElementById( 'add-yellow' ).addEventListener( 'mousedown', e => {
@@ -72,7 +75,7 @@ ClassicEditor.create( document.querySelector( '#editor' ), {
 
 	model.enqueueChanges( () => {
 		const root = model.getRoot();
-		const range = new Range( new Position( root, [ 0, 10 ] ), new Position( root, [ 0, 16 ] ) );
+		const range = new Range( new Position( root, [ 0, 10 ] ), new Position( root, [ 1, 5 ] ) );
 		const name = 'highlight:yellow:' + uid();
 
 		markerNames.push( name );