|
@@ -18,6 +18,7 @@ import {
|
|
|
import env from '@ckeditor/ckeditor5-utils/src/env';
|
|
import env from '@ckeditor/ckeditor5-utils/src/env';
|
|
|
|
|
|
|
|
import '../theme/widget.css';
|
|
import '../theme/widget.css';
|
|
|
|
|
+import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* The widget plugin. It enables base support for widgets.
|
|
* The widget plugin. It enables base support for widgets.
|
|
@@ -99,8 +100,24 @@ export default class Widget extends Plugin {
|
|
|
view.addObserver( MouseObserver );
|
|
view.addObserver( MouseObserver );
|
|
|
this.listenTo( viewDocument, 'mousedown', ( ...args ) => this._onMousedown( ...args ) );
|
|
this.listenTo( viewDocument, 'mousedown', ( ...args ) => this._onMousedown( ...args ) );
|
|
|
|
|
|
|
|
- // Handle custom keydown behaviour.
|
|
|
|
|
- this.listenTo( viewDocument, 'keydown', ( ...args ) => this._onKeydown( ...args ), { priority: 'high' } );
|
|
|
|
|
|
|
+ // There are two keydown listeners working on different priorities. This allows other
|
|
|
|
|
+ // features such as WidgetTypeAround or TableKeyboard to attach their listeners in between
|
|
|
|
|
+ // and customize the behavior even further in different content/selection scenarios.
|
|
|
|
|
+ //
|
|
|
|
|
+ // * The first listener handles changing the selection on arrow key press
|
|
|
|
|
+ // if the widget is selected or if the selection is next to a widget and the widget
|
|
|
|
|
+ // should become selected upon the arrow key press.
|
|
|
|
|
+ //
|
|
|
|
|
+ // * The second (late) listener makes sure the default browser action on arrow key press is
|
|
|
|
|
+ // prevented when a widget is selected. This prevents the selection from being moved
|
|
|
|
|
+ // from a fake selection container.
|
|
|
|
|
+ this.listenTo( viewDocument, 'keydown', ( ...args ) => {
|
|
|
|
|
+ this._handleSelectionChangeOnArrowKeyPress( ...args );
|
|
|
|
|
+ }, { priority: 'high' } );
|
|
|
|
|
+
|
|
|
|
|
+ this.listenTo( viewDocument, 'keydown', ( ...args ) => {
|
|
|
|
|
+ this._preventDefaultOnArrowKeyPress( ...args );
|
|
|
|
|
+ }, { priority: priorities.get( 'high' ) - 20 } );
|
|
|
|
|
|
|
|
// Handle custom delete behaviour.
|
|
// Handle custom delete behaviour.
|
|
|
this.listenTo( viewDocument, 'delete', ( evt, data ) => {
|
|
this.listenTo( viewDocument, 'delete', ( evt, data ) => {
|
|
@@ -165,25 +182,92 @@ export default class Widget extends Plugin {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Handles {@link module:engine/view/document~Document#event:keydown keydown} events.
|
|
|
|
|
|
|
+ * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and changes
|
|
|
|
|
+ * the model selection when:
|
|
|
|
|
+ *
|
|
|
|
|
+ * * arrow key is pressed when the widget is selected,
|
|
|
|
|
+ * * the selection is next to a widget and the widget should become selected upon the arrow key press.
|
|
|
|
|
+ *
|
|
|
|
|
+ * See {@link #_preventDefaultOnArrowKeyPress}.
|
|
|
*
|
|
*
|
|
|
* @private
|
|
* @private
|
|
|
* @param {module:utils/eventinfo~EventInfo} eventInfo
|
|
* @param {module:utils/eventinfo~EventInfo} eventInfo
|
|
|
* @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
|
|
* @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
|
|
|
*/
|
|
*/
|
|
|
- _onKeydown( eventInfo, domEventData ) {
|
|
|
|
|
|
|
+ _handleSelectionChangeOnArrowKeyPress( eventInfo, domEventData ) {
|
|
|
const keyCode = domEventData.keyCode;
|
|
const keyCode = domEventData.keyCode;
|
|
|
- let wasHandled = false;
|
|
|
|
|
|
|
|
|
|
// Checks if the keys were handled and then prevents the default event behaviour and stops
|
|
// Checks if the keys were handled and then prevents the default event behaviour and stops
|
|
|
// the propagation.
|
|
// the propagation.
|
|
|
- if ( isArrowKeyCode( keyCode ) ) {
|
|
|
|
|
- const isForward = isForwardArrowKeyCode( keyCode, this.editor.locale.contentLanguageDirection );
|
|
|
|
|
|
|
+ if ( !isArrowKeyCode( keyCode ) ) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const model = this.editor.model;
|
|
|
|
|
+ const schema = model.schema;
|
|
|
|
|
+ const modelSelection = model.document.selection;
|
|
|
|
|
+ const objectElement = modelSelection.getSelectedElement();
|
|
|
|
|
+ const isForward = isForwardArrowKeyCode( keyCode, this.editor.locale.contentLanguageDirection );
|
|
|
|
|
+
|
|
|
|
|
+ // If object element is selected.
|
|
|
|
|
+ if ( objectElement && schema.isObject( objectElement ) ) {
|
|
|
|
|
+ const position = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
|
|
|
|
|
+ const newRange = schema.getNearestSelectionRange( position, isForward ? 'forward' : 'backward' );
|
|
|
|
|
+
|
|
|
|
|
+ if ( newRange ) {
|
|
|
|
|
+ model.change( writer => {
|
|
|
|
|
+ writer.setSelection( newRange );
|
|
|
|
|
+ } );
|
|
|
|
|
+
|
|
|
|
|
+ domEventData.preventDefault();
|
|
|
|
|
+ eventInfo.stop();
|
|
|
|
|
+
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If selection is next to object element.
|
|
|
|
|
+ // Return if not collapsed.
|
|
|
|
|
+ if ( !modelSelection.isCollapsed ) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const objectElement2 = this._getObjectElementNextToSelection( isForward );
|
|
|
|
|
+
|
|
|
|
|
+ if ( !!objectElement2 && schema.isObject( objectElement2 ) ) {
|
|
|
|
|
+ this._setSelectionOverElement( objectElement2 );
|
|
|
|
|
+
|
|
|
|
|
+ domEventData.preventDefault();
|
|
|
|
|
+ eventInfo.stop();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and prevents
|
|
|
|
|
+ * the default browser behavior to make sure the fake selection is not being moved from a fake selection
|
|
|
|
|
+ * container.
|
|
|
|
|
+ *
|
|
|
|
|
+ * See {@link #_handleSelectionChangeOnArrowKeyPress}.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @private
|
|
|
|
|
+ * @param {module:utils/eventinfo~EventInfo} eventInfo
|
|
|
|
|
+ * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
|
|
|
|
|
+ */
|
|
|
|
|
+ _preventDefaultOnArrowKeyPress( eventInfo, domEventData ) {
|
|
|
|
|
+ const keyCode = domEventData.keyCode;
|
|
|
|
|
|
|
|
- wasHandled = this._handleArrowKeys( isForward );
|
|
|
|
|
|
|
+ // Checks if the keys were handled and then prevents the default event behaviour and stops
|
|
|
|
|
+ // the propagation.
|
|
|
|
|
+ if ( !isArrowKeyCode( keyCode ) ) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if ( wasHandled ) {
|
|
|
|
|
|
|
+ const model = this.editor.model;
|
|
|
|
|
+ const schema = model.schema;
|
|
|
|
|
+ const objectElement = model.document.selection.getSelectedElement();
|
|
|
|
|
+
|
|
|
|
|
+ // If object element is selected.
|
|
|
|
|
+ if ( objectElement && schema.isObject( objectElement ) ) {
|
|
|
domEventData.preventDefault();
|
|
domEventData.preventDefault();
|
|
|
eventInfo.stop();
|
|
eventInfo.stop();
|
|
|
}
|
|
}
|
|
@@ -231,49 +315,6 @@ export default class Widget extends Plugin {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Handles arrow keys.
|
|
|
|
|
- *
|
|
|
|
|
- * @private
|
|
|
|
|
- * @param {Boolean} isForward Set to true if arrow key should be handled in forward direction.
|
|
|
|
|
- * @returns {Boolean|undefined} Returns `true` if keys were handled correctly.
|
|
|
|
|
- */
|
|
|
|
|
- _handleArrowKeys( isForward ) {
|
|
|
|
|
- const model = this.editor.model;
|
|
|
|
|
- const schema = model.schema;
|
|
|
|
|
- const modelDocument = model.document;
|
|
|
|
|
- const modelSelection = modelDocument.selection;
|
|
|
|
|
- const objectElement = modelSelection.getSelectedElement();
|
|
|
|
|
-
|
|
|
|
|
- // If object element is selected.
|
|
|
|
|
- if ( objectElement && schema.isObject( objectElement ) ) {
|
|
|
|
|
- const position = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
|
|
|
|
|
- const newRange = schema.getNearestSelectionRange( position, isForward ? 'forward' : 'backward' );
|
|
|
|
|
-
|
|
|
|
|
- if ( newRange ) {
|
|
|
|
|
- model.change( writer => {
|
|
|
|
|
- writer.setSelection( newRange );
|
|
|
|
|
- } );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // If selection is next to object element.
|
|
|
|
|
- // Return if not collapsed.
|
|
|
|
|
- if ( !modelSelection.isCollapsed ) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const objectElement2 = this._getObjectElementNextToSelection( isForward );
|
|
|
|
|
-
|
|
|
|
|
- if ( !!objectElement2 && schema.isObject( objectElement2 ) ) {
|
|
|
|
|
- this._setSelectionOverElement( objectElement2 );
|
|
|
|
|
-
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* Sets {@link module:engine/model/selection~Selection document's selection} over given element.
|
|
* Sets {@link module:engine/model/selection~Selection document's selection} over given element.
|
|
|
*
|
|
*
|