mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge pull request #11699 from SamGondelman/ui
Pointers generate PointerEvents (WIP)
This commit is contained in:
commit
55d819d7e3
26 changed files with 625 additions and 206 deletions
|
@ -623,6 +623,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
|
||||
|
||||
// Set dependencies
|
||||
DependencyManager::set<PickManager>();
|
||||
DependencyManager::set<PointerManager>();
|
||||
DependencyManager::set<LaserPointerScriptingInterface>();
|
||||
DependencyManager::set<RayPickScriptingInterface>();
|
||||
DependencyManager::set<PointerScriptingInterface>();
|
||||
DependencyManager::set<PickScriptingInterface>();
|
||||
DependencyManager::set<Cursor::Manager>();
|
||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||
DependencyManager::set<StatTracker>();
|
||||
|
@ -703,13 +709,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
|
||||
DependencyManager::set<FadeEffect>();
|
||||
|
||||
DependencyManager::set<PickManager>();
|
||||
DependencyManager::set<PointerManager>();
|
||||
DependencyManager::set<LaserPointerScriptingInterface>();
|
||||
DependencyManager::set<RayPickScriptingInterface>();
|
||||
DependencyManager::set<PointerScriptingInterface>();
|
||||
DependencyManager::set<PickScriptingInterface>();
|
||||
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
||||
|
@ -1474,13 +1473,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity,
|
||||
[this](const EntityItemID& entityItemID, const PointerEvent& event) {
|
||||
if (getEntities()->wantsKeyboardFocus(entityItemID)) {
|
||||
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
|
||||
setKeyboardFocusEntity(entityItemID);
|
||||
} else {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
if (event.shouldFocus()) {
|
||||
if (getEntities()->wantsKeyboardFocus(entityItemID)) {
|
||||
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
|
||||
setKeyboardFocusEntity(entityItemID);
|
||||
} else {
|
||||
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5899,6 +5900,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
|
||||
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
DependencyManager::get<PickScriptingInterface>()->registerMetaTypes(scriptEngine.data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
connect(scriptEngine.data(), &ScriptEngine::printedMessage,
|
||||
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
#include <pointers/PickManager.h>
|
||||
#include "PickScriptingInterface.h"
|
||||
|
||||
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
|
||||
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps)),
|
||||
_renderingEnabled(enabled),
|
||||
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
|
||||
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) :
|
||||
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps), enabled, hover),
|
||||
_triggers(triggers),
|
||||
_renderStates(renderStates),
|
||||
_defaultRenderStates(defaultRenderStates),
|
||||
_faceAvatar(faceAvatar),
|
||||
|
@ -49,28 +49,6 @@ LaserPointer::~LaserPointer() {
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointer::enable() {
|
||||
Pointer::enable();
|
||||
withWriteLock([&] {
|
||||
_renderingEnabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
void LaserPointer::disable() {
|
||||
Pointer::disable();
|
||||
withWriteLock([&] {
|
||||
_renderingEnabled = false;
|
||||
if (!_currentRenderState.empty()) {
|
||||
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
}
|
||||
if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LaserPointer::setRenderState(const std::string& state) {
|
||||
withWriteLock([&] {
|
||||
if (!_currentRenderState.empty() && state != _currentRenderState) {
|
||||
|
@ -105,7 +83,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant&
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) {
|
||||
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) {
|
||||
if (!renderState.getStartID().isNull()) {
|
||||
QVariantMap startProps;
|
||||
startProps.insert("position", vec3toVariant(pickRay.origin));
|
||||
|
@ -199,35 +177,46 @@ void LaserPointer::disableRenderState(const RenderState& renderState) {
|
|||
}
|
||||
}
|
||||
|
||||
void LaserPointer::update() {
|
||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||
withReadLock([&] {
|
||||
QVariantMap prevRayPickResult = DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
|
||||
IntersectionType type = IntersectionType(prevRayPickResult["type"].toInt());
|
||||
PickRay pickRay = PickRay(prevRayPickResult["searchRay"].toMap());
|
||||
QUuid uid = prevRayPickResult["objectID"].toUuid();
|
||||
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
|
||||
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult["distance"].toFloat();
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
} else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
}
|
||||
});
|
||||
void LaserPointer::updateVisuals(const QVariantMap& prevRayPickResult) {
|
||||
IntersectionType type = IntersectionType(prevRayPickResult["type"].toInt());
|
||||
PickRay pickRay = PickRay(prevRayPickResult["searchRay"].toMap());
|
||||
QUuid uid = prevRayPickResult["objectID"].toUuid();
|
||||
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
|
||||
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
|
||||
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult["distance"].toFloat();
|
||||
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
|
||||
} else if (!_currentRenderState.empty()) {
|
||||
disableRenderState(_renderStates[_currentRenderState]);
|
||||
disableRenderState(_defaultRenderStates[_currentRenderState].second);
|
||||
}
|
||||
}
|
||||
|
||||
void LaserPointer::setLength(const float length) {
|
||||
Pointer::PickedObject LaserPointer::getHoveredObject(const QVariantMap& pickResult) {
|
||||
return Pointer::PickedObject(pickResult["objectID"].toUuid(), IntersectionType(pickResult["type"].toUInt()));
|
||||
}
|
||||
|
||||
Pointer::Buttons LaserPointer::getPressedButtons() {
|
||||
std::unordered_set<std::string> toReturn;
|
||||
for (const PointerTrigger& trigger : _triggers) {
|
||||
// TODO: right now, LaserPointers don't support axes, only on/off buttons
|
||||
if (trigger.getEndpoint()->peek() >= 1.0f) {
|
||||
toReturn.insert(trigger.getButton());
|
||||
}
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void LaserPointer::setLength(float length) {
|
||||
withWriteLock([&] {
|
||||
_laserLength = length;
|
||||
});
|
||||
}
|
||||
|
||||
void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) {
|
||||
void LaserPointer::setLockEndUUID(const QUuid& objectID, bool isOverlay) {
|
||||
withWriteLock([&] {
|
||||
_objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay);
|
||||
});
|
||||
|
@ -290,4 +279,80 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
|
|||
}
|
||||
|
||||
return RenderState(startID, pathID, endID);
|
||||
}
|
||||
|
||||
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const {
|
||||
uint32_t id = 0;
|
||||
glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]);
|
||||
glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]);
|
||||
QVariantMap searchRay = pickResult["searchRay"].toMap();
|
||||
glm::vec3 direction = vec3FromVariant(searchRay["direction"]);
|
||||
QUuid pickedID = pickResult["objectID"].toUuid();
|
||||
glm::vec2 pos2D;
|
||||
if (pickedID != target.objectID) {
|
||||
glm::vec3 origin = vec3FromVariant(searchRay["origin"]);
|
||||
if (target.type == ENTITY) {
|
||||
intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction);
|
||||
} else if (target.type == OVERLAY) {
|
||||
intersection = intersectRayWithOverlayXYPlane(target.objectID, origin, direction);
|
||||
}
|
||||
}
|
||||
if (target.type == ENTITY) {
|
||||
pos2D = projectOntoEntityXYPlane(target.objectID, intersection);
|
||||
} else if (target.type == OVERLAY) {
|
||||
pos2D = projectOntoOverlayXYPlane(target.objectID, intersection);
|
||||
}
|
||||
return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons);
|
||||
}
|
||||
|
||||
glm::vec3 LaserPointer::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const {
|
||||
glm::vec3 n = rotation * Vectors::FRONT;
|
||||
float t = glm::dot(n, point - origin) / glm::dot(n, direction);
|
||||
return origin + t * direction;
|
||||
}
|
||||
|
||||
glm::vec3 LaserPointer::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const {
|
||||
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
|
||||
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
|
||||
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||
return intersectRayWithXYPlane(origin, direction, position, rotation, DEFAULT_REGISTRATION_POINT);
|
||||
}
|
||||
|
||||
glm::vec3 LaserPointer::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const {
|
||||
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
|
||||
return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint());
|
||||
}
|
||||
|
||||
glm::vec2 LaserPointer::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const {
|
||||
glm::quat invRot = glm::inverse(rotation);
|
||||
glm::vec3 localPos = invRot * (worldPos - position);
|
||||
glm::vec3 invDimensions = glm::vec3(1.0f / dimensions.x, 1.0f / dimensions.y, 1.0f / dimensions.z);
|
||||
|
||||
glm::vec3 normalizedPos = (localPos * invDimensions) + registrationPoint;
|
||||
return glm::vec2(normalizedPos.x * dimensions.x, (1.0f - normalizedPos.y) * dimensions.y);
|
||||
}
|
||||
|
||||
glm::vec2 LaserPointer::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const {
|
||||
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
|
||||
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
|
||||
glm::vec3 dimensions;
|
||||
|
||||
float dpi = qApp->getOverlays().getProperty(overlayID, "dpi").value.toFloat();
|
||||
if (dpi > 0) {
|
||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
|
||||
glm::vec3 resolution = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "resolution").value), 1);
|
||||
glm::vec3 scale = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
|
||||
const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
dimensions = (resolution * INCHES_TO_METERS / dpi) * scale;
|
||||
} else {
|
||||
dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01);
|
||||
}
|
||||
|
||||
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
|
||||
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
|
||||
}
|
||||
|
||||
glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const {
|
||||
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
|
||||
return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint());
|
||||
}
|
|
@ -49,31 +49,34 @@ private:
|
|||
};
|
||||
|
||||
class LaserPointer : public Pointer {
|
||||
|
||||
using Parent = Pointer;
|
||||
public:
|
||||
typedef std::unordered_map<std::string, RenderState> RenderStateMap;
|
||||
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
|
||||
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled);
|
||||
~LaserPointer();
|
||||
|
||||
void enable() override;
|
||||
void disable() override;
|
||||
|
||||
void setRenderState(const std::string& state) override;
|
||||
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
|
||||
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override;
|
||||
|
||||
void setLength(const float length) override;
|
||||
void setLockEndUUID(QUuid objectID, const bool isOverlay) override;
|
||||
void setLength(float length) override;
|
||||
void setLockEndUUID(const QUuid& objectID, bool isOverlay) override;
|
||||
|
||||
void update() override;
|
||||
void updateVisuals(const QVariantMap& prevRayPickResult) override;
|
||||
|
||||
PickedObject getHoveredObject(const QVariantMap& pickResult) override;
|
||||
Pointer::Buttons getPressedButtons() override;
|
||||
|
||||
static RenderState buildRenderState(const QVariantMap& propMap);
|
||||
|
||||
protected:
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const override;
|
||||
|
||||
private:
|
||||
bool _renderingEnabled;
|
||||
PointerTriggers _triggers;
|
||||
float _laserLength { 0.0f };
|
||||
std::string _currentRenderState { "" };
|
||||
RenderStateMap _renderStates;
|
||||
|
@ -85,9 +88,17 @@ private:
|
|||
std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(QUuid(), false)};
|
||||
|
||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState);
|
||||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState);
|
||||
void disableRenderState(const RenderState& renderState);
|
||||
|
||||
|
||||
glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const;
|
||||
glm::vec3 intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const;
|
||||
glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const;
|
||||
glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const;
|
||||
glm::vec2 projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const;
|
||||
glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LaserPointer_h
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include "JointRayPick.h"
|
||||
#include "MouseRayPick.h"
|
||||
|
||||
#include <pointers/Pick.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
QUuid PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
|
||||
switch (type) {
|
||||
case PickQuery::PickType::Ray:
|
||||
|
@ -105,3 +108,22 @@ void PickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue
|
|||
void PickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) {
|
||||
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
QScriptValue pickTypesToScriptValue(QScriptEngine* engine, const PickQuery::PickType& pickType) {
|
||||
return pickType;
|
||||
}
|
||||
|
||||
void pickTypesFromScriptValue(const QScriptValue& object, PickQuery::PickType& pickType) {
|
||||
pickType = static_cast<PickQuery::PickType>(object.toUInt16());
|
||||
}
|
||||
|
||||
void PickScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
||||
QScriptValue pickTypes = engine->newObject();
|
||||
auto metaEnum = QMetaEnum::fromType<PickQuery::PickType>();
|
||||
for (int i = 0; i < PickQuery::PickType::NUM_PICK_TYPES; ++i) {
|
||||
pickTypes.setProperty(metaEnum.key(i), metaEnum.value(i));
|
||||
}
|
||||
engine->globalObject().setProperty("PickType", pickTypes);
|
||||
|
||||
qScriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue);
|
||||
}
|
|
@ -35,6 +35,8 @@ class PickScriptingInterface : public QObject, public Dependency {
|
|||
public:
|
||||
QUuid createRayPick(const QVariant& properties);
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
Q_INVOKABLE void enablePick(const QUuid& uid);
|
||||
|
|
|
@ -21,7 +21,7 @@ void PointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptV
|
|||
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
|
||||
}
|
||||
|
||||
QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType type, const QVariant& properties) const {
|
||||
QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) const {
|
||||
switch (type) {
|
||||
case PickQuery::PickType::Ray:
|
||||
return createLaserPointer(properties);
|
||||
|
@ -61,7 +61,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
|
|||
LaserPointer::RenderStateMap renderStates;
|
||||
if (propertyMap["renderStates"].isValid()) {
|
||||
QList<QVariant> renderStateVariants = propertyMap["renderStates"].toList();
|
||||
for (QVariant& renderStateVariant : renderStateVariants) {
|
||||
for (const QVariant& renderStateVariant : renderStateVariants) {
|
||||
if (renderStateVariant.isValid()) {
|
||||
QVariantMap renderStateMap = renderStateVariant.toMap();
|
||||
if (renderStateMap["name"].isValid()) {
|
||||
|
@ -75,7 +75,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
|
|||
LaserPointer::DefaultRenderStateMap defaultRenderStates;
|
||||
if (propertyMap["defaultRenderStates"].isValid()) {
|
||||
QList<QVariant> renderStateVariants = propertyMap["defaultRenderStates"].toList();
|
||||
for (QVariant& renderStateVariant : renderStateVariants) {
|
||||
for (const QVariant& renderStateVariant : renderStateVariants) {
|
||||
if (renderStateVariant.isValid()) {
|
||||
QVariantMap renderStateMap = renderStateVariant.toMap();
|
||||
if (renderStateMap["name"].isValid() && renderStateMap["distance"].isValid()) {
|
||||
|
@ -87,7 +87,31 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
|
|||
}
|
||||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
|
||||
bool hover = false;
|
||||
if (propertyMap["hover"].isValid()) {
|
||||
hover = propertyMap["hover"].toBool();
|
||||
}
|
||||
|
||||
PointerTriggers triggers;
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
if (propertyMap["triggers"].isValid()) {
|
||||
QList<QVariant> triggerVariants = propertyMap["triggers"].toList();
|
||||
for (const QVariant& triggerVariant : triggerVariants) {
|
||||
if (triggerVariant.isValid()) {
|
||||
QVariantMap triggerMap = triggerVariant.toMap();
|
||||
if (triggerMap["action"].isValid() && triggerMap["button"].isValid()) {
|
||||
controller::Endpoint::Pointer endpoint = userInputMapper->endpointFor(controller::Input(triggerMap["action"].toUInt()));
|
||||
if (endpoint) {
|
||||
std::string button = triggerMap["button"].toString().toStdString();
|
||||
triggers.emplace_back(endpoint, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
|
||||
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
|
||||
}
|
||||
|
||||
void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {
|
||||
|
|
|
@ -22,13 +22,13 @@ public:
|
|||
QUuid createLaserPointer(const QVariant& properties) const;
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE QUuid createPointer(const PickQuery::PickType type, const QVariant& properties) const;
|
||||
Q_INVOKABLE QUuid createPointer(const PickQuery::PickType& type, const QVariant& properties) const;
|
||||
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(QUuid uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
|
||||
|
||||
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); }
|
||||
|
@ -37,6 +37,14 @@ public slots:
|
|||
|
||||
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
|
||||
|
||||
signals:
|
||||
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverContinue(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverEnd(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_PointerScriptingInterface_h
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <QVariant>
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
#include "PickScriptingInterface.h"
|
||||
#include <pointers/PickManager.h>
|
||||
|
||||
#include "StaticRayPick.h"
|
||||
|
|
|
@ -16,8 +16,24 @@
|
|||
#include "RegisteredMetaTypes.h"
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "PickScriptingInterface.h"
|
||||
|
||||
class RayPickScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT)
|
||||
Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT)
|
||||
Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT)
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public slots:
|
||||
|
@ -30,6 +46,21 @@ public slots:
|
|||
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(); }
|
||||
static unsigned int PICK_AVATARS() { return PickScriptingInterface::PICK_AVATARS(); }
|
||||
static unsigned int PICK_HUD() { return PickScriptingInterface::PICK_HUD(); }
|
||||
static unsigned int PICK_COARSE() { return PickScriptingInterface::PICK_COARSE(); }
|
||||
static unsigned int PICK_INCLUDE_INVISIBLE() { return PickScriptingInterface::PICK_INCLUDE_INVISIBLE(); }
|
||||
static unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE(); }
|
||||
static unsigned int PICK_ALL_INTERSECTIONS() { return PickScriptingInterface::PICK_ALL_INTERSECTIONS(); }
|
||||
static unsigned int INTERSECTED_NONE() { return PickScriptingInterface::INTERSECTED_NONE(); }
|
||||
static unsigned int INTERSECTED_ENTITY() { return PickScriptingInterface::INTERSECTED_ENTITY(); }
|
||||
static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_OVERLAY(); }
|
||||
static unsigned int INTERSECTED_AVATAR() { return PickScriptingInterface::INTERSECTED_AVATAR(); }
|
||||
static unsigned int INTERSECTED_HUD() { return PickScriptingInterface::INTERSECTED_HUD(); }
|
||||
};
|
||||
|
||||
#endif // hifi_RayPickScriptingInterface_h
|
||||
|
|
|
@ -37,10 +37,22 @@
|
|||
#include "Web3DOverlay.h"
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
|
||||
|
||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||
|
||||
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::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent);
|
||||
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent);
|
||||
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent);
|
||||
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent);
|
||||
}
|
||||
|
||||
void Overlays::cleanupAllOverlays() {
|
||||
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
|
||||
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
|
||||
|
@ -717,15 +729,15 @@ bool Overlays::isAddedOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
mousePressEvent(overlayID, event);
|
||||
mousePressPointerEvent(overlayID, event);
|
||||
}
|
||||
|
||||
void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
mouseReleaseEvent(overlayID, event);
|
||||
mouseReleasePointerEvent(overlayID, event);
|
||||
}
|
||||
|
||||
void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
mouseMoveEvent(overlayID, event);
|
||||
mouseMovePointerEvent(overlayID, event);
|
||||
}
|
||||
|
||||
void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
|
@ -737,7 +749,7 @@ void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEve
|
|||
}
|
||||
|
||||
void Overlays::sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
hoverLeaveEvent(overlayID, event);
|
||||
hoverLeavePointerEvent(overlayID, event);
|
||||
}
|
||||
|
||||
OverlayID Overlays::getKeyboardFocusOverlay() {
|
||||
|
@ -879,7 +891,7 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
|
|||
_currentClickingOnOverlayID = rayPickResult.overlayID;
|
||||
|
||||
PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
|
||||
mousePressEvent(_currentClickingOnOverlayID, pointerEvent);
|
||||
mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent);
|
||||
return true;
|
||||
}
|
||||
// if we didn't press on an overlay, disable overlay keyboard focus
|
||||
|
@ -889,16 +901,18 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Overlays::mousePressEvent(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
void Overlays::mousePressPointerEvent(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) {
|
||||
// Focus keyboard on web overlays
|
||||
DependencyManager::get<EntityScriptingInterface>()->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
setKeyboardFocusOverlay(overlayID);
|
||||
if (event.shouldFocus()) {
|
||||
// Focus keyboard on web overlays
|
||||
DependencyManager::get<EntityScriptingInterface>()->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
|
||||
setKeyboardFocusOverlay(overlayID);
|
||||
}
|
||||
|
||||
// Send to web overlay
|
||||
QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event));
|
||||
|
@ -926,7 +940,7 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Overlays::hoverLeaveEvent(const OverlayID& overlayID, const PointerEvent& 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;
|
||||
if (getOverlayType(overlayID) == "web3d") {
|
||||
|
@ -948,14 +962,14 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
|||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
|
||||
mouseReleaseEvent(rayPickResult.overlayID, pointerEvent);
|
||||
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
|
||||
}
|
||||
|
||||
_currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Overlays::mouseReleaseEvent(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
void Overlays::mouseReleasePointerEvent(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") {
|
||||
|
@ -977,12 +991,12 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
|
||||
mouseMoveEvent(rayPickResult.overlayID, pointerEvent);
|
||||
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
|
||||
|
||||
// If previously hovering over a different overlay then leave hover on that overlay.
|
||||
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
|
||||
hoverLeaveEvent(_currentHoverOverOverlayID, pointerEvent);
|
||||
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
|
||||
}
|
||||
|
||||
// If hovering over a new overlay then enter hover on that overlay.
|
||||
|
@ -998,7 +1012,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
// If previously hovering an overlay then leave hover.
|
||||
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
|
||||
hoverLeaveEvent(_currentHoverOverOverlayID, pointerEvent);
|
||||
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
|
||||
|
||||
_currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
@ -1006,7 +1020,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Overlays::mouseMoveEvent(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
void Overlays::mouseMovePointerEvent(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") {
|
||||
|
|
|
@ -85,7 +85,7 @@ class Overlays : public QObject {
|
|||
Q_PROPERTY(OverlayID keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay)
|
||||
|
||||
public:
|
||||
Overlays() {};
|
||||
Overlays();
|
||||
|
||||
void init();
|
||||
void update(float deltatime);
|
||||
|
@ -107,11 +107,6 @@ public:
|
|||
bool mouseReleaseEvent(QMouseEvent* event);
|
||||
bool mouseMoveEvent(QMouseEvent* event);
|
||||
|
||||
void mousePressEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseMoveEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseReleaseEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void hoverLeaveEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
void cleanupAllOverlays();
|
||||
|
||||
public slots:
|
||||
|
@ -310,6 +305,11 @@ public slots:
|
|||
OverlayID getKeyboardFocusOverlay();
|
||||
void setKeyboardFocusOverlay(const OverlayID& id);
|
||||
|
||||
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 hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Emitted when an overlay is deleted
|
||||
|
|
|
@ -66,7 +66,7 @@ const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
|||
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
|
||||
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||
_touchDevice.setType(QTouchDevice::TouchScreen);
|
||||
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
|
||||
_touchDevice.setName("Web3DOverlayTouchDevice");
|
||||
_touchDevice.setMaximumTouchPoints(4);
|
||||
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
|
@ -349,6 +349,12 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) {
|
|||
}
|
||||
|
||||
void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
_pressed = true;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
if (_inputMode == Touch) {
|
||||
handlePointerEventAsTouch(event);
|
||||
} else {
|
||||
|
@ -361,19 +367,8 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
//do not send secondary button events to tablet
|
||||
if (event.getButton() == PointerEvent::SecondaryButton ||
|
||||
//do not block composed events
|
||||
event.getButtons() == PointerEvent::SecondaryButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QPointF windowPoint;
|
||||
{
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
windowPoint = QPointF(windowPos.x, windowPos.y);
|
||||
}
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
Qt::TouchPointState state = Qt::TouchPointStationary;
|
||||
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
|
||||
|
@ -412,14 +407,13 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
|
|||
}
|
||||
|
||||
touchEvent.setWindow(_webSurface->getWindow());
|
||||
touchEvent.setDevice(&_touchDevice);
|
||||
touchEvent.setTarget(_webSurface->getRootItem());
|
||||
touchEvent.setTouchPoints(touchPoints);
|
||||
touchEvent.setTouchPointStates(touchPointStates);
|
||||
}
|
||||
|
||||
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
|
||||
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
|
||||
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
|
||||
//
|
||||
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
|
||||
// receive mouse events
|
||||
|
@ -466,12 +460,6 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {
|
|||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
this->_pressed = true;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
this->_pressed = false;
|
||||
}
|
||||
|
||||
Qt::MouseButtons buttons = Qt::NoButton;
|
||||
if (event.getButtons() & PointerEvent::PrimaryButton) {
|
||||
buttons |= Qt::LeftButton;
|
||||
|
|
|
@ -129,6 +129,8 @@ namespace controller {
|
|||
template <typename F>
|
||||
void withLock(F&& f) { Locker locker(_lock); f(); }
|
||||
|
||||
EndpointPointer endpointFor(const Input& endpoint) const;
|
||||
|
||||
signals:
|
||||
void actionEvent(int action, float state);
|
||||
void inputEvent(int input, float state);
|
||||
|
@ -161,7 +163,6 @@ namespace controller {
|
|||
void disableMapping(const MappingPointer& mapping);
|
||||
EndpointPointer endpointFor(const QJSValue& endpoint);
|
||||
EndpointPointer endpointFor(const QScriptValue& endpoint);
|
||||
EndpointPointer endpointFor(const Input& endpoint) const;
|
||||
EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second);
|
||||
ConditionalPointer conditionalFor(const QJSValue& endpoint);
|
||||
ConditionalPointer conditionalFor(const QScriptValue& endpoint);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
|
||||
setup_hifi_library(Widgets Network Script)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui pointers)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gl)
|
||||
include_hifi_library_headers(ktx)
|
||||
|
@ -12,6 +12,7 @@ include_hifi_library_headers(animation)
|
|||
include_hifi_library_headers(fbx)
|
||||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(avatars)
|
||||
include_hifi_library_headers(controllers)
|
||||
|
||||
target_bullet()
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
#include "RenderableWebEntityItem.h"
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
|
||||
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
||||
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
||||
|
||||
|
@ -58,6 +60,15 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
|
||||
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
|
||||
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
|
||||
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
|
||||
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
|
||||
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
|
||||
|
||||
// Forward mouse events to web entities
|
||||
auto handlePointerEvent = [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
|
||||
|
@ -69,7 +80,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(PointerEvent, event));
|
||||
}
|
||||
};
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
|
||||
|
|
|
@ -227,6 +227,8 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
|||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
_webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
_webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
loadSourceURL();
|
||||
_webSurface->resume();
|
||||
|
@ -315,21 +317,9 @@ void WebEntityRenderer::loadSourceURL() {
|
|||
void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) {
|
||||
if (!_lastLocked && _webSurface && _pressed) {
|
||||
// If the user mouses off the entity while the button is down, simulate a touch end.
|
||||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
point.setState(Qt::TouchPointReleased);
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
point.setScenePos(windowPoint);
|
||||
point.setPos(windowPoint);
|
||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||
touchPoints.push_back(point);
|
||||
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr,
|
||||
Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
|
||||
touchEvent->setWindow(_webSurface->getWindow());
|
||||
touchEvent->setDevice(&_touchDevice);
|
||||
touchEvent->setTarget(_webSurface->getRootItem());
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
|
||||
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
|
||||
handlePointerEvent(endEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,57 +329,95 @@ void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
_pressed = true;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
if (event.getType() == PointerEvent::Move) {
|
||||
// Forward a mouse move event to webSurface
|
||||
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
|
||||
|
||||
Qt::TouchPointState state = Qt::TouchPointStationary;
|
||||
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
|
||||
state = Qt::TouchPointPressed;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
state = Qt::TouchPointReleased;
|
||||
} else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) {
|
||||
state = Qt::TouchPointMoved;
|
||||
}
|
||||
|
||||
QEvent::Type touchType = QEvent::TouchUpdate;
|
||||
if (_activeTouchPoints.empty()) {
|
||||
// If the first active touch point is being created, send a begin
|
||||
touchType = QEvent::TouchBegin;
|
||||
} if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) {
|
||||
// If the last active touch point is being released, send an end
|
||||
touchType = QEvent::TouchEnd;
|
||||
}
|
||||
|
||||
{
|
||||
// Forward a touch update event to webSurface
|
||||
if (event.getType() == PointerEvent::Press) {
|
||||
this->_pressed = true;
|
||||
} else if (event.getType() == PointerEvent::Release) {
|
||||
this->_pressed = false;
|
||||
}
|
||||
|
||||
QEvent::Type type;
|
||||
Qt::TouchPointState touchPointState;
|
||||
switch (event.getType()) {
|
||||
case PointerEvent::Press:
|
||||
type = QEvent::TouchBegin;
|
||||
touchPointState = Qt::TouchPointPressed;
|
||||
break;
|
||||
case PointerEvent::Release:
|
||||
type = QEvent::TouchEnd;
|
||||
touchPointState = Qt::TouchPointReleased;
|
||||
break;
|
||||
case PointerEvent::Move:
|
||||
default:
|
||||
type = QEvent::TouchUpdate;
|
||||
touchPointState = Qt::TouchPointMoved;
|
||||
break;
|
||||
}
|
||||
|
||||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
point.setState(touchPointState);
|
||||
point.setState(state);
|
||||
point.setPos(windowPoint);
|
||||
point.setScreenPos(windowPoint);
|
||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||
touchPoints.push_back(point);
|
||||
|
||||
QTouchEvent* touchEvent = new QTouchEvent(type);
|
||||
touchEvent->setWindow(_webSurface->getWindow());
|
||||
touchEvent->setDevice(&_touchDevice);
|
||||
touchEvent->setTarget(_webSurface->getRootItem());
|
||||
touchEvent->setTouchPoints(touchPoints);
|
||||
touchEvent->setTouchPointStates(touchPointState);
|
||||
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||
_activeTouchPoints[event.getID()] = point;
|
||||
}
|
||||
|
||||
QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers());
|
||||
{
|
||||
QList<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,6 +12,8 @@
|
|||
#include <WebEntityItem.h>
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
#include <QTouchEvent>
|
||||
|
||||
class OffscreenQmlSurface;
|
||||
class PointerEvent;
|
||||
|
||||
|
@ -58,13 +60,16 @@ private:
|
|||
QSharedPointer<OffscreenQmlSurface> _webSurface;
|
||||
glm::vec3 _contextPosition;
|
||||
gpu::TexturePointer _texture;
|
||||
bool _pressed{ false };
|
||||
QString _lastSourceUrl;
|
||||
uint16_t _lastDPI;
|
||||
bool _lastLocked;
|
||||
QTimer _timer;
|
||||
uint64_t _lastRenderTime { 0 };
|
||||
Transform _renderTransform;
|
||||
|
||||
bool _pressed{ false };
|
||||
bool _touchBeginAccepted{ false };
|
||||
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
set(TARGET_NAME pointers)
|
||||
setup_hifi_library()
|
||||
GroupSources(src)
|
||||
link_hifi_libraries(shared)
|
||||
link_hifi_libraries(shared controllers)
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
const PickFilter PickFilter::NOTHING;
|
||||
|
||||
int pickTypeMetaTypeId = qRegisterMetaType<PickQuery::PickType>("PickType");
|
||||
|
||||
PickQuery::PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled) :
|
||||
_filter(filter),
|
||||
_maxDistance(maxDistance),
|
||||
|
|
|
@ -136,7 +136,9 @@ public:
|
|||
|
||||
enum PickType {
|
||||
Ray = 0,
|
||||
Stylus
|
||||
Stylus,
|
||||
|
||||
NUM_PICK_TYPES
|
||||
};
|
||||
Q_ENUM(PickType)
|
||||
|
||||
|
@ -189,6 +191,7 @@ private:
|
|||
QVector<QUuid> _ignoreItems;
|
||||
QVector<QUuid> _includeItems;
|
||||
};
|
||||
Q_DECLARE_METATYPE(PickQuery::PickType)
|
||||
|
||||
template<typename T>
|
||||
class Pick : public PickQuery {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
#include "PickManager.h"
|
||||
#include "PointerManager.h"
|
||||
|
||||
Pointer::~Pointer() {
|
||||
DependencyManager::get<PickManager>()->removePick(_pickUID);
|
||||
|
@ -16,9 +17,16 @@ Pointer::~Pointer() {
|
|||
|
||||
void Pointer::enable() {
|
||||
DependencyManager::get<PickManager>()->enablePick(_pickUID);
|
||||
withWriteLock([&] {
|
||||
_enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
void Pointer::disable() {
|
||||
// Disable the pointer first, then the pick, so someone can't try to use it while it's in a bad state
|
||||
withWriteLock([&] {
|
||||
_enabled = false;
|
||||
});
|
||||
DependencyManager::get<PickManager>()->disablePick(_pickUID);
|
||||
}
|
||||
|
||||
|
@ -26,7 +34,7 @@ const QVariantMap Pointer::getPrevPickResult() {
|
|||
return DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
|
||||
}
|
||||
|
||||
void Pointer::setPrecisionPicking(const bool precisionPicking) {
|
||||
void Pointer::setPrecisionPicking(bool precisionPicking) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(_pickUID, precisionPicking);
|
||||
}
|
||||
|
||||
|
@ -36,4 +44,136 @@ void Pointer::setIgnoreItems(const QVector<QUuid>& ignoreItems) const {
|
|||
|
||||
void Pointer::setIncludeItems(const QVector<QUuid>& includeItems) const {
|
||||
DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems);
|
||||
}
|
||||
|
||||
void Pointer::update() {
|
||||
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
|
||||
withReadLock([&] {
|
||||
QVariantMap pickResult = getPrevPickResult();
|
||||
updateVisuals(pickResult);
|
||||
generatePointerEvents(pickResult);
|
||||
});
|
||||
}
|
||||
|
||||
void Pointer::generatePointerEvents(const QVariantMap& pickResult) {
|
||||
// TODO: avatars/HUD?
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
|
||||
// Hover events
|
||||
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) {
|
||||
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);
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent);
|
||||
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
} else {
|
||||
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this is basically repeated code. is there a way to clean it up?
|
||||
if (hoveredObject.type == ENTITY) {
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
|
||||
emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent);
|
||||
} else {
|
||||
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent);
|
||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
} else {
|
||||
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger begin
|
||||
const std::string SHOULD_FOCUS_BUTTON = "Focus";
|
||||
for (const std::string& button : newButtons) {
|
||||
hoveredEvent.setType(PointerEvent::Press);
|
||||
hoveredEvent.setButton(chooseButton(button));
|
||||
hoveredEvent.setShouldFocus(button == SHOULD_FOCUS_BUTTON);
|
||||
if (hoveredObject.type == ENTITY) {
|
||||
emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent);
|
||||
} else if (hoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent);
|
||||
}
|
||||
_triggeredObjects[button] = hoveredObject;
|
||||
}
|
||||
|
||||
// Trigger continue
|
||||
for (const std::string& button : sameButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
|
||||
triggeredEvent.setType(PointerEvent::Move);
|
||||
hoveredEvent.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);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger end
|
||||
for (const std::string& button : _prevButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
|
||||
triggeredEvent.setType(PointerEvent::Release);
|
||||
hoveredEvent.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);
|
||||
}
|
||||
_triggeredObjects.erase(button);
|
||||
}
|
||||
|
||||
_prevHoveredObject = hoveredObject;
|
||||
_prevButtons = buttons;
|
||||
}
|
||||
|
||||
PointerEvent::Button Pointer::chooseButton(const std::string& button) {
|
||||
const std::string PRIMARY_BUTTON = "Primary";
|
||||
const std::string SECONDARY_BUTTON = "Secondary";
|
||||
const std::string TERTIARY_BUTTON = "Tertiary";
|
||||
if (button == PRIMARY_BUTTON) {
|
||||
return PointerEvent::PrimaryButton;
|
||||
} else if (button == SECONDARY_BUTTON) {
|
||||
return PointerEvent::SecondaryButton;
|
||||
} else if (button == TERTIARY_BUTTON) {
|
||||
return PointerEvent::TertiaryButton;
|
||||
} else {
|
||||
return PointerEvent::NoButtons;
|
||||
}
|
||||
}
|
|
@ -8,15 +8,37 @@
|
|||
#ifndef hifi_Pointer_h
|
||||
#define hifi_Pointer_h
|
||||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QUuid>
|
||||
#include <QVector>
|
||||
#include <QVariant>
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include <controllers/impl/Endpoint.h>
|
||||
#include "PointerEvent.h"
|
||||
|
||||
#include "Pick.h"
|
||||
|
||||
class PointerTrigger {
|
||||
public:
|
||||
PointerTrigger(controller::Endpoint::Pointer endpoint, const std::string& button) : _endpoint(endpoint), _button(button) {}
|
||||
|
||||
controller::Endpoint::Pointer getEndpoint() const { return _endpoint; }
|
||||
const std::string& getButton() const { return _button; }
|
||||
|
||||
private:
|
||||
controller::Endpoint::Pointer _endpoint;
|
||||
std::string _button { "" };
|
||||
};
|
||||
|
||||
using PointerTriggers = std::vector<PointerTrigger>;
|
||||
|
||||
class Pointer : protected ReadWriteLockable {
|
||||
public:
|
||||
Pointer(const QUuid& uid) : _pickUID(uid) {}
|
||||
Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {}
|
||||
|
||||
virtual ~Pointer();
|
||||
|
||||
|
@ -27,20 +49,47 @@ public:
|
|||
virtual void setRenderState(const std::string& state) = 0;
|
||||
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
|
||||
|
||||
virtual void setPrecisionPicking(const bool precisionPicking);
|
||||
virtual void setPrecisionPicking(bool precisionPicking);
|
||||
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
|
||||
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
|
||||
|
||||
// Pointers can choose to implement these
|
||||
virtual void setLength(const float length) {}
|
||||
virtual void setLockEndUUID(QUuid objectID, const bool isOverlay) {}
|
||||
virtual void setLength(float length) {}
|
||||
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {}
|
||||
|
||||
virtual void update() = 0;
|
||||
void update();
|
||||
virtual void updateVisuals(const QVariantMap& pickResult) = 0;
|
||||
void generatePointerEvents(const QVariantMap& pickResult);
|
||||
|
||||
struct PickedObject {
|
||||
PickedObject() {}
|
||||
PickedObject(const QUuid& objectID, IntersectionType type) : objectID(objectID), type(type) {}
|
||||
|
||||
QUuid objectID;
|
||||
IntersectionType type;
|
||||
} typedef PickedObject;
|
||||
|
||||
using Buttons = std::unordered_set<std::string>;
|
||||
|
||||
virtual PickedObject getHoveredObject(const QVariantMap& pickResult) = 0;
|
||||
virtual Buttons getPressedButtons() = 0;
|
||||
|
||||
QUuid getRayUID() { return _pickUID; }
|
||||
|
||||
protected:
|
||||
const QUuid _pickUID;
|
||||
bool _enabled;
|
||||
bool _hover;
|
||||
|
||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const = 0;
|
||||
|
||||
private:
|
||||
PickedObject _prevHoveredObject;
|
||||
Buttons _prevButtons;
|
||||
std::unordered_map<std::string, PickedObject> _triggeredObjects;
|
||||
|
||||
PointerEvent::Button chooseButton(const std::string& button);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_Pick_h
|
||||
|
|
|
@ -79,7 +79,7 @@ void PointerManager::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void PointerManager::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const {
|
||||
void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setPrecisionPicking(precisionPicking);
|
||||
|
@ -100,14 +100,14 @@ void PointerManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>& inc
|
|||
}
|
||||
}
|
||||
|
||||
void PointerManager::setLength(const QUuid& uid, const float length) const {
|
||||
void PointerManager::setLength(const QUuid& uid, float length) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setLength(length);
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const {
|
||||
void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
pointer->setLockEndUUID(objectID, isOverlay);
|
||||
|
|
|
@ -31,12 +31,12 @@ public:
|
|||
void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
||||
const QVariantMap getPrevPickResult(const QUuid& uid) const;
|
||||
|
||||
void setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const;
|
||||
void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignoreEntities) const;
|
||||
void setIncludeItems(const QUuid& uid, const QVector<QUuid>& includeEntities) const;
|
||||
|
||||
void setLength(const QUuid& uid, const float length) const;
|
||||
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const;
|
||||
void setLength(const QUuid& uid, float length) const;
|
||||
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const;
|
||||
|
||||
void update();
|
||||
|
||||
|
@ -45,13 +45,19 @@ private:
|
|||
QHash<QUuid, std::shared_ptr<Pointer>> _pointers;
|
||||
|
||||
signals:
|
||||
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerContinueOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerEndOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverContinueOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverEndOverlay(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
|
||||
void hoverEnter(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverOver(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverLeave(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerBeginEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerContinueEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerEndEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverBeginEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverContinueEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverEndEntity(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
};
|
||||
|
||||
#endif // hifi_pointers_PointerManager_h
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
PointerEvent(EventType type, uint32_t id,
|
||||
const glm::vec2& pos2D, const glm::vec3& pos3D,
|
||||
const glm::vec3& normal, const glm::vec3& direction,
|
||||
Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers);
|
||||
Button button, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::KeyboardModifier::NoModifier);
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event);
|
||||
static void fromScriptValue(const QScriptValue& object, PointerEvent& event);
|
||||
|
@ -55,6 +55,11 @@ public:
|
|||
Button getButton() const { return _button; }
|
||||
uint32_t getButtons() const { return _buttons; }
|
||||
Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; }
|
||||
bool shouldFocus() const { return _shouldFocus; }
|
||||
|
||||
void setType(EventType type) { _type = type; }
|
||||
void setButton(Button button) { _button = button; }
|
||||
void setShouldFocus(bool focus) { _shouldFocus = focus; }
|
||||
|
||||
private:
|
||||
EventType _type;
|
||||
|
@ -67,6 +72,8 @@ private:
|
|||
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
|
||||
|
||||
bool _shouldFocus { true };
|
||||
};
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const PointerEvent& p);
|
||||
|
|
|
@ -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