Divec has uploaded a new change for review.
https://gerrit.wikimedia.org/r/89369
Change subject: WIP:Use native left/right/backspace/delete within text
......................................................................
WIP:Use native left/right/backspace/delete within text
TODO: Need to revisit the rerendering requirements on backspace/delete
within text
modules/ve/ce/ve.ce.Surface.js
* Native left/right cursoring inside a text node (or consecutive text nodes)
* Native backspace/delete
* Document the purpose of handleLeftOrRightArrowKey
Change-Id: I7cc1c3d91610f4dea1fd7e5b1b8b4bde334aad1f
---
M modules/ve/ce/ve.ce.Surface.js
1 file changed, 84 insertions(+), 9 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor
refs/changes/69/89369/1
diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js
index bf24cd9..7b73a6e 100644
--- a/modules/ve/ce/ve.ce.Surface.js
+++ b/modules/ve/ce/ve.ce.Surface.js
@@ -544,11 +544,9 @@
this.handleEnter( e );
break;
case ve.Keys.BACKSPACE:
- e.preventDefault();
this.handleDelete( e, true );
break;
case ve.Keys.DELETE:
- e.preventDefault();
this.handleDelete( e, false );
break;
default:
@@ -1081,6 +1079,7 @@
}
this.incRenderLock();
try {
+ // TODO: revisit rerendering requirements
this.model.change(
ve.dm.Transaction.newFromRemoval(
this.documentView.model,
range ),
@@ -1145,15 +1144,17 @@
}
if ( data.length > 0 ) {
- this.model.change(
- ve.dm.Transaction.newFromInsertion(
- this.documentView.model, nodeOffset + 1
+ fromLeft,
- data
- ),
- newRange
- );
+ // TODO: revisit rerendering requirements
+ this.model.change(
+ ve.dm.Transaction.newFromInsertion(
+ this.documentView.model, nodeOffset + 1 +
fromLeft,
+ data
+ ),
+ newRange
+ );
}
if ( fromLeft + fromRight < previousData.length ) {
+ // TODO: revisit rerendering requirements
this.model.change(
ve.dm.Transaction.newFromRemoval(
this.documentView.model,
@@ -1218,7 +1219,18 @@
/*! Utilities */
/**
+ * At times we can't rely on the browser's native Left/Right handling. For
instance:
+ * * To prevent cursoring into an image caption
+ * * Or into a template in IE (which does not respect contenteditable=false
inside contenteditable=true)
+ * * To allow selection of elements (e.g. placing the cursor "on image")
+ * * To prevent the cursor disappearing on some browsers e.g. in a table
before the first tr
+ * * To handle backwards selection in IE (which cannot be set
programmatically, so needs faking)
+ * * To give more consistency between browsers
+ *
+ * On the other hand, the browser's native handling is positively required to
handle cursoring
+ * over multi-codepoint Indic grapheme clusters, combining accents etc.
* @method
+ * @param {jQuery.Event} e Key down event
*/
ve.ce.Surface.prototype.handleLeftOrRightArrowKey = function ( e ) {
var selection, range, direction;
@@ -1226,6 +1238,13 @@
// As we are not able to handle it programmatically (because we don't
know at which offsets
// lines starts and ends) let it happen natively.
if ( e.metaKey ) {
+ return;
+ }
+
+ // If we're cursoring by character, within a text node, then use native
browser handling
+ // in order to handle grapheme clusters correctly.
+ if ( ! e.altKey && ! e.ctrlKey && this.willMoveInsideTextNode(
+ e.keyCode === ve.Keys.LEFT ? 'backward' : 'forward' ) ) {
return;
}
@@ -1258,6 +1277,55 @@
// TODO: onDocumentKeyDown does this anyway
this.surfaceObserver.startTimerLoop();
this.surfaceObserver.pollOnce();
+};
+
+/**
+ * Tests whether the moving cursor will stay inside the text node.
+ *
+ * Assumes the cursor will move by one character (the exact meaning of which
is potentially
+ * browser- and font-specific). Will also return true if the cursor will move
between adjacent
+ * text nodes, as some browsers will create these.
+ *
+ * @method
+ * @param {string} direction Direction of motion; 'forward' or 'backward'
+ * @returns {boolean} Whether the cursor won't enter or cross a non-text node
+ */
+ve.ce.Surface.prototype.willMoveInsideTextNode = function ( direction ) {
+ var range, node, backward = direction === 'backward',
+ sel = rangy.getSelection( this.getElementDocument() );
+ if ( sel.rangeCount === 0 ) {
+ return false;
+ }
+
+ // If moving backwards, get first range; if moving forwards, get last
range
+ if ( backward ) {
+ range = sel.getRangeAt( 0 );
+ node = range.startContainer;
+ } else {
+ range = sel.getRangeAt( sel.rangeCount - 1 );
+ node = range.endContainer;
+ }
+
+ if ( node.nodeType !== node.TEXT_NODE ) {
+ return false;
+ }
+
+ // Kludge: in Chrome, cursoring can split text nodes in two, so check
carefully. Note
+ // we can't just normalize() the parent node, because that changes the
selection state.
+ if ( range.startOffset === 0 && node.previousSibling !== null &&
+ node.previousSibling.nodeType === node.TEXT_NODE ) {
+ return true;
+ }
+ if ( range.endOffset === node.nodeValue.length && node.nextSibling !==
null &&
+ node.nextSibling.nodeType === node.TEXT_NODE ) {
+ return true;
+ }
+
+ if ( backward ) {
+ return range.startOffset > 0;
+ } else {
+ return range.endOffset < node.nodeValue.length;
+ }
};
/**
@@ -1517,6 +1585,13 @@
docLength, tx, startNode, endNode, endNodeData, nodeToDelete;
if ( rangeToRemove.isCollapsed() ) {
+ if ( ! e.altKey && ! e.ctrlKey && this.willMoveInsideTextNode(
+ backspace ? 'backward' : 'forward' ) ) {
+ // handle natively
+ return;
+ }
+ e.preventDefault();
+
// In case when the range is collapsed use the same logic that
is used for cursor left and
// right movement in order to figure out range to remove.
rangeToRemove = this.getDocument().getRelativeRange(
--
To view, visit https://gerrit.wikimedia.org/r/89369
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7cc1c3d91610f4dea1fd7e5b1b8b4bde334aad1f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits