(mostly?) fix touch events

This commit is contained in:
SamGondelman 2017-11-13 12:58:31 -08:00
parent cbf0543646
commit 2c0272f304
13 changed files with 100 additions and 59 deletions

View file

@ -45,7 +45,7 @@ extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest
Overlays::Overlays() {
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterOverlay);
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent);
@ -741,7 +741,7 @@ void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerE
}
void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) {
emit hoverEnterOverlay(overlayID, event);
hoverEnterPointerEvent(overlayID, event);
}
void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event) {
@ -938,6 +938,21 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
return false;
}
void Overlays::hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
if (getOverlayType(overlayID) == "web3d") {
thisOverlay = std::static_pointer_cast<Web3DOverlay>(getOverlay(overlayID));
}
if (thisOverlay) {
// Send to web overlay
QMetaObject::invokeMethod(thisOverlay.get(), "hoverEnterOverlay", Q_ARG(PointerEvent, event));
}
// emit to scripts
emit hoverEnterOverlay(overlayID, event);
}
void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
@ -1014,7 +1029,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent);
hoverEnterPointerEvent(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.

View file

@ -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 hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);

View file

@ -337,6 +337,14 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) {
_webSurface->setProxyWindow(proxyWindow);
}
void Web3DOverlay::hoverEnterOverlay(const PointerEvent& event) {
if (_inputMode == Mouse) {
handlePointerEvent(event);
} else if (_webSurface) {
_webSurface->hoverBeginEvent(event, _touchDevice);
}
}
void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) {
if (_inputMode == Mouse) {
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
@ -351,12 +359,6 @@ void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) {
}
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 {

View file

@ -39,6 +39,7 @@ public:
QObject* getEventHandler();
void setProxyWindow(QWindow* proxyWindow);
Q_INVOKABLE void hoverEnterOverlay(const PointerEvent& event);
Q_INVOKABLE void hoverLeaveOverlay(const PointerEvent& event);
Q_INVOKABLE void handlePointerEvent(const PointerEvent& event);
void handlePointerEventAsTouch(const PointerEvent& event);
@ -97,7 +98,6 @@ private:
int _geometryId { 0 };
bool _showKeyboardFocusHighlight{ true };
bool _pressed{ false };
QTouchDevice _touchDevice;
uint8_t _desiredMaxFPS { 10 };

View file

@ -83,6 +83,16 @@ 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::hoverEnterEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->getType() == EntityTypes::Web) {
thisEntity = std::static_pointer_cast<render::entities::WebEntityRenderer>(renderableForEntityId(entityID));
}
if (thisEntity) {
QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(PointerEvent, event));
}
});
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;

View file

@ -314,6 +314,12 @@ void WebEntityRenderer::loadSourceURL() {
}
}
void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) {
if (!_lastLocked && _webSurface) {
_webSurface->hoverBeginEvent(event, _touchDevice);
}
}
void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) {
if (!_lastLocked && _webSurface) {
_webSurface->hoverEndEvent(event, _touchDevice);

View file

@ -25,6 +25,7 @@ class WebEntityRenderer : public TypedEntityRenderer<WebEntityItem> {
public:
WebEntityRenderer(const EntityItemPointer& entity);
Q_INVOKABLE void hoverEnterEntity(const PointerEvent& event);
Q_INVOKABLE void hoverLeaveEntity(const PointerEvent& event);
Q_INVOKABLE void handlePointerEvent(const PointerEvent& event);

View file

@ -76,11 +76,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
Buttons buttons;
Buttons newButtons;
Buttons sameButtons;
const std::string PRIMARY_BUTTON = "Primary";
bool primaryPressed = false;
if (_enabled && shouldTrigger(pickResult)) {
buttons = getPressedButtons();
primaryPressed = buttons.find(PRIMARY_BUTTON) != buttons.end();
for (const std::string& button : buttons) {
if (_prevButtons.find(button) == _prevButtons.end()) {
newButtons.insert(button);
@ -97,8 +94,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
hoveredEvent.setType(PointerEvent::Move);
hoveredEvent.setID(pointerID);
bool releaseOnHoverLeave = !primaryPressed || (!_enabled && _prevEnabled) || (!doHover && _prevDoHover);
hoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
bool moveOnHoverLeave = (!_enabled && _prevEnabled) || (!doHover && _prevDoHover);
hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
// if shouldHover && !_prevDoHover, only send hoverBegin
if (_enabled && _hover && doHover && !_prevDoHover) {
@ -117,7 +114,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
} else {
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
prevHoveredEvent.setID(pointerID);
prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
prevHoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent);
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
}
@ -139,7 +136,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
} else {
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
prevHoveredEvent.setID(pointerID);
prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
prevHoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent);
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
}

View file

@ -59,14 +59,14 @@ public:
uint32_t getButtons() const { return _buttons; }
Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; }
bool shouldFocus() const { return _shouldFocus; }
bool sendReleaseOnHoverLeave() const { return _releaseOnHoverLeave; }
bool sendMoveOnHoverLeave() const { return _moveOnHoverLeave; }
void setID(uint32_t id) { _id = id; }
void setType(EventType type) { _type = type; }
void setButton(Button button);
void setShouldFocus(bool focus) { _shouldFocus = focus; }
void setReleaseOnHoverLeave(bool releaseOnHoverLeave) { _releaseOnHoverLeave = releaseOnHoverLeave; }
void setPos2D(const glm::vec2& pos2D) { _pos2D = pos2D; }
void setShouldFocus(bool focus) { _shouldFocus = focus; }
void setMoveOnHoverLeave(bool moveOnHoverLeave) { _moveOnHoverLeave = moveOnHoverLeave; }
static const unsigned int INVALID_POINTER_ID { 0 };
@ -83,7 +83,7 @@ private:
Qt::KeyboardModifiers _keyboardModifiers { Qt::KeyboardModifier::NoModifier }; // set of keys held when event was generated
bool _shouldFocus { true };
bool _releaseOnHoverLeave { true };
bool _moveOnHoverLeave { true };
};
QDebug& operator<<(QDebug& dbg, const PointerEvent& p);

View file

@ -96,7 +96,7 @@ OffscreenUi::OffscreenUi() {
});
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenUi::handlePointerEvent);
connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenUi::hoverBeginEvent);
connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &OffscreenUi::handlePointerEvent);
connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &OffscreenUi::hoverEndEvent);
connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &OffscreenUi::handlePointerEvent);
@ -104,6 +104,10 @@ OffscreenUi::OffscreenUi() {
connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &OffscreenUi::handlePointerEvent);
}
void OffscreenUi::hoverBeginEvent(const PointerEvent& event) {
OffscreenQmlSurface::hoverBeginEvent(event, _touchDevice);
}
void OffscreenUi::hoverEndEvent(const PointerEvent& event) {
OffscreenQmlSurface::hoverEndEvent(event, _touchDevice);
}

View file

@ -250,6 +250,7 @@ public slots:
void removeModalDialog(QObject* modal);
private slots:
void hoverBeginEvent(const PointerEvent& event);
void hoverEndEvent(const PointerEvent& event);
void handlePointerEvent(const PointerEvent& event);

View file

@ -931,7 +931,7 @@ unsigned int OffscreenQmlSurface::deviceIdByTouchPoint(qreal x, qreal y) {
auto mapped = _rootItem->mapFromGlobal(QPoint(x, y));
for (auto pair : _activeTouchPoints) {
if (mapped.x() == (int)pair.second.pos().x() && mapped.y() == (int)pair.second.pos().y()) {
if (mapped.x() == (int)pair.second.touchPoint.pos().x() && mapped.y() == (int)pair.second.touchPoint.pos().y()) {
return pair.first;
}
}
@ -954,45 +954,51 @@ PointerEvent::EventType OffscreenQmlSurface::choosePointerEventType(QEvent::Type
}
}
void OffscreenQmlSurface::hoverBeginEvent(const PointerEvent& event, class QTouchDevice& device) {
handlePointerEvent(event, device);
_activeTouchPoints[event.getID()].hovering = true;
}
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);
_activeTouchPoints[event.getID()].hovering = false;
// Send a fake mouse move event if
// - the event told us to
// - we aren't pressing with this ID
if (event.sendMoveOnHoverLeave() || !_activeTouchPoints[event.getID()].pressed) {
// 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);
// If we aren't pressing, we want to release this TouchPoint
handlePointerEvent(endMoveEvent, device, !_activeTouchPoints[event.getID()].pressed);
}
}
bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device) {
bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release) {
// 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) {
} else if (event.getType() == PointerEvent::Release && event.getButton() == PointerEvent::PrimaryButton) {
state = Qt::TouchPointReleased;
} else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) {
} else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].touchPoint.pos()) {
state = Qt::TouchPointMoved;
}
// Remove the touch point if:
// - this was a hover end event and the mouse wasn't pressed
// - this was a release event and we aren't still hovering
auto touchPoint = _activeTouchPoints.find(event.getID());
bool removeTouchPoint = release || (touchPoint != _activeTouchPoints.end() && !touchPoint->second.hovering && state == Qt::TouchPointReleased);
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())) {
} else if (removeTouchPoint && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) {
// If the last active touch point is being released, send an end
touchType = QEvent::TouchEnd;
}
@ -1003,7 +1009,12 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
point.setState(state);
point.setPos(windowPoint);
point.setScreenPos(windowPoint);
_activeTouchPoints[event.getID()] = point;
_activeTouchPoints[event.getID()].touchPoint = point;
if (state == Qt::TouchPointPressed) {
_activeTouchPoints[event.getID()].pressed = true;
} else if (state == Qt::TouchPointReleased) {
_activeTouchPoints[event.getID()].pressed = false;
}
}
QTouchEvent touchEvent(touchType, &device, event.getKeyboardModifiers());
@ -1011,8 +1022,8 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
QList<QTouchEvent::TouchPoint> touchPoints;
Qt::TouchPointStates touchPointStates;
for (const auto& entry : _activeTouchPoints) {
touchPointStates |= entry.second.state();
touchPoints.push_back(entry.second);
touchPointStates |= entry.second.touchPoint.state();
touchPoints.push_back(entry.second.touchPoint);
}
touchEvent.setWindow(_quickWindow);
@ -1037,7 +1048,6 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
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
@ -1046,7 +1056,6 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
QCoreApplication::sendEvent(_quickWindow, &mouseEvent);
eventsAccepted &= mouseEvent.isAccepted();
}
#endif
if (touchType == QEvent::TouchBegin) {
_touchBeginAccepted = QCoreApplication::sendEvent(_quickWindow, &touchEvent);
@ -1055,22 +1064,10 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
}
eventsAccepted &= touchEvent.isAccepted();
// If this was a release event, remove the point from the active touch points
if (state == Qt::TouchPointReleased) {
if (removeTouchPoint) {
_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;
}

View file

@ -144,8 +144,9 @@ private slots:
void onFocusObjectChanged(QObject* newFocus);
public slots:
void hoverBeginEvent(const PointerEvent& event, class QTouchDevice& device);
void hoverEndEvent(const PointerEvent& event, class QTouchDevice& device);
bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device);
bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device, bool release = false);
private:
QQuickWindow* _quickWindow { nullptr };
@ -173,9 +174,15 @@ private:
QQuickItem* _currentFocusItem { nullptr };
bool _pressed { false };
struct TouchState {
QTouchEvent::TouchPoint touchPoint;
bool hovering { false };
bool pressed { false };
};
bool _pressed;
bool _touchBeginAccepted { false };
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
std::map<uint32_t, TouchState> _activeTouchPoints;
};
#endif