mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 23:33:48 +02:00
Merge pull request #11719 from SamGondelman/ui
PointerEvents: HUD events, fix entity/overlay hover
This commit is contained in:
commit
179f132fdf
41 changed files with 782 additions and 494 deletions
|
@ -64,11 +64,12 @@ Item {
|
|||
keyItem.state = "mouseOver";
|
||||
|
||||
var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY);
|
||||
var deviceId = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
var hand = deviceId - 1; // based on touchEventUtils.js, deviceId is 'hand + 1', so 'hand' is 'deviceId' - 1
|
||||
var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
|
||||
if (hand == leftHand || hand == rightHand) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, hand);
|
||||
if (Pointers.isLeftHand(pointerID)) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand);
|
||||
} else if (Pointers.isRightHand(pointerID)) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, rightHand);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1812,11 +1812,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||
|
||||
DependencyManager::get<PickManager>()->setShouldPickHUDOperator([&]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
|
||||
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([&](const glm::vec3& intersection) {
|
||||
const glm::vec2 MARGIN(25.0f);
|
||||
glm::vec2 maxPos = _controllerScriptingInterface->getViewportDimensions() - MARGIN;
|
||||
glm::vec2 pos2D = DependencyManager::get<HMDScriptingInterface>()->overlayFromWorldPoint(intersection);
|
||||
return glm::max(MARGIN, glm::min(pos2D, maxPos));
|
||||
});
|
||||
|
||||
// Setup the mouse ray pick and related operators
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(
|
||||
PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true)));
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](QUuid rayPickID) {
|
||||
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](unsigned int rayPickID) {
|
||||
RayToEntityIntersectionResult entityResult;
|
||||
entityResult.intersects = false;
|
||||
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(rayPickID);
|
||||
|
@ -1832,7 +1838,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
}
|
||||
return entityResult;
|
||||
});
|
||||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) {
|
||||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
|
||||
|
@ -3290,7 +3296,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
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
|
||||
|
@ -3336,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(),
|
||||
|
@ -3366,7 +3372,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
void Application::mouseDoublePressEvent(QMouseEvent* event) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
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(),
|
||||
|
@ -3392,7 +3398,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
|
|||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
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(),
|
||||
|
|
|
@ -20,6 +20,9 @@ public:
|
|||
|
||||
PickRay getMathematicalPick() const override;
|
||||
|
||||
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
|
||||
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
|
||||
|
||||
private:
|
||||
std::string _jointName;
|
||||
glm::vec3 _posOffset;
|
||||
|
|
|
@ -290,7 +290,6 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
|
|||
}
|
||||
|
||||
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const {
|
||||
uint32_t id = 0;
|
||||
QUuid pickedID;
|
||||
glm::vec3 intersection, surfaceNormal, direction, origin;
|
||||
if (target.type != NONE) {
|
||||
|
@ -302,7 +301,7 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P
|
|||
origin = vec3FromVariant(searchRay["origin"]);
|
||||
pickedID = rayPickResult->objectID;;
|
||||
}
|
||||
|
||||
|
||||
glm::vec2 pos2D;
|
||||
if (pickedID != target.objectID) {
|
||||
if (target.type == ENTITY) {
|
||||
|
@ -315,8 +314,10 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P
|
|||
pos2D = projectOntoEntityXYPlane(target.objectID, intersection);
|
||||
} else if (target.type == OVERLAY) {
|
||||
pos2D = projectOntoOverlayXYPlane(target.objectID, intersection);
|
||||
} else if (target.type == HUD) {
|
||||
pos2D = DependencyManager::get<PickManager>()->calculatePos2DFromHUD(intersection);
|
||||
}
|
||||
return PointerEvent(PointerEvent::Move, id, 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 {
|
||||
|
|
|
@ -67,14 +67,17 @@ public:
|
|||
|
||||
void updateVisuals(const PickResultPointer& prevRayPickResult) override;
|
||||
|
||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||
Pointer::Buttons getPressedButtons() override;
|
||||
|
||||
static RenderState buildRenderState(const QVariantMap& propMap);
|
||||
|
||||
protected:
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override;
|
||||
|
||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||
Pointer::Buttons getPressedButtons() override;
|
||||
|
||||
bool shouldHover() override { return _currentRenderState != ""; }
|
||||
bool shouldTrigger() override { return _currentRenderState != ""; }
|
||||
|
||||
private:
|
||||
PointerTriggers _triggers;
|
||||
float _laserLength { 0.0f };
|
||||
|
|
|
@ -14,21 +14,22 @@
|
|||
#include "RegisteredMetaTypes.h"
|
||||
#include "PointerScriptingInterface.h"
|
||||
|
||||
void LaserPointerScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) const {
|
||||
void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
||||
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||
}
|
||||
void LaserPointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) const {
|
||||
|
||||
void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
||||
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||
unsigned int LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||
return DependencyManager::get<PointerScriptingInterface>()->createLaserPointer(properties);
|
||||
}
|
||||
|
||||
void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {
|
||||
void LaserPointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
||||
DependencyManager::get<PointerScriptingInterface>()->editRenderState(uid, renderState, properties);
|
||||
}
|
||||
|
||||
QVariantMap LaserPointerScriptingInterface::getPrevRayPickResult(const QUuid& uid) const {
|
||||
QVariantMap LaserPointerScriptingInterface::getPrevRayPickResult(unsigned int uid) const {
|
||||
return DependencyManager::get<PointerScriptingInterface>()->getPrevPickResult(uid);
|
||||
}
|
||||
|
|
|
@ -20,21 +20,25 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createLaserPointer(const QVariant& properties) const;
|
||||
Q_INVOKABLE void enableLaserPointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
Q_INVOKABLE void disableLaserPointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
Q_INVOKABLE void removeLaserPointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const;
|
||||
Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(const QUuid& uid) const;
|
||||
public:
|
||||
Q_INVOKABLE unsigned int createLaserPointer(const QVariant& properties) const;
|
||||
Q_INVOKABLE void enableLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
Q_INVOKABLE void disableLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
Q_INVOKABLE void removeLaserPointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid) const;
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const;
|
||||
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const;
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const;
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const;
|
||||
|
||||
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
|
||||
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isLeftHand(uid); }
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isRightHand(uid); }
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ public:
|
|||
MouseRayPick(const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false);
|
||||
|
||||
PickRay getMathematicalPick() const override;
|
||||
|
||||
bool isMouse() const override { return true; }
|
||||
};
|
||||
|
||||
#endif // hifi_MouseRayPick_h
|
||||
|
|
|
@ -20,16 +20,16 @@
|
|||
#include <pointers/Pick.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
QUuid PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
|
||||
unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
|
||||
switch (type) {
|
||||
case PickQuery::PickType::Ray:
|
||||
return createRayPick(properties);
|
||||
default:
|
||||
return QUuid();
|
||||
return PickManager::INVALID_PICK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
QUuid PickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||
unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||
QVariantMap propMap = properties.toMap();
|
||||
|
||||
bool enabled = false;
|
||||
|
@ -78,22 +78,22 @@ QUuid PickScriptingInterface::createRayPick(const QVariant& properties) {
|
|||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled));
|
||||
}
|
||||
|
||||
return QUuid();
|
||||
return PickManager::INVALID_PICK_ID;
|
||||
}
|
||||
|
||||
void PickScriptingInterface::enablePick(const QUuid& uid) {
|
||||
void PickScriptingInterface::enablePick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->enablePick(uid);
|
||||
}
|
||||
|
||||
void PickScriptingInterface::disablePick(const QUuid& uid) {
|
||||
void PickScriptingInterface::disablePick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->disablePick(uid);
|
||||
}
|
||||
|
||||
void PickScriptingInterface::removePick(const QUuid& uid) {
|
||||
void PickScriptingInterface::removePick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->removePick(uid);
|
||||
}
|
||||
|
||||
QVariantMap PickScriptingInterface::getPrevPickResult(const QUuid& uid) {
|
||||
QVariantMap PickScriptingInterface::getPrevPickResult(unsigned int uid) {
|
||||
QVariantMap result;
|
||||
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
||||
if (pickResult) {
|
||||
|
@ -102,18 +102,30 @@ QVariantMap PickScriptingInterface::getPrevPickResult(const QUuid& uid) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) {
|
||||
void PickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) {
|
||||
void PickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) {
|
||||
DependencyManager::get<PickManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||
}
|
||||
|
||||
void PickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) {
|
||||
void PickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) {
|
||||
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
bool PickScriptingInterface::isLeftHand(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isLeftHand(uid);
|
||||
}
|
||||
|
||||
bool PickScriptingInterface::isRightHand(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isRightHand(uid);
|
||||
}
|
||||
|
||||
bool PickScriptingInterface::isMouse(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isMouse(uid);
|
||||
}
|
||||
|
||||
QScriptValue pickTypesToScriptValue(QScriptEngine* engine, const PickQuery::PickType& pickType) {
|
||||
return pickType;
|
||||
}
|
||||
|
|
|
@ -33,21 +33,25 @@ class PickScriptingInterface : public QObject, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
QUuid createRayPick(const QVariant& properties);
|
||||
unsigned int createRayPick(const QVariant& properties);
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
Q_INVOKABLE void enablePick(unsigned int uid);
|
||||
Q_INVOKABLE void disablePick(unsigned int uid);
|
||||
Q_INVOKABLE void removePick(unsigned int uid);
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
Q_INVOKABLE void enablePick(const QUuid& uid);
|
||||
Q_INVOKABLE void disablePick(const QUuid& uid);
|
||||
Q_INVOKABLE void removePick(const QUuid& uid);
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid);
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities);
|
||||
|
||||
static constexpr unsigned int PICK_NOTHING() { return 0; }
|
||||
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }
|
||||
static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); }
|
||||
|
|
|
@ -16,18 +16,19 @@
|
|||
#include "LaserPointer.h"
|
||||
#include "StylusPointer.h"
|
||||
|
||||
void PointerScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) const {
|
||||
void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const {
|
||||
DependencyManager::get<PointerManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||
}
|
||||
void PointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) const {
|
||||
|
||||
void PointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const {
|
||||
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) {
|
||||
// Interaction with managers should always happen ont he main thread
|
||||
unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) {
|
||||
// Interaction with managers should always happen on the main thread
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
QUuid result;
|
||||
BLOCKING_INVOKE_METHOD(this, "createPointer", Q_RETURN_ARG(QUuid, result), Q_ARG(PickQuery::PickType, type), Q_ARG(QVariant, properties));
|
||||
unsigned int result;
|
||||
BLOCKING_INVOKE_METHOD(this, "createPointer", Q_RETURN_ARG(unsigned int, result), Q_ARG(PickQuery::PickType, type), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -37,11 +38,11 @@ QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type,
|
|||
case PickQuery::PickType::Stylus:
|
||||
return createStylus(properties);
|
||||
default:
|
||||
return QUuid();
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
QUuid PointerScriptingInterface::createStylus(const QVariant& properties) const {
|
||||
unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const {
|
||||
bilateral::Side side = bilateral::Side::Invalid;
|
||||
{
|
||||
QVariant handVar = properties.toMap()["hand"];
|
||||
|
@ -51,13 +52,13 @@ QUuid PointerScriptingInterface::createStylus(const QVariant& properties) const
|
|||
}
|
||||
|
||||
if (bilateral::Side::Invalid == side) {
|
||||
return QUuid();
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<StylusPointer>(side));
|
||||
}
|
||||
|
||||
QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||
unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const {
|
||||
QVariantMap propertyMap = properties.toMap();
|
||||
|
||||
bool faceAvatar = false;
|
||||
|
@ -141,7 +142,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
|
|||
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
|
||||
}
|
||||
|
||||
void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {
|
||||
void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
||||
QVariantMap propMap = properties.toMap();
|
||||
|
||||
QVariant startProps;
|
||||
|
@ -162,12 +163,11 @@ void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString&
|
|||
DependencyManager::get<PointerManager>()->editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps);
|
||||
}
|
||||
|
||||
QVariantMap PointerScriptingInterface::getPrevPickResult(const QUuid& uid) const {
|
||||
QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const {
|
||||
QVariantMap result;
|
||||
auto pickResult = DependencyManager::get<PointerManager>()->getPrevPickResult(uid);
|
||||
if (pickResult) {
|
||||
result = pickResult->toVariantMap();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -19,24 +19,27 @@ class PointerScriptingInterface : public QObject, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
QUuid createLaserPointer(const QVariant& properties) const;
|
||||
QUuid createStylus(const QVariant& properties) const;
|
||||
unsigned int createLaserPointer(const QVariant& properties) const;
|
||||
unsigned int createStylus(const QVariant& properties) const;
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createPointer(const PickQuery::PickType& type, const QVariant& properties);
|
||||
Q_INVOKABLE void enablePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
Q_INVOKABLE void disablePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
Q_INVOKABLE void removePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const;
|
||||
Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid) const;
|
||||
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties);
|
||||
Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const;
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const;
|
||||
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const;
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const;
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const;
|
||||
|
||||
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
|
||||
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isLeftHand(uid); }
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isRightHand(uid); }
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
|
||||
|
||||
signals:
|
||||
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
|
|
|
@ -20,23 +20,23 @@
|
|||
#include "JointRayPick.h"
|
||||
#include "MouseRayPick.h"
|
||||
|
||||
QUuid RayPickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||
unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) {
|
||||
return DependencyManager::get<PickScriptingInterface>()->createRayPick(properties);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::enableRayPick(const QUuid& uid) {
|
||||
void RayPickScriptingInterface::enableRayPick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->enablePick(uid);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::disableRayPick(const QUuid& uid) {
|
||||
void RayPickScriptingInterface::disableRayPick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->disablePick(uid);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::removeRayPick(const QUuid& uid) {
|
||||
void RayPickScriptingInterface::removeRayPick(unsigned int uid) {
|
||||
DependencyManager::get<PickManager>()->removePick(uid);
|
||||
}
|
||||
|
||||
QVariantMap RayPickScriptingInterface::getPrevRayPickResult(const QUuid& uid) {
|
||||
QVariantMap RayPickScriptingInterface::getPrevRayPickResult(unsigned int uid) {
|
||||
QVariantMap result;
|
||||
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResult(uid);
|
||||
if (pickResult) {
|
||||
|
@ -45,14 +45,26 @@ QVariantMap RayPickScriptingInterface::getPrevRayPickResult(const QUuid& uid) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) {
|
||||
void RayPickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(uid, precisionPicking);
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) {
|
||||
void RayPickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) {
|
||||
DependencyManager::get<PickManager>()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems));
|
||||
}
|
||||
|
||||
void RayPickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) {
|
||||
void RayPickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) {
|
||||
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
bool RayPickScriptingInterface::isLeftHand(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isLeftHand(uid);
|
||||
}
|
||||
|
||||
bool RayPickScriptingInterface::isRightHand(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isRightHand(uid);
|
||||
}
|
||||
|
||||
bool RayPickScriptingInterface::isMouse(unsigned int uid) {
|
||||
return DependencyManager::get<PickManager>()->isMouse(uid);
|
||||
}
|
|
@ -36,17 +36,22 @@ class RayPickScriptingInterface : public QObject, public Dependency {
|
|||
Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT)
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
Q_INVOKABLE unsigned int createRayPick(const QVariant& properties);
|
||||
Q_INVOKABLE void enableRayPick(unsigned int uid);
|
||||
Q_INVOKABLE void disableRayPick(unsigned int uid);
|
||||
Q_INVOKABLE void removeRayPick(unsigned int uid);
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid);
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createRayPick(const QVariant& properties);
|
||||
Q_INVOKABLE void enableRayPick(const QUuid& uid);
|
||||
Q_INVOKABLE void disableRayPick(const QUuid& uid);
|
||||
Q_INVOKABLE void removeRayPick(const QUuid& uid);
|
||||
Q_INVOKABLE QVariantMap getPrevRayPickResult(const QUuid& uid);
|
||||
|
||||
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities);
|
||||
|
||||
static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); }
|
||||
static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); }
|
||||
static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); }
|
||||
|
|
|
@ -448,7 +448,7 @@ void StylusPointer::updateStylusTarget() {
|
|||
}
|
||||
}
|
||||
|
||||
void StylusPointer::update(float deltaTime) {
|
||||
void StylusPointer::update(unsigned int pointerID, float deltaTime) {
|
||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||
withReadLock([&] {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
|
||||
void enable() override;
|
||||
void disable() override;
|
||||
void update(float deltaTime) override;
|
||||
void update(unsigned int pointerID, float deltaTime) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -97,6 +97,8 @@ private:
|
|||
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {}
|
||||
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) override { return PickedObject(); }
|
||||
virtual Buttons getPressedButtons() override { return {}; }
|
||||
bool shouldHover() override { return true; }
|
||||
bool shouldTrigger() override { return true; }
|
||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override { return PointerEvent(); }
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <commerce/Ledger.h>
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
@ -79,8 +81,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
static const uint32_t MOUSE_HW_ID = 0;
|
||||
static const uint32_t LEFT_HAND_HW_ID = 1;
|
||||
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters
|
||||
static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims
|
||||
|
@ -100,7 +100,7 @@ void ContextOverlayInterface::setEnabled(bool enabled) {
|
|||
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
|
||||
if (contextOverlayFilterPassed(entityItemID)) {
|
||||
if (event.getID() == MOUSE_HW_ID) {
|
||||
if (event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get<PointerManager>()->isMouse(event.getID())) {
|
||||
enableEntityHighlight(entityItemID);
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
|
|||
glm::vec3 normal;
|
||||
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
|
||||
float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE;
|
||||
if (event.getID() == LEFT_HAND_HW_ID) {
|
||||
if (DependencyManager::get<PointerManager>()->isLeftHand(event.getID())) {
|
||||
offsetAngle *= -1.0f;
|
||||
}
|
||||
contextOverlayPosition = cameraPosition +
|
||||
|
@ -253,13 +253,15 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID&
|
|||
}
|
||||
|
||||
void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) {
|
||||
if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != MOUSE_HW_ID) {
|
||||
bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get<PointerManager>()->isMouse(event.getID());
|
||||
if (contextOverlayFilterPassed(entityID) && _enabled && !isMouse) {
|
||||
enableEntityHighlight(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) {
|
||||
if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != MOUSE_HW_ID) {
|
||||
bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get<PointerManager>()->isMouse(event.getID());
|
||||
if (_currentEntityWithContextOverlay != entityID && _enabled && !isMouse) {
|
||||
disableEntityHighlight(entityID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,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::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) {
|
||||
|
@ -784,8 +784,6 @@ float Overlays::height() {
|
|||
return offscreenUi->getWindow()->size().height();
|
||||
}
|
||||
|
||||
static const uint32_t MOUSE_POINTER_ID = 0;
|
||||
|
||||
static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay,
|
||||
const RayToOverlayIntersectionResult& rayPickResult) {
|
||||
|
||||
|
@ -846,7 +844,7 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay
|
|||
|
||||
glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult);
|
||||
|
||||
PointerEvent pointerEvent(eventType, MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal,
|
||||
PointerEvent pointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal,
|
||||
ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers());
|
||||
|
||||
return pointerEvent;
|
||||
|
@ -940,6 +938,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<Web3DOverlay> thisOverlay;
|
||||
if (getOverlayType(overlayID) == "web3d") {
|
||||
thisOverlay = std::static_pointer_cast<Web3DOverlay>(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<Web3DOverlay> thisOverlay;
|
||||
|
@ -1005,7 +1018,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
// Hover over current overlay.
|
||||
emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent);
|
||||
hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent);
|
||||
|
||||
_currentHoverOverOverlayID = rayPickResult.overlayID;
|
||||
} else {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -56,6 +56,8 @@
|
|||
#include "ui/Snapshot.h"
|
||||
#include "SoundCache.h"
|
||||
|
||||
#include "raypick/PointerScriptingInterface.h"
|
||||
|
||||
static const float DPI = 30.47f;
|
||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
static const float METERS_TO_INCHES = 39.3701f;
|
||||
|
@ -160,14 +162,6 @@ void Web3DOverlay::buildWebSurface() {
|
|||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
}
|
||||
|
||||
void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) {
|
||||
if ((_pressed || (!_activeTouchPoints.empty() && _touchBeginAccepted))) {
|
||||
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
|
||||
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
|
||||
handlePointerEvent(endEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::update(float deltatime) {
|
||||
if (_webSurface) {
|
||||
// update globalPosition
|
||||
|
@ -224,6 +218,7 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
|
||||
|
@ -253,18 +248,12 @@ void Web3DOverlay::onResizeWebSurface() {
|
|||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||
}
|
||||
|
||||
const int INVALID_DEVICE_ID = -1;
|
||||
|
||||
Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
auto mapped = _webSurface->getRootItem()->mapFromGlobal(QPoint(x, y));
|
||||
|
||||
for (auto pair : _activeTouchPoints) {
|
||||
if (mapped.x() == (int)pair.second.pos().x() && mapped.y() == (int)pair.second.pos().y()) {
|
||||
return pair.first;
|
||||
}
|
||||
unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
if (_webSurface) {
|
||||
return _webSurface->deviceIdByTouchPoint(x, y);
|
||||
} else {
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
|
||||
return INVALID_DEVICE_ID;
|
||||
}
|
||||
|
||||
void Web3DOverlay::render(RenderArgs* args) {
|
||||
|
@ -348,6 +337,19 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) {
|
|||
_webSurface->setProxyWindow(proxyWindow);
|
||||
}
|
||||
|
||||
void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) {
|
||||
if (_inputMode == Mouse) {
|
||||
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);
|
||||
} else if (_webSurface) {
|
||||
_webSurface->hoverEndEvent(event, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
_pressed = true;
|
||||
|
@ -363,93 +365,11 @@ void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
|
|||
}
|
||||
|
||||
void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
|
||||
if (!_webSurface) {
|
||||
return;
|
||||
if (_webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->handlePointerEvent(webEvent, _touchDevice);
|
||||
}
|
||||
|
||||
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) {
|
||||
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<QTouchEvent::TouchPoint> 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 Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {
|
||||
|
@ -485,7 +405,7 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
|
||||
QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers());
|
||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
void destroyWebSurface();
|
||||
void onResizeWebSurface();
|
||||
|
||||
Q_INVOKABLE int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
@ -98,8 +98,6 @@ private:
|
|||
bool _showKeyboardFocusHighlight{ true };
|
||||
|
||||
bool _pressed{ false };
|
||||
bool _touchBeginAccepted { false };
|
||||
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
|
||||
QTouchDevice _touchDevice;
|
||||
|
||||
uint8_t _desiredMaxFPS { 10 };
|
||||
|
|
|
@ -51,11 +51,11 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
_displayModelBounds(false),
|
||||
_layeredZones(this)
|
||||
{
|
||||
setMouseRayPickResultOperator([](QUuid rayPickID) {
|
||||
setMouseRayPickResultOperator([](unsigned int rayPickID) {
|
||||
RayToEntityIntersectionResult entityResult;
|
||||
return entityResult;
|
||||
});
|
||||
setSetPrecisionPickingOperator([](QUuid rayPickID, bool value) {});
|
||||
setSetPrecisionPickingOperator([](unsigned int rayPickID, bool value) {});
|
||||
EntityRenderer::initEntityRenderers();
|
||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
|
@ -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<render::entities::WebEntityRenderer> thisEntity;
|
||||
auto entity = getEntity(entityID);
|
||||
|
@ -570,8 +571,6 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
|
|||
}
|
||||
}
|
||||
|
||||
static const uint32_t MOUSE_POINTER_ID = 0;
|
||||
|
||||
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||
// process these events.
|
||||
|
@ -592,7 +591,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
@ -624,7 +623,7 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {
|
|||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects && rayPickResult.entity) {
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier);
|
||||
|
@ -656,7 +655,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
|||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
@ -672,7 +671,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
|||
// we're releasing the button, then this is considered a clickReleaseOn event
|
||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
@ -698,7 +697,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects && rayPickResult.entity) {
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
@ -712,7 +711,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
// then we need to send the hover leave.
|
||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
@ -743,7 +742,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
// send the hover leave for our previous entity
|
||||
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
|
||||
PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event),
|
||||
|
|
|
@ -74,9 +74,9 @@ public:
|
|||
static float getEntityLoadingPriority(const EntityItem& item) { return _calculateEntityLoadingPriorityFunc(item); }
|
||||
static void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { _calculateEntityLoadingPriorityFunc = fn; }
|
||||
|
||||
void setMouseRayPickID(QUuid rayPickID) { _mouseRayPickID = rayPickID; }
|
||||
void setMouseRayPickResultOperator(std::function<RayToEntityIntersectionResult(QUuid)> getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
|
||||
void setSetPrecisionPickingOperator(std::function<void(QUuid, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
||||
void setMouseRayPickID(unsigned int rayPickID) { _mouseRayPickID = rayPickID; }
|
||||
void setMouseRayPickResultOperator(std::function<RayToEntityIntersectionResult(unsigned int)> getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
|
||||
void setSetPrecisionPickingOperator(std::function<void(unsigned int, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
||||
|
||||
void shutdown();
|
||||
void update(bool simulate);
|
||||
|
@ -182,9 +182,9 @@ private:
|
|||
|
||||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
||||
QUuid _mouseRayPickID;
|
||||
std::function<RayToEntityIntersectionResult(QUuid)> _getPrevRayPickResultOperator;
|
||||
std::function<void(QUuid, bool)> _setPrecisionPickingOperator;
|
||||
unsigned int _mouseRayPickID;
|
||||
std::function<RayToEntityIntersectionResult(unsigned int)> _getPrevRayPickResultOperator;
|
||||
std::function<void(unsigned int, bool)> _setPrecisionPickingOperator;
|
||||
|
||||
class LayeredZone {
|
||||
public:
|
||||
|
|
|
@ -315,109 +315,18 @@ 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.
|
||||
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
|
||||
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
|
||||
handlePointerEvent(endEvent);
|
||||
if (!_lastLocked && _webSurface) {
|
||||
_webSurface->hoverEndEvent(event, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) {
|
||||
// Ignore mouse interaction if we're locked
|
||||
if (_lastLocked || !_webSurface) {
|
||||
return;
|
||||
if (!_lastLocked && _webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
_webSurface->handlePointerEvent(webEvent, _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<QTouchEvent::TouchPoint> 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) {
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <WebEntityItem.h>
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
#include <QTouchEvent>
|
||||
|
||||
class OffscreenQmlSurface;
|
||||
class PointerEvent;
|
||||
|
||||
|
@ -66,10 +64,6 @@ private:
|
|||
QTimer _timer;
|
||||
uint64_t _lastRenderTime { 0 };
|
||||
Transform _renderTransform;
|
||||
|
||||
bool _pressed{ false };
|
||||
bool _touchBeginAccepted{ false };
|
||||
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
|
|
@ -182,6 +182,10 @@ public:
|
|||
void setIgnoreItems(const QVector<QUuid>& items);
|
||||
void setIncludeItems(const QVector<QUuid>& items);
|
||||
|
||||
virtual bool isLeftHand() const { return false; }
|
||||
virtual bool isRightHand() const { return false; }
|
||||
virtual bool isMouse() const { return false; }
|
||||
|
||||
private:
|
||||
PickFilter _filter;
|
||||
const float _maxDistance;
|
||||
|
@ -206,4 +210,13 @@ public:
|
|||
virtual PickResultPointer getHUDIntersection(const T& pick) = 0;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<PickQuery::PickType> {
|
||||
size_t operator()(const PickQuery::PickType& a) const {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_Pick_h
|
||||
|
|
|
@ -37,7 +37,7 @@ template<typename T>
|
|||
class PickCacheOptimizer {
|
||||
|
||||
public:
|
||||
void update(QHash<QUuid, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD);
|
||||
void update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD);
|
||||
|
||||
protected:
|
||||
typedef std::unordered_map<T, std::unordered_map<PickCacheKey, PickResultPointer>> PickCache;
|
||||
|
@ -67,22 +67,19 @@ void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultP
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void PickCacheOptimizer<T>::update(QHash<QUuid, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD) {
|
||||
void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD) {
|
||||
PickCache results;
|
||||
for (const auto& uid : picks.keys()) {
|
||||
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(picks[uid]);
|
||||
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& pickPair : picks) {
|
||||
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(pickPair.second);
|
||||
|
||||
T mathematicalPick = pick->getMathematicalPick();
|
||||
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
|
||||
|
||||
if (!mathematicalPick) {
|
||||
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
|
||||
pick->setPickResult(res);
|
||||
continue;
|
||||
}
|
||||
|
||||
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
|
||||
|
||||
if (pick->getFilter().doesPickEntities()) {
|
||||
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
|
||||
|
|
|
@ -9,38 +9,43 @@
|
|||
|
||||
PickManager::PickManager() {
|
||||
setShouldPickHUDOperator([]() { return false; });
|
||||
setCalculatePos2DFromHUDOperator([](const glm::vec3& intersection) { return glm::vec2(NAN); });
|
||||
}
|
||||
|
||||
QUuid PickManager::addPick(PickQuery::PickType type, const std::shared_ptr<PickQuery> pick) {
|
||||
QUuid id = QUuid::createUuid();
|
||||
unsigned int PickManager::addPick(PickQuery::PickType type, const std::shared_ptr<PickQuery> pick) {
|
||||
unsigned int id = INVALID_PICK_ID;
|
||||
withWriteLock([&] {
|
||||
_picks[type][id] = pick;
|
||||
_typeMap[id] = type;
|
||||
// Don't let the pick IDs overflow
|
||||
if (_nextPickID < UINT32_MAX) {
|
||||
id = _nextPickID++;
|
||||
_picks[type][id] = pick;
|
||||
_typeMap[id] = type;
|
||||
}
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
std::shared_ptr<PickQuery> PickManager::findPick(const QUuid& uid) const {
|
||||
std::shared_ptr<PickQuery> PickManager::findPick(unsigned int uid) const {
|
||||
return resultWithReadLock<std::shared_ptr<PickQuery>>([&] {
|
||||
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<PickQuery>();
|
||||
});
|
||||
}
|
||||
|
||||
void PickManager::removePick(const QUuid& uid) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PickResultPointer PickManager::getPrevPickResult(const QUuid& uid) const {
|
||||
PickResultPointer PickManager::getPrevPickResult(unsigned int uid) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
return pick->getPrevPickResult();
|
||||
|
@ -48,35 +53,35 @@ PickResultPointer PickManager::getPrevPickResult(const QUuid& uid) const {
|
|||
return PickResultPointer();
|
||||
}
|
||||
|
||||
void PickManager::enablePick(const QUuid& uid) const {
|
||||
void PickManager::enablePick(unsigned int uid) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->enable();
|
||||
}
|
||||
}
|
||||
|
||||
void PickManager::disablePick(const QUuid& uid) const {
|
||||
void PickManager::disablePick(unsigned int uid) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->disable();
|
||||
}
|
||||
}
|
||||
|
||||
void PickManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const {
|
||||
void PickManager::setPrecisionPicking(unsigned int uid, bool precisionPicking) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->setPrecisionPicking(precisionPicking);
|
||||
}
|
||||
}
|
||||
|
||||
void PickManager::setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignore) const {
|
||||
void PickManager::setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->setIgnoreItems(ignore);
|
||||
}
|
||||
}
|
||||
|
||||
void PickManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>& include) const {
|
||||
void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
pick->setIncludeItems(include);
|
||||
|
@ -84,7 +89,7 @@ void PickManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>& includ
|
|||
}
|
||||
|
||||
void PickManager::update() {
|
||||
QHash<PickQuery::PickType, QHash<QUuid, std::shared_ptr<PickQuery>>> cachedPicks;
|
||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;
|
||||
withReadLock([&] {
|
||||
cachedPicks = _picks;
|
||||
});
|
||||
|
@ -92,4 +97,28 @@ void PickManager::update() {
|
|||
bool shouldPickHUD = _shouldPickHUDOperator();
|
||||
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD);
|
||||
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], false);
|
||||
}
|
||||
|
||||
bool PickManager::isLeftHand(unsigned int uid) {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
return pick->isLeftHand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PickManager::isRightHand(unsigned int uid) {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
return pick->isRightHand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PickManager::isMouse(unsigned int uid) {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
return pick->isMouse();
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -22,30 +22,40 @@ public:
|
|||
|
||||
void update();
|
||||
|
||||
QUuid addPick(PickQuery::PickType type, const std::shared_ptr<PickQuery> pick);
|
||||
void removePick(const QUuid& uid);
|
||||
void enablePick(const QUuid& uid) const;
|
||||
void disablePick(const QUuid& uid) const;
|
||||
unsigned int addPick(PickQuery::PickType type, const std::shared_ptr<PickQuery> pick);
|
||||
void removePick(unsigned int uid);
|
||||
void enablePick(unsigned int uid) const;
|
||||
void disablePick(unsigned int uid) const;
|
||||
|
||||
PickResultPointer getPrevPickResult(const QUuid& uid) const;
|
||||
PickResultPointer getPrevPickResult(unsigned int uid) const;
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> getPrevPickResultTyped(const QUuid& uid) const {
|
||||
std::shared_ptr<T> getPrevPickResultTyped(unsigned int uid) const {
|
||||
return std::static_pointer_cast<T>(getPrevPickResult(uid));
|
||||
}
|
||||
|
||||
void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignore) const;
|
||||
void setIncludeItems(const QUuid& uid, const QVector<QUuid>& include) const;
|
||||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
|
||||
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
|
||||
|
||||
bool isLeftHand(unsigned int uid);
|
||||
bool isRightHand(unsigned int uid);
|
||||
bool isMouse(unsigned int uid);
|
||||
|
||||
void setShouldPickHUDOperator(std::function<bool()> shouldPickHUDOperator) { _shouldPickHUDOperator = shouldPickHUDOperator; }
|
||||
void setCalculatePos2DFromHUDOperator(std::function<glm::vec2(const glm::vec3&)> calculatePos2DFromHUDOperator) { _calculatePos2DFromHUDOperator = calculatePos2DFromHUDOperator; }
|
||||
glm::vec2 calculatePos2DFromHUD(const glm::vec3& intersection) { return _calculatePos2DFromHUDOperator(intersection); }
|
||||
|
||||
static const unsigned int INVALID_PICK_ID { 0 };
|
||||
|
||||
protected:
|
||||
std::function<bool()> _shouldPickHUDOperator;
|
||||
std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator;
|
||||
|
||||
std::shared_ptr<PickQuery> findPick(const QUuid& uid) const;
|
||||
QHash<PickQuery::PickType, QHash<QUuid, std::shared_ptr<PickQuery>>> _picks;
|
||||
QHash<QUuid, PickQuery::PickType> _typeMap;
|
||||
std::shared_ptr<PickQuery> findPick(unsigned int uid) const;
|
||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> _picks;
|
||||
std::unordered_map<unsigned int, PickQuery::PickType> _typeMap;
|
||||
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
||||
|
||||
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
||||
PickCacheOptimizer<StylusTip> _stylusPickCacheOptimizer;
|
||||
|
|
|
@ -46,32 +46,78 @@ void Pointer::setIncludeItems(const QVector<QUuid>& includeItems) const {
|
|||
DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems);
|
||||
}
|
||||
|
||||
void Pointer::update(float deltaTime) {
|
||||
bool Pointer::isLeftHand() const {
|
||||
return DependencyManager::get<PickManager>()->isLeftHand(_pickUID);
|
||||
}
|
||||
|
||||
bool Pointer::isRightHand() const {
|
||||
return DependencyManager::get<PickManager>()->isRightHand(_pickUID);
|
||||
}
|
||||
|
||||
bool Pointer::isMouse() const {
|
||||
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
|
||||
}
|
||||
|
||||
void Pointer::update(unsigned int pointerID, float deltaTime) {
|
||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||
withReadLock([&] {
|
||||
auto pickResult = getPrevPickResult();
|
||||
updateVisuals(pickResult);
|
||||
generatePointerEvents(pickResult);
|
||||
generatePointerEvents(pointerID, pickResult);
|
||||
});
|
||||
}
|
||||
|
||||
void Pointer::generatePointerEvents(const PickResultPointer& pickResult) {
|
||||
void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult) {
|
||||
// TODO: avatars/HUD?
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
|
||||
// NOTE: After this loop: _prevButtons = buttons that were removed
|
||||
// If switching to disabled or should stop triggering, release all buttons
|
||||
Buttons buttons;
|
||||
Buttons newButtons;
|
||||
Buttons sameButtons;
|
||||
const std::string PRIMARY_BUTTON = "Primary";
|
||||
bool primaryPressed = false;
|
||||
if (_enabled && shouldTrigger()) {
|
||||
buttons = getPressedButtons();
|
||||
primaryPressed = buttons.find(PRIMARY_BUTTON) != buttons.end();
|
||||
for (const std::string& button : buttons) {
|
||||
if (_prevButtons.find(button) == _prevButtons.end()) {
|
||||
newButtons.insert(button);
|
||||
} else {
|
||||
sameButtons.insert(button);
|
||||
_prevButtons.erase(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hover events
|
||||
bool doHover = shouldHover();
|
||||
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
|
||||
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
|
||||
hoveredEvent.setType(PointerEvent::Move);
|
||||
// TODO: set buttons on hover events
|
||||
hoveredEvent.setButton(PointerEvent::NoButtons);
|
||||
if (_enabled && _hover) {
|
||||
hoveredEvent.setID(pointerID);
|
||||
bool releaseOnHoverLeave = !primaryPressed || (!_enabled && _prevEnabled) || (!doHover && _prevDoHover);
|
||||
hoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
|
||||
|
||||
// if shouldHover && !_prevDoHover, only send hoverBegin
|
||||
if (_enabled && _hover && doHover && !_prevDoHover) {
|
||||
if (hoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
} else if (hoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
} else if (hoveredObject.type == HUD) {
|
||||
emit pointerManager->hoverBeginHUD(hoveredEvent);
|
||||
}
|
||||
} else if (_enabled && _hover && doHover) {
|
||||
if (hoveredObject.type == OVERLAY) {
|
||||
if (_prevHoveredObject.type == OVERLAY) {
|
||||
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
|
||||
emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
} else {
|
||||
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
|
||||
prevHoveredEvent.setID(pointerID);
|
||||
prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent);
|
||||
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
|
@ -79,6 +125,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& 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(hoveredEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +138,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) {
|
|||
emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent);
|
||||
} else {
|
||||
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
|
||||
prevHoveredEvent.setID(pointerID);
|
||||
prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave);
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent);
|
||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
|
@ -97,25 +147,36 @@ void Pointer::generatePointerEvents(const PickResultPointer& 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(hoveredEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hoveredObject.type == HUD) {
|
||||
if (_prevHoveredObject.type == HUD) {
|
||||
// There's only one HUD
|
||||
emit pointerManager->hoverContinueHUD(hoveredEvent);
|
||||
} else {
|
||||
emit pointerManager->hoverBeginHUD(hoveredEvent);
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger events
|
||||
Buttons buttons;
|
||||
Buttons newButtons;
|
||||
Buttons sameButtons;
|
||||
// NOTE: After this loop: _prevButtons = buttons that were removed
|
||||
// If !_enabled, release all buttons
|
||||
if (_enabled) {
|
||||
buttons = getPressedButtons();
|
||||
for (const std::string& button : buttons) {
|
||||
if (_prevButtons.find(button) == _prevButtons.end()) {
|
||||
newButtons.insert(button);
|
||||
} else {
|
||||
sameButtons.insert(button);
|
||||
_prevButtons.erase(button);
|
||||
if (_hover) {
|
||||
// send hoverEnd events if we disable the pointer, disable hovering, or actually stop hovering over an object
|
||||
if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover) || (hoveredObject.type == NONE && _prevHoveredObject.type != NONE)) {
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == HUD) {
|
||||
emit pointerManager->hoverEndHUD(hoveredEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +191,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& 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(hoveredEvent);
|
||||
}
|
||||
_triggeredObjects[button] = hoveredObject;
|
||||
}
|
||||
|
@ -137,30 +200,38 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) {
|
|||
// Trigger continue
|
||||
for (const std::string& button : sameButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
|
||||
triggeredEvent.setID(pointerID);
|
||||
triggeredEvent.setType(PointerEvent::Move);
|
||||
hoveredEvent.setButton(chooseButton(button));
|
||||
triggeredEvent.setButton(chooseButton(button));
|
||||
if (_triggeredObjects[button].type == ENTITY) {
|
||||
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(triggeredEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger end
|
||||
for (const std::string& button : _prevButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
|
||||
triggeredEvent.setID(pointerID);
|
||||
triggeredEvent.setType(PointerEvent::Release);
|
||||
hoveredEvent.setButton(chooseButton(button));
|
||||
triggeredEvent.setButton(chooseButton(button));
|
||||
if (_triggeredObjects[button].type == ENTITY) {
|
||||
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(triggeredEvent);
|
||||
}
|
||||
_triggeredObjects.erase(button);
|
||||
}
|
||||
|
||||
_prevHoveredObject = hoveredObject;
|
||||
_prevButtons = buttons;
|
||||
_prevEnabled = _enabled;
|
||||
_prevDoHover = doHover;
|
||||
}
|
||||
|
||||
PointerEvent::Button Pointer::chooseButton(const std::string& button) {
|
||||
|
|
|
@ -39,7 +39,7 @@ using PointerTriggers = std::vector<PointerTrigger>;
|
|||
|
||||
class Pointer : protected ReadWriteLockable {
|
||||
public:
|
||||
Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {}
|
||||
Pointer(unsigned int uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {}
|
||||
|
||||
virtual ~Pointer();
|
||||
|
||||
|
@ -54,13 +54,17 @@ public:
|
|||
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
|
||||
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
|
||||
|
||||
bool isLeftHand() const;
|
||||
bool isRightHand() const;
|
||||
bool isMouse() const;
|
||||
|
||||
// Pointers can choose to implement these
|
||||
virtual void setLength(float length) {}
|
||||
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {}
|
||||
|
||||
virtual void update(float deltaTime);
|
||||
virtual void update(unsigned int pointerID, float deltaTime);
|
||||
virtual void updateVisuals(const PickResultPointer& pickResult) {}
|
||||
void generatePointerEvents(const PickResultPointer& pickResult);
|
||||
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
|
||||
|
||||
struct PickedObject {
|
||||
PickedObject(const QUuid& objectID = QUuid(), IntersectionType type = IntersectionType::NONE) : objectID(objectID), type(type) {}
|
||||
|
@ -71,21 +75,26 @@ public:
|
|||
|
||||
using Buttons = std::unordered_set<std::string>;
|
||||
|
||||
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0;
|
||||
virtual Buttons getPressedButtons() = 0;
|
||||
|
||||
QUuid getRayUID() { return _pickUID; }
|
||||
unsigned int getRayUID() { return _pickUID; }
|
||||
|
||||
protected:
|
||||
const QUuid _pickUID;
|
||||
const unsigned int _pickUID;
|
||||
bool _enabled;
|
||||
bool _hover;
|
||||
|
||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const = 0;
|
||||
|
||||
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0;
|
||||
virtual Buttons getPressedButtons() = 0;
|
||||
|
||||
virtual bool shouldHover() = 0;
|
||||
virtual bool shouldTrigger() = 0;
|
||||
|
||||
private:
|
||||
PickedObject _prevHoveredObject;
|
||||
Buttons _prevButtons;
|
||||
bool _prevEnabled { false };
|
||||
bool _prevDoHover { false };
|
||||
std::unordered_map<std::string, PickedObject> _triggeredObjects;
|
||||
|
||||
PointerEvent::Button chooseButton(const std::string& button);
|
||||
|
|
|
@ -8,60 +8,67 @@
|
|||
|
||||
#include "PointerManager.h"
|
||||
|
||||
std::shared_ptr<Pointer> PointerManager::find(const QUuid& uid) const {
|
||||
#include "PickManager.h"
|
||||
|
||||
std::shared_ptr<Pointer> PointerManager::find(unsigned int uid) const {
|
||||
return resultWithReadLock<std::shared_ptr<Pointer>>([&] {
|
||||
auto itr = _pointers.find(uid);
|
||||
if (itr != _pointers.end()) {
|
||||
return *itr;
|
||||
return itr->second;
|
||||
}
|
||||
return std::shared_ptr<Pointer>();
|
||||
});
|
||||
}
|
||||
|
||||
QUuid PointerManager::addPointer(std::shared_ptr<Pointer> pointer) {
|
||||
QUuid result;
|
||||
if (!pointer->getRayUID().isNull()) {
|
||||
result = QUuid::createUuid();
|
||||
withWriteLock([&] { _pointers[result] = pointer; });
|
||||
unsigned int PointerManager::addPointer(std::shared_ptr<Pointer> pointer) {
|
||||
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) {
|
||||
result = _nextPointerID++;
|
||||
_pointers[result] = pointer;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void PointerManager::removePointer(const QUuid& uid) {
|
||||
void PointerManager::removePointer(unsigned int uid) {
|
||||
withWriteLock([&] {
|
||||
_pointers.remove(uid);
|
||||
_pointers.erase(uid);
|
||||
});
|
||||
}
|
||||
|
||||
void PointerManager::enablePointer(const QUuid& uid) const {
|
||||
void PointerManager::enablePointer(unsigned int uid) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->enable();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::disablePointer(const QUuid& uid) const {
|
||||
void PointerManager::disablePointer(unsigned int uid) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->disable();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setRenderState(const QUuid& uid, const std::string& renderState) const {
|
||||
void PointerManager::setRenderState(unsigned int uid, const std::string& renderState) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setRenderState(renderState);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const {
|
||||
void PointerManager::editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->editRenderState(state, startProps, pathProps, endProps);
|
||||
}
|
||||
}
|
||||
|
||||
PickResultPointer PointerManager::getPrevPickResult(const QUuid& uid) const {
|
||||
PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const {
|
||||
PickResultPointer result;
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
|
@ -71,46 +78,70 @@ PickResultPointer PointerManager::getPrevPickResult(const QUuid& uid) const {
|
|||
}
|
||||
|
||||
void PointerManager::update(float deltaTime) {
|
||||
auto cachedPointers = resultWithReadLock<QList<std::shared_ptr<Pointer>>>([&] {
|
||||
return _pointers.values();
|
||||
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
|
||||
return _pointers;
|
||||
});
|
||||
|
||||
for (const auto& pointer : cachedPointers) {
|
||||
pointer->update(deltaTime);
|
||||
for (const auto& pointerPair : cachedPointers) {
|
||||
pointerPair.second->update(pointerPair.first, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const {
|
||||
void PointerManager::setPrecisionPicking(unsigned int uid, bool precisionPicking) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setPrecisionPicking(precisionPicking);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignoreEntities) const {
|
||||
void PointerManager::setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setIgnoreItems(ignoreEntities);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>& includeEntities) const {
|
||||
void PointerManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includeEntities) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setIncludeItems(includeEntities);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setLength(const QUuid& uid, float length) const {
|
||||
void PointerManager::setLength(unsigned int uid, float length) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setLength(length);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const {
|
||||
void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setLockEndUUID(objectID, isOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
bool PointerManager::isLeftHand(unsigned int uid) {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
return pointer->isLeftHand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PointerManager::isRightHand(unsigned int uid) {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
return pointer->isRightHand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PointerManager::isMouse(unsigned int uid) {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
return pointer->isMouse();
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -23,26 +23,33 @@ class PointerManager : public QObject, public Dependency, protected ReadWriteLoc
|
|||
public:
|
||||
PointerManager() {}
|
||||
|
||||
QUuid addPointer(std::shared_ptr<Pointer> pointer);
|
||||
void removePointer(const QUuid& uid);
|
||||
void enablePointer(const QUuid& uid) const;
|
||||
void disablePointer(const QUuid& uid) const;
|
||||
void setRenderState(const QUuid& uid, const std::string& renderState) const;
|
||||
void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
||||
PickResultPointer getPrevPickResult(const QUuid& uid) const;
|
||||
unsigned int addPointer(std::shared_ptr<Pointer> pointer);
|
||||
void removePointer(unsigned int uid);
|
||||
void enablePointer(unsigned int uid) const;
|
||||
void disablePointer(unsigned int uid) const;
|
||||
void setRenderState(unsigned int uid, const std::string& renderState) const;
|
||||
void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
||||
PickResultPointer getPrevPickResult(unsigned int uid) const;
|
||||
|
||||
void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignoreEntities) const;
|
||||
void setIncludeItems(const QUuid& uid, const QVector<QUuid>& includeEntities) const;
|
||||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;
|
||||
void setIncludeItems(unsigned int uid, const QVector<QUuid>& includeEntities) const;
|
||||
|
||||
void setLength(const QUuid& uid, float length) const;
|
||||
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const;
|
||||
void setLength(unsigned int uid, float length) const;
|
||||
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const;
|
||||
|
||||
void update(float deltaTime);
|
||||
|
||||
bool isLeftHand(unsigned int uid);
|
||||
bool isRightHand(unsigned int uid);
|
||||
bool isMouse(unsigned int uid);
|
||||
|
||||
static const unsigned int MOUSE_POINTER_ID { PointerEvent::INVALID_POINTER_ID + 1 };
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pointer> find(const QUuid& uid) const;
|
||||
QHash<QUuid, std::shared_ptr<Pointer>> _pointers;
|
||||
std::shared_ptr<Pointer> find(unsigned int uid) const;
|
||||
std::unordered_map<unsigned int, std::shared_ptr<Pointer>> _pointers;
|
||||
unsigned int _nextPointerID { MOUSE_POINTER_ID + 1 };
|
||||
|
||||
signals:
|
||||
void triggerBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
|
@ -58,6 +65,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 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
|
||||
|
|
|
@ -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,11 @@ PointerEvent::PointerEvent(EventType type, uint32_t id,
|
|||
_buttons(buttons),
|
||||
_keyboardModifiers(keyboardModifiers)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void PointerEvent::setButton(Button button) {
|
||||
_button = button;
|
||||
_buttons |= button;
|
||||
}
|
||||
|
||||
QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) {
|
||||
|
|
|
@ -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(), const glm::vec3& pos3D = glm::vec3(),
|
||||
const glm::vec3& normal = glm::vec3(), const glm::vec3& direction = glm::vec3(),
|
||||
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 = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier);
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event);
|
||||
static void fromScriptValue(const QScriptValue& object, PointerEvent& event);
|
||||
|
@ -56,24 +59,31 @@ public:
|
|||
uint32_t getButtons() const { return _buttons; }
|
||||
Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; }
|
||||
bool shouldFocus() const { return _shouldFocus; }
|
||||
bool sendReleaseOnHoverLeave() const { return _releaseOnHoverLeave; }
|
||||
|
||||
void setID(uint32_t id) { _id = id; }
|
||||
void setType(EventType type) { _type = type; }
|
||||
void setButton(Button button) { _button = button; }
|
||||
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 };
|
||||
};
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const PointerEvent& p);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "ui/Logging.h"
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
|
||||
// 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<PointerManager>();
|
||||
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<QMouseEvent*>(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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<QWheelEvent*>(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<QMouseEvent*>(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", transformedPos);
|
||||
}
|
||||
mappedEvent.ignore();
|
||||
if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {
|
||||
return mappedEvent.isAccepted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -951,6 +927,153 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
|
|||
return false;
|
||||
}
|
||||
|
||||
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()) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
|
||||
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<QTouchEvent::TouchPoint> 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;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <ThreadHelpers.h>
|
||||
|
||||
#include <QTouchEvent>
|
||||
#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,10 @@ public:
|
|||
static std::function<void(uint32_t, void*)> getDiscardLambda();
|
||||
static size_t getUsedTextureMemory();
|
||||
|
||||
PointerEvent::EventType choosePointerEventType(QEvent::Type type);
|
||||
|
||||
unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
signals:
|
||||
void focusObjectChanged(QObject* newFocus);
|
||||
void focusTextChanged(bool focusText);
|
||||
|
@ -136,6 +143,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 +172,10 @@ private:
|
|||
QWindow* _proxyWindow { nullptr };
|
||||
|
||||
QQuickItem* _currentFocusItem { nullptr };
|
||||
|
||||
bool _pressed { false };
|
||||
bool _touchBeginAccepted { false };
|
||||
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/tablet-ui/tabletUI.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js"
|
||||
//"system/controllers/controllerScripts.js"
|
||||
// "system/chat.js"
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in a new issue