Merge pull request #1457 from bromagosa/edition-improvements

text editing improvements
dev
Jens Mönig 2016-10-24 09:24:59 +02:00 zatwierdzone przez GitHub
commit 31aed55227
2 zmienionych plików z 215 dodań i 35 usunięć

Wyświetl plik

@ -7545,7 +7545,6 @@ InputSlotMorph.prototype.mouseDownLeft = function (pos) {
this.escalateEvent('mouseDownLeft', pos); this.escalateEvent('mouseDownLeft', pos);
} else { } else {
this.contents().edit(); this.contents().edit();
this.contents().selectAll();
} }
}; };
@ -7556,7 +7555,6 @@ InputSlotMorph.prototype.mouseClickLeft = function (pos) {
this.dropDownMenu(); this.dropDownMenu();
} else { } else {
this.contents().edit(); this.contents().edit();
this.contents().selectAll();
} }
}; };

Wyświetl plik

@ -1245,6 +1245,11 @@ function fontHeight(height) {
return minHeight * 1.2; // assuming 1/5 font size for ascenders return minHeight * 1.2; // assuming 1/5 font size for ascenders
} }
function isWordChar(aCharacter) {
// can't use \b or \w because they ignore diacritics
return aCharacter.match(/[A-zÀ-ÿ0-9]/);
}
function newCanvas(extentPoint, nonRetina) { function newCanvas(extentPoint, nonRetina) {
// answer a new empty instance of Canvas, don't display anywhere // answer a new empty instance of Canvas, don't display anywhere
// nonRetina - optional Boolean "false" // nonRetina - optional Boolean "false"
@ -4986,6 +4991,9 @@ CursorMorph.prototype.initializeClipboardHandler = function () {
'keydown', 'keydown',
function (event) { function (event) {
myself.processKeyDown(event); myself.processKeyDown(event);
if (event.shiftKey) {
myself.world().currentKey = 16;
}
this.value = myself.target.selection(); this.value = myself.target.selection();
this.select(); this.select();
@ -4998,7 +5006,15 @@ CursorMorph.prototype.initializeClipboardHandler = function () {
}, },
false false
); );
this.clipboardHandler.addEventListener(
'keyup',
function (event) {
myself.world().currentKey = null;
},
false
);
this.clipboardHandler.addEventListener( this.clipboardHandler.addEventListener(
'input', 'input',
function (event) { function (event) {
@ -5056,28 +5072,47 @@ CursorMorph.prototype.processKeyPress = function (event) {
CursorMorph.prototype.processKeyDown = function (event) { CursorMorph.prototype.processKeyDown = function (event) {
// this.inspectKeyEvent(event); // this.inspectKeyEvent(event);
var shift = event.shiftKey; var shift = event.shiftKey,
wordNavigation = event.ctrlKey || event.altKey,
selecting = this.target.selection().length > 0;
this.keyDownEventUsed = false; this.keyDownEventUsed = false;
if (event.ctrlKey && (!event.altKey)) { if (event.ctrlKey && (!event.altKey)) {
this.ctrl(event.keyCode, event.shiftKey); this.ctrl(event.keyCode, event.shiftKey);
// notify target's parent of key event // notify target's parent of key event
this.target.escalateEvent('reactToKeystroke', event); this.target.escalateEvent('reactToKeystroke', event);
return;
} }
if (event.metaKey) { if (event.metaKey) {
this.cmd(event.keyCode, event.shiftKey); this.cmd(event.keyCode, event.shiftKey);
// notify target's parent of key event // notify target's parent of key event
this.target.escalateEvent('reactToKeystroke', event); this.target.escalateEvent('reactToKeystroke', event);
return;
} }
switch (event.keyCode) { switch (event.keyCode) {
case 37: case 37:
this.goLeft(shift); if (selecting && !shift && !wordNavigation) {
this.gotoSlot(Math.min(this.target.startMark, this.target.endMark));
this.target.clearSelection();
} else {
this.goLeft(
shift,
wordNavigation ?
this.slot - this.target.previousWordFrom(this.slot)
: 1);
}
this.keyDownEventUsed = true; this.keyDownEventUsed = true;
break; break;
case 39: case 39:
this.goRight(shift); if (selecting && !shift && !wordNavigation) {
this.gotoSlot(Math.max(this.target.startMark, this.target.endMark));
this.target.clearSelection();
} else {
this.goRight(
shift,
wordNavigation ?
this.target.nextWordFrom(this.slot) - this.slot
: 1);
}
this.keyDownEventUsed = true; this.keyDownEventUsed = true;
break; break;
case 38: case 38:
@ -5168,9 +5203,9 @@ CursorMorph.prototype.gotoSlot = function (slot) {
} }
}; };
CursorMorph.prototype.goLeft = function (shift) { CursorMorph.prototype.goLeft = function (shift, howMany) {
this.updateSelection(shift); this.updateSelection(shift);
this.gotoSlot(this.slot - 1); this.gotoSlot(this.slot - (howMany || 1));
this.updateSelection(shift); this.updateSelection(shift);
}; };
@ -5213,7 +5248,7 @@ CursorMorph.prototype.gotoPos = function (aPoint) {
CursorMorph.prototype.updateSelection = function (shift) { CursorMorph.prototype.updateSelection = function (shift) {
if (shift) { if (shift) {
if (!this.target.endMark && !this.target.startMark) { if (isNil(this.target.endMark) && isNil(this.target.startMark)) {
this.target.startMark = this.slot; this.target.startMark = this.slot;
this.target.endMark = this.slot; this.target.endMark = this.slot;
} else if (this.target.endMark !== this.slot) { } else if (this.target.endMark !== this.slot) {
@ -7793,7 +7828,7 @@ StringMorph.prototype.renderWithBlanks = function (context, startX, y) {
}); });
}; };
// StringMorph mesuring: // StringMorph measuring:
StringMorph.prototype.slotPosition = function (slot) { StringMorph.prototype.slotPosition = function (slot) {
// answer the position point of the given index ("slot") // answer the position point of the given index ("slot")
@ -7818,8 +7853,10 @@ StringMorph.prototype.slotPosition = function (slot) {
}; };
StringMorph.prototype.slotAt = function (aPoint) { StringMorph.prototype.slotAt = function (aPoint) {
// answer the slot (index) closest to the given point // answer the slot (index) closest to the given point taking
// in account how far from the middle of the character it is,
// so the cursor can be moved accordingly // so the cursor can be moved accordingly
var txt = this.isPassword ? var txt = this.isPassword ?
this.password('*', this.text.length) : this.text, this.password('*', this.text.length) : this.text,
idx = 0, idx = 0,
@ -7837,7 +7874,14 @@ StringMorph.prototype.slotAt = function (aPoint) {
} }
} }
} }
return idx - 1;
// see where our click fell with respect to the middle of the char
if (aPoint.x - this.left() >
charX - context.measureText(txt[idx - 1]).width / 2) {
return idx;
} else {
return idx - 1;
}
}; };
StringMorph.prototype.upFrom = function (slot) { StringMorph.prototype.upFrom = function (slot) {
@ -7860,6 +7904,41 @@ StringMorph.prototype.endOfLine = function () {
return this.text.length; return this.text.length;
}; };
StringMorph.prototype.previousWordFrom = function (aSlot) {
// answer the slot (index) slots indicating the position of the
// previous word to the left of aSlot
var index = aSlot - 1;
// while the current character is non-word one, we skip it, so that
// if we are in the middle of a non-alphanumeric sequence, we'll get
// right to the beginning of the previous word
while (index > 0 && !isWordChar(this.text[index])) {
index -= 1
}
// while the current character is a word one, we skip it until we
// find the beginning of the current word
while (index > 0 && isWordChar(this.text[index - 1])) {
index -= 1
}
return index;
};
StringMorph.prototype.nextWordFrom = function (aSlot) {
var index = aSlot;
while (index < this.endOfLine() && !isWordChar(this.text[index])) {
index += 1
}
while (index < this.endOfLine() && isWordChar(this.text[index])) {
index += 1
}
return index;
};
StringMorph.prototype.rawHeight = function () { StringMorph.prototype.rawHeight = function () {
// answer my corrected fontSize // answer my corrected fontSize
return this.height() / 1.2; return this.height() / 1.2;
@ -8025,13 +8104,13 @@ StringMorph.prototype.selectionStartSlot = function () {
StringMorph.prototype.clearSelection = function () { StringMorph.prototype.clearSelection = function () {
if (!this.currentlySelecting && if (!this.currentlySelecting &&
this.startMark === 0 && isNil(this.startMark) &&
this.endMark === 0) { isNil(this.endMark)) {
return; return;
} }
this.currentlySelecting = false; this.currentlySelecting = false;
this.startMark = 0; this.startMark = null;
this.endMark = 0; this.endMark = null;
this.drawNew(); this.drawNew();
this.changed(); this.changed();
}; };
@ -8047,8 +8126,13 @@ StringMorph.prototype.deleteSelection = function () {
}; };
StringMorph.prototype.selectAll = function () { StringMorph.prototype.selectAll = function () {
var cursor;
if (this.isEditable) { if (this.isEditable) {
this.startMark = 0; this.startMark = 0;
cursor = this.root().cursor;
if (cursor) {
cursor.gotoSlot(this.text.length);
}
this.endMark = this.text.length; this.endMark = this.text.length;
this.drawNew(); this.drawNew();
this.changed(); this.changed();
@ -8056,13 +8140,31 @@ StringMorph.prototype.selectAll = function () {
}; };
StringMorph.prototype.mouseDownLeft = function (pos) { StringMorph.prototype.mouseDownLeft = function (pos) {
if (this.isEditable) { if (this.world().currentKey === 16) {
this.shiftClick(pos);
} else if (this.isEditable) {
this.clearSelection(); this.clearSelection();
} else { } else {
this.escalateEvent('mouseDownLeft', pos); this.escalateEvent('mouseDownLeft', pos);
} }
}; };
StringMorph.prototype.shiftClick = function (pos) {
var cursor = this.root().cursor;
if (cursor) {
if (!this.startMark) {
this.startMark = cursor.slot;
}
cursor.gotoPos(pos);
this.endMark = cursor.slot;
this.drawNew();
this.changed();
}
this.currentlySelecting = false;
this.escalateEvent('mouseDownLeft', pos);
};
StringMorph.prototype.mouseClickLeft = function (pos) { StringMorph.prototype.mouseClickLeft = function (pos) {
var cursor; var cursor;
if (this.isEditable) { if (this.isEditable) {
@ -8079,18 +8181,79 @@ StringMorph.prototype.mouseClickLeft = function (pos) {
} }
}; };
StringMorph.prototype.mouseDoubleClick = function (pos) {
// selects the word at pos
// if there is no word, we select whatever is between
// the previous and next words
var slot = this.slotAt(pos);
if (this.isEditable) {
this.edit();
if (slot === this.text.length) {
slot -= 1;
}
if (isWordChar(this.text[slot])) {
this.selectWordAt(slot);
} else {
this.selectBetweenWordsAt(slot);
}
} else {
this.escalateEvent('mouseDoubleClick', pos);
}
};
StringMorph.prototype.selectWordAt = function (slot) {
var cursor = this.root().cursor;
if (slot === 0 || isWordChar(this.text[slot - 1])) {
cursor.gotoSlot(this.previousWordFrom(slot));
this.startMark = cursor.slot;
this.endMark = this.nextWordFrom(cursor.slot);
} else {
cursor.gotoSlot(slot);
this.startMark = slot;
this.endMark = this.nextWordFrom(slot);
}
this.drawNew();
this.changed();
};
StringMorph.prototype.selectBetweenWordsAt = function (slot) {
var cursor = this.root().cursor;
cursor.gotoSlot(this.nextWordFrom(this.previousWordFrom(slot)));
this.startMark = cursor.slot;
this.endMark = cursor.slot;
while (this.endMark < this.text.length
&& !isWordChar(this.text[this.endMark])) {
this.endMark += 1
}
this.drawNew();
this.changed();
};
StringMorph.prototype.enableSelecting = function () { StringMorph.prototype.enableSelecting = function () {
this.mouseDownLeft = function (pos) { this.mouseDownLeft = function (pos) {
var crs = this.root().cursor, var crs = this.root().cursor,
already = crs ? crs.target === this : false; already = crs ? crs.target === this : false;
this.clearSelection(); if (this.world().currentKey === 16) {
if (this.isEditable && (!this.isDraggable)) { this.shiftClick(pos);
this.edit(); } else {
this.root().cursor.gotoPos(pos); this.clearSelection();
this.startMark = this.slotAt(pos); if (this.isEditable && (!this.isDraggable)) {
this.endMark = this.startMark; this.edit();
this.currentlySelecting = true; this.root().cursor.gotoPos(pos);
if (!already) {this.escalateEvent('mouseDownLeft', pos); } this.startMark = this.slotAt(pos);
this.endMark = this.startMark;
this.currentlySelecting = true;
if (!already) {this.escalateEvent('mouseDownLeft', pos); }
}
} }
}; };
this.mouseMove = function (pos) { this.mouseMove = function (pos) {
@ -8410,8 +8573,10 @@ TextMorph.prototype.slotPosition = function (slot) {
}; };
TextMorph.prototype.slotAt = function (aPoint) { TextMorph.prototype.slotAt = function (aPoint) {
// answer the slot (index) closest to the given point // answer the slot (index) closest to the given point taking
// in account how far from the middle of the character it is,
// so the cursor can be moved accordingly // so the cursor can be moved accordingly
var charX = 0, var charX = 0,
row = 0, row = 0,
col = 0, col = 0,
@ -8423,11 +8588,19 @@ TextMorph.prototype.slotAt = function (aPoint) {
row += 1; row += 1;
} }
row = Math.max(row, 1); row = Math.max(row, 1);
while (aPoint.x - this.left() > charX) { while (aPoint.x - this.left() > charX) {
charX += context.measureText(this.lines[row - 1][col]).width; charX += context.measureText(this.lines[row - 1][col]).width;
col += 1; col += 1;
} }
return this.lineSlots[Math.max(row - 1, 0)] + col - 1;
// see where our click fell with respect to the middle of the char
if (aPoint.x - this.left() >
charX - context.measureText(this.lines[row - 1][col]).width / 2) {
return this.lineSlots[Math.max(row - 1, 0)] + col;
} else {
return this.lineSlots[Math.max(row - 1, 0)] + col - 1;
}
}; };
TextMorph.prototype.upFrom = function (slot) { TextMorph.prototype.upFrom = function (slot) {
@ -8469,6 +8642,10 @@ TextMorph.prototype.endOfLine = function (slot) {
this.lines[this.columnRow(slot).y].length - 1; this.lines[this.columnRow(slot).y].length - 1;
}; };
TextMorph.prototype.previousWordFrom = StringMorph.prototype.previousWordFrom;
TextMorph.prototype.nextWordFrom = StringMorph.prototype.nextWordFrom;
// TextMorph editing: // TextMorph editing:
TextMorph.prototype.edit = StringMorph.prototype.edit; TextMorph.prototype.edit = StringMorph.prototype.edit;
@ -8486,8 +8663,17 @@ TextMorph.prototype.selectAll = StringMorph.prototype.selectAll;
TextMorph.prototype.mouseDownLeft = StringMorph.prototype.mouseDownLeft; TextMorph.prototype.mouseDownLeft = StringMorph.prototype.mouseDownLeft;
TextMorph.prototype.shiftClick = StringMorph.prototype.shiftClick;
TextMorph.prototype.mouseClickLeft = StringMorph.prototype.mouseClickLeft; TextMorph.prototype.mouseClickLeft = StringMorph.prototype.mouseClickLeft;
TextMorph.prototype.mouseDoubleClick = StringMorph.prototype.mouseDoubleClick;
TextMorph.prototype.selectWordAt = StringMorph.prototype.selectWordAt;
TextMorph.prototype.selectBetweenWordsAt
= StringMorph.prototype.selectBetweenWordsAt;
TextMorph.prototype.enableSelecting = StringMorph.prototype.enableSelecting; TextMorph.prototype.enableSelecting = StringMorph.prototype.enableSelecting;
TextMorph.prototype.disableSelecting = StringMorph.prototype.disableSelecting; TextMorph.prototype.disableSelecting = StringMorph.prototype.disableSelecting;
@ -10569,7 +10755,6 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
this.broken = []; this.broken = [];
this.hand = new HandMorph(this); this.hand = new HandMorph(this);
this.keyboardReceiver = null; this.keyboardReceiver = null;
this.lastEditedText = null;
this.cursor = null; this.cursor = null;
this.activeMenu = null; this.activeMenu = null;
this.activeHandle = null; this.activeHandle = null;
@ -11334,9 +11519,6 @@ WorldMorph.prototype.edit = function (aStringOrTextMorph) {
if (this.cursor) { if (this.cursor) {
this.cursor.destroy(); this.cursor.destroy();
} }
if (this.lastEditedText) {
this.lastEditedText.clearSelection();
}
this.cursor = new CursorMorph(aStringOrTextMorph); this.cursor = new CursorMorph(aStringOrTextMorph);
aStringOrTextMorph.parent.add(this.cursor); aStringOrTextMorph.parent.add(this.cursor);
this.keyboardReceiver = this.cursor; this.keyboardReceiver = this.cursor;
@ -11399,10 +11581,10 @@ WorldMorph.prototype.slide = function (aStringOrTextMorph) {
WorldMorph.prototype.stopEditing = function () { WorldMorph.prototype.stopEditing = function () {
if (this.cursor) { if (this.cursor) {
this.lastEditedText = this.cursor.target; this.cursor.target.escalateEvent('reactToEdit', this.cursor.target);
this.cursor.target.clearSelection();
this.cursor.destroy(); this.cursor.destroy();
this.cursor = null; this.cursor = null;
this.lastEditedText.escalateEvent('reactToEdit', this.lastEditedText);
} }
this.keyboardReceiver = null; this.keyboardReceiver = null;
if (this.virtualKeyboard) { if (this.virtualKeyboard) {