Create comment immediately when double-clicking after a line
This assumes that any empty selection of just one line that ends at the start of a line is an after-end-of-line selection. We capture it on mouseup to avoid comments appearing while dragging. Known issues: Occasionally, when the comment is created on an empty line, discarding the comment can trigger another mouseup that creates a new comment. Occasionally the selection of a line hangs around and double-clicking after the end of the line doesn't do anything until the selection is cleared. Change-Id: Ifeea4f25e2ee4d4643766e4bdff31a74fedca68f
This commit is contained in:
@@ -65,14 +65,17 @@
|
|||||||
*
|
*
|
||||||
* @param {Selection} selection A DOM Selection living in the shadow DOM of
|
* @param {Selection} selection A DOM Selection living in the shadow DOM of
|
||||||
* the diff element.
|
* the diff element.
|
||||||
|
* @param {boolean} isMouseUp If true, this is called due to a mouseup
|
||||||
|
* event, in which case we might want to immediately create a comment.
|
||||||
*/
|
*/
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection, isMouseUp) {
|
||||||
// Can't use up or down events to handle selection started and/or ended in
|
// Can't use up or down events to handle selection started and/or ended in
|
||||||
// in comment threads or outside of diff.
|
// in comment threads or outside of diff.
|
||||||
// Debounce removeActionBox to give it a chance to react to click/tap.
|
// Debounce removeActionBox to give it a chance to react to click/tap.
|
||||||
this._removeActionBoxDebounced();
|
this._removeActionBoxDebounced();
|
||||||
this.debounce(
|
this.debounce(
|
||||||
'selectionChange', () => this._handleSelection(selection), 200);
|
'selectionChange', () => this._handleSelection(selection, isMouseUp),
|
||||||
|
200);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getThreadEl(e) {
|
_getThreadEl(e) {
|
||||||
@@ -112,8 +115,12 @@
|
|||||||
|
|
||||||
_indexOfCommentRange(side, range) {
|
_indexOfCommentRange(side, range) {
|
||||||
function rangesEqual(a, b) {
|
function rangesEqual(a, b) {
|
||||||
if (!a && !b) { return true; }
|
if (!a && !b) {
|
||||||
if (!a || !b) { return false; }
|
return true;
|
||||||
|
}
|
||||||
|
if (!a || !b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return a.start_line === b.start_line &&
|
return a.start_line === b.start_line &&
|
||||||
a.start_character === b.start_character &&
|
a.start_character === b.start_character &&
|
||||||
a.end_line === b.end_line &&
|
a.end_line === b.end_line &&
|
||||||
@@ -290,7 +297,7 @@
|
|||||||
actionBox.placeBelow(range);
|
actionBox.placeBelow(range);
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleSelection(selection) {
|
_handleSelection(selection, isMouseUp) {
|
||||||
const normalizedRange = this._getNormalizedRange(selection);
|
const normalizedRange = this._getNormalizedRange(selection);
|
||||||
if (!normalizedRange) {
|
if (!normalizedRange) {
|
||||||
return;
|
return;
|
||||||
@@ -313,6 +320,24 @@
|
|||||||
|
|
||||||
// TODO (viktard): Drop empty first and last lines from selection.
|
// TODO (viktard): Drop empty first and last lines from selection.
|
||||||
|
|
||||||
|
// If this was a mouse-up event, we create a comment immediately if
|
||||||
|
// the selection is from the end of a line to the start of the next line.
|
||||||
|
// Rather than trying to find the line contents, we just check if the
|
||||||
|
// selection is empty to see that it's at the end of a line.
|
||||||
|
// In this case, we select the entire start line.
|
||||||
|
if (isMouseUp && start.line === end.line - 1 && end.column === 0) {
|
||||||
|
const content = domRange.cloneContents().querySelector('.contentText');
|
||||||
|
if (this._getLength(content) === 0) {
|
||||||
|
this.fire('create-range-comment', {side: start.side, range: {
|
||||||
|
start_line: start.line,
|
||||||
|
start_character: 0,
|
||||||
|
end_line: start.line,
|
||||||
|
end_character: start.column,
|
||||||
|
}});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const actionBox = document.createElement('gr-selection-action-box');
|
const actionBox = document.createElement('gr-selection-action-box');
|
||||||
const root = Polymer.dom(this.root);
|
const root = Polymer.dom(this.root);
|
||||||
root.insertBefore(actionBox, root.firstElementChild);
|
root.insertBefore(actionBox, root.firstElementChild);
|
||||||
|
@@ -282,22 +282,38 @@
|
|||||||
_enableSelectionObserver(loggedIn, isAttached) {
|
_enableSelectionObserver(loggedIn, isAttached) {
|
||||||
if (loggedIn && isAttached) {
|
if (loggedIn && isAttached) {
|
||||||
this.listen(document, 'selectionchange', '_handleSelectionChange');
|
this.listen(document, 'selectionchange', '_handleSelectionChange');
|
||||||
|
this.listen(document, 'mouseup', '_handleMouseUp');
|
||||||
} else {
|
} else {
|
||||||
this.unlisten(document, 'selectionchange', '_handleSelectionChange');
|
this.unlisten(document, 'selectionchange', '_handleSelectionChange');
|
||||||
|
this.unlisten(document, 'mouseup', '_handleMouseUp');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleSelectionChange() {
|
_handleSelectionChange() {
|
||||||
|
// Because of shadow DOM selections, we handle the selectionchange here,
|
||||||
|
// and pass the shadow DOM selection into gr-diff-highlight, where the
|
||||||
|
// corresponding range is determined and normalized.
|
||||||
|
const selection = this._getShadowOrDocumentSelection();
|
||||||
|
this.$.highlights.handleSelectionChange(selection, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleMouseUp(e) {
|
||||||
|
// To handle double-click outside of text creating comments, we check on
|
||||||
|
// mouse-up if there's a selection that just covers a line change. We
|
||||||
|
// can't do that on selection change since the user may still be dragging.
|
||||||
|
const selection = this._getShadowOrDocumentSelection();
|
||||||
|
this.$.highlights.handleSelectionChange(selection, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Gets the current selection, preferring the shadow DOM selection. */
|
||||||
|
_getShadowOrDocumentSelection() {
|
||||||
// When using native shadow DOM, the selection returned by
|
// When using native shadow DOM, the selection returned by
|
||||||
// document.getSelection() cannot reference the actual DOM elements making
|
// document.getSelection() cannot reference the actual DOM elements making
|
||||||
// up the diff, because they are in the shadow DOM of the gr-diff element.
|
// up the diff, because they are in the shadow DOM of the gr-diff element.
|
||||||
// For this reason, we handle the selectionchange here, and pass the
|
// This takes the shadow DOM selection if one exists.
|
||||||
// shadow DOM selection into gr-diff-highlight, where the corresponding
|
return this.root.getSelection ?
|
||||||
// range is determined and normalized.
|
|
||||||
const selection = this.root.getSelection ?
|
|
||||||
this.root.getSelection() :
|
this.root.getSelection() :
|
||||||
document.getSelection();
|
document.getSelection();
|
||||||
this.$.highlights.handleSelectionChange(selection);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_observeNodes() {
|
_observeNodes() {
|
||||||
|
Reference in New Issue
Block a user