fixed behaviour of shift click and shift keys (#3274)

removed some glitches, removed duplicate code
This commit is contained in:
Andrea Bogazzi 2016-09-18 23:22:49 +02:00 committed by GitHub
parent 77f37db3c9
commit 07094a4ec5
4 changed files with 160 additions and 211 deletions

View file

@ -824,6 +824,58 @@
*/
insertNewline: function() {
this.insertChars('\n');
},
/**
* Set the selectionStart and selectionEnd according to the ne postion of cursor
* mimic the key - mouse navigation when shift is pressed.
*/
setSelectionStartEndWithShift: function(start, end, newSelection) {
if (newSelection <= start) {
if (end === start) {
this._selectionDirection = 'left';
}
else if (this._selectionDirection === 'right') {
this._selectionDirection = 'left';
this.selectionEnd = start;
}
this.selectionStart = newSelection;
}
else if (newSelection > start && newSelection < end) {
if (this._selectionDirection === 'right') {
this.selectionEnd = newSelection;
}
else {
this.selectionStart = newSelection;
}
}
else {
// newSelection is > selection start and end
if (end === start) {
this._selectionDirection = 'right';
}
else if (this._selectionDirection === 'left') {
this._selectionDirection = 'right';
this.selectionStart = end;
}
this.selectionEnd = newSelection;
}
},
setSelectionInBoundaries: function() {
var length = this.text.length;
if (this.selectionStart > length) {
this.selectionStart = length;
}
else if (this.selectionStart < 0) {
this.selectionStart = 0;
}
if (this.selectionEnd > length) {
this.selectionEnd = length;
}
else if (this.selectionEnd < 0) {
this.selectionEnd = 0;
}
}
});
})();

View file

@ -148,35 +148,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
var newSelection = this.getSelectionStartFromPointer(e),
start = this.selectionStart, end = this.selectionEnd;
if (e.shiftKey) {
if (newSelection < start) {
if (end === start) {
this._selectionDirection = 'left';
}
else if (this._selectionDirection === 'right') {
this._selectionDirection = 'left';
this.selectionEnd = start;
}
this.selectionStart = newSelection;
}
else if (newSelection > start && newSelection < end) {
if (this._selectionDirection === 'right') {
this.selectionEnd = newSelection;
}
else {
this.selectionStart = newSelection;
}
}
else {
// newSelection is > selection start and end
if (end === start) {
this._selectionDirection = 'right';
}
else if (this._selectionDirection === 'left') {
this._selectionDirection = 'right';
this.selectionStart = end;
}
this.selectionEnd = newSelection;
}
this.setSelectionStartEndWithShift(start, end, newSelection);
}
else {
this.selectionStart = newSelection;

View file

@ -255,6 +255,25 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return (e && e.clipboardData) || fabric.window.clipboardData;
},
/**
* Finds the width in pixels before the cursor on the same line
* @private
* @param {Number} lineIndex
* @param {Number} charIndex
* @return {Number} widthBeforeCursor width before cursor
*/
_getWidthBeforeCursor: function(lineIndex, charIndex) {
var textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex),
widthOfLine = this._getLineWidth(this.ctx, lineIndex),
widthBeforeCursor = this._getLineLeftOffset(widthOfLine), _char;
for (var i = 0, len = textBeforeCursor.length; i < len; i++) {
_char = textBeforeCursor[i];
widthBeforeCursor += this._getWidthOfChar(this.ctx, _char, lineIndex, i);
}
return widthBeforeCursor;
},
/**
* Gets start offset of a selection
* @param {Event} e Event object
@ -262,64 +281,89 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @return {Number}
*/
getDownCursorOffset: function(e, isRight) {
var selectionProp = isRight ? this.selectionEnd : this.selectionStart,
var selectionProp = this._getSelectionForOffset(e, isRight),
cursorLocation = this.get2DCursorLocation(selectionProp),
_char, lineLeftOffset, lineIndex = cursorLocation.lineIndex,
textOnSameLineBeforeCursor = this._textLines[lineIndex].slice(0, cursorLocation.charIndex),
textOnSameLineAfterCursor = this._textLines[lineIndex].slice(cursorLocation.charIndex),
textOnNextLine = this._textLines[lineIndex + 1] || '';
lineIndex = cursorLocation.lineIndex;
// if on last line, down cursor goes to end of line
if (lineIndex === this._textLines.length - 1 || e.metaKey || e.keyCode === 34) {
// move to the end of a text
return this.text.length - selectionProp;
}
var charIndex = cursorLocation.charIndex,
widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
indexOnOtherLine = this._getIndexOnLine(lineIndex + 1, widthBeforeCursor),
textAfterCursor = this._textLines[lineIndex].slice(charIndex);
var widthOfSameLineBeforeCursor = this._getLineWidth(this.ctx, lineIndex);
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
widthOfCharsOnSameLineBeforeCursor += this._getWidthOfChar(this.ctx, _char, lineIndex, i);
}
var indexOnNextLine = this._getIndexOnNextLine(
cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor);
return textOnSameLineAfterCursor.length + 1 + indexOnNextLine;
return textAfterCursor.length + indexOnOtherLine + 2;
},
/**
* private
* Helps finding if the offset should be counted from Start or End
* @param {Event} e Event object
* @param {Boolean} isRight
* @return {Number}
*/
_getSelectionForOffset: function(e, isRight) {
if (e.shiftKey && this.selectionStart !== this.selectionEnd && isRight) {
return this.selectionEnd;
}
else {
return this.selectionStart;
}
},
/**
* @param {Event} e Event object
* @param {Boolean} isRight
* @return {Number}
*/
getUpCursorOffset: function(e, isRight) {
var selectionProp = this._getSelectionForOffset(e, isRight),
cursorLocation = this.get2DCursorLocation(selectionProp),
lineIndex = cursorLocation.lineIndex;
if (lineIndex === 0 || e.metaKey || e.keyCode === 33) {
// if on first line, up cursor goes to start of line
return -selectionProp;
}
var charIndex = cursorLocation.charIndex,
widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
indexOnOtherLine = this._getIndexOnLine(lineIndex - 1, widthBeforeCursor),
textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex);
// return a negative offset
return -this._textLines[lineIndex - 1].length + indexOnOtherLine - textBeforeCursor.length;
},
/**
* find for a given width it founds the matching character.
* @private
*/
_getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor) {
var lineIndex = cursorLocation.lineIndex + 1,
widthOfNextLine = this._getLineWidth(this.ctx, lineIndex),
lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
widthOfCharsOnNextLine = lineLeftOffset,
indexOnNextLine = 0,
_getIndexOnLine: function(lineIndex, width) {
var widthOfLine = this._getLineWidth(this.ctx, lineIndex),
textOnLine = this._textLines[lineIndex],
lineLeftOffset = this._getLineLeftOffset(widthOfLine),
widthOfCharsOnLine = lineLeftOffset,
indexOnLine = 0,
foundMatch;
for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
for (var j = 0, jlen = textOnLine.length; j < jlen; j++) {
var _char = textOnNextLine[j],
var _char = textOnLine[j],
widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnNextLine += widthOfChar;
widthOfCharsOnLine += widthOfChar;
if (widthOfCharsOnNextLine > widthOfCharsOnSameLineBeforeCursor) {
if (widthOfCharsOnLine > width) {
foundMatch = true;
var leftEdge = widthOfCharsOnNextLine - widthOfChar,
rightEdge = widthOfCharsOnNextLine,
offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
var leftEdge = widthOfCharsOnLine - widthOfChar,
rightEdge = widthOfCharsOnLine,
offsetFromLeftEdge = Math.abs(leftEdge - width),
offsetFromRightEdge = Math.abs(rightEdge - width);
indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
indexOnLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
break;
}
@ -327,12 +371,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// reached end
if (!foundMatch) {
indexOnNextLine = textOnNextLine.length;
indexOnLine = textOnLine.length - 1;
}
return indexOnNextLine;
return indexOnLine;
},
/**
* Moves cursor down
* @param {Event} e Event object
@ -344,124 +389,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this._moveCursorUpOrDown('Down', e);
},
/**
* Moves cursor down without keeping selection
* @param {Number} offset
*/
moveCursorDownWithoutShift: function(offset) {
this._selectionDirection = 'right';
this.selectionEnd = this.selectionEnd + offset;
this.selectionStart = this.selectionEnd;
return offset !== 0;
},
/**
* private
*/
swapSelectionPoints: function() {
var swapSel = this.selectionEnd;
this.selectionEnd = this.selectionStart;
this.selectionStart = swapSel;
},
/**
* Moves cursor down while keeping selection
* @param {Number} offset
*/
moveCursorDownWithShift: function(offset) {
if (this.selectionEnd === this.selectionStart) {
this._selectionDirection = 'right';
}
if (this._selectionDirection === 'right') {
this.selectionEnd += offset;
}
else {
this.selectionStart += offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === 'left') {
this.swapSelectionPoints();
this._selectionDirection = 'right';
}
if (this.selectionEnd > this.text.length) {
this.selectionEnd = this.text.length;
}
return offset !== 0;
},
/**
* @param {Event} e Event object
* @param {Boolean} isRight
* @return {Number}
*/
getUpCursorOffset: function(e, isRight) {
var selectionProp = isRight ? this.selectionEnd : this.selectionStart,
cursorLocation = this.get2DCursorLocation(selectionProp),
lineIndex = cursorLocation.lineIndex;
// if on first line, up cursor goes to start of line
if (lineIndex === 0 || e.metaKey || e.keyCode === 33) {
return selectionProp;
}
var textOnSameLineBeforeCursor = this._textLines[lineIndex].slice(0, cursorLocation.charIndex),
textOnPreviousLine = this._textLines[lineIndex - 1] || '',
_char,
widthOfSameLineBeforeCursor = this._getLineWidth(this.ctx, cursorLocation.lineIndex),
lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
widthOfCharsOnSameLineBeforeCursor = lineLeftOffset;
for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
_char = textOnSameLineBeforeCursor[i];
widthOfCharsOnSameLineBeforeCursor += this._getWidthOfChar(this.ctx, _char, lineIndex, i);
}
var indexOnPrevLine = this._getIndexOnPrevLine(
cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor);
return textOnPreviousLine.length - indexOnPrevLine + textOnSameLineBeforeCursor.length;
},
/**
* @private
*/
_getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor) {
var lineIndex = cursorLocation.lineIndex - 1,
widthOfPreviousLine = this._getLineWidth(this.ctx, lineIndex),
lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
widthOfCharsOnPreviousLine = lineLeftOffset,
indexOnPrevLine = 0,
foundMatch;
for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
var _char = textOnPreviousLine[j],
widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
widthOfCharsOnPreviousLine += widthOfChar;
if (widthOfCharsOnPreviousLine > widthOfCharsOnSameLineBeforeCursor) {
foundMatch = true;
var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
rightEdge = widthOfCharsOnPreviousLine,
offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
break;
}
}
// reached end
if (!foundMatch) {
indexOnPrevLine = textOnPreviousLine.length - 1;
}
return indexOnPrevLine;
},
/**
* Moves cursor up
* @param {Event} e Event object
@ -479,16 +406,18 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
_moveCursorUpOrDown: function(direction, e) {
// getUpCursorOffset
// getDownCursorOffset
var action = 'get' + direction + 'CursorOffset',
moveAction = 'moveCursor' + direction,
offset = this[action](e, this._selectionDirection === 'right');
if (e.shiftKey) {
moveAction += 'WithShift';
this.moveCursorWithShift(offset);
}
else {
moveAction += 'WithoutShift';
this.moveCursorWithoutShift(offset);
}
if (this[moveAction](offset)) {
if (offset !== 0) {
this.setSelectionInBoundaries();
this.abortCursorAnimation();
this._currentCursorOpacity = 1;
this.initDelayedCursor();
@ -498,23 +427,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
},
/**
* Moves cursor up with shift
* Moves cursor with shift
* @param {Number} offset
*/
moveCursorUpWithShift: function(offset) {
if (this.selectionEnd === this.selectionStart) {
this._selectionDirection = 'left';
}
if (this._selectionDirection === 'right') {
this.selectionEnd -= offset;
}
else {
this.selectionStart -= offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === 'right') {
this.swapSelectionPoints();
this._selectionDirection = 'left';
}
moveCursorWithShift: function(offset) {
var newSelection = this._selectionDirection === 'left'
? this.selectionStart + offset
: this.selectionEnd + offset;
this.setSelectionStartEndWithShift(this.selectionStart, this.selectionEnd, newSelection);
return offset !== 0;
},
@ -522,10 +442,15 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* Moves cursor up without shift
* @param {Number} offset
*/
moveCursorUpWithoutShift: function(offset) {
this._selectionDirection = 'left';
this.selectionStart -= offset;
this.selectionEnd = this.selectionStart;
moveCursorWithoutShift: function(offset) {
if (offset < 0) {
this.selectionStart += offset;
this.selectionEnd = this.selectionStart;
}
else {
this.selectionEnd += offset;
this.selectionStart = this.selectionEnd;
}
return offset !== 0;
},

View file

@ -103,7 +103,7 @@
iText.selectionStart = 28;
iText.selectionEnd = 31;
iText.moveCursorUp({ shiftKey: false});
iText.moveCursorUp({ shiftKey: false });
equal(selection, 1, 'should fire');
equal(iText.selectionStart, 9, 'should move to upper line');
equal(iText.selectionEnd, 9, 'should move to upper line');
@ -111,7 +111,7 @@
iText.selectionStart = 1;
iText.selectionEnd = 4;
iText.moveCursorDown({ shiftKey: false});
iText.moveCursorDown({ shiftKey: false });
equal(selection, 1, 'should fire');
equal(iText.selectionStart, 24, 'should move to down line');
equal(iText.selectionEnd, 24, 'should move to down line');
@ -119,7 +119,7 @@
iText.selectionStart = 28;
iText.selectionEnd = 31;
iText.moveCursorLeft({ shiftKey: false});
iText.moveCursorLeft({ shiftKey: false });
equal(selection, 1, 'should fire');
equal(iText.selectionStart, 28, 'should move to selection Start');
equal(iText.selectionEnd, 28, 'should move to selection Start');