diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d81332e7d5..d9770818e1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1504,14 +1504,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); - auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &Application::handleHUDPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &Application::handleHUDPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &Application::handleHUDPointerEvent); - connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &Application::handleHUDPointerEvent); - connect(pointerManager.data(), &PointerManager::triggerContinueHUD, this, &Application::handleHUDPointerEvent); - connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &Application::handleHUDPointerEvent); - // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; @@ -3304,7 +3296,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = compositor.getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); auto button = event->button(); auto buttons = event->buttons(); // Determine if the ReticleClick Action is 1 and if so, fake include the LeftMouseButton @@ -3350,7 +3342,7 @@ void Application::mousePressEvent(QMouseEvent* event) { offscreenUi->unfocusWindows(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), @@ -3380,7 +3372,7 @@ void Application::mousePressEvent(QMouseEvent* event) { void Application::mouseDoublePressEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), @@ -3406,7 +3398,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), @@ -7487,32 +7479,4 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; } - -void Application::handleHUDPointerEvent(const QUuid& id, const PointerEvent& event) { - QEvent::Type type = QEvent::Type::MouseMove; - switch (event.getType()) { - case PointerEvent::Press: - type = QEvent::Type::MouseButtonPress; - break; - case PointerEvent::DoublePress: - type = QEvent::Type::MouseButtonDblClick; - break; - case PointerEvent::Release: - type = QEvent::Type::MouseButtonRelease; - break; - case PointerEvent::Move: - type = QEvent::Type::MouseMove; - break; - default: - break; - } - - QPointF screenPos(event.getPos2D().x, event.getPos2D().y); - //QMouseEvent mouseEvent(type, screenPos, Qt::MouseButton(event.getButton()), Qt::MouseButtons(event.getButtons()), event.getKeyboardModifiers()); - QMouseEvent moveEvent(QEvent::Type::MouseMove, screenPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - sendEvent(_glWidget, &moveEvent); - QMouseEvent mouseEvent(type, screenPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - sendEvent(_glWidget, &mouseEvent); -} - #include "Application.moc" diff --git a/interface/src/Application.h b/interface/src/Application.h index a229c4b91a..d2a55db216 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -434,8 +434,6 @@ private slots: void handleSandboxStatus(QNetworkReply* reply); void switchDisplayMode(); - - void handleHUDPointerEvent(const QUuid& id, const PointerEvent& event); private: static void initDisplay(); void init(); diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 2506c6222d..7d681cd35d 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -303,7 +303,7 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const Q } else if (target.type == HUD) { pos2D = DependencyManager::get()->calculatePos2DFromHUD(intersection); } - return PointerEvent(PointerEvent::Move, 0, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons); + return PointerEvent(pos2D, intersection, surfaceNormal, direction); } 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 { diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index a59e96ccbf..445a7e9b69 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -25,7 +25,7 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, case PickQuery::PickType::Ray: return createRayPick(properties); default: - return 0; + return PickManager::INVALID_PICK_ID; } } @@ -78,7 +78,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { return DependencyManager::get()->addPick(PickQuery::Ray, std::make_shared(position, direction, filter, maxDistance, enabled)); } - return 0; + return PickManager::INVALID_PICK_ID; } void PickScriptingInterface::enablePick(unsigned int uid) { diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index b7211830a8..2f044acc4c 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -26,7 +26,7 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& case PickQuery::PickType::Ray: return createLaserPointer(properties); default: - return 0; + return PointerEvent::INVALID_POINTER_ID; } } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index cdeba865ba..b87a1a994f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -315,111 +315,17 @@ void WebEntityRenderer::loadSourceURL() { } void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { - if (!_lastLocked && _webSurface && _pressed && event.sendReleaseOnHoverLeave()) { - PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), - event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - handlePointerEvent(endEvent); - // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. - PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); - handlePointerEvent(endMoveEvent); + if (!_lastLocked && _webSurface) { + _webSurface->hoverEndEvent(event, _touchDevice); } } -void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { +void WebEntityRenderer::handlePointerEvent(PointerEvent& event) { + event.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); // Ignore mouse interaction if we're locked - if (_lastLocked || !_webSurface) { - return; + if (!_lastLocked && _webSurface) { + _webSurface->handlePointerEvent(event, _touchDevice); } - - 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); - - 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; - } - - { - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(state); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - _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, event.getKeyboardModifiers()); - 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, event.getKeyboardModifiers()); - 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 8adbc17a75..84f2a6ccb7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -12,8 +12,6 @@ #include #include "RenderableEntityItem.h" -#include - class OffscreenQmlSurface; class PointerEvent; @@ -28,7 +26,7 @@ public: WebEntityRenderer(const EntityItemPointer& entity); Q_INVOKABLE void hoverLeaveEntity(const PointerEvent& event); - Q_INVOKABLE void handlePointerEvent(const PointerEvent& event); + Q_INVOKABLE void handlePointerEvent(PointerEvent& event); protected: virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override; @@ -66,10 +64,6 @@ private: 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/Pick.h b/libraries/pointers/src/pointers/Pick.h index 9ab17f87d8..e4ce5d3dce 100644 --- a/libraries/pointers/src/pointers/Pick.h +++ b/libraries/pointers/src/pointers/Pick.h @@ -206,4 +206,13 @@ public: virtual PickResultPointer getHUDIntersection(const T& pick) = 0; }; +namespace std { + template <> + struct hash { + size_t operator()(const PickQuery::PickType& a) const { + return a; + } + }; +} + #endif // hifi_Pick_h diff --git a/libraries/pointers/src/pointers/PickCacheOptimizer.h b/libraries/pointers/src/pointers/PickCacheOptimizer.h index 18285f3a6f..46038d14b5 100644 --- a/libraries/pointers/src/pointers/PickCacheOptimizer.h +++ b/libraries/pointers/src/pointers/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(QHash>& picks, bool shouldPickHUD); + void update(std::unordered_map>& picks, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,10 +67,11 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(QHash>& picks, bool shouldPickHUD) { +void PickCacheOptimizer::update(std::unordered_map>& picks, bool shouldPickHUD) { PickCache results; - for (const auto& uid : picks.keys()) { - std::shared_ptr> pick = std::static_pointer_cast>(picks[uid]); + for (const auto& pickPair : picks) { + unsigned int uid = pickPair.first; + std::shared_ptr> pick = std::static_pointer_cast>(pickPair.second); T mathematicalPick = pick->getMathematicalPick(); PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap()); diff --git a/libraries/pointers/src/pointers/PickManager.cpp b/libraries/pointers/src/pointers/PickManager.cpp index 903113d79d..cb9d715ba9 100644 --- a/libraries/pointers/src/pointers/PickManager.cpp +++ b/libraries/pointers/src/pointers/PickManager.cpp @@ -13,7 +13,7 @@ PickManager::PickManager() { } unsigned int PickManager::addPick(PickQuery::PickType type, const std::shared_ptr pick) { - unsigned int id = 0; + unsigned int id = INVALID_PICK_ID; withWriteLock([&] { // Don't let the pick IDs overflow if (_nextPickID < UINT32_MAX) { @@ -29,7 +29,7 @@ std::shared_ptr PickManager::findPick(unsigned int uid) const { return resultWithReadLock>([&] { auto type = _typeMap.find(uid); if (type != _typeMap.end()) { - return _picks[type.value()][uid]; + return _picks.find(type->second)->second.find(uid)->second; } return std::shared_ptr(); }); @@ -39,8 +39,8 @@ void PickManager::removePick(unsigned int uid) { withWriteLock([&] { auto type = _typeMap.find(uid); if (type != _typeMap.end()) { - _picks[type.value()].remove(uid); - _typeMap.remove(uid); + _picks[type->second].erase(uid); + _typeMap.erase(uid); } }); } @@ -89,7 +89,7 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector& includ } void PickManager::update() { - QHash>> cachedPicks; + std::unordered_map>> cachedPicks; withReadLock([&] { cachedPicks = _picks; }); diff --git a/libraries/pointers/src/pointers/PickManager.h b/libraries/pointers/src/pointers/PickManager.h index 7ff4fcc941..0794ac0a41 100644 --- a/libraries/pointers/src/pointers/PickManager.h +++ b/libraries/pointers/src/pointers/PickManager.h @@ -37,16 +37,16 @@ public: void setCalculatePos2DFromHUDOperator(std::function calculatePos2DFromHUDOperator) { _calculatePos2DFromHUDOperator = calculatePos2DFromHUDOperator; } glm::vec2 calculatePos2DFromHUD(const glm::vec3& intersection) { return _calculatePos2DFromHUDOperator(intersection); } + static const unsigned int INVALID_PICK_ID { 0 }; + protected: std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; std::shared_ptr findPick(unsigned int uid) const; - QHash>> _picks; - QHash _typeMap; - // 0 = invalid - const unsigned int FIRST_PICK_ID { 1 }; - unsigned int _nextPickID { FIRST_PICK_ID }; + std::unordered_map>> _picks; + std::unordered_map _typeMap; + unsigned int _nextPickID { INVALID_PICK_ID + 1 }; PickCacheOptimizer _rayPickCacheOptimizer; }; diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index 354669a55e..0fc5c54ad2 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -95,7 +95,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p } else if (hoveredObject.type == OVERLAY) { emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == HUD) { - emit pointerManager->hoverBeginHUD(hoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverBeginHUD(hoveredEvent); } } else if (_enabled && _hover && doHover) { if (hoveredObject.type == OVERLAY) { @@ -114,7 +114,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { - emit pointerManager->hoverEndHUD(_prevHoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverEndHUD(hoveredEvent); } } } @@ -136,7 +136,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p if (_prevHoveredObject.type == OVERLAY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { - emit pointerManager->hoverEndHUD(_prevHoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverEndHUD(hoveredEvent); } } } @@ -144,9 +144,9 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p if (hoveredObject.type == HUD) { if (_prevHoveredObject.type == HUD) { // There's only one HUD - emit pointerManager->hoverContinueHUD(hoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverContinueHUD(hoveredEvent); } else { - emit pointerManager->hoverBeginHUD(hoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverBeginHUD(hoveredEvent); if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == OVERLAY) { @@ -164,7 +164,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p } else if (_prevHoveredObject.type == OVERLAY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == HUD) { - emit pointerManager->hoverEndHUD(_prevHoveredObject.objectID, hoveredEvent); + emit pointerManager->hoverEndHUD(hoveredEvent); } } } @@ -180,7 +180,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p } else if (hoveredObject.type == OVERLAY) { emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == HUD) { - emit pointerManager->triggerBeginHUD(hoveredObject.objectID, hoveredEvent); + emit pointerManager->triggerBeginHUD(hoveredEvent); } _triggeredObjects[button] = hoveredObject; } @@ -196,7 +196,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p } else if (_triggeredObjects[button].type == OVERLAY) { emit pointerManager->triggerContinueOverlay(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == HUD) { - emit pointerManager->triggerContinueHUD(_triggeredObjects[button].objectID, triggeredEvent); + emit pointerManager->triggerContinueHUD(triggeredEvent); } } @@ -211,7 +211,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const QVariantMap& p } else if (_triggeredObjects[button].type == OVERLAY) { emit pointerManager->triggerEndOverlay(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == HUD) { - emit pointerManager->triggerEndHUD(_triggeredObjects[button].objectID, triggeredEvent); + emit pointerManager->triggerEndHUD(triggeredEvent); } _triggeredObjects.erase(button); } diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp index b6d826c6f1..b117b24770 100644 --- a/libraries/pointers/src/pointers/PointerManager.cpp +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -8,19 +8,21 @@ #include "PointerManager.h" +#include "PickManager.h" + std::shared_ptr PointerManager::find(unsigned int uid) const { return resultWithReadLock>([&] { auto itr = _pointers.find(uid); if (itr != _pointers.end()) { - return *itr; + return itr->second; } return std::shared_ptr(); }); } unsigned int PointerManager::addPointer(std::shared_ptr pointer) { - unsigned int result = 0; - if (pointer->getRayUID() > 0) { + unsigned int result = PointerEvent::INVALID_POINTER_ID; + if (pointer->getRayUID() != PickManager::INVALID_PICK_ID) { withWriteLock([&] { // Don't let the pointer IDs overflow if (_nextPointerID < UINT32_MAX) { @@ -34,7 +36,7 @@ unsigned int PointerManager::addPointer(std::shared_ptr pointer) { void PointerManager::removePointer(unsigned int uid) { withWriteLock([&] { - _pointers.remove(uid); + _pointers.erase(uid); }); } @@ -75,12 +77,12 @@ const QVariantMap PointerManager::getPrevPickResult(unsigned int uid) const { } void PointerManager::update() { - auto cachedPointers = resultWithReadLock>>([&] { + auto cachedPointers = resultWithReadLock>>([&] { return _pointers; }); - for (const auto& uid : cachedPointers.keys()) { - cachedPointers[uid]->update(uid); + for (const auto& pointerPair : cachedPointers) { + pointerPair.second->update(pointerPair.first); } } diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index fcb347fb82..a415c9dbd9 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -40,11 +40,12 @@ public: void update(); + static const unsigned int MOUSE_POINTER_ID { PointerEvent::INVALID_POINTER_ID + 1 }; + private: std::shared_ptr find(unsigned int uid) const; - QHash> _pointers; - // 0 = invalid, 1 = reserved for system mouse - unsigned int _nextPointerID { 2 }; + std::unordered_map> _pointers; + unsigned int _nextPointerID { MOUSE_POINTER_ID + 1 }; signals: void triggerBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent); @@ -61,12 +62,12 @@ signals: void hoverContinueEntity(const QUuid& id, const PointerEvent& pointerEvent); void hoverEndEntity(const QUuid& id, const PointerEvent& pointerEvent); - void triggerBeginHUD(const QUuid& id, const PointerEvent& pointerEvent); - void triggerContinueHUD(const QUuid& id, const PointerEvent& pointerEvent); - void triggerEndHUD(const QUuid& id, const PointerEvent& pointerEvent); - void hoverBeginHUD(const QUuid& id, const PointerEvent& pointerEvent); - void hoverContinueHUD(const QUuid& id, const PointerEvent& pointerEvent); - void hoverEndHUD(const QUuid& id, const PointerEvent& pointerEvent); + void triggerBeginHUD(const PointerEvent& pointerEvent); + void triggerContinueHUD(const PointerEvent& pointerEvent); + void triggerEndHUD(const PointerEvent& pointerEvent); + void hoverBeginHUD(const PointerEvent& pointerEvent); + void hoverContinueHUD(const PointerEvent& pointerEvent); + void hoverEndHUD(const PointerEvent& pointerEvent); }; #endif // hifi_pointers_PointerManager_h diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index 183b7c332a..f0f868655c 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -20,8 +20,28 @@ static bool areFlagsSet(uint32_t flags, uint32_t mask) { return (flags & mask) != 0; } -PointerEvent::PointerEvent() { - ; +PointerEvent::PointerEvent(EventType type, uint32_t id) : + _type(type), + _id(id) +{ +} + +PointerEvent::PointerEvent(EventType type, uint32_t id, const glm::vec2& pos2D, Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers) : + _type(type), + _id(id), + _pos2D(pos2D), + _button(button), + _buttons(buttons), + _keyboardModifiers(keyboardModifiers) +{ +} + +PointerEvent::PointerEvent(const glm::vec2& pos2D, const glm::vec3& pos3D, const glm::vec3& normal, const glm::vec3& direction) : + _pos2D(pos2D), + _pos3D(pos3D), + _normal(normal), + _direction(direction) +{ } PointerEvent::PointerEvent(EventType type, uint32_t id, @@ -38,7 +58,6 @@ PointerEvent::PointerEvent(EventType type, uint32_t id, _buttons(buttons), _keyboardModifiers(keyboardModifiers) { - ; } void PointerEvent::setButton(Button button) { diff --git a/libraries/shared/src/PointerEvent.h b/libraries/shared/src/PointerEvent.h index f6b1a81602..6ace262fe1 100644 --- a/libraries/shared/src/PointerEvent.h +++ b/libraries/shared/src/PointerEvent.h @@ -35,11 +35,14 @@ public: NumEventTypes }; - PointerEvent(); + PointerEvent() {} + PointerEvent(EventType type, uint32_t id); + PointerEvent(EventType type, uint32_t id, const glm::vec2& pos2D, Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers); + PointerEvent(const glm::vec2& pos2D, const glm::vec3& pos3D, const glm::vec3& normal, const glm::vec3& direction); PointerEvent(EventType type, uint32_t id, - const glm::vec2& pos2D = glm::vec2(NAN), const glm::vec3& pos3D = glm::vec3(NAN), - const glm::vec3& normal = glm::vec3(NAN), const glm::vec3& direction = glm::vec3(NAN), - Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::KeyboardModifier::NoModifier); + const glm::vec2& pos2D, const glm::vec3& pos3D, + const glm::vec3& normal, const glm::vec3& direction, + Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers); static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event); static void fromScriptValue(const QScriptValue& object, PointerEvent& event); @@ -63,18 +66,21 @@ public: void setButton(Button button); void setShouldFocus(bool focus) { _shouldFocus = focus; } void setReleaseOnHoverLeave(bool releaseOnHoverLeave) { _releaseOnHoverLeave = releaseOnHoverLeave; } + void setPos2D(const glm::vec2& pos2D) { _pos2D = pos2D; } + + static const unsigned int INVALID_POINTER_ID { 0 }; private: EventType _type; - uint32_t _id; // used to identify the pointer. (left vs right hand, for example) - glm::vec2 _pos2D; // (in meters) projected onto the xy plane of entities dimension box, (0, 0) is upper right hand corner - glm::vec3 _pos3D; // surface location in world coordinates (in meters) - glm::vec3 _normal; // surface normal - glm::vec3 _direction; // incoming direction of pointer ray. + uint32_t _id { INVALID_POINTER_ID }; // used to identify the pointer. (left vs right hand, for example) + glm::vec2 _pos2D { glm::vec2(NAN) }; // (in meters) projected onto the xy plane of entities dimension box, (0, 0) is upper right hand corner + glm::vec3 _pos3D { glm::vec3(NAN) }; // surface location in world coordinates (in meters) + glm::vec3 _normal { glm::vec3(NAN) }; // surface normal + glm::vec3 _direction { glm::vec3(NAN) }; // incoming direction of pointer ray. Button _button { NoButtons }; // button associated with this event, (if type is Press, this will be the button that is pressed) uint32_t _buttons { NoButtons }; // the current state of all the buttons. - Qt::KeyboardModifiers _keyboardModifiers; // set of keys held when event was generated + Qt::KeyboardModifiers _keyboardModifiers { Qt::KeyboardModifier::NoModifier }; // set of keys held when event was generated bool _shouldFocus { true }; bool _releaseOnHoverLeave { true }; diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index f28157ff97..334cce97e5 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,6 +1,7 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns) -link_hifi_libraries(shared networking gl audio) +link_hifi_libraries(shared networking gl audio pointers) +include_hifi_library_headers(controllers) # Required for some low level GL interaction in the OffscreenQMLSurface target_opengl() diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 297ed9ca50..c41ef9d4a4 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -28,6 +28,7 @@ #include "ui/Logging.h" +#include // Needs to match the constants in resources/qml/Global.js class OffscreenFlags : public QObject { @@ -84,7 +85,31 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { return false; } +static QTouchDevice _touchDevice; OffscreenUi::OffscreenUi() { + static std::once_flag once; + std::call_once(once, [&] { + _touchDevice.setCapabilities(QTouchDevice::Position); + _touchDevice.setType(QTouchDevice::TouchScreen); + _touchDevice.setName("OffscreenUiTouchDevice"); + _touchDevice.setMaximumTouchPoints(4); + }); + + auto pointerManager = DependencyManager::get(); + connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &OffscreenUi::hoverEndEvent); + connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerContinueHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &OffscreenUi::handlePointerEvent); +} + +void OffscreenUi::hoverEndEvent(const PointerEvent& event) { + OffscreenQmlSurface::hoverEndEvent(event, _touchDevice); +} + +void OffscreenUi::handlePointerEvent(const PointerEvent& event) { + OffscreenQmlSurface::handlePointerEvent(event, _touchDevice); } QObject* OffscreenUi::getFlags() { @@ -1072,6 +1097,23 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { // let the parent class do it's work bool result = OffscreenQmlSurface::eventFilter(originalDestination, event); + switch (event->type()) { + // Fall through + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: { + QMouseEvent* mouseEvent = static_cast(event); + QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); + PointerEvent pointerEvent(choosePointerEventType(mouseEvent->type()), PointerManager::MOUSE_POINTER_ID, glm::vec2(transformedPos.x(), transformedPos.y()), + PointerEvent::Button(mouseEvent->button()), mouseEvent->buttons(), mouseEvent->modifiers()); + result = OffscreenQmlSurface::handlePointerEvent(pointerEvent, _touchDevice); + break; + } + default: + break; + } + // Check if this is a key press/release event that might need special attention auto type = event->type(); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 93c55d06f7..0261591295 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -249,6 +249,10 @@ signals: public slots: void removeModalDialog(QObject* modal); +private slots: + void hoverEndEvent(const PointerEvent& event); + void handlePointerEvent(const PointerEvent& event); + private: QString fileDialog(const QVariantMap& properties); ModalDialogListener *fileDialogAsync(const QVariantMap &properties); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 08662031da..e2675008f9 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -855,7 +855,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec return QPointF(offscreenPosition.x, offscreenPosition.y); } -QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget) { +QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint) { return _mouseTranslator(originalPoint); } @@ -909,7 +909,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even case QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos(), originalDestination); + QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos()); QWheelEvent mappedEvent( transformedPos, wheelEvent->delta(), wheelEvent->buttons(), @@ -920,30 +920,6 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even } break; } - - // Fall through - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: { - QMouseEvent* mouseEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos(), originalDestination); - QMouseEvent mappedEvent(mouseEvent->type(), - transformedPos, - mouseEvent->screenPos(), mouseEvent->button(), - mouseEvent->buttons(), mouseEvent->modifiers()); - if (event->type() == QEvent::MouseMove) { - // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install - // need to investigate into why this crash is happening. - //_qmlContext->setContextProperty("lastMousePosition", mouseEvent.localPos()); - } - mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { - return mappedEvent.isAccepted(); - } - break; - } - default: break; } @@ -951,6 +927,141 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even return false; } +PointerEvent::EventType OffscreenQmlSurface::choosePointerEventType(QEvent::Type type) { + switch (type) { + case QEvent::MouseButtonDblClick: + return PointerEvent::DoublePress; + case QEvent::MouseButtonPress: + return PointerEvent::Press; + case QEvent::MouseButtonRelease: + return PointerEvent::Release; + case QEvent::MouseMove: + return PointerEvent::Move; + default: + return PointerEvent::Move; + } +} + +void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QTouchDevice& device) { + if (!_paused && _quickWindow && _pressed && event.sendReleaseOnHoverLeave()) { + PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), + event.getButton(), event.getButtons(), event.getKeyboardModifiers()); + handlePointerEvent(endEvent, device); + // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. + PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); + handlePointerEvent(endMoveEvent, device); + } +} + +bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device) { + // Ignore mouse interaction if we're paused + if (_paused || !_quickWindow) { + return false; + } + + if (event.getType() == PointerEvent::Press) { + _pressed = true; + } else if (event.getType() == PointerEvent::Release) { + _pressed = false; + } + + QPointF windowPoint(event.getPos2D().x, event.getPos2D().y); + + 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; + } + + { + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(state); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + _activeTouchPoints[event.getID()] = point; + } + + QTouchEvent touchEvent(touchType, &device, event.getKeyboardModifiers()); + { + QList touchPoints; + Qt::TouchPointStates touchPointStates; + for (const auto& entry : _activeTouchPoints) { + touchPointStates |= entry.second.state(); + touchPoints.push_back(entry.second); + } + + touchEvent.setWindow(_quickWindow); + touchEvent.setDevice(&device); + touchEvent.setTarget(_rootItem); + touchEvent.setTouchPoints(touchPoints); + touchEvent.setTouchPointStates(touchPointStates); + } + + // Send mouse events to the 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; + } + + bool eventsAccepted = false; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if (event.getType() == PointerEvent::Move) { + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install + // need to investigate into why this crash is happening. + //_qmlContext->setContextProperty("lastMousePosition", windowPoint); + QCoreApplication::sendEvent(_quickWindow, &mouseEvent); + eventsAccepted &= mouseEvent.isAccepted(); + } +#endif + + if (touchType == QEvent::TouchBegin) { + _touchBeginAccepted = QCoreApplication::sendEvent(_quickWindow, &touchEvent); + } else if (_touchBeginAccepted) { + QCoreApplication::sendEvent(_quickWindow, &touchEvent); + } + eventsAccepted &= touchEvent.isAccepted(); + + // 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) { + // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install + // need to investigate into why this crash is happening. + //_qmlContext->setContextProperty("lastMousePosition", windowPoint); + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); + eventsAccepted &= mouseEvent.isAccepted(); + } +#endif + + return eventsAccepted; +} + void OffscreenQmlSurface::pause() { _paused = true; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 12ee9e59a1..1f1817faa2 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -21,6 +21,9 @@ #include #include +#include +#include "PointerEvent.h" + class QWindow; class QMyQuickRenderControl; class OffscreenGLCanvas; @@ -79,7 +82,7 @@ public: QObject* getEventHandler(); QQmlContext* getSurfaceContext(); - QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); + QPointF mapToVirtualScreen(const QPointF& originalPoint); bool eventFilter(QObject* originalDestination, QEvent* event) override; void setKeyboardRaised(QObject* object, bool raised, bool numeric = false, bool passwordField = false); @@ -95,6 +98,8 @@ public: static std::function getDiscardLambda(); static size_t getUsedTextureMemory(); + PointerEvent::EventType choosePointerEventType(QEvent::Type type); + signals: void focusObjectChanged(QObject* newFocus); void focusTextChanged(bool focusText); @@ -136,6 +141,10 @@ private slots: void updateQuick(); void onFocusObjectChanged(QObject* newFocus); +public slots: + void hoverEndEvent(const PointerEvent& event, class QTouchDevice& device); + bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device); + private: QQuickWindow* _quickWindow { nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; @@ -161,6 +170,10 @@ private: QWindow* _proxyWindow { nullptr }; QQuickItem* _currentFocusItem { nullptr }; + + bool _pressed { false }; + bool _touchBeginAccepted { false }; + std::map _activeTouchPoints; }; #endif