From e9a5acda06373ac98a8a7c70c53f67df29b222c0 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 27 Oct 2017 10:39:37 -0700 Subject: [PATCH 1/2] control hovering, preset buttons for clicking, constness, scroll off surface --- interface/src/raypick/LaserPointer.cpp | 56 ++++++++++++++----- interface/src/raypick/LaserPointer.h | 18 +++--- .../src/raypick/PointerScriptingInterface.cpp | 7 ++- libraries/pointers/src/pointers/Pointer.cpp | 36 ++++++++---- libraries/pointers/src/pointers/Pointer.h | 14 +++-- .../pointers/src/pointers/PointerManager.cpp | 6 +- .../pointers/src/pointers/PointerManager.h | 6 +- 7 files changed, 100 insertions(+), 43 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index b8122a2388..92a404e765 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -17,9 +17,9 @@ #include #include "PickScriptingInterface.h" -LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) : - Pointer(DependencyManager::get()->createRayPick(rayProps), enabled), +LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, + const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) : + Pointer(DependencyManager::get()->createRayPick(rayProps), enabled, hover), _triggers(triggers), _renderStates(renderStates), _defaultRenderStates(defaultRenderStates), @@ -83,7 +83,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& } } -void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) { +void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) { if (!renderState.getStartID().isNull()) { QVariantMap startProps; startProps.insert("position", vec3toVariant(pickRay.origin)); @@ -210,13 +210,13 @@ Pointer::Buttons LaserPointer::getPressedButtons() { return toReturn; } -void LaserPointer::setLength(const float length) { +void LaserPointer::setLength(float length) { withWriteLock([&] { _laserLength = length; }); } -void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { +void LaserPointer::setLockEndUUID(const QUuid& objectID, bool isOverlay) { withWriteLock([&] { _objectLockEnd = std::pair(objectID, isOverlay); }); @@ -281,21 +281,49 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) { return RenderState(startID, pathID, endID); } -PointerEvent LaserPointer::buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const { +PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const { uint32_t id = 0; glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]); glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]); glm::vec3 direction = -surfaceNormal; - IntersectionType type = IntersectionType(pickResult["type"].toUInt()); + QUuid pickedID = pickResult["objectID"].toUuid(); glm::vec2 pos2D; - if (type == ENTITY) { - pos2D = projectOntoEntityXYPlane(uid, intersection); - } else if (type == OVERLAY) { - pos2D = projectOntoOverlayXYPlane(uid, intersection); + if (pickedID != target.objectID) { + QVariantMap searchRay = pickResult["searchRay"].toMap(); + glm::vec3 origin = vec3FromVariant(searchRay["origin"]); + glm::vec3 direction = vec3FromVariant(searchRay["direction"]); + if (target.type == ENTITY) { + intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction); + } else if (target.type == OVERLAY) { + intersection = intersectRayWithOverlayXYPlane(target.objectID, origin, direction); + } + } + if (target.type == ENTITY) { + pos2D = projectOntoEntityXYPlane(target.objectID, intersection); + } else if (target.type == OVERLAY) { + pos2D = projectOntoOverlayXYPlane(target.objectID, intersection); } return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons); } +glm::vec3 LaserPointer::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const { + glm::vec3 n = rotation * Vectors::FRONT; + float t = glm::dot(n, point - origin) / glm::dot(n, direction); + return origin + t * direction; +} + +glm::vec3 LaserPointer::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const { + glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); + glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); + const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f); + return intersectRayWithXYPlane(origin, direction, position, rotation, DEFAULT_REGISTRATION_POINT); +} + +glm::vec3 LaserPointer::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const { + auto props = DependencyManager::get()->getEntityProperties(entityID); + return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint()); +} + glm::vec2 LaserPointer::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const { glm::quat invRot = glm::inverse(rotation); glm::vec3 localPos = invRot * (worldPos - position); @@ -325,7 +353,7 @@ glm::vec2 LaserPointer::projectOntoOverlayXYPlane(const QUuid& overlayID, const return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); } -glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entity, const glm::vec3& worldPos) const { - auto props = DependencyManager::get()->getEntityProperties(entity); +glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const { + auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint()); } \ No newline at end of file diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 167de88385..bdd3f2ffa0 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -54,16 +54,16 @@ public: typedef std::unordered_map RenderStateMap; typedef std::unordered_map> DefaultRenderStateMap; - LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); + LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, + bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled); ~LaserPointer(); void setRenderState(const std::string& state) override; // You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays. void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override; - void setLength(const float length) override; - void setLockEndUUID(QUuid objectID, const bool isOverlay) override; + void setLength(float length) override; + void setLockEndUUID(const QUuid& objectID, bool isOverlay) override; void updateVisuals(const QVariantMap& prevRayPickResult) override; @@ -73,7 +73,7 @@ public: static RenderState buildRenderState(const QVariantMap& propMap); protected: - PointerEvent buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const override; + PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const override; private: PointerTriggers _triggers; @@ -88,10 +88,14 @@ private: std::pair _objectLockEnd { std::pair(QUuid(), false)}; void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); - void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState); + void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState); void disableRenderState(const RenderState& renderState); - glm::vec2 projectOntoEntityXYPlane(const QUuid& entity, const glm::vec3& worldPos) const; + + glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const; + glm::vec3 intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const; + glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const; + glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const; glm::vec2 projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const; glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const; diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 5ac209d295..c3a4ac164a 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -87,6 +87,11 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) } } + bool hover = false; + if (propertyMap["hover"].isValid()) { + hover = propertyMap["hover"].toBool(); + } + PointerTriggers triggers; auto userInputMapper = DependencyManager::get(); if (propertyMap["triggers"].isValid()) { @@ -105,7 +110,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) } } - return DependencyManager::get()->addPointer(std::make_shared(properties, renderStates, defaultRenderStates, triggers, + return DependencyManager::get()->addPointer(std::make_shared(properties, renderStates, defaultRenderStates, hover, triggers, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled)); } diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index 0075762f02..af560a45ab 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -33,7 +33,7 @@ const QVariantMap Pointer::getPrevPickResult() { return DependencyManager::get()->getPrevPickResult(_pickUID); } -void Pointer::setPrecisionPicking(const bool precisionPicking) { +void Pointer::setPrecisionPicking(bool precisionPicking) { DependencyManager::get()->setPrecisionPicking(_pickUID, precisionPicking); } @@ -60,16 +60,17 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Hover events Pointer::PickedObject hoveredObject = getHoveredObject(pickResult); - PointerEvent hoveredEvent = buildPointerEvent(hoveredObject.objectID, pickResult); + PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult); hoveredEvent.setType(PointerEvent::Move); + // TODO: set buttons on hover events hoveredEvent.setButton(PointerEvent::NoButtons); - if (_enabled) { + if (_enabled && _hover) { if (hoveredObject.type == OVERLAY) { if (_prevHoveredObject.type == OVERLAY) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent); } else { - PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject.objectID, pickResult); + PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); } @@ -87,7 +88,7 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent); } else { - PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject.objectID, pickResult); + PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); } @@ -122,7 +123,7 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { const std::string SHOULD_FOCUS_BUTTON = "Focus"; for (const std::string& button : newButtons) { hoveredEvent.setType(PointerEvent::Press); - hoveredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); hoveredEvent.setShouldFocus(button == SHOULD_FOCUS_BUTTON); if (hoveredObject.type == ENTITY) { emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent); @@ -134,9 +135,9 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Trigger continue for (const std::string& button : sameButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button].objectID, pickResult); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); triggeredEvent.setType(PointerEvent::Move); - triggeredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { @@ -146,9 +147,9 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Trigger end for (const std::string& button : _prevButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button].objectID, pickResult); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); triggeredEvent.setType(PointerEvent::Release); - triggeredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { @@ -159,4 +160,19 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { _prevHoveredObject = hoveredObject; _prevButtons = buttons; +} + +PointerEvent::Button Pointer::chooseButton(const std::string& button) { + const std::string PRIMARY_BUTTON = "Primary"; + const std::string SECONDARY_BUTTON = "Secondary"; + const std::string TERTIARY_BUTTON = "Tertiary"; + if (button == PRIMARY_BUTTON) { + return PointerEvent::PrimaryButton; + } else if (button == SECONDARY_BUTTON) { + return PointerEvent::SecondaryButton; + } else if (button == TERTIARY_BUTTON) { + return PointerEvent::TertiaryButton; + } else { + return PointerEvent::NoButtons; + } } \ No newline at end of file diff --git a/libraries/pointers/src/pointers/Pointer.h b/libraries/pointers/src/pointers/Pointer.h index 595daebc33..ca35c38c7a 100644 --- a/libraries/pointers/src/pointers/Pointer.h +++ b/libraries/pointers/src/pointers/Pointer.h @@ -38,7 +38,7 @@ using PointerTriggers = std::vector; class Pointer : protected ReadWriteLockable { public: - Pointer(const QUuid& uid, bool enabled) : _pickUID(uid), _enabled(enabled) {} + Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {} virtual ~Pointer(); @@ -49,13 +49,13 @@ public: virtual void setRenderState(const std::string& state) = 0; virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0; - virtual void setPrecisionPicking(const bool precisionPicking); + virtual void setPrecisionPicking(bool precisionPicking); virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; // Pointers can choose to implement these - virtual void setLength(const float length) {} - virtual void setLockEndUUID(QUuid objectID, const bool isOverlay) {} + virtual void setLength(float length) {} + virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {} void update(); virtual void updateVisuals(const QVariantMap& pickResult) = 0; @@ -79,13 +79,17 @@ public: protected: const QUuid _pickUID; bool _enabled; + bool _hover; - virtual PointerEvent buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const = 0; + virtual PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const = 0; private: PickedObject _prevHoveredObject; Buttons _prevButtons; std::unordered_map _triggeredObjects; + + PointerEvent::Button chooseButton(const std::string& button); + }; #endif // hifi_Pick_h diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp index a475ba4d83..2d41543b6b 100644 --- a/libraries/pointers/src/pointers/PointerManager.cpp +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -79,7 +79,7 @@ void PointerManager::update() { } } -void PointerManager::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const { +void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { auto pointer = find(uid); if (pointer) { pointer->setPrecisionPicking(precisionPicking); @@ -100,14 +100,14 @@ void PointerManager::setIncludeItems(const QUuid& uid, const QVector& inc } } -void PointerManager::setLength(const QUuid& uid, const float length) const { +void PointerManager::setLength(const QUuid& uid, float length) const { auto pointer = find(uid); if (pointer) { pointer->setLength(length); } } -void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const { +void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { auto pointer = find(uid); if (pointer) { pointer->setLockEndUUID(objectID, isOverlay); diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index 6e13c09851..9f477d9eb2 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -31,12 +31,12 @@ public: void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; const QVariantMap getPrevPickResult(const QUuid& uid) const; - void setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const; + void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const; void setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const; void setIncludeItems(const QUuid& uid, const QVector& includeEntities) const; - void setLength(const QUuid& uid, const float length) const; - void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const; + void setLength(const QUuid& uid, float length) const; + void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const; void update(); From 76eb4c656ee8a51a4812faa1a5eb45a1b62464f1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 30 Oct 2017 16:34:02 -0700 Subject: [PATCH 2/2] fix multi-touch and keyboard on web entities --- interface/src/raypick/LaserPointer.cpp | 5 +- interface/src/ui/overlays/Web3DOverlay.cpp | 32 ++-- .../src/RenderableWebEntityItem.cpp | 140 +++++++++++------- .../src/RenderableWebEntityItem.h | 7 +- libraries/pointers/src/pointers/Pointer.cpp | 3 +- 5 files changed, 104 insertions(+), 83 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 92a404e765..83e3757514 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -285,13 +285,12 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const Q uint32_t id = 0; glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]); glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]); - glm::vec3 direction = -surfaceNormal; + QVariantMap searchRay = pickResult["searchRay"].toMap(); + glm::vec3 direction = vec3FromVariant(searchRay["direction"]); QUuid pickedID = pickResult["objectID"].toUuid(); glm::vec2 pos2D; if (pickedID != target.objectID) { - QVariantMap searchRay = pickResult["searchRay"].toMap(); glm::vec3 origin = vec3FromVariant(searchRay["origin"]); - glm::vec3 direction = vec3FromVariant(searchRay["direction"]); if (target.type == ENTITY) { intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction); } else if (target.type == OVERLAY) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 0c75803d35..e32246b666 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -66,7 +66,7 @@ const QString Web3DOverlay::QML = "Web3DOverlay.qml"; Web3DOverlay::Web3DOverlay() : _dpi(DPI) { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); - _touchDevice.setName("RenderableWebEntityItemTouchDevice"); + _touchDevice.setName("Web3DOverlayTouchDevice"); _touchDevice.setMaximumTouchPoints(4); _geometryId = DependencyManager::get()->allocateID(); @@ -332,6 +332,12 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { + if (event.getType() == PointerEvent::Press) { + _pressed = true; + } else if (event.getType() == PointerEvent::Release) { + _pressed = false; + } + if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { @@ -344,19 +350,8 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } - //do not send secondary button events to tablet - if (event.getButton() == PointerEvent::SecondaryButton || - //do not block composed events - event.getButtons() == PointerEvent::SecondaryButton) { - return; - } - - - QPointF windowPoint; - { - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); - windowPoint = QPointF(windowPos.x, windowPos.y); - } + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); + QPointF windowPoint(windowPos.x, windowPos.y); Qt::TouchPointState state = Qt::TouchPointStationary; if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { @@ -395,14 +390,13 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { } touchEvent.setWindow(_webSurface->getWindow()); + touchEvent.setDevice(&_touchDevice); touchEvent.setTarget(_webSurface->getRootItem()); touchEvent.setTouchPoints(touchPoints); touchEvent.setTouchPointStates(touchPointStates); } // 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". // // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will // receive mouse events @@ -449,12 +443,6 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); - if (event.getType() == PointerEvent::Press) { - this->_pressed = true; - } else if (event.getType() == PointerEvent::Release) { - this->_pressed = false; - } - Qt::MouseButtons buttons = Qt::NoButton; if (event.getButtons() & PointerEvent::PrimaryButton) { buttons |= Qt::LeftButton; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index a2e574a829..ad4230c55b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -227,6 +227,8 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { _webSurface->setMaxFps(DEFAULT_MAX_FPS); // FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml. _webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant()); + // Let us interact with the keyboard + _webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get().data()); _fadeStartTime = usecTimestampNow(); loadSourceURL(); _webSurface->resume(); @@ -315,21 +317,9 @@ void WebEntityRenderer::loadSourceURL() { void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { if (!_lastLocked && _webSurface && _pressed) { // If the user mouses off the entity while the button is down, simulate a touch end. - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(Qt::TouchPointReleased); - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI); - QPointF windowPoint(windowPos.x, windowPos.y); - point.setScenePos(windowPoint); - point.setPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); - QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, - Qt::NoModifier, Qt::TouchPointReleased, touchPoints); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setDevice(&_touchDevice); - touchEvent->setTarget(_webSurface->getRootItem()); - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), + event.getButton(), event.getButtons(), event.getKeyboardModifiers()); + handlePointerEvent(endEvent); } } @@ -339,57 +329,95 @@ void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { return; } + if (event.getType() == PointerEvent::Press) { + _pressed = true; + } else if (event.getType() == PointerEvent::Release) { + _pressed = false; + } + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI); QPointF windowPoint(windowPos.x, windowPos.y); - if (event.getType() == PointerEvent::Move) { - // Forward a mouse move event to webSurface - QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + + Qt::TouchPointState state = Qt::TouchPointStationary; + if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { + state = Qt::TouchPointPressed; + } else if (event.getType() == PointerEvent::Release) { + state = Qt::TouchPointReleased; + } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) { + state = Qt::TouchPointMoved; + } + + QEvent::Type touchType = QEvent::TouchUpdate; + if (_activeTouchPoints.empty()) { + // If the first active touch point is being created, send a begin + touchType = QEvent::TouchBegin; + } if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) { + // If the last active touch point is being released, send an end + touchType = QEvent::TouchEnd; } { - // Forward a touch update event to webSurface - if (event.getType() == PointerEvent::Press) { - this->_pressed = true; - } else if (event.getType() == PointerEvent::Release) { - this->_pressed = false; - } - - QEvent::Type type; - Qt::TouchPointState touchPointState; - switch (event.getType()) { - case PointerEvent::Press: - type = QEvent::TouchBegin; - touchPointState = Qt::TouchPointPressed; - break; - case PointerEvent::Release: - type = QEvent::TouchEnd; - touchPointState = Qt::TouchPointReleased; - break; - case PointerEvent::Move: - default: - type = QEvent::TouchUpdate; - touchPointState = Qt::TouchPointMoved; - break; - } - QTouchEvent::TouchPoint point; point.setId(event.getID()); - point.setState(touchPointState); + point.setState(state); point.setPos(windowPoint); point.setScreenPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); - - QTouchEvent* touchEvent = new QTouchEvent(type); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setDevice(&_touchDevice); - touchEvent->setTarget(_webSurface->getRootItem()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(touchPointState); - - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + _activeTouchPoints[event.getID()] = point; } + + QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + { + QList touchPoints; + Qt::TouchPointStates touchPointStates; + for (const auto& entry : _activeTouchPoints) { + touchPointStates |= entry.second.state(); + touchPoints.push_back(entry.second); + } + + touchEvent.setWindow(_webSurface->getWindow()); + touchEvent.setDevice(&_touchDevice); + touchEvent.setTarget(_webSurface->getRootItem()); + touchEvent.setTouchPoints(touchPoints); + touchEvent.setTouchPointStates(touchPointStates); + } + + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // + // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will + // receive mouse events + Qt::MouseButton button = Qt::NoButton; + Qt::MouseButtons buttons = Qt::NoButton; + if (event.getButton() == PointerEvent::PrimaryButton) { + button = Qt::LeftButton; + } + if (event.getButtons() & PointerEvent::PrimaryButton) { + buttons |= Qt::LeftButton; + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if (event.getType() == PointerEvent::Move) { + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); + } +#endif + + if (touchType == QEvent::TouchBegin) { + _touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + } else if (_touchBeginAccepted) { + QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + } + + // If this was a release event, remove the point from the active touch points + if (state == Qt::TouchPointReleased) { + _activeTouchPoints.erase(event.getID()); + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) + if (event.getType() == PointerEvent::Move) { + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); + } +#endif } void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 2d162e57fe..8adbc17a75 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -12,6 +12,8 @@ #include #include "RenderableEntityItem.h" +#include + class OffscreenQmlSurface; class PointerEvent; @@ -58,13 +60,16 @@ private: QSharedPointer _webSurface; glm::vec3 _contextPosition; gpu::TexturePointer _texture; - bool _pressed{ false }; QString _lastSourceUrl; uint16_t _lastDPI; bool _lastLocked; QTimer _timer; uint64_t _lastRenderTime { 0 }; Transform _renderTransform; + + bool _pressed{ false }; + bool _touchBeginAccepted{ false }; + std::map _activeTouchPoints; }; } } // namespace diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index af560a45ab..8796b1e47d 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -23,10 +23,11 @@ void Pointer::enable() { } void Pointer::disable() { - DependencyManager::get()->disablePick(_pickUID); + // Disable the pointer first, then the pick, so someone can't try to use it while it's in a bad state withWriteLock([&] { _enabled = false; }); + DependencyManager::get()->disablePick(_pickUID); } const QVariantMap Pointer::getPrevPickResult() {