mirror of
https://github.com/overte-org/overte.git
synced 2025-08-11 01:23:17 +02:00
commit
f1f8f6323a
34 changed files with 769 additions and 371 deletions
|
@ -11,9 +11,12 @@
|
||||||
var POLL_FREQUENCY = 500; // ms
|
var POLL_FREQUENCY = 500; // ms
|
||||||
var MAX_WARNINGS = 3;
|
var MAX_WARNINGS = 3;
|
||||||
var numWarnings = 0;
|
var numWarnings = 0;
|
||||||
|
var isKeyboardRaised = false;
|
||||||
|
var isNumericKeyboard = false;
|
||||||
|
var KEYBOARD_HEIGHT = 200;
|
||||||
|
|
||||||
function shouldRaiseKeyboard() {
|
function shouldRaiseKeyboard() {
|
||||||
if (document.activeElement.nodeName == "INPUT" || document.activeElement.nodeName == "TEXTAREA") {
|
if (document.activeElement.nodeName === "INPUT" || document.activeElement.nodeName === "TEXTAREA") {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// check for contenteditable attribute
|
// check for contenteditable attribute
|
||||||
|
@ -27,15 +30,39 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function shouldSetNumeric() {
|
||||||
|
return document.activeElement.type === "number";
|
||||||
|
};
|
||||||
|
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
var event = shouldRaiseKeyboard() ? "_RAISE_KEYBOARD" : "_LOWER_KEYBOARD";
|
var keyboardRaised = shouldRaiseKeyboard();
|
||||||
if (typeof EventBridge != "undefined") {
|
var numericKeyboard = shouldSetNumeric();
|
||||||
EventBridge.emitWebEvent(event);
|
|
||||||
|
if (keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard) {
|
||||||
|
|
||||||
|
if (typeof EventBridge !== "undefined") {
|
||||||
|
EventBridge.emitWebEvent(
|
||||||
|
keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "")) : "_LOWER_KEYBOARD"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
if (numWarnings < MAX_WARNINGS) {
|
if (numWarnings < MAX_WARNINGS) {
|
||||||
console.log("WARNING: no global EventBridge object found");
|
console.log("WARNING: no global EventBridge object found");
|
||||||
numWarnings++;
|
numWarnings++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isKeyboardRaised) {
|
||||||
|
var delta = document.activeElement.getBoundingClientRect().bottom + 10
|
||||||
|
- (document.body.clientHeight - KEYBOARD_HEIGHT);
|
||||||
|
if (delta > 0) {
|
||||||
|
setTimeout(function () {
|
||||||
|
document.body.scrollTop += delta;
|
||||||
|
}, 500); // Allow time for keyboard to be raised in QML.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isKeyboardRaised = keyboardRaised;
|
||||||
|
isNumericKeyboard = numericKeyboard;
|
||||||
|
}
|
||||||
}, POLL_FREQUENCY);
|
}, POLL_FREQUENCY);
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -69,8 +69,12 @@ Window {
|
||||||
|
|
||||||
AddressBarDialog {
|
AddressBarDialog {
|
||||||
id: addressBarDialog
|
id: addressBarDialog
|
||||||
|
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
implicitWidth: backgroundImage.width
|
implicitWidth: backgroundImage.width
|
||||||
implicitHeight: backgroundImage.height
|
implicitHeight: backgroundImage.height + (keyboardRaised ? 200 : 0)
|
||||||
|
|
||||||
// The buttons have their button state changed on hover, so we have to manually fix them up here
|
// The buttons have their button state changed on hover, so we have to manually fix them up here
|
||||||
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
||||||
|
@ -252,8 +256,8 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
width: 938;
|
width: 938
|
||||||
height: 625;
|
height: 625
|
||||||
scale: 0.8 // Reset scale of Window to 1.0 (counteract address bar's scale value of 1.25)
|
scale: 0.8 // Reset scale of Window to 1.0 (counteract address bar's scale value of 1.25)
|
||||||
HifiControls.WebView {
|
HifiControls.WebView {
|
||||||
anchors.fill: parent;
|
anchors.fill: parent;
|
||||||
|
@ -270,6 +274,35 @@ Window {
|
||||||
horizontalCenter: scroll.horizontalCenter;
|
horizontalCenter: scroll.horizontalCenter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual keyboard, letters
|
||||||
|
HifiControls.Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
y: parent.keyboardRaised ? parent.height : 0
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
enabled: parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
y: parent.keyboardRaised ? parent.height : 0
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && parent.punctuationMode
|
||||||
|
enabled: parent.keyboardRaised && parent.punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
|
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
|
import QtWebChannel 1.0
|
||||||
import QtWebEngine 1.2
|
import QtWebEngine 1.2
|
||||||
|
|
||||||
import "controls-uit"
|
import "controls-uit"
|
||||||
|
@ -19,6 +20,9 @@ ScrollingWindow {
|
||||||
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
||||||
property alias url: webview.url
|
property alias url: webview.url
|
||||||
property alias webView: webview
|
property alias webView: webview
|
||||||
|
|
||||||
|
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||||
|
|
||||||
x: 100
|
x: 100
|
||||||
y: 100
|
y: 100
|
||||||
|
|
||||||
|
@ -130,10 +134,11 @@ ScrollingWindow {
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
if (text.indexOf("http") != 0) {
|
if (text.indexOf("http") != 0) {
|
||||||
text = "http://" + text
|
text = "http://" + text;
|
||||||
}
|
}
|
||||||
root.hidePermissionsBar();
|
root.hidePermissionsBar();
|
||||||
webview.url = text
|
root.keyboardRaised = false;
|
||||||
|
webview.url = text;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,32 +202,60 @@ ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebEngineView {
|
WebView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "https://highfidelity.com"
|
url: "https://highfidelity.com"
|
||||||
|
|
||||||
|
property alias eventBridgeWrapper: eventBridgeWrapper
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: eventBridgeWrapper
|
||||||
|
WebChannel.id: "eventBridgeWrapper"
|
||||||
|
property var eventBridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||||
|
|
||||||
|
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: createGlobalEventBridge
|
||||||
|
sourceCode: eventBridgeJavaScriptToInject
|
||||||
|
injectionPoint: WebEngineScript.DocumentCreation
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect when may want to raise and lower keyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: raiseAndLowerKeyboard
|
||||||
|
injectionPoint: WebEngineScript.Deferred
|
||||||
|
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||||
|
|
||||||
anchors.top: buttons.bottom
|
anchors.top: buttons.bottom
|
||||||
anchors.topMargin: 8
|
anchors.topMargin: 8
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
onFeaturePermissionRequested: {
|
||||||
permissionsBar.securityOrigin = securityOrigin;
|
permissionsBar.securityOrigin = securityOrigin;
|
||||||
permissionsBar.feature = feature;
|
permissionsBar.feature = feature;
|
||||||
root.showPermissionsBar();
|
root.showPermissionsBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
||||||
addressBar.text = loadRequest.url
|
addressBar.text = loadRequest.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onIconChanged: {
|
onIconChanged: {
|
||||||
console.log("New icon: " + icon)
|
console.log("New icon: " + icon)
|
||||||
}
|
}
|
||||||
onNewViewRequested: {
|
|
||||||
var component = Qt.createComponent("Browser.qml");
|
|
||||||
var newWindow = component.createObject(desktop);
|
|
||||||
request.openIn(newWindow.webView)
|
|
||||||
}
|
|
||||||
onWindowCloseRequested: {
|
onWindowCloseRequested: {
|
||||||
root.destroy();
|
root.destroy();
|
||||||
}
|
}
|
||||||
|
@ -230,8 +263,6 @@ ScrollingWindow {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
desktop.initWebviewProfileHandlers(webview.profile)
|
desktop.initWebviewProfileHandlers(webview.profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
profile: desktop.browserProfile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // item
|
} // item
|
||||||
|
|
|
@ -66,6 +66,24 @@ Windows.ScrollingWindow {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||||
|
|
||||||
|
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: createGlobalEventBridge
|
||||||
|
sourceCode: eventBridgeJavaScriptToInject
|
||||||
|
injectionPoint: WebEngineScript.DocumentCreation
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect when may want to raise and lower keyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: raiseAndLowerKeyboard
|
||||||
|
injectionPoint: WebEngineScript.Deferred
|
||||||
|
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import "windows"
|
||||||
import "controls-uit"
|
import "controls-uit"
|
||||||
import "styles-uit"
|
import "styles-uit"
|
||||||
|
|
||||||
|
|
||||||
ScrollingWindow {
|
ScrollingWindow {
|
||||||
id: toolWindow
|
id: toolWindow
|
||||||
resizable: true
|
resizable: true
|
||||||
|
@ -47,10 +48,18 @@ ScrollingWindow {
|
||||||
property alias y: toolWindow.y
|
property alias y: toolWindow.y
|
||||||
}
|
}
|
||||||
|
|
||||||
TabView {
|
Item {
|
||||||
id: tabView;
|
id: toolWindowTabViewItem
|
||||||
|
height: pane.scrollHeight
|
||||||
width: pane.contentWidth
|
width: pane.contentWidth
|
||||||
height: pane.scrollHeight // Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
TabView {
|
||||||
|
id: tabView
|
||||||
|
width: pane.contentWidth
|
||||||
|
// Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
|
||||||
|
height: pane.scrollHeight
|
||||||
property int tabCount: 0
|
property int tabCount: 0
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
@ -60,10 +69,10 @@ ScrollingWindow {
|
||||||
// (required for letting the C++ code access the webview)
|
// (required for letting the C++ code access the webview)
|
||||||
active: true
|
active: true
|
||||||
enabled: false
|
enabled: false
|
||||||
property string originalUrl: "";
|
property string originalUrl: ""
|
||||||
|
|
||||||
WebView {
|
WebView {
|
||||||
id: webView;
|
id: webView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: false
|
enabled: false
|
||||||
property alias eventBridgeWrapper: eventBridgeWrapper
|
property alias eventBridgeWrapper: eventBridgeWrapper
|
||||||
|
@ -71,11 +80,11 @@ ScrollingWindow {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: eventBridgeWrapper
|
id: eventBridgeWrapper
|
||||||
WebChannel.id: "eventBridgeWrapper"
|
WebChannel.id: "eventBridgeWrapper"
|
||||||
property var eventBridge;
|
property var eventBridge
|
||||||
}
|
}
|
||||||
|
|
||||||
webChannel.registeredObjects: [eventBridgeWrapper]
|
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||||
onEnabledChanged: toolWindow.updateVisiblity();
|
onEnabledChanged: toolWindow.updateVisiblity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,6 +144,7 @@ ScrollingWindow {
|
||||||
tabOverlap: 0
|
tabOverlap: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateVisiblity() {
|
function updateVisiblity() {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
@ -224,7 +234,6 @@ ScrollingWindow {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (properties.width) {
|
if (properties.width) {
|
||||||
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.2
|
||||||
|
|
||||||
WebEngineView {
|
WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
|
|
|
@ -109,8 +109,13 @@ Column {
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
// Events are propogated so that any active control is defocused.
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: toggleCollapsed()
|
propagateComposedEvents: true
|
||||||
|
onPressed: {
|
||||||
|
toggleCollapsed();
|
||||||
|
mouse.accepted = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -304,13 +304,13 @@ Item {
|
||||||
Key {
|
Key {
|
||||||
id: key31
|
id: key31
|
||||||
width: 43
|
width: 43
|
||||||
glyph: ","
|
glyph: "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key33
|
id: key33
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "."
|
glyph: "?"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
|
@ -208,49 +208,49 @@ Item {
|
||||||
Key {
|
Key {
|
||||||
id: key22
|
id: key22
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "_"
|
glyph: ","
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key23
|
id: key23
|
||||||
width: 43
|
width: 43
|
||||||
glyph: ";"
|
glyph: "."
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key24
|
id: key24
|
||||||
width: 43
|
width: 43
|
||||||
glyph: ":"
|
glyph: ";"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key25
|
id: key25
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "'"
|
glyph: ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key26
|
id: key26
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "\""
|
glyph: "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key31
|
id: key31
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "<"
|
glyph: "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key33
|
id: key33
|
||||||
width: 43
|
width: 43
|
||||||
glyph: ">"
|
glyph: "<"
|
||||||
}
|
}
|
||||||
|
|
||||||
Key {
|
Key {
|
||||||
id: key36
|
id: key36
|
||||||
width: 43
|
width: 43
|
||||||
glyph: "?"
|
glyph: ">"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
import QtWebChannel 1.0
|
import QtWebChannel 1.0
|
||||||
|
import "../controls-uit" as HiFiControls
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias url: root.url
|
property alias url: root.url
|
||||||
|
@ -105,7 +106,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual keyboard, letters
|
// virtual keyboard, letters
|
||||||
Keyboard {
|
HiFiControls.Keyboard {
|
||||||
id: keyboard1
|
id: keyboard1
|
||||||
y: keyboardRaised ? parent.height : 0
|
y: keyboardRaised ? parent.height : 0
|
||||||
height: keyboardRaised ? 200 : 0
|
height: keyboardRaised ? 200 : 0
|
||||||
|
@ -119,7 +120,7 @@ Item {
|
||||||
anchors.bottomMargin: 0
|
anchors.bottomMargin: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardPunctuation {
|
HiFiControls.KeyboardPunctuation {
|
||||||
id: keyboard2
|
id: keyboard2
|
||||||
y: keyboardRaised ? parent.height : 0
|
y: keyboardRaised ? parent.height : 0
|
||||||
height: keyboardRaised ? 200 : 0
|
height: keyboardRaised ? 200 : 0
|
||||||
|
|
|
@ -22,6 +22,7 @@ ModalWindow {
|
||||||
implicitWidth: 640;
|
implicitWidth: 640;
|
||||||
implicitHeight: 320;
|
implicitHeight: 320;
|
||||||
visible: true;
|
visible: true;
|
||||||
|
keyboardEnabled: false // Disable ModalWindow's keyboard.
|
||||||
|
|
||||||
signal selected(var result);
|
signal selected(var result);
|
||||||
signal canceled();
|
signal canceled();
|
||||||
|
@ -50,6 +51,10 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
onKeyboardRaisedChanged: d.resize();
|
||||||
|
|
||||||
property var warning: "";
|
property var warning: "";
|
||||||
property var result;
|
property var result;
|
||||||
|
|
||||||
|
@ -110,7 +115,9 @@ ModalWindow {
|
||||||
var targetWidth = Math.max(titleWidth, pane.width);
|
var targetWidth = Math.max(titleWidth, pane.width);
|
||||||
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
|
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
|
||||||
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
|
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
|
||||||
(buttons.height + 3 * hifi.dimensions.contentSpacing.y);
|
(buttons.height + 3 * hifi.dimensions.contentSpacing.y) +
|
||||||
|
(root.keyboardRaised ? (200 + hifi.dimensions.contentSpacing.y) : 0);
|
||||||
|
|
||||||
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
|
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
|
||||||
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
|
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
|
||||||
d.maxHeight : targetHeight);
|
d.maxHeight : targetHeight);
|
||||||
|
@ -130,7 +137,6 @@ ModalWindow {
|
||||||
left: parent.left;
|
left: parent.left;
|
||||||
right: parent.right;
|
right: parent.right;
|
||||||
margins: 0;
|
margins: 0;
|
||||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME make a text field type that can be bound to a history for autocompletion
|
// FIXME make a text field type that can be bound to a history for autocompletion
|
||||||
|
@ -142,7 +148,43 @@ ModalWindow {
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left;
|
left: parent.left;
|
||||||
right: parent.right;
|
right: parent.right;
|
||||||
bottom: parent.bottom;
|
bottom: keyboard.top;
|
||||||
|
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyboard
|
||||||
|
|
||||||
|
height: keyboardRaised ? 200 : 0
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: keyboardRaised ? hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
visible: keyboardRaised && !punctuationMode
|
||||||
|
enabled: keyboardRaised && !punctuationMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
visible: keyboardRaised && punctuationMode
|
||||||
|
enabled: keyboardRaised && punctuationMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ ModalWindow {
|
||||||
id: root
|
id: root
|
||||||
resizable: true
|
resizable: true
|
||||||
implicitWidth: 480
|
implicitWidth: 480
|
||||||
implicitHeight: 360
|
implicitHeight: 360 + (fileDialogItem.keyboardRaised ? 200 + hifi.dimensions.contentSpacing.y : 0)
|
||||||
|
|
||||||
minSize: Qt.vector2d(360, 240)
|
minSize: Qt.vector2d(360, 240)
|
||||||
draggable: true
|
draggable: true
|
||||||
|
@ -100,16 +100,23 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: fileDialogItem
|
||||||
clip: true
|
clip: true
|
||||||
width: pane.width
|
width: pane.width
|
||||||
height: pane.height
|
height: pane.height
|
||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
|
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
// Clear selection when click on internal unused area.
|
// Clear selection when click on internal unused area.
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
drag.target: root
|
drag.target: root
|
||||||
onClicked: d.clearSelection()
|
onClicked: {
|
||||||
|
d.clearSelection();
|
||||||
|
frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
@ -619,7 +626,7 @@ ModalWindow {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: selectionType.visible ? selectionType.left: parent.right
|
right: selectionType.visible ? selectionType.left: parent.right
|
||||||
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
|
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
|
||||||
bottom: buttonRow.top
|
bottom: keyboard1.top
|
||||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||||
}
|
}
|
||||||
readOnly: !root.saveDialog
|
readOnly: !root.saveDialog
|
||||||
|
@ -640,6 +647,28 @@ ModalWindow {
|
||||||
KeyNavigation.right: openButton
|
KeyNavigation.right: openButton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
enabled: visible
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: buttonRow.top
|
||||||
|
anchors.bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && parent.punctuationMode
|
||||||
|
enabled: visible
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: buttonRow.top
|
||||||
|
anchors.bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: buttonRow
|
id: buttonRow
|
||||||
anchors {
|
anchors {
|
||||||
|
|
|
@ -97,9 +97,9 @@ ScrollingWindow {
|
||||||
|
|
||||||
footer: Row {
|
footer: Row {
|
||||||
anchors {
|
anchors {
|
||||||
right: parent.right;
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
rightMargin: hifi.dimensions.contentMargin.x
|
rightMargin: hifi.dimensions.contentMargin.x
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
}
|
||||||
spacing: hifi.dimensions.contentSpacing.x
|
spacing: hifi.dimensions.contentSpacing.x
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,17 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: modalWindowItem
|
||||||
clip: true
|
clip: true
|
||||||
width: pane.width
|
width: pane.width
|
||||||
height: pane.height
|
height: pane.height
|
||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
|
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
|
onKeyboardRaisedChanged: d.resize();
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
readonly property int minWidth: 480
|
readonly property int minWidth: 480
|
||||||
|
@ -69,14 +75,14 @@ ModalWindow {
|
||||||
var targetWidth = Math.max(titleWidth, pane.width)
|
var targetWidth = Math.max(titleWidth, pane.width)
|
||||||
var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height
|
var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height
|
||||||
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
|
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
|
||||||
root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
|
root.height = ((targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + (modalWindowItem.keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
bottom: buttons.top;
|
bottom: keyboard1.top;
|
||||||
left: parent.left;
|
left: parent.left;
|
||||||
right: parent.right;
|
right: parent.right;
|
||||||
margins: 0
|
margins: 0
|
||||||
|
@ -110,6 +116,35 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual keyboard, letters
|
||||||
|
Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
y: parent.keyboardRaised ? parent.height : 0
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
enabled: parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: buttons.top
|
||||||
|
anchors.bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
y: parent.keyboardRaised ? parent.height : 0
|
||||||
|
height: parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardRaised && parent.punctuationMode
|
||||||
|
enabled: parent.keyboardRaised && parent.punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: buttons.top
|
||||||
|
anchors.bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||||
|
}
|
||||||
|
|
||||||
Flow {
|
Flow {
|
||||||
id: buttons
|
id: buttons
|
||||||
focus: true
|
focus: true
|
||||||
|
|
|
@ -45,7 +45,7 @@ ScrollingWindow {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: root.height
|
height: root.height - (keyboardRaised ? 200 : 0)
|
||||||
radius: 4
|
radius: 4
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
|
|
||||||
|
@ -128,6 +128,10 @@ ScrollingWindow {
|
||||||
}
|
}
|
||||||
onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
|
onCountChanged: MyAvatar.setAttachmentsVariant(attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollBy(delta) {
|
||||||
|
flickableItem.contentY += delta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,5 +208,22 @@ ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onKeyboardRaisedChanged: {
|
||||||
|
if (keyboardRaised) {
|
||||||
|
// Scroll to item with focus if necessary.
|
||||||
|
var footerHeight = newAttachmentButton.height + buttonRow.height + 3 * hifi.dimensions.contentSpacing.y;
|
||||||
|
var delta = activator.mouseY
|
||||||
|
- (activator.height + activator.y - 200 - footerHeight - hifi.dimensions.controlLineHeight);
|
||||||
|
|
||||||
|
if (delta > 0) {
|
||||||
|
scrollView.scrollBy(delta);
|
||||||
|
} else {
|
||||||
|
// HACK: Work around for case where are 100% scrolled; stops window from erroneously scrolling to 100% when show keyboard.
|
||||||
|
scrollView.scrollBy(-1);
|
||||||
|
scrollView.scrollBy(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ ScrollingWindow {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: root.height
|
height: root.height - (keyboardRaised ? 200 : 0)
|
||||||
radius: 4
|
radius: 4
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
// Window.qml
|
// Window.qml
|
||||||
//
|
//
|
||||||
|
@ -15,6 +16,7 @@ import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
import "."
|
import "."
|
||||||
import "../styles-uit"
|
import "../styles-uit"
|
||||||
|
import "../controls-uit" as HiFiControls
|
||||||
|
|
||||||
// FIXME how do I set the initial position of a window without
|
// FIXME how do I set the initial position of a window without
|
||||||
// overriding places where the a individual client of the window
|
// overriding places where the a individual client of the window
|
||||||
|
@ -23,12 +25,18 @@ import "../styles-uit"
|
||||||
// FIXME how to I enable dragging without allowing the window to lay outside
|
// FIXME how to I enable dragging without allowing the window to lay outside
|
||||||
// of the desktop? How do I ensure when the desktop resizes all the windows
|
// of the desktop? How do I ensure when the desktop resizes all the windows
|
||||||
// are still at least partially visible?
|
// are still at least partially visible?
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: window
|
id: window
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
children: [ swallower, frame, pane, activator ]
|
children: [ swallower, frame, defocuser, pane, activator ]
|
||||||
|
|
||||||
property var footer: Item { } // Optional static footer at the bottom of the dialog.
|
property var footer: Item { } // Optional static footer at the bottom of the dialog.
|
||||||
|
readonly property var footerContentHeight: footer.height > 0 ? (footer.height + 2 * hifi.dimensions.contentSpacing.y + 3) : 0
|
||||||
|
|
||||||
|
property bool keyboardEnabled: true // Set false if derived control implements its own keyboard.
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
// Scrollable window content.
|
// Scrollable window content.
|
||||||
// FIXME this should not define any visual content in this type. The base window
|
// FIXME this should not define any visual content in this type. The base window
|
||||||
|
@ -73,7 +81,7 @@ Window {
|
||||||
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
|
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: parent.isScrolling ? 1 : 0
|
anchors.rightMargin: parent.isScrolling ? 1 : 0
|
||||||
anchors.bottomMargin: footer.height > 0 ? footerPane.height : 0
|
anchors.bottomMargin: footerPane.height
|
||||||
|
|
||||||
style: ScrollViewStyle {
|
style: ScrollViewStyle {
|
||||||
|
|
||||||
|
@ -116,21 +124,36 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollBy(delta) {
|
||||||
|
scrollView.flickableItem.contentY += delta;
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// Optional non-scrolling footer.
|
// Optional non-scrolling footer.
|
||||||
id: footerPane
|
id: footerPane
|
||||||
|
|
||||||
|
property alias keyboardEnabled: window.keyboardEnabled
|
||||||
|
property alias keyboardRaised: window.keyboardRaised
|
||||||
|
property alias punctuationMode: window.punctuationMode
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
}
|
}
|
||||||
width: parent.contentWidth
|
width: parent.contentWidth
|
||||||
height: footer.height + 2 * hifi.dimensions.contentSpacing.y + 3
|
height: footerContentHeight + (keyboardEnabled && keyboardRaised ? 200 : 0)
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
visible: footer.height > 0
|
visible: footer.height > 0 || keyboardEnabled && keyboardRaised
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
// Horizontal rule.
|
// Horizontal rule.
|
||||||
anchors.fill: parent
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: footer.height > 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -148,10 +171,53 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors {
|
||||||
anchors.topMargin: 3 // Horizontal rule.
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: hifi.dimensions.contentSpacing.y + 3
|
||||||
|
}
|
||||||
children: [ footer ]
|
children: [ footer ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HiFiControls.Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
height: parent.keyboardEnabled && parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardEnabled && parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
enabled: parent.keyboardEnabled && parent.keyboardRaised && !parent.punctuationMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiControls.KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
height: parent.keyboardEnabled && parent.keyboardRaised ? 200 : 0
|
||||||
|
visible: parent.keyboardEnabled && parent.keyboardRaised && parent.punctuationMode
|
||||||
|
enabled: parent.keyboardEnabled && parent.keyboardRaised && parent.punctuationMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyboardRaisedChanged: {
|
||||||
|
if (keyboardEnabled && keyboardRaised) {
|
||||||
|
var delta = activator.mouseY
|
||||||
|
- (activator.height + activator.y - 200 - footerContentHeight - hifi.dimensions.controlLineHeight);
|
||||||
|
|
||||||
|
if (delta > 0) {
|
||||||
|
pane.scrollBy(delta);
|
||||||
|
} else {
|
||||||
|
// HACK: Work around for case where are 100% scrolled; stops window from erroneously scrolling to 100% when show keyboard.
|
||||||
|
pane.scrollBy(-1);
|
||||||
|
pane.scrollBy(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ Fadable {
|
||||||
implicitHeight: content ? content.height : 0
|
implicitHeight: content ? content.height : 0
|
||||||
implicitWidth: content ? content.width : 0
|
implicitWidth: content ? content.width : 0
|
||||||
x: desktop.invalid_position; y: desktop.invalid_position;
|
x: desktop.invalid_position; y: desktop.invalid_position;
|
||||||
children: [ swallower, frame, content, activator ]
|
children: [ swallower, frame, defocuser, content, activator ]
|
||||||
|
|
||||||
//
|
//
|
||||||
// Custom properties
|
// Custom properties
|
||||||
|
@ -122,6 +122,21 @@ Fadable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This mouse area defocuses the current control so that the HMD keyboard gets hidden.
|
||||||
|
property var defocuser: MouseArea {
|
||||||
|
width: frame.decoration ? frame.decoration.width : window.width
|
||||||
|
height: frame.decoration ? frame.decoration.height : window.height
|
||||||
|
x: frame.decoration ? frame.decoration.anchors.leftMargin : 0
|
||||||
|
y: frame.decoration ? frame.decoration.anchors.topMargin : 0
|
||||||
|
propagateComposedEvents: true
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
enabled: window.visible
|
||||||
|
onPressed: {
|
||||||
|
frame.forceActiveFocus();
|
||||||
|
mouse.accepted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This mouse area serves to swallow mouse events while the mouse is over the window
|
// This mouse area serves to swallow mouse events while the mouse is over the window
|
||||||
// to prevent things like mouse wheel events from reaching the application and changing
|
// to prevent things like mouse wheel events from reaching the application and changing
|
||||||
// the camera if the user is scrolling through a list and gets to the end.
|
// the camera if the user is scrolling through a list and gets to the end.
|
||||||
|
|
|
@ -486,7 +486,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
// FIXME move to header, or better yet, design some kind of UI manager
|
// FIXME move to header, or better yet, design some kind of UI manager
|
||||||
// to take care of highlighting keyboard focused items, rather than
|
// to take care of highlighting keyboard focused items, rather than
|
||||||
// continuing to overburden Application.cpp
|
// continuing to overburden Application.cpp
|
||||||
Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
std::shared_ptr<Cube3DOverlay> _keyboardFocusHighlight{ nullptr };
|
||||||
int _keyboardFocusHighlightID{ -1 };
|
int _keyboardFocusHighlightID{ -1 };
|
||||||
|
|
||||||
|
|
||||||
|
@ -3625,7 +3625,7 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) {
|
||||||
_keyboardFocusedItem.set(entityItemID);
|
_keyboardFocusedItem.set(entityItemID);
|
||||||
_lastAcceptedKeyPress = usecTimestampNow();
|
_lastAcceptedKeyPress = usecTimestampNow();
|
||||||
if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
|
if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
|
||||||
_keyboardFocusHighlight = new Cube3DOverlay();
|
_keyboardFocusHighlight = std::make_shared<Cube3DOverlay>();
|
||||||
_keyboardFocusHighlight->setAlpha(1.0f);
|
_keyboardFocusHighlight->setAlpha(1.0f);
|
||||||
_keyboardFocusHighlight->setBorderSize(1.0f);
|
_keyboardFocusHighlight->setBorderSize(1.0f);
|
||||||
_keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
|
_keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
|
||||||
|
|
|
@ -38,39 +38,6 @@ static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||||
static int MAX_WINDOW_SIZE = 4096;
|
static int MAX_WINDOW_SIZE = 4096;
|
||||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||||
|
|
||||||
void WebEntityAPIHelper::synthesizeKeyPress(QString key) {
|
|
||||||
if (_renderableWebEntityItem) {
|
|
||||||
_renderableWebEntityItem->synthesizeKeyPress(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebEntityAPIHelper::emitScriptEvent(const QVariant& message) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
|
||||||
} else {
|
|
||||||
emit scriptEventReceived(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebEntityAPIHelper::emitWebEvent(const QVariant& message) {
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
|
||||||
} else {
|
|
||||||
// special case to handle raising and lowering the virtual keyboard
|
|
||||||
if (message.type() == QVariant::String && message.toString() == "_RAISE_KEYBOARD" && _renderableWebEntityItem) {
|
|
||||||
if (_renderableWebEntityItem) {
|
|
||||||
_renderableWebEntityItem->setKeyboardRaised(true);
|
|
||||||
}
|
|
||||||
} else if (message.type() == QVariant::String && message.toString() == "_LOWER_KEYBOARD" && _renderableWebEntityItem) {
|
|
||||||
if (_renderableWebEntityItem) {
|
|
||||||
_renderableWebEntityItem->setKeyboardRaised(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit webEventReceived(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
||||||
entity->setProperties(properties);
|
entity->setProperties(properties);
|
||||||
|
@ -85,21 +52,9 @@ RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemI
|
||||||
_touchDevice.setType(QTouchDevice::TouchScreen);
|
_touchDevice.setType(QTouchDevice::TouchScreen);
|
||||||
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
|
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
|
||||||
_touchDevice.setMaximumTouchPoints(4);
|
_touchDevice.setMaximumTouchPoints(4);
|
||||||
|
|
||||||
_webEntityAPIHelper = new WebEntityAPIHelper;
|
|
||||||
_webEntityAPIHelper->setRenderableWebEntityItem(this);
|
|
||||||
_webEntityAPIHelper->moveToThread(qApp->thread());
|
|
||||||
|
|
||||||
// forward web events to EntityScriptingInterface
|
|
||||||
auto entities = DependencyManager::get<EntityScriptingInterface>();
|
|
||||||
QObject::connect(_webEntityAPIHelper, &WebEntityAPIHelper::webEventReceived, [=](const QVariant& message) {
|
|
||||||
emit entities->webEventReceived(entityItemID, message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
||||||
_webEntityAPIHelper->setRenderableWebEntityItem(nullptr);
|
|
||||||
_webEntityAPIHelper->deleteLater();
|
|
||||||
destroyWebSurface();
|
destroyWebSurface();
|
||||||
qDebug() << "Destroyed web entity " << getID();
|
qDebug() << "Destroyed web entity " << getID();
|
||||||
}
|
}
|
||||||
|
@ -148,10 +103,16 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
||||||
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
||||||
});
|
});
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
_webSurface->getRootItem()->setProperty("eventBridge", QVariant::fromValue(_webEntityAPIHelper));
|
|
||||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
|
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
|
||||||
_webSurface->getRootContext()->setContextProperty("webEntity", _webEntityAPIHelper);
|
|
||||||
|
// forward web events to EntityScriptingInterface
|
||||||
|
auto entities = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
const EntityItemID entityItemID = getID();
|
||||||
|
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, [=](const QVariant& message) {
|
||||||
|
emit entities->webEventReceived(entityItemID, message);
|
||||||
|
});
|
||||||
|
|
||||||
// Restore the original GL context
|
// Restore the original GL context
|
||||||
currentContext->makeCurrent(currentSurface);
|
currentContext->makeCurrent(currentSurface);
|
||||||
|
|
||||||
|
@ -370,7 +331,6 @@ void RenderableWebEntityItem::destroyWebSurface() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RenderableWebEntityItem::update(const quint64& now) {
|
void RenderableWebEntityItem::update(const quint64& now) {
|
||||||
auto interval = now - _lastRenderTime;
|
auto interval = now - _lastRenderTime;
|
||||||
if (interval > MAX_NO_RENDER_INTERVAL) {
|
if (interval > MAX_NO_RENDER_INTERVAL) {
|
||||||
|
@ -378,78 +338,13 @@ void RenderableWebEntityItem::update(const quint64& now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool RenderableWebEntityItem::isTransparent() {
|
bool RenderableWebEntityItem::isTransparent() {
|
||||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF-8 encoded symbols
|
|
||||||
static const uint8_t UPWARDS_WHITE_ARROW_FROM_BAR[] = { 0xE2, 0x87, 0xAA, 0x00 }; // shift
|
|
||||||
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x86, 0x90, 0x00 }; // backspace
|
|
||||||
static const uint8_t LEFTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA6, 0x00 }; // left arrow
|
|
||||||
static const uint8_t RIGHTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA8, 0x00 }; // right arrow
|
|
||||||
static const uint8_t ASTERISIM[] = { 0xE2, 0x81, 0x82, 0x00 }; // symbols
|
|
||||||
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 }; // return
|
|
||||||
static const char PUNCTUATION_STRING[] = "&123";
|
|
||||||
static const char ALPHABET_STRING[] = "abc";
|
|
||||||
|
|
||||||
static bool equals(const QByteArray& byteArray, const uint8_t* ptr) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < byteArray.size(); i++) {
|
|
||||||
if ((char)ptr[i] != byteArray[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ptr[i] == 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableWebEntityItem::synthesizeKeyPress(QString key) {
|
|
||||||
auto eventHandler = getEventHandler();
|
|
||||||
if (eventHandler) {
|
|
||||||
auto utf8Key = key.toUtf8();
|
|
||||||
|
|
||||||
int scanCode = (int)utf8Key[0];
|
|
||||||
QString keyString = key;
|
|
||||||
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
|
|
||||||
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
|
||||||
return; // ignore
|
|
||||||
} else if (equals(utf8Key, LEFT_ARROW)) {
|
|
||||||
scanCode = Qt::Key_Backspace;
|
|
||||||
keyString = "\x08";
|
|
||||||
} else if (equals(utf8Key, RETURN_SYMBOL)) {
|
|
||||||
scanCode = Qt::Key_Return;
|
|
||||||
keyString = "\x0d";
|
|
||||||
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
|
|
||||||
scanCode = Qt::Key_Left;
|
|
||||||
keyString = "";
|
|
||||||
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
|
|
||||||
scanCode = Qt::Key_Right;
|
|
||||||
keyString = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
|
||||||
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
|
||||||
QCoreApplication::postEvent(eventHandler, pressEvent);
|
|
||||||
QCoreApplication::postEvent(eventHandler, releaseEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
|
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
|
||||||
if (_webEntityAPIHelper) {
|
|
||||||
_webEntityAPIHelper->emitScriptEvent(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableWebEntityItem::setKeyboardRaised(bool raised) {
|
|
||||||
|
|
||||||
// raise the keyboard only while in HMD mode and it's being requested.
|
|
||||||
bool value = AbstractViewStateInterface::instance()->isHMDMode() && raised;
|
|
||||||
|
|
||||||
if (_webSurface) {
|
if (_webSurface) {
|
||||||
auto rootItem = _webSurface->getRootItem();
|
_webSurface->emitScriptEvent(message);
|
||||||
if (rootItem) {
|
|
||||||
rootItem->setProperty("keyboardRaised", QVariant(value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,36 +13,18 @@
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QTouchEvent>
|
#include <QTouchEvent>
|
||||||
#include <PointerEvent.h>
|
#include <PointerEvent.h>
|
||||||
|
#include <gl/OffscreenQmlSurface.h>
|
||||||
|
|
||||||
#include <WebEntityItem.h>
|
#include <WebEntityItem.h>
|
||||||
|
|
||||||
#include "RenderableEntityItem.h"
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
class OffscreenQmlSurface;
|
|
||||||
class QWindow;
|
class QWindow;
|
||||||
class QObject;
|
class QObject;
|
||||||
class EntityTreeRenderer;
|
class EntityTreeRenderer;
|
||||||
class RenderableWebEntityItem;
|
class RenderableWebEntityItem;
|
||||||
|
|
||||||
class WebEntityAPIHelper : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
void setRenderableWebEntityItem(RenderableWebEntityItem* renderableWebEntityItem) {
|
|
||||||
_renderableWebEntityItem = renderableWebEntityItem;
|
|
||||||
}
|
|
||||||
Q_INVOKABLE void synthesizeKeyPress(QString key);
|
|
||||||
|
|
||||||
// event bridge
|
|
||||||
public slots:
|
|
||||||
void emitScriptEvent(const QVariant& scriptMessage);
|
|
||||||
void emitWebEvent(const QVariant& webMessage);
|
|
||||||
signals:
|
|
||||||
void scriptEventReceived(const QVariant& message);
|
|
||||||
void webEventReceived(const QVariant& message);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
RenderableWebEntityItem* _renderableWebEntityItem{ nullptr };
|
|
||||||
};
|
|
||||||
|
|
||||||
class RenderableWebEntityItem : public WebEntityItem {
|
class RenderableWebEntityItem : public WebEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -64,15 +46,11 @@ public:
|
||||||
bool needsToCallUpdate() const override { return _webSurface != nullptr; }
|
bool needsToCallUpdate() const override { return _webSurface != nullptr; }
|
||||||
|
|
||||||
virtual void emitScriptEvent(const QVariant& message) override;
|
virtual void emitScriptEvent(const QVariant& message) override;
|
||||||
void setKeyboardRaised(bool raised);
|
|
||||||
|
|
||||||
SIMPLE_RENDERABLE();
|
SIMPLE_RENDERABLE();
|
||||||
|
|
||||||
virtual bool isTransparent() override;
|
virtual bool isTransparent() override;
|
||||||
|
|
||||||
public:
|
|
||||||
void synthesizeKeyPress(QString key);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool buildWebSurface(EntityTreeRenderer* renderer);
|
bool buildWebSurface(EntityTreeRenderer* renderer);
|
||||||
void destroyWebSurface();
|
void destroyWebSurface();
|
||||||
|
@ -86,7 +64,6 @@ private:
|
||||||
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
|
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
|
||||||
uint64_t _lastRenderTime{ 0 };
|
uint64_t _lastRenderTime{ 0 };
|
||||||
QTouchDevice _touchDevice;
|
QTouchDevice _touchDevice;
|
||||||
WebEntityAPIHelper* _webEntityAPIHelper;
|
|
||||||
|
|
||||||
QMetaObject::Connection _mousePressConnection;
|
QMetaObject::Connection _mousePressConnection;
|
||||||
QMetaObject::Connection _mouseReleaseConnection;
|
QMetaObject::Connection _mouseReleaseConnection;
|
||||||
|
|
|
@ -427,6 +427,19 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QOb
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Refactor with similar code in RenderableWebEntityItem
|
||||||
|
QString javaScriptToInject;
|
||||||
|
QFile webChannelFile(":qtwebchannel/qwebchannel.js");
|
||||||
|
QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js");
|
||||||
|
if (webChannelFile.open(QFile::ReadOnly | QFile::Text) &&
|
||||||
|
createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
QString webChannelStr = QTextStream(&webChannelFile).readAll();
|
||||||
|
QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
|
||||||
|
javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
|
||||||
|
} else {
|
||||||
|
qWarning() << "Unable to find qwebchannel.js or createGlobalEventBridge.js";
|
||||||
|
}
|
||||||
|
|
||||||
QQmlContext* newContext = new QQmlContext(_qmlEngine, qApp);
|
QQmlContext* newContext = new QQmlContext(_qmlEngine, qApp);
|
||||||
QObject* newObject = _qmlComponent->beginCreate(newContext);
|
QObject* newObject = _qmlComponent->beginCreate(newContext);
|
||||||
if (_qmlComponent->isError()) {
|
if (_qmlComponent->isError()) {
|
||||||
|
@ -439,6 +452,9 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QOb
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newObject->setProperty("eventBridge", QVariant::fromValue(this));
|
||||||
|
newContext->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
||||||
|
|
||||||
f(newContext, newObject);
|
f(newContext, newObject);
|
||||||
_qmlComponent->completeCreate();
|
_qmlComponent->completeCreate();
|
||||||
|
|
||||||
|
@ -541,7 +557,6 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
switch (event->type()) {
|
switch (event->type()) {
|
||||||
case QEvent::Resize: {
|
case QEvent::Resize: {
|
||||||
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
|
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
|
||||||
|
@ -610,6 +625,9 @@ void OffscreenQmlSurface::pause() {
|
||||||
void OffscreenQmlSurface::resume() {
|
void OffscreenQmlSurface::resume() {
|
||||||
_paused = false;
|
_paused = false;
|
||||||
_render = true;
|
_render = true;
|
||||||
|
|
||||||
|
getRootItem()->setProperty("eventBridge", QVariant::fromValue(this));
|
||||||
|
getRootContext()->setContextProperty("webEntity", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OffscreenQmlSurface::isPaused() const {
|
bool OffscreenQmlSurface::isPaused() const {
|
||||||
|
@ -667,15 +685,37 @@ QVariant OffscreenQmlSurface::returnFromUiThread(std::function<QVariant()> funct
|
||||||
return function();
|
return function();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::focusDestroyed(QObject *obj) {
|
||||||
|
_currentFocusItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
|
void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
|
||||||
if (!object) {
|
QQuickItem* item = dynamic_cast<QQuickItem*>(object);
|
||||||
|
if (!item) {
|
||||||
setFocusText(false);
|
setFocusText(false);
|
||||||
|
_currentFocusItem = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QInputMethodQueryEvent query(Qt::ImEnabled);
|
QInputMethodQueryEvent query(Qt::ImEnabled);
|
||||||
qApp->sendEvent(object, &query);
|
qApp->sendEvent(object, &query);
|
||||||
setFocusText(query.value(Qt::ImEnabled).toBool());
|
setFocusText(query.value(Qt::ImEnabled).toBool());
|
||||||
|
|
||||||
|
if (_currentFocusItem) {
|
||||||
|
disconnect(_currentFocusItem, &QObject::destroyed, this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raise and lower keyboard for QML text fields.
|
||||||
|
// HTML text fields are handled in emitWebEvent() methods - testing READ_ONLY_PROPERTY prevents action for HTML files.
|
||||||
|
const char* READ_ONLY_PROPERTY = "readOnly";
|
||||||
|
bool raiseKeyboard = item->hasActiveFocus() && item->property(READ_ONLY_PROPERTY) == false;
|
||||||
|
if (_currentFocusItem && !raiseKeyboard) {
|
||||||
|
setKeyboardRaised(_currentFocusItem, false);
|
||||||
|
}
|
||||||
|
setKeyboardRaised(item, raiseKeyboard); // Always set focus so that alphabetic / numeric setting is updated.
|
||||||
|
|
||||||
|
_currentFocusItem = item;
|
||||||
|
connect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::setFocusText(bool newFocusText) {
|
void OffscreenQmlSurface::setFocusText(bool newFocusText) {
|
||||||
|
@ -685,4 +725,103 @@ void OffscreenQmlSurface::setFocusText(bool newFocusText) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UTF-8 encoded symbols
|
||||||
|
static const uint8_t UPWARDS_WHITE_ARROW_FROM_BAR[] = { 0xE2, 0x87, 0xAA, 0x00 }; // shift
|
||||||
|
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x86, 0x90, 0x00 }; // backspace
|
||||||
|
static const uint8_t LEFTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA6, 0x00 }; // left arrow
|
||||||
|
static const uint8_t RIGHTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA8, 0x00 }; // right arrow
|
||||||
|
static const uint8_t ASTERISIM[] = { 0xE2, 0x81, 0x82, 0x00 }; // symbols
|
||||||
|
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 }; // return
|
||||||
|
static const char PUNCTUATION_STRING[] = "&123";
|
||||||
|
static const char ALPHABET_STRING[] = "abc";
|
||||||
|
|
||||||
|
static bool equals(const QByteArray& byteArray, const uint8_t* ptr) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < byteArray.size(); i++) {
|
||||||
|
if ((char)ptr[i] != byteArray[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr[i] == 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::synthesizeKeyPress(QString key) {
|
||||||
|
auto eventHandler = getEventHandler();
|
||||||
|
if (eventHandler) {
|
||||||
|
auto utf8Key = key.toUtf8();
|
||||||
|
|
||||||
|
int scanCode = (int)utf8Key[0];
|
||||||
|
QString keyString = key;
|
||||||
|
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
|
||||||
|
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
||||||
|
return; // ignore
|
||||||
|
} else if (equals(utf8Key, LEFT_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Backspace;
|
||||||
|
keyString = "\x08";
|
||||||
|
} else if (equals(utf8Key, RETURN_SYMBOL)) {
|
||||||
|
scanCode = Qt::Key_Return;
|
||||||
|
keyString = "\x0d";
|
||||||
|
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Left;
|
||||||
|
keyString = "";
|
||||||
|
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Right;
|
||||||
|
keyString = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QCoreApplication::postEvent(eventHandler, pressEvent);
|
||||||
|
QCoreApplication::postEvent(eventHandler, releaseEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) {
|
||||||
|
if (!object) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* item = dynamic_cast<QQuickItem*>(object);
|
||||||
|
while (item) {
|
||||||
|
// Numeric value may be set in parameter from HTML UI; for QML UI, detect numeric fields here.
|
||||||
|
numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox";
|
||||||
|
|
||||||
|
if (item->property("keyboardRaised").isValid()) {
|
||||||
|
if (item->property("punctuationMode").isValid()) {
|
||||||
|
item->setProperty("punctuationMode", QVariant(numeric));
|
||||||
|
}
|
||||||
|
item->setProperty("keyboardRaised", QVariant(raised));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = dynamic_cast<QQuickItem*>(item->parentItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::emitScriptEvent(const QVariant& message) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||||
|
} else {
|
||||||
|
emit scriptEventReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurface::emitWebEvent(const QVariant& message) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||||
|
} else {
|
||||||
|
// Special case to handle raising and lowering the virtual keyboard.
|
||||||
|
const QString RAISE_KEYBOARD = "_RAISE_KEYBOARD";
|
||||||
|
const QString RAISE_KEYBOARD_NUMERIC = "_RAISE_KEYBOARD_NUMERIC";
|
||||||
|
const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD";
|
||||||
|
QString messageString = message.type() == QVariant::String ? message.toString() : "";
|
||||||
|
if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) {
|
||||||
|
setKeyboardRaised(_currentFocusItem, true, messageString == RAISE_KEYBOARD_NUMERIC);
|
||||||
|
} else if (messageString == LOWER_KEYBOARD) {
|
||||||
|
setKeyboardRaised(_currentFocusItem, false);
|
||||||
|
} else {
|
||||||
|
emit webEventReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "OffscreenQmlSurface.moc"
|
#include "OffscreenQmlSurface.moc"
|
||||||
|
|
|
@ -30,7 +30,6 @@ class QQmlContext;
|
||||||
class QQmlComponent;
|
class QQmlComponent;
|
||||||
class QQuickWindow;
|
class QQuickWindow;
|
||||||
class QQuickItem;
|
class QQuickItem;
|
||||||
|
|
||||||
class OffscreenQmlSurface : public QObject {
|
class OffscreenQmlSurface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
|
||||||
|
@ -74,6 +73,9 @@ public:
|
||||||
QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
|
QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
|
||||||
bool eventFilter(QObject* originalDestination, QEvent* event) override;
|
bool eventFilter(QObject* originalDestination, QEvent* event) override;
|
||||||
|
|
||||||
|
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
|
||||||
|
Q_INVOKABLE void synthesizeKeyPress(QString key);
|
||||||
|
|
||||||
using TextureAndFence = std::pair<uint32_t, void*>;
|
using TextureAndFence = std::pair<uint32_t, void*>;
|
||||||
// Checks to see if a new texture is available. If one is, the function returns true and
|
// Checks to see if a new texture is available. If one is, the function returns true and
|
||||||
// textureAndFence will be populated with the texture ID and a fence which will be signalled
|
// textureAndFence will be populated with the texture ID and a fence which will be signalled
|
||||||
|
@ -90,6 +92,16 @@ signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onAboutToQuit();
|
void onAboutToQuit();
|
||||||
|
void focusDestroyed(QObject *obj);
|
||||||
|
|
||||||
|
// event bridge
|
||||||
|
public slots:
|
||||||
|
void emitScriptEvent(const QVariant& scriptMessage);
|
||||||
|
void emitWebEvent(const QVariant& webMessage);
|
||||||
|
signals:
|
||||||
|
void scriptEventReceived(const QVariant& message);
|
||||||
|
void webEventReceived(const QVariant& message);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool filterEnabled(QObject* originalDestination, QEvent* event) const;
|
bool filterEnabled(QObject* originalDestination, QEvent* event) const;
|
||||||
|
@ -137,6 +149,8 @@ private:
|
||||||
uint8_t _maxFps { 60 };
|
uint8_t _maxFps { 60 };
|
||||||
MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } };
|
MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } };
|
||||||
QWindow* _proxyWindow { nullptr };
|
QWindow* _proxyWindow { nullptr };
|
||||||
|
|
||||||
|
QQuickItem* _currentFocusItem { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,10 +42,39 @@ void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
|
||||||
void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
|
void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
||||||
|
} else {
|
||||||
|
// Special case to handle raising and lowering the virtual keyboard.
|
||||||
|
const QString RAISE_KEYBOARD = "_RAISE_KEYBOARD";
|
||||||
|
const QString RAISE_KEYBOARD_NUMERIC = "_RAISE_KEYBOARD_NUMERIC";
|
||||||
|
const QString LOWER_KEYBOARD = "_LOWER_KEYBOARD";
|
||||||
|
QString messageString = webMessage.type() == QVariant::String ? webMessage.toString() : "";
|
||||||
|
if (messageString.left(RAISE_KEYBOARD.length()) == RAISE_KEYBOARD) {
|
||||||
|
setKeyboardRaised(asQuickItem(), true, messageString == RAISE_KEYBOARD_NUMERIC);
|
||||||
|
} else if (messageString == LOWER_KEYBOARD) {
|
||||||
|
setKeyboardRaised(asQuickItem(), false);
|
||||||
} else {
|
} else {
|
||||||
emit webEventReceived(webMessage);
|
emit webEventReceived(webMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setKeyboardRaised(QObject* object, bool raised, bool numeric) {
|
||||||
|
if (!object) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* item = dynamic_cast<QQuickItem*>(object);
|
||||||
|
while (item) {
|
||||||
|
if (item->property("keyboardRaised").isValid()) {
|
||||||
|
if (item->property("punctuationMode").isValid()) {
|
||||||
|
item->setProperty("punctuationMode", QVariant(numeric));
|
||||||
|
}
|
||||||
|
item->setProperty("keyboardRaised", QVariant(raised));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = dynamic_cast<QQuickItem*>(item->parentItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString QmlWebWindowClass::getURL() const {
|
QString QmlWebWindowClass::getURL() const {
|
||||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||||
|
|
|
@ -33,6 +33,9 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString qmlSource() const override { return "QmlWebWindow.qml"; }
|
QString qmlSource() const override { return "QmlWebWindow.qml"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -123,59 +123,6 @@ static bool _keyboardShown { false };
|
||||||
static bool _overlayRevealed { false };
|
static bool _overlayRevealed { false };
|
||||||
static const uint32_t SHOW_KEYBOARD_DELAY_MS = 400;
|
static const uint32_t SHOW_KEYBOARD_DELAY_MS = 400;
|
||||||
|
|
||||||
void showOpenVrKeyboard(bool show = true) {
|
|
||||||
if (!_overlay) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show) {
|
|
||||||
// To avoid flickering the keyboard when a text element is only briefly selected,
|
|
||||||
// show the keyboard asynchrnously after a very short delay, but only after we check
|
|
||||||
// that the current focus object is still one that is text enabled
|
|
||||||
QTimer::singleShot(SHOW_KEYBOARD_DELAY_MS, [] {
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
|
||||||
auto currentFocus = offscreenUi->getWindow()->focusObject();
|
|
||||||
QInputMethodQueryEvent query(Qt::ImEnabled | Qt::ImQueryInput | Qt::ImHints);
|
|
||||||
qApp->sendEvent(currentFocus, &query);
|
|
||||||
// Current focus isn't text enabled, bail early.
|
|
||||||
if (!query.value(Qt::ImEnabled).toBool()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We're going to show the keyboard now...
|
|
||||||
_keyboardFocusObject = currentFocus;
|
|
||||||
_currentHints = Qt::InputMethodHints(query.value(Qt::ImHints).toUInt());
|
|
||||||
vr::EGamepadTextInputMode inputMode = vr::k_EGamepadTextInputModeNormal;
|
|
||||||
if (_currentHints & Qt::ImhHiddenText) {
|
|
||||||
inputMode = vr::k_EGamepadTextInputModePassword;
|
|
||||||
}
|
|
||||||
vr::EGamepadTextInputLineMode lineMode = vr::k_EGamepadTextInputLineModeSingleLine;
|
|
||||||
if (_currentHints & Qt::ImhMultiLine) {
|
|
||||||
lineMode = vr::k_EGamepadTextInputLineModeMultipleLines;
|
|
||||||
}
|
|
||||||
_existingText = query.value(Qt::ImSurroundingText).toString();
|
|
||||||
|
|
||||||
auto showKeyboardResult = _overlay->ShowKeyboard(inputMode, lineMode, "Keyboard", 1024,
|
|
||||||
_existingText.toLocal8Bit().toStdString().c_str(), false, 0);
|
|
||||||
|
|
||||||
if (vr::VROverlayError_None == showKeyboardResult) {
|
|
||||||
_keyboardShown = true;
|
|
||||||
// Try to position the keyboard slightly below where the user is looking.
|
|
||||||
mat4 headPose = cancelOutRollAndPitch(toGlm(_nextSimPoseData.vrPoses[0].mDeviceToAbsoluteTracking));
|
|
||||||
mat4 keyboardTransform = glm::translate(headPose, vec3(0, -0.5, -1));
|
|
||||||
keyboardTransform = keyboardTransform * glm::rotate(mat4(), 3.14159f / 4.0f, vec3(-1, 0, 0));
|
|
||||||
auto keyboardTransformVr = toOpenVr(keyboardTransform);
|
|
||||||
_overlay->SetKeyboardTransformAbsolute(vr::ETrackingUniverseOrigin::TrackingUniverseStanding, &keyboardTransformVr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_keyboardFocusObject = nullptr;
|
|
||||||
if (_keyboardShown) {
|
|
||||||
_overlay->HideKeyboard();
|
|
||||||
_keyboardShown = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateFromOpenVrKeyboardInput() {
|
void updateFromOpenVrKeyboardInput() {
|
||||||
auto chars = _overlay->GetKeyboardText(textArray, 8192);
|
auto chars = _overlay->GetKeyboardText(textArray, 8192);
|
||||||
auto newText = QString(QByteArray(textArray, chars));
|
auto newText = QString(QByteArray(textArray, chars));
|
||||||
|
@ -220,23 +167,6 @@ void enableOpenVrKeyboard(PluginContainer* container) {
|
||||||
QTimer::singleShot(KEYBOARD_DELAY_MS, [&] { _overlayRevealed = false; });
|
QTimer::singleShot(KEYBOARD_DELAY_MS, [&] { _overlayRevealed = false; });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_focusConnection = QObject::connect(offscreenUi->getWindow(), &QQuickWindow::focusObjectChanged, [](QObject* object) {
|
|
||||||
if (object != _keyboardFocusObject) {
|
|
||||||
showOpenVrKeyboard(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_focusTextConnection = QObject::connect(offscreenUi.data(), &OffscreenUi::focusTextChanged, [](bool focusText) {
|
|
||||||
if (_openVrDisplayActive) {
|
|
||||||
if (_overlayRevealed) {
|
|
||||||
// suppress at most one text focus event
|
|
||||||
_overlayRevealed = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showOpenVrKeyboard(focusText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||||
|
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||||
<script type="text/javascript" src="js/entityList.js"></script>
|
<script type="text/javascript" src="js/entityList.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='loaded();'>
|
<body onload='loaded();'>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||||
|
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||||
<script type="text/javascript" src="js/entityProperties.js"></script>
|
<script type="text/javascript" src="js/entityProperties.js"></script>
|
||||||
<script src="js/jsoneditor.min.js"></script>
|
<script src="js/jsoneditor.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||||
<script type="text/javascript" src="js/spinButtons.js"></script>
|
<script type="text/javascript" src="js/spinButtons.js"></script>
|
||||||
|
<script type="text/javascript" src="js/keyboardControl.js"></script>
|
||||||
<script type="text/javascript" src="js/gridControls.js"></script>
|
<script type="text/javascript" src="js/gridControls.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='loaded();'>
|
<body onload='loaded();'>
|
||||||
|
|
|
@ -122,6 +122,8 @@ function loaded() {
|
||||||
focus: false,
|
focus: false,
|
||||||
entityIds: selection,
|
entityIds: selection,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
refreshFooter();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRowDoubleClicked() {
|
function onRowDoubleClicked() {
|
||||||
|
@ -184,6 +186,7 @@ function loaded() {
|
||||||
function clearEntities() {
|
function clearEntities() {
|
||||||
entities = {};
|
entities = {};
|
||||||
entityList.clear();
|
entityList.clear();
|
||||||
|
refreshFooter();
|
||||||
}
|
}
|
||||||
|
|
||||||
var elSortOrder = {
|
var elSortOrder = {
|
||||||
|
@ -236,13 +239,16 @@ function loaded() {
|
||||||
refreshFooter();
|
refreshFooter();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSelectedEntities(selectedEntities) {
|
function updateSelectedEntities(selectedIDs) {
|
||||||
var notFound = false;
|
var notFound = false;
|
||||||
for (var id in entities) {
|
for (var id in entities) {
|
||||||
entities[id].el.className = '';
|
entities[id].el.className = '';
|
||||||
}
|
}
|
||||||
for (var i = 0; i < selectedEntities.length; i++) {
|
|
||||||
var id = selectedEntities[i];
|
selectedEntities = [];
|
||||||
|
for (var i = 0; i < selectedIDs.length; i++) {
|
||||||
|
var id = selectedIDs[i];
|
||||||
|
selectedEntities.push(id);
|
||||||
if (id in entities) {
|
if (id in entities) {
|
||||||
var entity = entities[id];
|
var entity = entities[id];
|
||||||
entity.el.className = 'selected';
|
entity.el.className = 'selected';
|
||||||
|
@ -251,10 +257,7 @@ function loaded() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: Fixes the footer and header text sometimes not displaying after adding or deleting entities.
|
refreshFooter();
|
||||||
// The problem appears to be a bug in the Qt HTML/CSS rendering (Qt 5.5).
|
|
||||||
document.getElementById("radius").focus();
|
|
||||||
document.getElementById("radius").blur();
|
|
||||||
|
|
||||||
return notFound;
|
return notFound;
|
||||||
}
|
}
|
||||||
|
@ -412,6 +415,8 @@ function loaded() {
|
||||||
|
|
||||||
augmentSpinButtons();
|
augmentSpinButtons();
|
||||||
|
|
||||||
|
setUpKeyboardControl();
|
||||||
|
|
||||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||||
document.addEventListener("contextmenu", function (event) {
|
document.addEventListener("contextmenu", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -1590,6 +1590,8 @@ function loaded() {
|
||||||
|
|
||||||
augmentSpinButtons();
|
augmentSpinButtons();
|
||||||
|
|
||||||
|
setUpKeyboardControl();
|
||||||
|
|
||||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||||
document.addEventListener("contextmenu", function(event) {
|
document.addEventListener("contextmenu", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -127,6 +127,8 @@ function loaded() {
|
||||||
|
|
||||||
augmentSpinButtons();
|
augmentSpinButtons();
|
||||||
|
|
||||||
|
setUpKeyboardControl();
|
||||||
|
|
||||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
67
scripts/system/html/js/keyboardControl.js
Normal file
67
scripts/system/html/js/keyboardControl.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// keyboardControl.js
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 28 Sep 2016.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
function setUpKeyboardControl() {
|
||||||
|
|
||||||
|
var lowerTimer = null;
|
||||||
|
var isRaised = false;
|
||||||
|
var KEYBOARD_HEIGHT = 200;
|
||||||
|
|
||||||
|
function raiseKeyboard() {
|
||||||
|
if (lowerTimer !== null) {
|
||||||
|
clearTimeout(lowerTimer);
|
||||||
|
lowerTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent("_RAISE_KEYBOARD" + (this.type === "number" ? "_NUMERIC" : ""));
|
||||||
|
|
||||||
|
if (!isRaised) {
|
||||||
|
var delta = this.getBoundingClientRect().bottom + 10 - (document.body.clientHeight - KEYBOARD_HEIGHT);
|
||||||
|
if (delta > 0) {
|
||||||
|
setTimeout(function () {
|
||||||
|
document.body.scrollTop += delta;
|
||||||
|
}, 500); // Allow time for keyboard to be raised in QML.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isRaised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLowerKeyboard() {
|
||||||
|
EventBridge.emitWebEvent("_LOWER_KEYBOARD");
|
||||||
|
lowerTimer = null;
|
||||||
|
isRaised = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lowerKeyboard() {
|
||||||
|
// Delay lowering keyboard a little in case immediately raise it again.
|
||||||
|
if (lowerTimer === null) {
|
||||||
|
lowerTimer = setTimeout(doLowerKeyboard, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function documentBlur() {
|
||||||
|
// Action any pending Lower keyboard event immediately upon leaving document window so that they don't interfere with
|
||||||
|
// other Entities Editor tab.
|
||||||
|
if (lowerTimer !== null) {
|
||||||
|
clearTimeout(lowerTimer);
|
||||||
|
doLowerKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputs = document.querySelectorAll("input[type=text], input[type=number], textarea");
|
||||||
|
for (var i = 0, length = inputs.length; i < length; i++) {
|
||||||
|
inputs[i].addEventListener("focus", raiseKeyboard);
|
||||||
|
inputs[i].addEventListener("blur", lowerKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("blur", documentBlur);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue