Skip to content

Commit

Permalink
Merge pull request #14199 from ckeditor/ck/14106-preserve-font-attrib…
Browse files Browse the repository at this point in the history
…utes-after-inserting-inline-objects

Fix (engine): Preserve font attributes after inserting inline widgets. Closes #14106.
  • Loading branch information
niegowski committed May 23, 2023
2 parents 6cb9cea + ea0f3c2 commit 30c31b6
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 9 deletions.
42 changes: 33 additions & 9 deletions packages/ckeditor5-engine/src/model/documentselection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type Element from './element';
import type Item from './item';
import type { default as Position, PositionOffset } from './position';
import type Range from './range';
import type Schema from './schema';

import {
CKEditorError,
Expand Down Expand Up @@ -1153,32 +1154,32 @@ class LiveSelection extends Selection {
// When gravity is overridden then don't take node before into consideration.
if ( !this.isGravityOverridden ) {
// ...look at the node before caret and take attributes from it if it is a character node.
attrs = getAttrsIfCharacter( nodeBefore! );
attrs = getTextAttributes( nodeBefore, schema );
}

// 3. If not, look at the node after caret...
if ( !attrs ) {
attrs = getAttrsIfCharacter( nodeAfter! );
attrs = getTextAttributes( nodeAfter, schema );
}

// 4. If not, try to find the first character on the left, that is in the same node.
// When gravity is overridden then don't take node before into consideration.
if ( !this.isGravityOverridden && !attrs ) {
let node = nodeBefore;

while ( node && !schema.isInline( node ) && !attrs ) {
while ( node && !attrs ) {
node = node.previousSibling;
attrs = getAttrsIfCharacter( node! );
attrs = getTextAttributes( node, schema );
}
}

// 5. If not found, try to find the first character on the right, that is in the same node.
if ( !attrs ) {
let node = nodeAfter;

while ( node && !schema.isInline( node ) && !attrs ) {
while ( node && !attrs ) {
node = node.nextSibling;
attrs = getAttrsIfCharacter( node! );
attrs = getTextAttributes( node, schema );
}
}

Expand Down Expand Up @@ -1211,14 +1212,37 @@ class LiveSelection extends Selection {
/**
* Helper function for {@link module:engine/model/liveselection~LiveSelection#_updateAttributes}.
*
* It takes model item, checks whether it is a text node (or text proxy) and, if so, returns it's attributes. If not, returns `null`.
* It checks if the passed model item is a text node (or text proxy) and, if so, returns it's attributes.
* If not, it checks if item is an inline object and does the same. Otherwise it returns `null`.
*/
function getAttrsIfCharacter( node: Item ) {
function getTextAttributes( node: Item | null, schema: Schema ): Iterable<[string, unknown]> | null {
if ( !node ) {
return null;
}

if ( node instanceof TextProxy || node instanceof Text ) {
return node.getAttributes();
}

return null;
if ( !schema.isInline( node ) ) {
return null;
}

// Stop on inline elements (such as `<softBreak>`) that are not objects (such as `<imageInline>` or `<mathml>`).
if ( !schema.isObject( node ) ) {
return [];
}

const attributes: Array<[string, unknown]> = [];

// Collect all attributes that can be applied to the text node.
for ( const [ key, value ] of node.getAttributes() ) {
if ( schema.checkAttribute( '$text', key ) ) {
attributes.push( [ key, value ] );
}
}

return attributes;
}

/**
Expand Down
35 changes: 35 additions & 0 deletions packages/ckeditor5-engine/tests/model/documentselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ describe( 'DocumentSelection', () => {
selection = doc.selection;
model.schema.register( 'p', { inheritAllFrom: '$block' } );

model.schema.extend( '$text', {
allowAttributes: 'bold'
} );

model.schema.register( 'widget', {
isObject: true
} );
Expand Down Expand Up @@ -1415,6 +1419,37 @@ describe( 'DocumentSelection', () => {
selection._restoreGravity( overrideGravityUid );
} );
} );

// #14106
describe( 'reads surrounding attributes from inline object elements', () => {
beforeEach( () => {
model.schema.register( 'imageInline', {
inheritAllFrom: '$inlineObject'
} );
} );

it( 'inherits attributes from a node before', () => {
setData( model, '<p><$text bold="true">Foo Bar.</$text><imageInline bold="true"></imageInline>[]</p>' );

expect( selection.hasAttribute( 'bold' ) ).to.equal( true );
} );

it( 'inherits attributes from a node after with override gravity', () => {
setData( model, '<p><$text>Foo Bar.</$text>[]<imageInline bold="true"></imageInline></p>' );

const overrideGravityUid = selection._overrideGravity();

expect( selection.hasAttribute( 'bold' ) ).to.equal( true );

selection._restoreGravity( overrideGravityUid );
} );

it( 'inherits attributes from <imageInline>, even without any text before it', () => {
setData( model, '<p><imageInline bold="true"></imageInline>[]</p>' );

expect( selection.hasAttribute( 'bold' ) ).to.equal( true );
} );
} );
} );

describe( '_overrideGravity()', () => {
Expand Down

0 comments on commit 30c31b6

Please sign in to comment.