Merge pull request #1309 from samhed/disappearing_cursor
Fix disappearing cursor
This commit is contained in:
commit
ffdd0dfeef
|
@ -132,7 +132,8 @@ export default class Cursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMouseLeave(event) {
|
_handleMouseLeave(event) {
|
||||||
this._hideCursor();
|
// Check if we should show the cursor on the element we are leaving to
|
||||||
|
this._updateVisibility(event.relatedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMouseMove(event) {
|
_handleMouseMove(event) {
|
||||||
|
@ -150,6 +151,25 @@ export default class Cursor {
|
||||||
// now and adjust visibility based on that.
|
// now and adjust visibility based on that.
|
||||||
let target = document.elementFromPoint(event.clientX, event.clientY);
|
let target = document.elementFromPoint(event.clientX, event.clientY);
|
||||||
this._updateVisibility(target);
|
this._updateVisibility(target);
|
||||||
|
|
||||||
|
// Captures end with a mouseup but we can't know the event order of
|
||||||
|
// mouseup vs releaseCapture.
|
||||||
|
//
|
||||||
|
// In the cases when releaseCapture comes first, the code above is
|
||||||
|
// enough.
|
||||||
|
//
|
||||||
|
// In the cases when the mouseup comes first, we need wait for the
|
||||||
|
// browser to flush all events and then check again if the cursor
|
||||||
|
// should be visible.
|
||||||
|
if (this._captureIsActive()) {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
// Refresh the target from elementFromPoint since queued events
|
||||||
|
// might have altered the DOM
|
||||||
|
target = document.elementFromPoint(event.clientX,
|
||||||
|
event.clientY);
|
||||||
|
this._updateVisibility(target);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleTouchStart(event) {
|
_handleTouchStart(event) {
|
||||||
|
@ -189,6 +209,9 @@ export default class Cursor {
|
||||||
// (i.e. are we over the target, or a child of the target without a
|
// (i.e. are we over the target, or a child of the target without a
|
||||||
// different cursor set)
|
// different cursor set)
|
||||||
_shouldShowCursor(target) {
|
_shouldShowCursor(target) {
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Easy case
|
// Easy case
|
||||||
if (target === this._target) {
|
if (target === this._target) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -207,6 +230,11 @@ export default class Cursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateVisibility(target) {
|
_updateVisibility(target) {
|
||||||
|
// When the cursor target has capture we want to show the cursor.
|
||||||
|
// So, if a capture is active - look at the captured element instead.
|
||||||
|
if (this._captureIsActive()) {
|
||||||
|
target = document.capturedElem;
|
||||||
|
}
|
||||||
if (this._shouldShowCursor(target)) {
|
if (this._shouldShowCursor(target)) {
|
||||||
this._showCursor();
|
this._showCursor();
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,4 +246,9 @@ export default class Cursor {
|
||||||
this._canvas.style.left = this._position.x + "px";
|
this._canvas.style.left = this._position.x + "px";
|
||||||
this._canvas.style.top = this._position.y + "px";
|
this._canvas.style.top = this._position.y + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_captureIsActive() {
|
||||||
|
return document.capturedElem &&
|
||||||
|
document.documentElement.contains(document.capturedElem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ export function stopEvent(e) {
|
||||||
|
|
||||||
// Emulate Element.setCapture() when not supported
|
// Emulate Element.setCapture() when not supported
|
||||||
let _captureRecursion = false;
|
let _captureRecursion = false;
|
||||||
let _captureElem = null;
|
let _elementForUnflushedEvents = null;
|
||||||
|
document.capturedElem = null;
|
||||||
function _captureProxy(e) {
|
function _captureProxy(e) {
|
||||||
// Recursion protection as we'll see our own event
|
// Recursion protection as we'll see our own event
|
||||||
if (_captureRecursion) return;
|
if (_captureRecursion) return;
|
||||||
|
@ -30,7 +31,11 @@ function _captureProxy(e) {
|
||||||
const newEv = new e.constructor(e.type, e);
|
const newEv = new e.constructor(e.type, e);
|
||||||
|
|
||||||
_captureRecursion = true;
|
_captureRecursion = true;
|
||||||
_captureElem.dispatchEvent(newEv);
|
if (document.capturedElem) {
|
||||||
|
document.capturedElem.dispatchEvent(newEv);
|
||||||
|
} else {
|
||||||
|
_elementForUnflushedEvents.dispatchEvent(newEv);
|
||||||
|
}
|
||||||
_captureRecursion = false;
|
_captureRecursion = false;
|
||||||
|
|
||||||
// Avoid double events
|
// Avoid double events
|
||||||
|
@ -48,58 +53,56 @@ function _captureProxy(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Follow cursor style of target element
|
// Follow cursor style of target element
|
||||||
function _captureElemChanged() {
|
function _capturedElemChanged() {
|
||||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
proxyElem.style.cursor = window.getComputedStyle(document.capturedElem).cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _captureObserver = new MutationObserver(_captureElemChanged);
|
const _captureObserver = new MutationObserver(_capturedElemChanged);
|
||||||
|
|
||||||
let _captureIndex = 0;
|
export function setCapture(target) {
|
||||||
|
if (target.setCapture) {
|
||||||
|
|
||||||
export function setCapture(elem) {
|
target.setCapture();
|
||||||
if (elem.setCapture) {
|
document.capturedElem = target;
|
||||||
|
|
||||||
elem.setCapture();
|
|
||||||
|
|
||||||
// IE releases capture on 'click' events which might not trigger
|
// IE releases capture on 'click' events which might not trigger
|
||||||
elem.addEventListener('mouseup', releaseCapture);
|
target.addEventListener('mouseup', releaseCapture);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Release any existing capture in case this method is
|
// Release any existing capture in case this method is
|
||||||
// called multiple times without coordination
|
// called multiple times without coordination
|
||||||
releaseCapture();
|
releaseCapture();
|
||||||
|
|
||||||
let captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
|
|
||||||
if (captureElem === null) {
|
if (proxyElem === null) {
|
||||||
captureElem = document.createElement("div");
|
proxyElem = document.createElement("div");
|
||||||
captureElem.id = "noVNC_mouse_capture_elem";
|
proxyElem.id = "noVNC_mouse_capture_elem";
|
||||||
captureElem.style.position = "fixed";
|
proxyElem.style.position = "fixed";
|
||||||
captureElem.style.top = "0px";
|
proxyElem.style.top = "0px";
|
||||||
captureElem.style.left = "0px";
|
proxyElem.style.left = "0px";
|
||||||
captureElem.style.width = "100%";
|
proxyElem.style.width = "100%";
|
||||||
captureElem.style.height = "100%";
|
proxyElem.style.height = "100%";
|
||||||
captureElem.style.zIndex = 10000;
|
proxyElem.style.zIndex = 10000;
|
||||||
captureElem.style.display = "none";
|
proxyElem.style.display = "none";
|
||||||
document.body.appendChild(captureElem);
|
document.body.appendChild(proxyElem);
|
||||||
|
|
||||||
// This is to make sure callers don't get confused by having
|
// This is to make sure callers don't get confused by having
|
||||||
// our blocking element as the target
|
// our blocking element as the target
|
||||||
captureElem.addEventListener('contextmenu', _captureProxy);
|
proxyElem.addEventListener('contextmenu', _captureProxy);
|
||||||
|
|
||||||
captureElem.addEventListener('mousemove', _captureProxy);
|
proxyElem.addEventListener('mousemove', _captureProxy);
|
||||||
captureElem.addEventListener('mouseup', _captureProxy);
|
proxyElem.addEventListener('mouseup', _captureProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
_captureElem = elem;
|
document.capturedElem = target;
|
||||||
_captureIndex++;
|
|
||||||
|
|
||||||
// Track cursor and get initial cursor
|
// Track cursor and get initial cursor
|
||||||
_captureObserver.observe(elem, {attributes: true});
|
_captureObserver.observe(target, {attributes: true});
|
||||||
_captureElemChanged();
|
_capturedElemChanged();
|
||||||
|
|
||||||
captureElem.style.display = "";
|
proxyElem.style.display = "";
|
||||||
|
|
||||||
// We listen to events on window in order to keep tracking if it
|
// We listen to events on window in order to keep tracking if it
|
||||||
// happens to leave the viewport
|
// happens to leave the viewport
|
||||||
|
@ -112,26 +115,26 @@ export function releaseCapture() {
|
||||||
if (document.releaseCapture) {
|
if (document.releaseCapture) {
|
||||||
|
|
||||||
document.releaseCapture();
|
document.releaseCapture();
|
||||||
|
document.capturedElem = null;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!_captureElem) {
|
if (!document.capturedElem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There might be events already queued, so we need to wait for
|
// There might be events already queued. The event proxy needs
|
||||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
// access to the captured element for these queued events.
|
||||||
window.setTimeout((expected) => {
|
// E.g. contextmenu (right-click) in Microsoft Edge
|
||||||
// Only clear it if it's the expected grab (i.e. no one
|
//
|
||||||
// else has initiated a new grab)
|
// Before removing the capturedElem pointer we save it to a
|
||||||
if (_captureIndex === expected) {
|
// temporary variable that the unflushed events can use.
|
||||||
_captureElem = null;
|
_elementForUnflushedEvents = document.capturedElem;
|
||||||
}
|
document.capturedElem = null;
|
||||||
}, 0, _captureIndex);
|
|
||||||
|
|
||||||
_captureObserver.disconnect();
|
_captureObserver.disconnect();
|
||||||
|
|
||||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.display = "none";
|
proxyElem.style.display = "none";
|
||||||
|
|
||||||
window.removeEventListener('mousemove', _captureProxy);
|
window.removeEventListener('mousemove', _captureProxy);
|
||||||
window.removeEventListener('mouseup', _captureProxy);
|
window.removeEventListener('mouseup', _captureProxy);
|
||||||
|
|
Loading…
Reference in New Issue