From b03fd44240fa6c2341f5b50ef49e8fe49552cd21 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 1 Apr 2017 10:40:06 +1300 Subject: [PATCH 1/4] Fix controls in HTML pages not responding to mouse events on tablet --- interface/src/ui/overlays/Web3DOverlay.cpp | 45 ++++++++++++++-------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index e50cf3a671..e134cc29c0 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -350,38 +350,31 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); - if (event.getType() == PointerEvent::Move) { - // Forward a mouse move event to the Web surface so that hover events are generated. - // Must send a mouse move event that matches up with touch move event in order for scroll bars to work. - - // Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - - QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); - } - if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { this->_pressed = true; } else if (event.getType() == PointerEvent::Release && event.getButton() == PointerEvent::PrimaryButton) { this->_pressed = false; } - QEvent::Type type; + QEvent::Type touchType; Qt::TouchPointState touchPointState; + QEvent::Type mouseType; switch (event.getType()) { case PointerEvent::Press: - type = QEvent::TouchBegin; + touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; + mouseType = QEvent::MouseButtonPress; break; case PointerEvent::Release: - type = QEvent::TouchEnd; + touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; + mouseType = QEvent::MouseButtonRelease; break; case PointerEvent::Move: default: - type = QEvent::TouchUpdate; + touchType = QEvent::TouchUpdate; touchPointState = Qt::TouchPointMoved; + mouseType = QEvent::MouseMove; break; } @@ -393,13 +386,30 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { QList touchPoints; touchPoints.push_back(point); - QTouchEvent* touchEvent = new QTouchEvent(type, &_touchDevice, event.getKeyboardModifiers()); + QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); touchEvent->setWindow(_webSurface->getWindow()); touchEvent->setTarget(_webSurface->getRootItem()); touchEvent->setTouchPoints(touchPoints); touchEvent->setTouchPointStates(touchPointState); QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + + Qt::MouseButtons buttons = Qt::NoButton; + if (event.getButtons() & PointerEvent::PrimaryButton) { + buttons |= Qt::LeftButton; + } + + Qt::MouseButton button = Qt::NoButton; + if (event.getButton() == PointerEvent::PrimaryButton) { + button = Qt::LeftButton; + } + + QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { @@ -421,11 +431,12 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { buttons |= Qt::LeftButton; } - QEvent::Type type; Qt::MouseButton button = Qt::NoButton; if (event.getButton() == PointerEvent::PrimaryButton) { button = Qt::LeftButton; } + + QEvent::Type type; switch (event.getType()) { case PointerEvent::Press: type = QEvent::MouseButtonPress; From 25ac82f162a72ff684d2022e6120b32eeeae6782 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 1 Apr 2017 10:47:04 +1300 Subject: [PATCH 2/4] Don't send "move" events for non-move events --- interface/src/ui/overlays/Web3DOverlay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index e134cc29c0..30593a5244 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -371,11 +371,12 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { mouseType = QEvent::MouseButtonRelease; break; case PointerEvent::Move: - default: touchType = QEvent::TouchUpdate; touchPointState = Qt::TouchPointMoved; mouseType = QEvent::MouseMove; break; + default: + return; } QTouchEvent::TouchPoint point; @@ -445,9 +446,10 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { type = QEvent::MouseButtonRelease; break; case PointerEvent::Move: - default: type = QEvent::MouseMove; break; + default: + return; } QMouseEvent* mouseEvent = new QMouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); From 260309ae24d3ba395baec53e865ef10bb3dd9237 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 1 Apr 2017 10:53:46 +1300 Subject: [PATCH 3/4] Remove CSS hack no longer needed --- scripts/system/html/css/edit-style.css | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index c033a2d2cd..06a60b5405 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -81,19 +81,6 @@ body { overflow-y: auto; } -/* HACK - Makes entity properties dialog's scrollbar work on tablet such that don't need to keep pointer within scrollbar width when - using scroll handle. -*/ -body { - padding-right: 0; - margin-right: -21px; -} -body > * { - margin-right: 42px; -} -/* END OF HACK */ - table { font-family: FiraSans-SemiBold; font-size: 15px; From f397adb22a2803e80cb3b525610040940882178a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 1 Apr 2017 11:06:27 +1300 Subject: [PATCH 4/4] Reinstate scrollbars to marketplace pages --- .../html/js/marketplacesInjectNoScrollbar.js | 349 ------------------ scripts/system/marketplaces/marketplaces.js | 10 +- 2 files changed, 2 insertions(+), 357 deletions(-) delete mode 100644 scripts/system/html/js/marketplacesInjectNoScrollbar.js diff --git a/scripts/system/html/js/marketplacesInjectNoScrollbar.js b/scripts/system/html/js/marketplacesInjectNoScrollbar.js deleted file mode 100644 index 86ec1b944a..0000000000 --- a/scripts/system/html/js/marketplacesInjectNoScrollbar.js +++ /dev/null @@ -1,349 +0,0 @@ -// -// marketplacesInject.js -// -// Created by David Rowe on 12 Nov 2016. -// Copyright 2016 High Fidelity, Inc. -// -// Injected into marketplace Web pages. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function () { - - // Event bridge messages. - var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; - var CLARA_IO_STATUS = "CLARA.IO STATUS"; - var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; - var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; - var GOTO_DIRECTORY = "GOTO_DIRECTORY"; - var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; - var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; - var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; - - var canWriteAssets = false; - var xmlHttpRequest = null; - var isPreparing = false; // Explicitly track download request status. - - function injectCommonCode(isDirectoryPage) { - - // Supporting styles from marketplaces.css. - // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. - $("head").append( - '' - ); - - // Supporting styles from edit-style.css. - // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. - $("head").append( - '' - ); - - // Footer. - var isInitialHiFiPage = location.href === "https://metaverse.highfidelity.com/marketplace?"; - $("body").append( - '
' + - (!isInitialHiFiPage ? '' : '') + - (isInitialHiFiPage ? '🛈 See also other marketplaces.' : '') + - (!isDirectoryPage ? '' : '') + - (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + - '
' - ); - - // Footer actions. - $("#back-button").on("click", function () { - window.history.back(); - }); - $("#all-markets").on("click", function () { - EventBridge.emitWebEvent(GOTO_DIRECTORY); - }); - } - - function injectDirectoryCode() { - - // Remove e-mail hyperlink. - var letUsKnow = $("#letUsKnow"); - letUsKnow.replaceWith(letUsKnow.html()); - - // Add button links. - $('#exploreClaraMarketplace').on('click', function () { - window.location = "https://clara.io/library?gameCheck=true&public=true"; - }); - $('#exploreHifiMarketplace').on('click', function () { - window.location = "http://www.highfidelity.com/marketplace"; - }); - } - - function injectHiFiCode() { - // Nothing to do. - } - - function updateClaraCode() { - // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. - - // Clara library page. - if (location.href.indexOf("clara.io/library") !== -1) { - // Make entries navigate to "Image" view instead of default "Real Time" view. - var elements = $("a.thumbnail"); - for (var i = 0, length = elements.length; i < length; i++) { - var value = elements[i].getAttribute("href"); - if (value.slice(-6) !== "/image") { - elements[i].setAttribute("href", value + "/image"); - } - } - } - - // Clara item page. - if (location.href.indexOf("clara.io/view/") !== -1) { - // Make site navigation links retain gameCheck etc. parameters. - var element = $("a[href^=\'/library\']")[0]; - var parameters = "?gameCheck=true&public=true"; - var href = element.getAttribute("href"); - if (href.slice(-parameters.length) !== parameters) { - element.setAttribute("href", href + parameters); - } - - // Remove unwanted buttons and replace download options with a single "Download to High Fidelity" button. - var buttons = $("a.embed-button").parent("div"); - var downloadFBX; - if (buttons.find("div.btn-group").length > 0) { - buttons.children(".btn-primary, .btn-group , .embed-button").each(function () { this.remove(); }); - if ($("#hifi-download-container").length === 0) { // Button hasn't been moved already. - downloadFBX = $(' Download to High Fidelity'); - buttons.prepend(downloadFBX); - downloadFBX[0].addEventListener("click", startAutoDownload); - } - } - - // Move the "Download to High Fidelity" button to be more visible on tablet. - if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) { - var downloadContainer = $('
'); - $(".top-title .col-sm-4").append(downloadContainer); - downloadContainer.append(downloadFBX); - } - - // Automatic download to High Fidelity. - function startAutoDownload() { - - // One file request at a time. - if (isPreparing) { - console.log("WARNIKNG: Clara.io FBX: Prepare only one download at a time"); - return; - } - - // User must be able to write to Asset Server. - if (!canWriteAssets) { - console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); - EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); - return; - } - - // User must be logged in. - var loginButton = $("#topnav a[href='/signup']"); - if (loginButton.length > 0) { - loginButton[0].click(); - return; - } - - // Obtain zip file to download for requested asset. - // Reference: https://clara.io/learn/sdk/api/export - - //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; - // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to - // be successful in generating zip files. - var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; - - var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; - var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); - - xmlHttpRequest = new XMLHttpRequest(); - var responseTextIndex = 0; - var zipFileURL = ""; - - xmlHttpRequest.onreadystatechange = function () { - // Messages are appended to responseText; process the new ones. - var message = this.responseText.slice(responseTextIndex); - var statusMessage = ""; - - if (isPreparing) { // Ignore messages in flight after finished/cancelled. - var lines = message.split(/[\n\r]+/); - - for (var i = 0, length = lines.length; i < length; i++) { - if (lines[i].slice(0, 5) === "data:") { - // Parse line. - var data; - try { - data = JSON.parse(lines[i].slice(5)); - } - catch (e) { - data = {}; - } - - // Extract status message. - if (data.hasOwnProperty("message") && data.message !== null) { - statusMessage = data.message; - console.log("Clara.io FBX: " + statusMessage); - } - - // Extract zip file URL. - if (data.hasOwnProperty("files") && data.files.length > 0) { - zipFileURL = data.files[0].url; - if (zipFileURL.slice(-4) !== ".zip") { - console.log(JSON.stringify(data)); // Data for debugging. - } - } - } - } - - if (statusMessage !== "") { - // Update the UI with the most recent status message. - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } - } - - responseTextIndex = this.responseText.length; - }; - - // Note: onprogress doesn't have computable total length so can't use it to determine % complete. - - xmlHttpRequest.onload = function () { - var statusMessage = ""; - - if (!isPreparing) { - return; - } - - isPreparing = false; - - var HTTP_OK = 200; - if (this.status !== HTTP_OK) { - statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; - console.log("ERROR: Clara.io FBX: " + statusMessage); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } else if (zipFileURL.slice(-4) !== ".zip") { - statusMessage = "Error creating zip file for download."; - console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } else { - EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL); - console.log("Clara.io FBX: File download initiated for " + zipFileURL); - } - - xmlHttpRequest = null; - } - - isPreparing = true; - - console.log("Clara.io FBX: Request zip file for " + uuid); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download"); - - xmlHttpRequest.open("POST", url, true); - xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); - xmlHttpRequest.send(); - } - } - } - - function injectClaraCode() { - - // Make space for marketplaces footer in Clara pages. - $("head").append( - '' - ); - - // Condense space. - $("head").append( - '' - ); - - // Move "Download to High Fidelity" button. - $("head").append( - '' - ); - - // Update code injected per page displayed. - var updateClaraCodeInterval = undefined; - updateClaraCode(); - updateClaraCodeInterval = setInterval(function () { - updateClaraCode(); - }, 1000); - - window.addEventListener("unload", function () { - clearInterval(updateClaraCodeInterval); - updateClaraCodeInterval = undefined; - }); - - EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS); - } - - function cancelClaraDownload() { - isPreparing = false; - - if (xmlHttpRequest) { - xmlHttpRequest.abort(); - xmlHttpRequest = null; - console.log("Clara.io FBX: File download cancelled"); - EventBridge.emitWebEvent(CLARA_IO_CANCELLED_DOWNLOAD); - } - } - - function onLoad() { - - EventBridge.scriptEventReceived.connect(function (message) { - if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) { - canWriteAssets = message.slice(-4) === "true"; - } - - if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) { - cancelClaraDownload(); - } - }); - - var DIRECTORY = 0; - var HIFI = 1; - var CLARA = 2; - var pageType = DIRECTORY; - - if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } - if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } - - injectCommonCode(pageType === DIRECTORY); - switch (pageType) { - case DIRECTORY: - injectDirectoryCode(); - break; - case HIFI: - injectHiFiCode(); - break; - case CLARA: - injectClaraCode(); - break; - } - } - - // Load / unload. - window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready(). - -}()); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3488352cc0..4d26bcadb6 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -19,7 +19,6 @@ var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); -var MARKETPLACES_INJECT_NO_SCROLLBAR_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInjectNoScrollbar.js"); var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; @@ -61,18 +60,13 @@ function showMarketplace() { shouldActivateButton = true; - // by default the marketplace should NOT have a scrollbar, except when tablet is in toolbar mode. - var injectURL = MARKETPLACES_INJECT_NO_SCROLLBAR_SCRIPT_URL; - if (tablet.toolbarMode) { - injectURL = MARKETPLACES_INJECT_SCRIPT_URL; - } - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, injectURL); + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); onMarketplaceScreen = true; tablet.webEventReceived.connect(function (message) { if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, injectURL); + tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); } if (message === QUERY_CAN_WRITE_ASSETS) {