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

Commit

Permalink
Merge 4a9e4e8 into 05e4e20
Browse files Browse the repository at this point in the history
  • Loading branch information
ma2ciek committed Feb 6, 2019
2 parents 05e4e20 + 4a9e4e8 commit 1533da3
Show file tree
Hide file tree
Showing 22 changed files with 192 additions and 113 deletions.
9 changes: 3 additions & 6 deletions src/controller/editingcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,11 @@ export default class EditingController {
//
// See https://github.com/ckeditor/ckeditor5-engine/issues/1528
this.listenTo( this.model, '_beforeChanges', () => {
this.view._renderingDisabled = true;
this.view._disableRendering( true );
}, { priority: 'highest' } );

this.listenTo( this.model, '_afterChanges', ( evt, { hasModelDocumentChanged } ) => {
this.view._renderingDisabled = false;
if ( hasModelDocumentChanged ) {
this.view.render();
}
this.listenTo( this.model, '_afterChanges', () => {
this.view._disableRendering( false );
}, { priority: 'lowest' } );

// Whenever model document is changed, convert those changes to the view (using model.Document#differ).
Expand Down
4 changes: 2 additions & 2 deletions src/model/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,8 @@ export default class Document {
}

/**
* Used to register a post-fixer callback. A post-fixer mechanism guarantees that the features that listen to
* the {@link module:engine/model/model~Model#event:_change model's change event} will operate on a correct model state.
* Used to register a post-fixer callback. A post-fixer mechanism guarantees that the features
* will operate on a correct model state.
*
* An execution of a feature may lead to an incorrect document tree state. The callbacks are used to fix the document tree after
* it has changed. Post-fixers are fired just after all changes from the outermost change block were applied but
Expand Down
8 changes: 1 addition & 7 deletions src/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,6 @@ export default class Model {
*/
_runPendingChanges() {
const ret = [];
let hasModelDocumentChanged = false;

this.fire( '_beforeChanges' );

Expand All @@ -696,9 +695,6 @@ export default class Model {
const callbackReturnValue = this._pendingChanges[ 0 ].callback( this._currentWriter );
ret.push( callbackReturnValue );

// Collect an information whether the model document has changed during from the last pending change.
hasModelDocumentChanged = hasModelDocumentChanged || this.document._hasDocumentChangedFromTheLastChangeBlock();

// Fire '_change' event before resetting differ.
this.fire( '_change', this._currentWriter );

Expand All @@ -708,7 +704,7 @@ export default class Model {
this._currentWriter = null;
}

this.fire( '_afterChanges', { hasModelDocumentChanged } );
this.fire( '_afterChanges' );

return ret;
}
Expand Down Expand Up @@ -739,8 +735,6 @@ export default class Model {
*
* @protected
* @event _afterChanges
* @param {Object} options
* @param {Boolean} options.hasModelDocumentChanged `true` if the model document has changed during the
* {@link module:engine/model/model~Model#change} or {@link module:engine/model/model~Model#enqueueChange} blocks.
*/

Expand Down
3 changes: 3 additions & 0 deletions src/view/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ export default class Document {
* As a parameter, a post-fixer callback receives a {@link module:engine/view/downcastwriter~DowncastWriter downcast writer}
* instance connected with the executed changes block.
*
* Note that registering a post-fixer won't re-render the editor's view. If the view should change after registering the post-fixer then
* it should be done manually calling `view.forceRender();`.
*
* @param {Function} postFixer
*/
registerPostFixer( postFixer ) {
Expand Down
4 changes: 2 additions & 2 deletions src/view/observer/focusobserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class FocusObserver extends DomEventObserver {
// overwrite new DOM selection with selection from the view.
// See https://github.com/ckeditor/ckeditor5-engine/issues/795 for more details.
// Long timeout is needed to solve #676 and https://github.com/ckeditor/ckeditor5-engine/issues/1157 issues.
this._renderTimeoutId = setTimeout( () => view.render(), 50 );
this._renderTimeoutId = setTimeout( () => view.forceRender(), 50 );
} );

document.on( 'blur', ( evt, data ) => {
Expand All @@ -47,7 +47,7 @@ export default class FocusObserver extends DomEventObserver {
document.isFocused = false;

// Re-render the document to update view elements.
view.render();
view.forceRender();
}
} );

Expand Down
2 changes: 1 addition & 1 deletion src/view/observer/mutationobserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ export default class MutationObserver extends Observer {

// If nothing changes on `mutations` event, at this point we have "dirty DOM" (changed) and de-synched
// view (which has not been changed). In order to "reset DOM" we render the view again.
this.view.render();
this.view.forceRender();

function sameNodes( child1, child2 ) {
// First level of comparison (array of children vs array of children) – use the Lodash's default behavior.
Expand Down
2 changes: 1 addition & 1 deletion src/view/observer/selectionobserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default class SelectionObserver extends Observer {
if ( this.selection.isSimilar( newViewSelection ) ) {
// If selection was equal and we are at this point of algorithm, it means that it was incorrect.
// Just re-render it, no need to fire any events, etc.
this.view.render();
this.view.forceRender();
} else {
const data = {
oldSelection: this.selection,
Expand Down
3 changes: 0 additions & 3 deletions src/view/placeholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ export function attachPlaceholder( view, element, placeholderText, checkFunction
placeholderText,
checkFunction
} );

// Update view right away.
view.render();
}

/**
Expand Down
78 changes: 59 additions & 19 deletions src/view/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class View {
* Instance of the {@link module:engine/view/document~Document} associated with this view controller.
*
* @readonly
* @member {module:engine/view/document~Document} module:engine/view/view~View#document
* @type {module:engine/view/document~Document}
*/
this.document = new Document();

Expand All @@ -76,15 +76,15 @@ export default class View {
* and {@link module:engine/view/observer/observer~Observer observers}.
*
* @readonly
* @member {module:engine/view/domconverter~DomConverter} module:engine/view/view~View#domConverter
* @type {module:engine/view/domconverter~DomConverter}
*/
this.domConverter = new DomConverter();

/**
* Instance of the {@link module:engine/view/renderer~Renderer renderer}.
*
* @protected
* @member {module:engine/view/renderer~Renderer} module:engine/view/view~View#renderer
* @type {module:engine/view/renderer~Renderer}
*/
this._renderer = new Renderer( this.domConverter, this.document.selection );
this._renderer.bind( 'isFocused' ).to( this.document );
Expand All @@ -93,55 +93,64 @@ export default class View {
* Roots of the DOM tree. Map on the `HTMLElement`s with roots names as keys.
*
* @readonly
* @member {Map} module:engine/view/view~View#domRoots
* @type {Map.<String, HTMLElement>}
*/
this.domRoots = new Map();

/**
* Map of registered {@link module:engine/view/observer/observer~Observer observers}.
*
* @private
* @member {Map.<Function, module:engine/view/observer/observer~Observer>} module:engine/view/view~View#_observers
* @type {Map.<Function, module:engine/view/observer/observer~Observer>}
*/
this._observers = new Map();

/**
* Is set to `true` when {@link #change view changes} are currently in progress.
*
* @private
* @member {Boolean} module:engine/view/view~View#_ongoingChange
* @type {Boolean}
*/
this._ongoingChange = false;

/**
* Used to prevent calling {@link #render} and {@link #change} during rendering view to the DOM.
*
* @private
* @member {Boolean} module:engine/view/view~View#_renderingInProgress
* @type {Boolean}
*/
this._renderingInProgress = false;

/**
* Used to prevent calling {@link #render} and {@link #change} during rendering view to the DOM.
*
* @private
* @member {Boolean} module:engine/view/view~View#_renderingInProgress
* @type {Boolean}
*/
this._postFixersInProgress = false;

/**
* Internal flag to temporary disable rendering. See usage in the editing controller.
* Internal flag to temporary disable rendering. See the usage in the {@link #_disableRendering}.
*
* @protected
* @member {Boolean} module:engine/view/view~View#_renderingDisabled
* @private
* @type {Boolean}
*/
this._renderingDisabled = false;

/**
* DowncastWriter instance used in {@link #change change method) callbacks.
* Internal flag that disables rendering when there are no changes since the last rendering.
* It stores information about changed selection and changed elements from attached document roots.
*
* @private
* @type {Boolean}
*/
this._hasChangedSinceTheLastRendering = false;

/**
* DowncastWriter instance used in {@link #change change method} callbacks.
*
* @private
* @member {module:engine/view/downcastwriter~DowncastWriter} module:engine/view/view~View#_writer
* @type {module:engine/view/downcastwriter~DowncastWriter}
*/
this._writer = new DowncastWriter( this.document );

Expand All @@ -163,6 +172,14 @@ export default class View {

// Informs that layout has changed after render.
this.document.fire( 'layoutChanged' );

// Reset the `_hasChangedSinceTheLastRendering` flag after rendering.
this._hasChangedSinceTheLastRendering = false;
} );

// Listen to the document selection changes directly.
this.listenTo( this.document.selection, 'change', () => {
this._hasChangedSinceTheLastRendering = true;
} );
}

Expand Down Expand Up @@ -192,6 +209,10 @@ export default class View {
viewRoot.on( 'change:attributes', ( evt, node ) => this._renderer.markToSync( 'attributes', node ) );
viewRoot.on( 'change:text', ( evt, node ) => this._renderer.markToSync( 'text', node ) );

viewRoot.on( 'change', () => {
this._hasChangedSinceTheLastRendering = true;
} );

for ( const observer of this._observers.values() ) {
observer.observe( domRoot, name );
}
Expand Down Expand Up @@ -293,7 +314,7 @@ export default class View {

if ( editable ) {
this.domConverter.focus( editable );
this.render();
this.forceRender();
} else {
/**
* Before focusing view document, selection should be placed inside one of the view's editables.
Expand All @@ -309,9 +330,10 @@ export default class View {

/**
* The `change()` method is the primary way of changing the view. You should use it to modify any node in the view tree.
* It makes sure that after all changes are made the view is rendered to the DOM. It prevents situations when the DOM is updated
* when the view state is not yet correct. It allows to nest calls one inside another and still performs a single rendering
* after all those changes are made. It also returns the return value of its callback.
* It makes sure that after all changes are made the view is rendered to the DOM (assuming that the view will be changed
* inside the callback). It prevents situations when the DOM is updated when the view state is not yet correct.It allows
* to nest calls one inside another and still performs a single rendering after all those changes are made.
* It also returns the return value of its callback.
*
* const text = view.change( writer => {
* const newText = writer.createText( 'foo' );
Expand Down Expand Up @@ -368,7 +390,8 @@ export default class View {

// This lock is used by editing controller to render changes from outer most model.change() once. As plugins might call
// view.change() inside model.change() block - this will ensures that postfixers and rendering are called once after all changes.
if ( !this._renderingDisabled ) {
// Also, we don't need to render anything if there're no changes since last rendering.
if ( !this._renderingDisabled && this._hasChangedSinceTheLastRendering ) {
this._postFixersInProgress = true;
this.document._callPostFixers( this._writer );
this._postFixersInProgress = false;
Expand All @@ -386,7 +409,8 @@ export default class View {
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `applying-view-changes-on-rendering` when
* trying to re-render when rendering to DOM has already started.
*/
render() {
forceRender() {
this._hasChangedSinceTheLastRendering = true;
this.change( () => {} );
}

Expand Down Expand Up @@ -542,6 +566,22 @@ export default class View {
return new Selection( selectable, placeOrOffset, options );
}

/**
* Disables or enables rendering. If the flag is set to `true` then the rendering will be disabled.
* If the flag is set to `false` and if there was some change in the meantime, then the rendering action will be performed.
*
* @protected
* @param {Boolean} flag A flag indicates whether the rendering should be disabled.
*/
_disableRendering( flag ) {
this._renderingDisabled = flag;

if ( flag == false ) {
// Render when you stop blocking rendering.
this.change( () => {} );
}
}

/**
* Renders all changes. In order to avoid triggering the observers (e.g. mutations) all observers are disabled
* before rendering and re-enabled after that.
Expand Down
2 changes: 1 addition & 1 deletion tests/controller/editingcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ describe( 'EditingController', () => {
} );

editing.view.document.isFocused = true;
editing.view.render();
editing.view.forceRender();

const domSelection = document.getSelection();
domSelection.removeAllRanges();
Expand Down
8 changes: 5 additions & 3 deletions tests/tickets/1653.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictest
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';

describe( 'Bug ckeditor5-engine#1653', () => {
it( '`DataController.parse()` should not invoke `editing.view.render()`', () => {
it( '`DataController.parse()` should not fire `editing.view#render`', () => {
let editor;

const element = document.createElement( 'div' );
Expand All @@ -20,10 +20,12 @@ describe( 'Bug ckeditor5-engine#1653', () => {
.then( newEditor => {
editor = newEditor;

const spy = sinon.spy( editor.editing.view, 'render' );
const editingViewSpy = sinon.spy();

editor.editing.view.on( 'fire', editingViewSpy );
editor.data.parse( '<p></p>' );

sinon.assert.notCalled( spy );
sinon.assert.notCalled( editingViewSpy );
} )
.then( () => {
element.remove();
Expand Down
2 changes: 1 addition & 1 deletion tests/view/manual/immutable.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ setData( view,
viewDocument.on( 'selectionChange', () => {
// Re-render view selection each time selection is changed.
// See https://github.com/ckeditor/ckeditor5-engine/issues/796.
view.render();
view.forceRender();
} );

view.focus();
2 changes: 1 addition & 1 deletion tests/view/manual/noselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ view.attachDomRoot( document.getElementById( 'editor' ) );
viewDocument.on( 'selectionChange', () => {
// Re-render view selection each time selection is changed.
// See https://github.com/ckeditor/ckeditor5-engine/issues/796.
view.render();
view.forceRender();
} );

setData( view,
Expand Down
2 changes: 1 addition & 1 deletion tests/view/observer/domeventobserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ describe( 'DomEventObserver', () => {
view.attachDomRoot( domRoot );
uiElement = createUIElement( 'p' );
viewRoot._appendChild( uiElement );
view.render();
view.forceRender();

domEvent = new MouseEvent( 'click', { bubbles: true } );
evtSpy = sinon.spy();
Expand Down
Loading

0 comments on commit 1533da3

Please sign in to comment.