Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #1296 from ckeditor/t/1210
Browse files Browse the repository at this point in the history
Other: Refactoring of the view API. Closes #1210.

BREAKING CHANGE: `view.Writer` is no longer an object literal with functions but a class.
BREAKING CHANGE: View controller `view.View` is introduced. Changes to the view document tree structure should be done by using writer provided to callback in `view.change()` method.
BREAKING CHANGE: View document is now separated from the DOM. `view.Renderer`, `view.DomConverter` and observers are moved to `view.View`. 
BREAKING CHANGE: `view#event:render` is introduced to indicate a moment when all changes are applied and document may be rendered to the DOM.
BREAKING CHANGE: Downcast converter helpers no longer accepts view elements instances as constructors are now protected. Callbacks using view writer should be used.
  • Loading branch information
Piotr Jasiun authored Feb 16, 2018
2 parents f37a97a + 8a07e76 commit dd9ae51
Show file tree
Hide file tree
Showing 107 changed files with 4,478 additions and 3,365 deletions.
18 changes: 13 additions & 5 deletions src/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import UpcastDispatcher from '../conversion/upcastdispatcher';
import { convertText, convertToModelFragment } from '../conversion/upcast-converters';

import ViewDocumentFragment from '../view/documentfragment';
import ViewDocument from '../view/document';
import ViewWriter from '../view/writer';

import ModelRange from '../model/range';

Expand Down Expand Up @@ -74,7 +76,7 @@ export default class DataController {
* @readonly
* @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher}
*/
this.downcastDispatcher = new DowncastDispatcher( this.model, {
this.downcastDispatcher = new DowncastDispatcher( {
mapper: this.mapper
} );
this.downcastDispatcher.on( 'insert:$text', insertText(), { priority: 'lowest' } );
Expand All @@ -85,7 +87,7 @@ export default class DataController {
* @readonly
* @member {module:engine/conversion/upcastdispatcher~UpcastDispatcher}
*/
this.upcastDispatcher = new UpcastDispatcher( this.model, {
this.upcastDispatcher = new UpcastDispatcher( {
schema: model.schema
} );

Expand Down Expand Up @@ -143,17 +145,21 @@ export default class DataController {
const modelRange = ModelRange.createIn( modelElementOrFragment );

const viewDocumentFragment = new ViewDocumentFragment();

// Create separate ViewWriter just for data conversion purposes.
// We have no view controller and rendering do DOM in DataController so view.change() block is not used here.
const viewWriter = new ViewWriter( new ViewDocument() );
this.mapper.bindElements( modelElementOrFragment, viewDocumentFragment );

this.downcastDispatcher.convertInsert( modelRange );
this.downcastDispatcher.convertInsert( modelRange, viewWriter );

if ( !modelElementOrFragment.is( 'documentFragment' ) ) {
// Then, if a document element is converted, convert markers.
// From all document markers, get those, which "intersect" with the converter element.
const markers = _getMarkersRelativeToElement( modelElementOrFragment );

for ( const [ name, range ] of markers ) {
this.downcastDispatcher.convertMarkerAdd( name, range );
this.downcastDispatcher.convertMarkerAdd( name, range, viewWriter );
}
}

Expand Down Expand Up @@ -221,7 +227,9 @@ export default class DataController {
* @returns {module:engine/model/documentfragment~DocumentFragment} Output document fragment.
*/
toModel( viewElementOrFragment, context = '$root' ) {
return this.upcastDispatcher.convert( viewElementOrFragment, context );
return this.model.change( writer => {
return this.upcastDispatcher.convert( viewElementOrFragment, writer, context );
} );
}

/**
Expand Down
39 changes: 21 additions & 18 deletions src/controller/editingcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import RootEditableElement from '../view/rooteditableelement';
import ViewDocument from '../view/document';
import View from '../view/view';
import Mapper from '../conversion/mapper';
import DowncastDispatcher from '../conversion/downcastdispatcher';
import {
Expand Down Expand Up @@ -49,12 +49,12 @@ export default class EditingController {
this.model = model;

/**
* View document.
* Editing view controller.
*
* @readonly
* @member {module:engine/view/document~Document}
* @member {module:engine/view/view~View}
*/
this.view = new ViewDocument();
this.view = new View();

/**
* Mapper which describes the model-view binding.
Expand All @@ -70,24 +70,23 @@ export default class EditingController {
* @readonly
* @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #downcastDispatcher
*/
this.downcastDispatcher = new DowncastDispatcher( this.model, {
mapper: this.mapper,
viewSelection: this.view.selection
this.downcastDispatcher = new DowncastDispatcher( {
mapper: this.mapper
} );

const doc = this.model.document;
const selection = doc.selection;
const markers = this.model.markers;

this.listenTo( doc, 'change', () => {
this.downcastDispatcher.convertChanges( doc.differ );
}, { priority: 'low' } );

this.listenTo( model, '_change', () => {
this.downcastDispatcher.convertSelection( doc.selection );
this.view.render();
this.view.change( writer => {
this.downcastDispatcher.convertChanges( doc.differ, writer );
this.downcastDispatcher.convertSelection( selection, markers, writer );
} );
}, { priority: 'low' } );

// Convert selection from view to model.
this.listenTo( this.view, 'selectionChange', convertSelectionChange( this.model, this.mapper ) );
this.listenTo( this.view.document, 'selectionChange', convertSelectionChange( this.model, this.mapper ) );

// Attach default model converters.
this.downcastDispatcher.on( 'insert:$text', insertText(), { priority: 'lowest' } );
Expand Down Expand Up @@ -122,7 +121,9 @@ export default class EditingController {
if ( _operationAffectsMarker( operation, marker ) ) {
// And if the operation in any way modifies the marker, remove the marker from the view.
removedMarkers.add( marker.name );
this.downcastDispatcher.convertMarkerRemove( marker.name, markerRange );
this.view.change( writer => {
this.downcastDispatcher.convertMarkerRemove( marker.name, markerRange, writer );
} );

// TODO: This stinks but this is the safest place to have this code.
this.model.document.differ.bufferMarkerChange( marker.name, markerRange, markerRange );
Expand All @@ -135,7 +136,9 @@ export default class EditingController {
this.listenTo( model.markers, 'remove', ( evt, marker ) => {
if ( !removedMarkers.has( marker.name ) ) {
removedMarkers.add( marker.name );
this.downcastDispatcher.convertMarkerRemove( marker.name, marker.getRange() );
this.view.change( writer => {
this.downcastDispatcher.convertMarkerRemove( marker.name, marker.getRange(), writer );
} );
}
} );

Expand All @@ -147,7 +150,7 @@ export default class EditingController {
// Binds {@link module:engine/view/document~Document#roots view roots collection} to
// {@link module:engine/model/document~Document#roots model roots collection} so creating
// model root automatically creates corresponding view root.
this.view.roots.bindTo( this.model.document.roots ).using( root => {
this.view.document.roots.bindTo( this.model.document.roots ).using( root => {
// $graveyard is a special root that has no reflection in the view.
if ( root.rootName == '$graveyard' ) {
return null;
Expand All @@ -156,7 +159,7 @@ export default class EditingController {
const viewRoot = new RootEditableElement( root.name );

viewRoot.rootName = root.rootName;
viewRoot.document = this.view;
viewRoot._document = this.view.document;
this.mapper.bindElements( root, viewRoot );

return viewRoot;
Expand Down
Loading

0 comments on commit dd9ae51

Please sign in to comment.