diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0db422953f..6038ff6c9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1811,6 +1811,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); DependencyManager::get()->setShouldPickHUDOperator([&]() { return DependencyManager::get()->isHMDMode(); }); + DependencyManager::get()->setCalculatePos2DFromHUDOperator([&](const glm::vec3& intersection) { + const glm::vec2 MARGIN(25.0f); + glm::vec2 maxPos = _controllerScriptingInterface->getViewportDimensions() - MARGIN; + glm::vec2 pos2D = DependencyManager::get()->overlayFromWorldPoint(intersection); + return glm::max(MARGIN, glm::min(pos2D, maxPos)); + }); // Setup the mouse ray pick and related operators DependencyManager::get()->setMouseRayPickID(DependencyManager::get()->addPick(PickQuery::Ray, std::make_shared( diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 83e3757514..d2d829e579 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -301,6 +301,8 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const Q pos2D = projectOntoEntityXYPlane(target.objectID, intersection); } else if (target.type == OVERLAY) { pos2D = projectOntoOverlayXYPlane(target.objectID, intersection); + } else if (target.type == HUD) { + pos2D = DependencyManager::get()->calculatePos2DFromHUD(intersection); } return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b37a376de3..5a7b1bd76c 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -46,7 +46,7 @@ extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest Overlays::Overlays() { auto pointerManager = DependencyManager::get(); connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterOverlay); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverOverlay); + connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent); connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent); connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent); @@ -745,7 +745,7 @@ void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEv } void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - emit hoverOverOverlay(overlayID, event); + hoverOverPointerEvent(overlayID, event); } void Overlays::sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { @@ -940,6 +940,21 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { return false; } +void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { + // TODO: generalize this to allow any overlay to recieve events + std::shared_ptr thisOverlay; + if (getOverlayType(overlayID) == "web3d") { + thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); + } + if (thisOverlay) { + // Send to web overlay + QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); + } + + // emit to scripts + emit hoverOverOverlay(overlayID, event); +} + void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { // TODO: generalize this to allow any overlay to recieve events std::shared_ptr thisOverlay; @@ -1005,7 +1020,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { } // Hover over current overlay. - emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); + hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent); _currentHoverOverOverlayID = rayPickResult.overlayID; } else { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 8bcfa99aba..a5ad013246 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -308,6 +308,7 @@ public slots: void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event); signals: diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ef056b88ac..4dc5729c03 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -83,6 +83,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); diff --git a/libraries/pointers/src/pointers/PickManager.cpp b/libraries/pointers/src/pointers/PickManager.cpp index 571f9f04cd..1c5025250a 100644 --- a/libraries/pointers/src/pointers/PickManager.cpp +++ b/libraries/pointers/src/pointers/PickManager.cpp @@ -9,6 +9,7 @@ PickManager::PickManager() { setShouldPickHUDOperator([]() { return false; }); + setCalculatePos2DFromHUDOperator([](const glm::vec3& intersection) { return glm::vec2(NAN); }); } QUuid PickManager::addPick(PickQuery::PickType type, const std::shared_ptr pick) { diff --git a/libraries/pointers/src/pointers/PickManager.h b/libraries/pointers/src/pointers/PickManager.h index b8abb077c7..e3b1d03080 100644 --- a/libraries/pointers/src/pointers/PickManager.h +++ b/libraries/pointers/src/pointers/PickManager.h @@ -34,9 +34,12 @@ public: void setIncludeItems(const QUuid& uid, const QVector& include) const; void setShouldPickHUDOperator(std::function shouldPickHUDOperator) { _shouldPickHUDOperator = shouldPickHUDOperator; } + void setCalculatePos2DFromHUDOperator(std::function calculatePos2DFromHUDOperator) { _calculatePos2DFromHUDOperator = calculatePos2DFromHUDOperator; } + glm::vec2 calculatePos2DFromHUD(const glm::vec3& intersection) { return _calculatePos2DFromHUDOperator(intersection); } protected: std::function _shouldPickHUDOperator; + std::function _calculatePos2DFromHUDOperator; std::shared_ptr findPick(const QUuid& uid) const; QHash>> _picks; diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index 8796b1e47d..2f92664da0 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -56,7 +56,7 @@ void Pointer::update() { } void Pointer::generatePointerEvents(const QVariantMap& pickResult) { - // TODO: avatars/HUD? + // TODO: avatars? auto pointerManager = DependencyManager::get(); // Hover events @@ -79,6 +79,8 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(_prevHoveredObject.objectID, hoveredEvent); } } } @@ -97,6 +99,22 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); if (_prevHoveredObject.type == OVERLAY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(_prevHoveredObject.objectID, hoveredEvent); + } + } + } + + if (hoveredObject.type == HUD) { + if (_prevHoveredObject.type == HUD) { + // There's only one HUD + emit pointerManager->hoverContinueHUD(hoveredObject.objectID, hoveredEvent); + } else { + emit pointerManager->hoverBeginHUD(hoveredObject.objectID, hoveredEvent); + if (_prevHoveredObject.type == ENTITY) { + emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == OVERLAY) { + emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } } } @@ -130,6 +148,8 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == OVERLAY) { emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent); + } else if (hoveredObject.type == HUD) { + emit pointerManager->triggerBeginHUD(hoveredObject.objectID, hoveredEvent); } _triggeredObjects[button] = hoveredObject; } @@ -143,6 +163,8 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent); } 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); } } @@ -155,6 +177,8 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent); } 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); } _triggeredObjects.erase(button); } diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index 9f477d9eb2..574be8320a 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -58,6 +58,13 @@ signals: void hoverBeginEntity(const QUuid& id, const PointerEvent& pointerEvent); 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); }; #endif // hifi_pointers_PointerManager_h diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index e35832391d..183b7c332a 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -41,6 +41,11 @@ PointerEvent::PointerEvent(EventType type, uint32_t id, ; } +void PointerEvent::setButton(Button button) { + _button = button; + _buttons |= button; +} + QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) { QScriptValue obj = engine->newObject(); diff --git a/libraries/shared/src/PointerEvent.h b/libraries/shared/src/PointerEvent.h index 074e5ab79b..d3b72f072c 100644 --- a/libraries/shared/src/PointerEvent.h +++ b/libraries/shared/src/PointerEvent.h @@ -58,7 +58,7 @@ public: bool shouldFocus() const { return _shouldFocus; } void setType(EventType type) { _type = type; } - void setButton(Button button) { _button = button; } + void setButton(Button button); void setShouldFocus(bool focus) { _shouldFocus = focus; } private: 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/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index ecd07a5874..7c3a159177 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -49,6 +49,8 @@ #include "Logging.h" +#include + Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml") Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl") Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") @@ -529,6 +531,13 @@ bool OffscreenQmlSurface::allowNewFrame(uint8_t fps) { } OffscreenQmlSurface::OffscreenQmlSurface() { + auto pointerManager = DependencyManager::get(); + connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenQmlSurface::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &OffscreenQmlSurface::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &OffscreenQmlSurface::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &OffscreenQmlSurface::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerContinueHUD, this, &OffscreenQmlSurface::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &OffscreenQmlSurface::handlePointerEvent); } OffscreenQmlSurface::~OffscreenQmlSurface() { @@ -932,14 +941,8 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even 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", transformedPos); - } - mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { - return mappedEvent.isAccepted(); + if (sendMouseEvent(mappedEvent)) { + return true; } break; } @@ -951,6 +954,43 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even return false; } +void OffscreenQmlSurface::handlePointerEvent(const QUuid& id, const PointerEvent& event) { + if (_paused) { + return; + } + + QEvent::Type type; + switch (event.getType()) { + case PointerEvent::Press: + qDebug() << event.getPos2D().x << event.getPos2D().y << event.getButton() << Qt::MouseButton(event.getButton()) << Qt::MouseButtons(event.getButtons()); + 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; + } + QMouseEvent mouseEvent(type, QPointF(event.getPos2D().x, event.getPos2D().y), Qt::MouseButton(event.getButton()), Qt::MouseButtons(event.getButtons()), event.getKeyboardModifiers()); + qDebug() << sendMouseEvent(mouseEvent); +} + +bool OffscreenQmlSurface::sendMouseEvent(QMouseEvent& mouseEvent) { + if (mouseEvent.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()); + } + mouseEvent.ignore(); + if (QCoreApplication::sendEvent(_quickWindow, &mouseEvent)) { + return mouseEvent.isAccepted(); + } +} + void OffscreenQmlSurface::pause() { _paused = true; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 12ee9e59a1..3deab07411 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -30,6 +30,8 @@ class QQmlContext; class QQmlComponent; class QQuickWindow; class QQuickItem; +class QMouseEvent; +class PointerEvent; // GPU resources are typically buffered for one copy being used by the renderer, // one copy in flight, and one copy being used by the receiver @@ -135,6 +137,7 @@ private: private slots: void updateQuick(); void onFocusObjectChanged(QObject* newFocus); + void handlePointerEvent(const QUuid& id, const PointerEvent& event); private: QQuickWindow* _quickWindow { nullptr }; @@ -161,6 +164,8 @@ private: QWindow* _proxyWindow { nullptr }; QQuickItem* _currentFocusItem { nullptr }; + + bool sendMouseEvent(QMouseEvent& mouseEvent); }; #endif