Allow moving the controlbar handle

This also adds emulation of Element.setCapture() as only Firefox
and Internet Explorer/Edge currently supports it.
This commit is contained in:
Samuel Mannehed 2016-09-14 13:09:12 +02:00 committed by Pierre Ossman
parent 65e3d7d6a6
commit 04b399e27d
4 changed files with 212 additions and 7 deletions

View File

@ -178,7 +178,8 @@ input[type=button]:active, select:active {
#noVNC_control_bar_handle {
position: absolute;
right: -15px;
top: 10%;
top: 0;
transform: translateY(35px);
width: 50px;
height: 50px;
z-index: -2;

113
app/ui.js
View File

@ -43,6 +43,10 @@ var UI;
hideKeyboardTimeout: null,
controlbarTimeout: null,
controlbarGrabbed: false,
controlbarDrag: false,
controlbarMouseDownClientY: 0,
controlbarMouseDownOffsetY: 0,
keyboardVisible: false,
isTouchDevice: false,
@ -198,13 +202,19 @@ var UI;
document.getElementById("noVNC_control_bar")
.addEventListener('keypress', UI.activateControlbar);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('click', UI.toggleControlbar);
document.getElementById("noVNC_view_drag_button")
.addEventListener('click', UI.toggleViewDrag);
document.getElementById("noVNC_send_ctrl_alt_del_button")
.addEventListener('click', UI.sendCtrlAltDel);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('mousedown', UI.controlbarHandleMouseDown);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('mouseup', UI.controlbarHandleMouseUp);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('mousemove', UI.dragControlbarHandle);
// resize events aren't available for elements
window.addEventListener('resize', UI.updateControlbarHandle);
},
addTouchSpecificHandlers: function() {
@ -235,6 +245,13 @@ var UI;
document.getElementById("noVNC_control_bar")
.addEventListener('input', UI.activateControlbar);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('touchstart', UI.controlbarHandleMouseDown);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('touchend', UI.controlbarHandleMouseUp);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('touchmove', UI.dragControlbarHandle);
window.addEventListener('load', UI.keyboardinputReset);
},
@ -497,6 +514,96 @@ var UI;
}
},
dragControlbarHandle: function (e) {
if (!UI.controlbarGrabbed) return;
var ptr = Util.getPointerEvent(e);
if (!UI.controlbarDrag) {
// The goal is to trigger on a certain physical width, the
// devicePixelRatio brings us a bit closer but is not optimal.
var dragThreshold = 10 * (window.devicePixelRatio || 1);
var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
if (dragDistance < dragThreshold) return;
UI.controlbarDrag = true;
}
var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
UI.moveControlbarHandle(eventY);
e.preventDefault();
e.stopPropagation();
},
// Move the handle but don't allow any position outside the bounds
moveControlbarHandle: function (posY) {
var handle = document.getElementById("noVNC_control_bar_handle");
var handleHeight = Util.getPosition(handle).height;
var controlbar = document.getElementById("noVNC_control_bar");
var controlbarBounds = Util.getPosition(controlbar);
var controlbarTop = controlbarBounds.y;
var controlbarBottom = controlbarBounds.y + controlbarBounds.height;
var margin = 10;
var viewportY = posY;
// Refuse coordinates outside the control bar
if (viewportY < controlbarTop + margin) {
viewportY = controlbarTop + margin;
} else if (viewportY > controlbarBottom - handleHeight - margin) {
viewportY = controlbarBottom - handleHeight - margin;
}
// Corner case: control bar too small for stable position
if (controlbarBounds.height < (handleHeight + margin * 2)) {
viewportY = controlbarTop + (controlbarBounds.height - handleHeight) / 2;
}
var relativeY = viewportY - controlbarTop;
handle.style.transform = "translateY(" + relativeY + "px)";
},
updateControlbarHandle: function () {
var handle = document.getElementById("noVNC_control_bar_handle");
var pos = Util.getPosition(handle);
UI.moveControlbarHandle(pos.y);
},
controlbarHandleMouseUp: function(e) {
if ((e.type == "mouseup") && (e.button != 0))
return;
// mouseup and mousedown on the same place toggles the controlbar
if (UI.controlbarGrabbed && !UI.controlbarDrag) {
UI.toggleControlbar();
e.preventDefault();
e.stopPropagation();
}
UI.controlbarGrabbed = false;
},
controlbarHandleMouseDown: function(e) {
if ((e.type == "mousedown") && (e.button != 0))
return;
var ptr = Util.getPointerEvent(e);
var handle = document.getElementById("noVNC_control_bar_handle");
var bounds = handle.getBoundingClientRect();
WebUtil.setCapture(handle);
UI.controlbarGrabbed = true;
UI.controlbarDrag = false;
UI.controlbarMouseDownClientY = ptr.clientY;
UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
e.preventDefault();
e.stopPropagation();
},
/* ------^-------
* /VISUAL
* ==============

View File

@ -278,6 +278,99 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
}
};
// Emulate Element.setCapture() when not supported
WebUtil._captureRecursion = false;
WebUtil._captureProxy = function (e) {
// Recursion protection as we'll see our own event
if (WebUtil._captureRecursion) return;
// Clone the event as we cannot dispatch an already dispatched event
var newEv = new e.constructor(e.type, e);
WebUtil._captureRecursion = true;
WebUtil._captureElem.dispatchEvent(newEv);
WebUtil._captureRecursion = false;
// Implicitly release the capture on button release
if ((e.type === "mouseup") || (e.type === "touchend")) {
WebUtil.releaseCapture();
}
};
WebUtil.setCapture = function (elem) {
if (elem.setCapture) {
elem.setCapture();
// IE releases capture on 'click' events which might not trigger
elem.addEventListener('mouseup', WebUtil.releaseCapture);
elem.addEventListener('touchend', WebUtil.releaseCapture);
} else {
// Safari on iOS 9 has a broken constructor for TouchEvent.
// We are fine in this case however, since Safari seems to
// have some sort of implicit setCapture magic anyway.
if (window.TouchEvent !== undefined) {
try {
new TouchEvent("touchstart");
} catch (TypeError) {
return;
}
}
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
if (captureElem === null) {
captureElem = document.createElement("div");
captureElem.id = "noVNC_mouse_capture_elem";
captureElem.style.position = "fixed";
captureElem.style.top = "0px";
captureElem.style.left = "0px";
captureElem.style.width = "100%";
captureElem.style.height = "100%";
captureElem.style.zIndex = 10000;
captureElem.style.display = "none";
document.body.appendChild(captureElem);
captureElem.addEventListener('mousemove', WebUtil._captureProxy);
captureElem.addEventListener('mouseup', WebUtil._captureProxy);
captureElem.addEventListener('touchmove', WebUtil._captureProxy);
captureElem.addEventListener('touchend', WebUtil._captureProxy);
}
WebUtil._captureElem = elem;
captureElem.style.display = null;
// We listen to events on window in order to keep tracking if it
// happens to leave the viewport
window.addEventListener('mousemove', WebUtil._captureProxy);
window.addEventListener('mouseup', WebUtil._captureProxy);
window.addEventListener('touchmove', WebUtil._captureProxy);
window.addEventListener('touchend', WebUtil._captureProxy);
}
};
WebUtil.releaseCapture = function () {
if (document.releaseCapture) {
document.releaseCapture();
} else {
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
WebUtil._captureElem = null;
captureElem.style.display = "none";
window.removeEventListener('mousemove', WebUtil._captureProxy);
window.removeEventListener('mouseup', WebUtil._captureProxy);
window.removeEventListener('touchmove', WebUtil._captureProxy);
window.removeEventListener('touchend', WebUtil._captureProxy);
}
};
// Dynamically load scripts without using document.write()
// Reference: http://unixpapa.com/js/dyna.html
//

View File

@ -204,14 +204,18 @@ Util.getPosition = function(obj) {
'width': objPosition.width, 'height': objPosition.height};
};
Util.getPointerEvent = function (e) {
var evt;
evt = (e ? e : window.event);
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
return evt;
};
// Get mouse event position in DOM element
Util.getEventPosition = function (e, obj, scale) {
"use strict";
var evt, docX, docY, pos;
//if (!e) evt = window.event;
evt = (e ? e : window.event);
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
evt = Util.getPointerEvent(e);
if (evt.pageX || evt.pageY) {
docX = evt.pageX;
docY = evt.pageY;