Handle triple click selection
Double-clicking selects the word underneath it on OS X and Linux text editors. Triple-clicking selects the whole line. Selected line can be copied or commented on. Feature: Issue 5375 Change-Id: I79fff57e7dbfde18ab741a3a67b61869fb52cecf
This commit is contained in:
@@ -94,13 +94,52 @@
|
||||
}
|
||||
},
|
||||
|
||||
_normalizeRange: function(range) {
|
||||
range = GrRangeNormalizer.normalize(range);
|
||||
return {
|
||||
start: this._normalizeSelectionSide(range.startContainer,
|
||||
range.startOffset),
|
||||
end: this._normalizeSelectionSide(range.endContainer, range.endOffset),
|
||||
};
|
||||
_normalizeRange: function(domRange) {
|
||||
var range = GrRangeNormalizer.normalize(domRange);
|
||||
return this._fixTripleClickSelection({
|
||||
start: this._normalizeSelectionSide(
|
||||
range.startContainer, range.startOffset),
|
||||
end: this._normalizeSelectionSide(
|
||||
range.endContainer, range.endOffset),
|
||||
}, domRange);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adjust triple click selection for the whole line.
|
||||
* domRange.endContainer may be one of the following:
|
||||
* 1) 0 offset at right column's line number cell, or
|
||||
* 2) 0 offset at left column's line number at the next line.
|
||||
* Case 1 means left column was triple clicked.
|
||||
* Case 2 means right column or unified view triple clicked.
|
||||
* @param {!Object} range Normalized range, ie column/line numbers
|
||||
* @param {!Range} domRange DOM Range object
|
||||
* @return {!Object} fixed normalized range
|
||||
*/
|
||||
_fixTripleClickSelection: function(range, domRange) {
|
||||
var start = range.start;
|
||||
var end = range.end;
|
||||
var endsAtOtherSideLineNum =
|
||||
domRange.endOffset === 0 &&
|
||||
domRange.endContainer.nodeName === 'TD' &&
|
||||
(domRange.endContainer.classList.contains('left') ||
|
||||
domRange.endContainer.classList.contains('right'));
|
||||
var endsOnOtherSideStart = endsAtOtherSideLineNum ||
|
||||
end &&
|
||||
end.column === 0 &&
|
||||
end.line === start.line &&
|
||||
end.side != start.side;
|
||||
if (endsOnOtherSideStart || endsAtOtherSideLineNum) {
|
||||
// Selection ends at the beginning of the next line.
|
||||
// Move the selection to the end of the previous line.
|
||||
range.end = {
|
||||
node: start.node,
|
||||
column: this._getLength(
|
||||
domRange.cloneContents().querySelector('.contentText')),
|
||||
side: start.side,
|
||||
line: start.line,
|
||||
};
|
||||
}
|
||||
return range;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@@ -26,35 +26,42 @@ limitations under the License.
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<style>
|
||||
.tab-indicator:before {
|
||||
color: #C62828;
|
||||
/* >> character */
|
||||
content: '\00BB';
|
||||
}
|
||||
</style>
|
||||
<gr-diff-highlight>
|
||||
<table id="diffTable">
|
||||
|
||||
<tbody class="section both">
|
||||
<tr class="diff-row side-by-side" left-type="both" right-type="both">
|
||||
<td class="left lineNum" data-value="138">138</td>
|
||||
<td class="left lineNum" data-value="138"></td>
|
||||
<td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
|
||||
<td class="right lineNum" data-value="119">119</td>
|
||||
<td class="right lineNum" data-value="119"></td>
|
||||
<td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody class="section delta">
|
||||
<tr class="diff-row side-by-side" left-type="remove" right-type="add">
|
||||
<td class="left lineNum" data-value="140">140</td>
|
||||
<td class="left lineNum" data-value="140"></td>
|
||||
<!-- Next tag is formatted to eliminate zero-length text nodes. -->
|
||||
<td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab withIndicator" style="tab-size:8;"></span></hl>udiam, <hl>quid</hl> sit, <span class="tab withIndicator" style="tab-size:8;"></span>quod <hl>Epicurum</hl></div><gr-diff-comment-thread>
|
||||
<td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;"> </span>quod <hl>Epicurum</hl></div><gr-diff-comment-thread>
|
||||
[Yet another random diff thread content here]
|
||||
</gr-diff-comment-thread></td>
|
||||
<td class="right lineNum" data-value="120">120</td>
|
||||
<td class="right lineNum" data-value="120"></td>
|
||||
<!-- Next tag is formatted to eliminate zero-length text nodes. -->
|
||||
<td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab withIndicator" style="tab-size:8;"></span></hl> otiosum, <span class="tab withIndicator" style="tab-size:8;"></span> audiam, sit, quod</div></td>
|
||||
<td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl> otiosum, <span class="tab-indicator" style="tab-size:8;"> </span> audiam, sit, quod</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody class="section both">
|
||||
<tr class="diff-row side-by-side" left-type="both" right-type="both">
|
||||
<td class="left lineNum" data-value="141"></td>
|
||||
<td class="content both"><div class="contentText">nam et<hl><span class="tab withIndicator" style="tab-size:8;"> </span></hl>complectitur<span class="tab withIndicator" style="tab-size:8;"> </span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
|
||||
<td class="content both"><div class="contentText">nam et<hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>complectitur<span class="tab-indicator" style="tab-size:8;"> </span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
|
||||
<td class="right lineNum" data-value="130"></td>
|
||||
<td class="content both"><div class="contentText">nam et complectitur verbis, quod vult, et dicit plane, quod intellegam;</div></td>
|
||||
</tr>
|
||||
@@ -95,7 +102,7 @@ limitations under the License.
|
||||
<td class="left lineNum" data-value="165"></td>
|
||||
<td class="content both"><div class="contentText">in physicis, quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
|
||||
<td class="right lineNum" data-value="147"></td>
|
||||
<td class="content both"><div class="contentText">in physicis, <hl><span class="tab withIndicator" style="tab-size:8;"> </span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
|
||||
<td class="content both"><div class="contentText">in physicis, <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -121,7 +128,7 @@ limitations under the License.
|
||||
|
||||
setup(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
element = fixture('basic');
|
||||
element = fixture('basic')[1];
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -290,7 +297,7 @@ limitations under the License.
|
||||
startLine: 119,
|
||||
startChar: 10,
|
||||
endLine: 120,
|
||||
endChar: 34,
|
||||
endChar: 36,
|
||||
});
|
||||
assert.equal(getActionSide(), 'right');
|
||||
});
|
||||
@@ -352,7 +359,7 @@ limitations under the License.
|
||||
startLine: 140,
|
||||
startChar: 2,
|
||||
endLine: 140,
|
||||
endChar: 60,
|
||||
endChar: 61,
|
||||
});
|
||||
assert.equal(getActionSide(), 'left');
|
||||
});
|
||||
@@ -361,7 +368,7 @@ limitations under the License.
|
||||
var contentText = stubContent(140, 'left');
|
||||
var contentTd = contentText.parentElement;
|
||||
|
||||
emulateSelection(contentTd.previousElementSibling.firstChild, 2,
|
||||
emulateSelection(contentTd.previousElementSibling, 0,
|
||||
contentText.firstChild, 2);
|
||||
assert.isFalse(element.isRangeSelected());
|
||||
});
|
||||
@@ -389,7 +396,7 @@ limitations under the License.
|
||||
assert.isTrue(element.isRangeSelected());
|
||||
assert.deepEqual(getActionRange(), {
|
||||
startLine: 140,
|
||||
startChar: 81,
|
||||
startChar: 83,
|
||||
endLine: 141,
|
||||
endChar: 4,
|
||||
});
|
||||
@@ -406,13 +413,14 @@ limitations under the License.
|
||||
startLine: 140,
|
||||
startChar: 4,
|
||||
endLine: 140,
|
||||
endChar: 81,
|
||||
endChar: 83,
|
||||
});
|
||||
assert.equal(getActionSide(), 'left');
|
||||
});
|
||||
|
||||
test('starts in context element', function() {
|
||||
var contextControl = diff.querySelector('.contextControl');
|
||||
var contextControl =
|
||||
diff.querySelector('.contextControl').querySelector('gr-button');
|
||||
var content = stubContent(146, 'right');
|
||||
emulateSelection(contextControl, 0, content.firstChild, 7);
|
||||
// TODO (viktard): Select nearest line.
|
||||
@@ -420,9 +428,10 @@ limitations under the License.
|
||||
});
|
||||
|
||||
test('ends in context element', function() {
|
||||
var contextControl = diff.querySelector('.contextControl');
|
||||
var contextControl =
|
||||
diff.querySelector('.contextControl').querySelector('gr-button');
|
||||
var content = stubContent(141, 'left');
|
||||
emulateSelection(content.firstChild, 2, contextControl, 0);
|
||||
emulateSelection(content.firstChild, 2, contextControl, 1);
|
||||
// TODO (viktard): Select nearest line.
|
||||
assert.isFalse(element.isRangeSelected());
|
||||
});
|
||||
@@ -459,13 +468,13 @@ limitations under the License.
|
||||
var content = stubContent(140, 'left');
|
||||
emulateSelection(
|
||||
content.querySelectorAll('hl')[3], 0,
|
||||
content.querySelectorAll('span')[1], 0);
|
||||
content.querySelectorAll('span')[1].nextSibling, 1);
|
||||
assert.isTrue(element.isRangeSelected());
|
||||
assert.deepEqual(getActionRange(), {
|
||||
startLine: 140,
|
||||
startChar: 51,
|
||||
endLine: 140,
|
||||
endChar: 68,
|
||||
endChar: 71,
|
||||
});
|
||||
assert.equal(getActionSide(), 'left');
|
||||
});
|
||||
@@ -485,7 +494,7 @@ limitations under the License.
|
||||
var content = stubContent(140, 'left');
|
||||
var child = content.lastChild.lastChild;
|
||||
var result = GrRangeNormalizer._getTextOffset(content, child);
|
||||
assert.equal(result, 73);
|
||||
assert.equal(result, 75);
|
||||
content = stubContent(146, 'right');
|
||||
child = content.lastChild;
|
||||
result = GrRangeNormalizer._getTextOffset(content, child);
|
||||
@@ -496,6 +505,50 @@ limitations under the License.
|
||||
// TODO (viktard): Empty lines in selection end.
|
||||
// TODO (viktard): Only empty lines selected.
|
||||
// TODO (viktard): Unified mode.
|
||||
|
||||
suite('triple click', function() {
|
||||
test('_fixTripleClickSelection', function() {
|
||||
var fakeRange = {
|
||||
startContainer: '',
|
||||
startOffset: '',
|
||||
endContainer: '',
|
||||
endOffset: ''
|
||||
};
|
||||
var fixedRange = {};
|
||||
sandbox.stub(GrRangeNormalizer, 'normalize').returns(fakeRange);
|
||||
sandbox.stub(element, '_normalizeSelectionSide');
|
||||
sandbox.stub(element, '_fixTripleClickSelection').returns(fixedRange);
|
||||
assert.strictEqual(element._normalizeRange({}), fixedRange);
|
||||
assert.isTrue(element._fixTripleClickSelection.called);
|
||||
});
|
||||
|
||||
test('left pane', function() {
|
||||
var startNode = stubContent(138, 'left');
|
||||
var endNode =
|
||||
stubContent(119, 'right').parentElement.previousElementSibling;
|
||||
builder.getLineNumberByChild.withArgs(endNode).returns(119);
|
||||
emulateSelection(startNode, 0, endNode, 0);
|
||||
assert.deepEqual(getActionRange(), {
|
||||
startLine: 138,
|
||||
startChar: 0,
|
||||
endLine: 138,
|
||||
endChar: 63,
|
||||
});
|
||||
});
|
||||
|
||||
test('right pane', function() {
|
||||
var startNode = stubContent(119, 'right');
|
||||
var endNode =
|
||||
stubContent(140, 'left').parentElement.previousElementSibling;
|
||||
emulateSelection(startNode, 0, endNode, 0);
|
||||
assert.deepEqual(getActionRange(), {
|
||||
startLine: 119,
|
||||
startChar: 0,
|
||||
endLine: 119,
|
||||
endChar: 63,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user