Skip to content

Right click fix #2241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 10, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions src/components/annotations/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,12 +593,10 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
xcenter + ',' + ycenter + ')'
});
},
doneFn: function(dragged) {
if(dragged) {
Plotly.relayout(gd, update);
var notesBox = document.querySelector('.js-notes-box-panel');
if(notesBox) notesBox.redraw(notesBox.selectedObj);
}
doneFn: function() {
Plotly.relayout(gd, update);
var notesBox = document.querySelector('.js-notes-box-panel');
if(notesBox) notesBox.redraw(notesBox.selectedObj);
}
});
}
Expand Down Expand Up @@ -673,13 +671,11 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {

setCursor(annTextGroupInner, csr);
},
doneFn: function(dragged) {
doneFn: function() {
setCursor(annTextGroupInner);
if(dragged) {
Plotly.relayout(gd, update);
var notesBox = document.querySelector('.js-notes-box-panel');
if(notesBox) notesBox.redraw(notesBox.selectedObj);
}
Plotly.relayout(gd, update);
var notesBox = document.querySelector('.js-notes-box-panel');
if(notesBox) notesBox.redraw(notesBox.selectedObj);
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,10 +584,10 @@ module.exports = function draw(gd, id) {
opts.xanchor, opts.yanchor);
setCursor(container, csr);
},
doneFn: function(dragged) {
doneFn: function() {
setCursor(container);

if(dragged && xf !== undefined && yf !== undefined) {
if(xf !== undefined && yf !== undefined) {
Plotly.restyle(gd,
{'colorbar.x': xf, 'colorbar.y': yf},
getTrace().index);
Expand Down
92 changes: 62 additions & 30 deletions src/components/dragelement/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,44 @@ dragElement.unhoverRaw = unhover.raw;
* - Freezes the cursor: whatever mouse cursor the drag element had when the
* interaction started gets copied to the coverSlip for use until mouseup
*
* If the user executes a drag bigger than MINDRAG, callbacks will fire as:
* prepFn, moveFn (1 or more times), doneFn
* If the user does not drag enough, prepFn and clickFn will fire.
*
* Note: If you cancel contextmenu, clickFn will fire even with a right click
* (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
* gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
* TODO: we should probably turn this into a `config` parameter, so we can fix it
* such that if you *don't* cancel contextmenu, we can prevent partial drags, which
* put you in a weird state.
*
* If the user clicks multiple times quickly, clickFn will fire each time
* but numClicks will increase to help you recognize doubleclicks.
*
* @param {object} options with keys:
* element (required) the DOM element to drag
* prepFn (optional) function(event, startX, startY)
* executed on mousedown
* startX and startY are the clientX and clientY pixel position
* of the mousedown event
* moveFn (optional) function(dx, dy, dragged)
* executed on move
* moveFn (optional) function(dx, dy)
* executed on move, ONLY after we've exceeded MINDRAG
* (we keep executing moveFn if you move back to where you started)
* dx and dy are the net pixel offset of the drag,
* dragged is true/false, has the mouse moved enough to
* constitute a drag
* doneFn (optional) function(dragged, numClicks, e)
* executed on mouseup, or mouseout of window since
* we don't get events after that
* dragged is as in moveFn
* doneFn (optional) function(e)
* executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
* sure that moveFn has been called at least once)
* numClicks is how many clicks we've registered within
* a doubleclick time
* e is the original event
* e is the original mouseup event
* clickFn (optional) function(numClicks, e)
* executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
* has not been called at all)
* numClicks is how many clicks we've registered within
* a doubleclick time
* e is the original mousedown event
*/
dragElement.init = function init(options) {
var gd = options.gd;
Expand All @@ -68,7 +88,9 @@ dragElement.init = function init(options) {
newMouseDownTime,
cursor,
dragCover,
initialTarget;
initialEvent,
initialTarget,
rightClick;

if(!gd._mouseDownTime) gd._mouseDownTime = 0;

Expand All @@ -86,7 +108,8 @@ dragElement.init = function init(options) {
startX = offset[0];
startY = offset[1];
initialTarget = e.target;
var rightClick = e.buttons && e.buttons === 2;
initialEvent = e;
rightClick = (e.buttons && e.buttons === 2) || e.ctrlKey;

newMouseDownTime = (new Date()).getTime();
if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) {
Expand Down Expand Up @@ -133,7 +156,7 @@ dragElement.init = function init(options) {
dragElement.unhover(gd);
}

if(options.moveFn) options.moveFn(dx, dy, gd._dragged);
if(gd._dragged && options.moveFn && !rightClick) options.moveFn(dx, dy);

return Lib.pauseEvent(e);
}
Expand Down Expand Up @@ -164,27 +187,36 @@ dragElement.init = function init(options) {
numClicks = Math.max(numClicks - 1, 1);
}

if(options.doneFn) options.doneFn(gd._dragged, numClicks, e);

if(!gd._dragged) {
var e2;

try {
e2 = new MouseEvent('click', e);
}
catch(err) {
var offset = pointerOffset(e);
e2 = document.createEvent('MouseEvents');
e2.initMouseEvent('click',
e.bubbles, e.cancelable,
e.view, e.detail,
e.screenX, e.screenY,
offset[0], offset[1],
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
e.button, e.relatedTarget);
if(gd._dragged) {
if(options.doneFn) options.doneFn(e);
}
else {
if(options.clickFn) options.clickFn(numClicks, initialEvent);

// If we haven't dragged, this should be a click. But because of the
// coverSlip changing the element, the natural system might not generate one,
// so we need to make our own. But right clicks don't normally generate
// click events, only contextmenu events, which happen on mousedown.
if(!rightClick) {
var e2;

try {
e2 = new MouseEvent('click', e);
}
catch(err) {
var offset = pointerOffset(e);
e2 = document.createEvent('MouseEvents');
e2.initMouseEvent('click',
e.bubbles, e.cancelable,
e.view, e.detail,
e.screenX, e.screenY,
offset[0], offset[1],
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
e.button, e.relatedTarget);
}

initialTarget.dispatchEvent(e2);
}

initialTarget.dispatchEvent(e2);
}

finishDrag(gd);
Expand Down
35 changes: 19 additions & 16 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,25 +334,28 @@ module.exports = function draw(gd) {
xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
},
doneFn: function(dragged, numClicks, e) {
if(dragged && xf !== undefined && yf !== undefined) {
doneFn: function() {
if(xf !== undefined && yf !== undefined) {
Plotly.relayout(gd, {'legend.x': xf, 'legend.y': yf});
} else {
var clickedTrace =
fullLayout._infolayer.selectAll('g.traces').filter(function() {
var bbox = this.getBoundingClientRect();
return (e.clientX >= bbox.left && e.clientX <= bbox.right &&
e.clientY >= bbox.top && e.clientY <= bbox.bottom);
});
if(clickedTrace.size() > 0) {
if(numClicks === 1) {
legend._clickTimeout = setTimeout(function() { handleClick(clickedTrace, gd, numClicks); }, DBLCLICKDELAY);
} else if(numClicks === 2) {
if(legend._clickTimeout) {
clearTimeout(legend._clickTimeout);
}
}
},
clickFn: function(numClicks, e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. This looks so much cleaner by splitting done and click logic. 💯

var clickedTrace =
fullLayout._infolayer.selectAll('g.traces').filter(function() {
var bbox = this.getBoundingClientRect();
return (e.clientX >= bbox.left && e.clientX <= bbox.right &&
e.clientY >= bbox.top && e.clientY <= bbox.bottom);
});
if(clickedTrace.size() > 0) {
if(numClicks === 1) {
legend._clickTimeout = setTimeout(function() {
handleClick(clickedTrace, gd, numClicks);
}, DBLCLICKDELAY);
} else if(numClicks === 2) {
if(legend._clickTimeout) {
clearTimeout(legend._clickTimeout);
}
handleClick(clickedTrace, gd, numClicks);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/components/shapes/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,9 @@ function setupDragElement(gd, shapePath, shapeOptions, index) {
dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
}

function endDrag(dragged) {
function endDrag() {
setCursor(shapePath);
if(dragged) {
Plotly.relayout(gd, update);
}
Plotly.relayout(gd, update);
}

function moveShape(dx, dy) {
Expand Down
Loading