mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Additional thread safety
This commit is contained in:
parent
b52dd7b822
commit
8372d73fec
23 changed files with 413 additions and 249 deletions
|
@ -63,10 +63,7 @@ void RenderingClient::sendAvatarPacket() {
|
|||
}
|
||||
|
||||
void RenderingClient::cleanupBeforeQuit() {
|
||||
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
|
||||
"stop", Qt::BlockingQueuedConnection);
|
||||
|
||||
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
|
||||
// destroy the AudioClient so it and its thread will safely go down
|
||||
DependencyManager::destroy<AudioClient>();
|
||||
}
|
||||
|
|
|
@ -1844,7 +1844,7 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
// FIXME: something else is holding a reference to AudioClient,
|
||||
// so it must be explicitly synchronously stopped here
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "cleanupBeforeQuit", Qt::BlockingQueuedConnection);
|
||||
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
|
||||
|
||||
// destroy Audio so it and its threads have a chance to go down safely
|
||||
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
@ -152,22 +153,31 @@ QString HMDScriptingInterface::preferredAudioOutput() const {
|
|||
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
|
||||
}
|
||||
|
||||
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
|
||||
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "setHandLasers", Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(int, hands), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([offscreenUi, enabled] {
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
||||
});
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
||||
return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
|
||||
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
|
||||
color, direction);
|
||||
}
|
||||
|
||||
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([offscreenUi, enabled] {
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
||||
});
|
||||
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "setExtraLaser", Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(glm::vec3, worldStart), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
auto sensorToWorld = myAvatar->getSensorToWorldMatrix();
|
||||
|
@ -179,11 +189,11 @@ bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enab
|
|||
color, sensorStart, sensorDirection);
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::disableExtraLaser() const {
|
||||
void HMDScriptingInterface::disableExtraLaser() {
|
||||
setExtraLaser(vec3(0), false, vec4(0), vec3(0));
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::disableHandLasers(int hands) const {
|
||||
void HMDScriptingInterface::disableHandLasers(int hands) {
|
||||
setHandLasers(hands, false, vec4(0), vec3(0));
|
||||
}
|
||||
|
||||
|
|
|
@ -51,11 +51,11 @@ public:
|
|||
Q_INVOKABLE void requestHideHandControllers();
|
||||
Q_INVOKABLE bool shouldShowHandControllers() const;
|
||||
|
||||
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
|
||||
Q_INVOKABLE void disableHandLasers(int hands) const;
|
||||
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction);
|
||||
Q_INVOKABLE void disableHandLasers(int hands);
|
||||
|
||||
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
|
||||
Q_INVOKABLE void disableExtraLaser() const;
|
||||
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction);
|
||||
Q_INVOKABLE void disableExtraLaser();
|
||||
|
||||
|
||||
/// Suppress the activation of any on-screen keyboard so that a script operation will
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <Trace.h>
|
||||
#include <StatTracker.h>
|
||||
|
@ -57,20 +58,25 @@ void TestScriptingInterface::waitIdle() {
|
|||
}
|
||||
|
||||
bool TestScriptingInterface::loadTestScene(QString scene) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "loadTestScene", Q_RETURN_ARG(bool, result), Q_ARG(QString, scene));
|
||||
return result;
|
||||
}
|
||||
|
||||
static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/";
|
||||
static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/";
|
||||
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
|
||||
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([scene]()->QVariant {
|
||||
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
|
||||
auto tree = qApp->getEntities()->getTree();
|
||||
auto treeIsClient = tree->getIsClient();
|
||||
// Force the tree to accept the load regardless of permissions
|
||||
tree->setIsClient(false);
|
||||
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
|
||||
tree->setIsClient(treeIsClient);
|
||||
return result;
|
||||
}).toBool();
|
||||
|
||||
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
|
||||
auto tree = qApp->getEntities()->getTree();
|
||||
auto treeIsClient = tree->getIsClient();
|
||||
// Force the tree to accept the load regardless of permissions
|
||||
tree->setIsClient(false);
|
||||
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
|
||||
tree->setIsClient(treeIsClient);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TestScriptingInterface::startTracing(QString logrules) {
|
||||
|
|
|
@ -45,11 +45,13 @@ Image3DOverlay::~Image3DOverlay() {
|
|||
}
|
||||
|
||||
void Image3DOverlay::update(float deltatime) {
|
||||
#if OVERLAY_PANELS
|
||||
if (usecTimestampNow() > _transformExpiry) {
|
||||
Transform transform = getTransform();
|
||||
applyTransformTo(transform);
|
||||
setTransform(transform);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Image3DOverlay::render(RenderArgs* args) {
|
||||
|
|
|
@ -84,9 +84,9 @@ public:
|
|||
void setColorPulse(float value) { _colorPulse = value; }
|
||||
void setAlphaPulse(float value) { _alphaPulse = value; }
|
||||
|
||||
virtual void setProperties(const QVariantMap& properties);
|
||||
virtual Overlay* createClone() const = 0;
|
||||
virtual QVariant getProperty(const QString& property);
|
||||
Q_INVOKABLE virtual void setProperties(const QVariantMap& properties);
|
||||
Q_INVOKABLE virtual Overlay* createClone() const = 0;
|
||||
Q_INVOKABLE virtual QVariant getProperty(const QString& property);
|
||||
|
||||
render::ItemID getRenderItemID() const { return _renderItemID; }
|
||||
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "OverlayPanel.h"
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
|
||||
#include <QVariant>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <DependencyManager.h>
|
||||
|
@ -185,3 +187,4 @@ void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
|
|||
pointTransformAtCamera(transform, getOffsetRotation());
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -22,6 +22,7 @@
|
|||
#include "Billboardable.h"
|
||||
#include "Overlay.h"
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
class PropertyBinding {
|
||||
public:
|
||||
PropertyBinding() {}
|
||||
|
@ -80,4 +81,6 @@ private:
|
|||
QScriptEngine* _scriptEngine;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_OverlayPanel_h
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <render/Scene.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
@ -40,8 +41,6 @@ Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
|
|||
|
||||
void Overlays::cleanupAllOverlays() {
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
QWriteLocker deleteLock(&_deleteLock);
|
||||
foreach(Overlay::Pointer overlay, _overlaysHUD) {
|
||||
_overlaysToDelete.push_back(overlay);
|
||||
}
|
||||
|
@ -50,19 +49,22 @@ void Overlays::cleanupAllOverlays() {
|
|||
}
|
||||
_overlaysHUD.clear();
|
||||
_overlaysWorld.clear();
|
||||
#if OVERLAY_PANELS
|
||||
_panels.clear();
|
||||
#endif
|
||||
}
|
||||
cleanupOverlaysToDelete();
|
||||
}
|
||||
|
||||
void Overlays::init() {
|
||||
#if OVERLAY_PANELS
|
||||
_scriptEngine = new QScriptEngine();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Overlays::update(float deltatime) {
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
|
||||
thisOverlay->update(deltatime);
|
||||
}
|
||||
|
@ -80,8 +82,6 @@ void Overlays::cleanupOverlaysToDelete() {
|
|||
render::Transaction transaction;
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
|
||||
do {
|
||||
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
|
||||
|
||||
|
@ -100,7 +100,6 @@ void Overlays::cleanupOverlaysToDelete() {
|
|||
|
||||
void Overlays::renderHUD(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(render_overlays, __FUNCTION__);
|
||||
QReadLocker lock(&_lock);
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -126,12 +125,10 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
void Overlays::disable() {
|
||||
QWriteLocker lock(&_lock);
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
void Overlays::enable() {
|
||||
QWriteLocker lock(&_lock);
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
|
@ -146,6 +143,12 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
|||
}
|
||||
|
||||
OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
hifi::qt::blockingInvokeMethod(this, "addOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(QString, type), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = nullptr;
|
||||
|
||||
if (type == ImageOverlay::TYPE) {
|
||||
|
@ -185,8 +188,7 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
|
|||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
||||
OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
|
||||
QWriteLocker lock(&_lock);
|
||||
OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
||||
OverlayID thisID = OverlayID(QUuid::createUuid());
|
||||
overlay->setOverlayID(thisID);
|
||||
overlay->setStackOrder(_stackOrder++);
|
||||
|
@ -205,14 +207,22 @@ OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
|
|||
}
|
||||
|
||||
OverlayID Overlays::cloneOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
hifi::qt::blockingInvokeMethod(this, "cloneOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
|
||||
if (thisOverlay) {
|
||||
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone()));
|
||||
#if OVERLAY_PANELS
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
|
||||
if (attachable && attachable->getParentPanel()) {
|
||||
attachable->getParentPanel()->addChild(cloneId);
|
||||
}
|
||||
#endif
|
||||
return cloneId;
|
||||
}
|
||||
|
||||
|
@ -220,21 +230,29 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
|
|||
}
|
||||
|
||||
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
|
||||
QWriteLocker lock(&_lock);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "editOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
thisOverlay->setProperties(properties.toMap());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Overlays::editOverlays(const QVariant& propertiesById) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "editOverlays", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, propertiesById));
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap map = propertiesById.toMap();
|
||||
bool success = true;
|
||||
QWriteLocker lock(&_lock);
|
||||
for (const auto& key : map.keys()) {
|
||||
OverlayID id = OverlayID(key);
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
|
@ -249,10 +267,14 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
|
|||
}
|
||||
|
||||
void Overlays::deleteOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id));
|
||||
return;
|
||||
}
|
||||
|
||||
Overlay::Pointer overlayToDelete;
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
overlayToDelete = _overlaysHUD.take(id);
|
||||
} else if (_overlaysWorld.contains(id)) {
|
||||
|
@ -262,19 +284,25 @@ void Overlays::deleteOverlay(OverlayID id) {
|
|||
}
|
||||
}
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
|
||||
if (attachable && attachable->getParentPanel()) {
|
||||
attachable->getParentPanel()->removeChild(id);
|
||||
attachable->setParentPanel(nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
_overlaysToDelete.push_back(overlayToDelete);
|
||||
|
||||
emit overlayDeleted(id);
|
||||
}
|
||||
|
||||
QString Overlays::getOverlayType(OverlayID overlayId) const {
|
||||
QString Overlays::getOverlayType(OverlayID overlayId) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(OverlayID, overlayId));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer overlay = getOverlay(overlayId);
|
||||
if (overlay) {
|
||||
return overlay->getType();
|
||||
|
@ -283,6 +311,12 @@ QString Overlays::getOverlayType(OverlayID overlayId) const {
|
|||
}
|
||||
|
||||
QObject* Overlays::getOverlayObject(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QObject* result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
return qobject_cast<QObject*>(&(*thisOverlay));
|
||||
|
@ -290,6 +324,7 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
OverlayID Overlays::getParentPanel(OverlayID childId) const {
|
||||
Overlay::Pointer overlay = getOverlay(childId);
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
|
||||
|
@ -330,10 +365,16 @@ void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 pointCopy = point;
|
||||
QReadLocker lock(&_lock);
|
||||
if (!_enabled) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
@ -365,9 +406,14 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
}
|
||||
|
||||
OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayPropertyResult result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getProperty", Q_RETURN_ARG(OverlayPropertyResult, result), Q_ARG(OverlayID, id), Q_ARG(QString, property));
|
||||
return result;
|
||||
}
|
||||
|
||||
OverlayPropertyResult result;
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
QReadLocker lock(&_lock);
|
||||
if (thisOverlay && thisOverlay->supportsGetProperty()) {
|
||||
result.value = thisOverlay->getProperty(property);
|
||||
}
|
||||
|
@ -405,7 +451,18 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
|
|||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly, bool collidableOnly) {
|
||||
QReadLocker lock(&_lock);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
RayToOverlayIntersectionResult result;
|
||||
hifi::qt::blockingInvokeMethod(this, "findRayIntersectionInternal", Q_RETURN_ARG(RayToOverlayIntersectionResult, result),
|
||||
Q_ARG(PickRay, ray),
|
||||
Q_ARG(bool, precisionPicking),
|
||||
Q_ARG(QVector<OverlayID>, overlaysToInclude),
|
||||
Q_ARG(QVector<OverlayID>, overlaysToDiscard),
|
||||
Q_ARG(bool, visibleOnly),
|
||||
Q_ARG(bool, collidableOnly));
|
||||
return result;
|
||||
}
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
bool bestIsFront = false;
|
||||
|
||||
|
@ -448,16 +505,6 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
|
|||
return result;
|
||||
}
|
||||
|
||||
RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
|
||||
intersects(false),
|
||||
overlayID(UNKNOWN_OVERLAY_ID),
|
||||
distance(0),
|
||||
face(),
|
||||
intersection(),
|
||||
extraInfo()
|
||||
{
|
||||
}
|
||||
|
||||
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
||||
auto obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
|
@ -531,7 +578,12 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar
|
|||
}
|
||||
|
||||
bool Overlays::isLoaded(OverlayID id) {
|
||||
QReadLocker lock(&_lock);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (!thisOverlay) {
|
||||
return false; // not found
|
||||
|
@ -539,7 +591,13 @@ bool Overlays::isLoaded(OverlayID id) {
|
|||
return thisOverlay->isLoaded();
|
||||
}
|
||||
|
||||
QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
|
||||
QSizeF Overlays::textSize(OverlayID id, const QString& text) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QSizeF result;
|
||||
hifi::qt::blockingInvokeMethod(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(OverlayID, id), Q_ARG(QString, text));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = _overlaysHUD[id];
|
||||
if (thisOverlay) {
|
||||
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
||||
|
@ -554,6 +612,7 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
|
|||
return QSizeF(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
|
||||
QWriteLocker lock(&_lock);
|
||||
|
||||
|
@ -607,8 +666,15 @@ void Overlays::deletePanel(OverlayID panelId) {
|
|||
|
||||
emit panelDeleted(panelId);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Overlays::isAddedOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
hifi::qt::blockingInvokeMethod(this, "isAddedOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
||||
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
|
||||
}
|
||||
|
||||
|
@ -636,20 +702,43 @@ void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) {
|
|||
emit hoverLeaveOverlay(id, event);
|
||||
}
|
||||
|
||||
OverlayID Overlays::getKeyboardFocusOverlay() const {
|
||||
OverlayID Overlays::getKeyboardFocusOverlay() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getKeyboardFocusOverlay", Q_RETURN_ARG(OverlayID, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return qApp->getKeyboardFocusOverlay();
|
||||
}
|
||||
|
||||
void Overlays::setKeyboardFocusOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setKeyboardFocusOverlay", Q_ARG(OverlayID, id));
|
||||
return;
|
||||
}
|
||||
|
||||
qApp->setKeyboardFocusOverlay(id);
|
||||
}
|
||||
|
||||
float Overlays::width() const {
|
||||
float Overlays::width() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result;
|
||||
hifi::qt::blockingInvokeMethod(this, "width", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().width();
|
||||
}
|
||||
|
||||
float Overlays::height() const {
|
||||
float Overlays::height() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result;
|
||||
hifi::qt::blockingInvokeMethod(this, "height", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().height();
|
||||
}
|
||||
|
@ -705,7 +794,6 @@ PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay r
|
|||
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
|
||||
|
||||
QReadLocker lock(&_lock);
|
||||
auto position = thisOverlay->getPosition();
|
||||
auto rotation = thisOverlay->getRotation();
|
||||
auto dimensions = thisOverlay->getSize();
|
||||
|
@ -854,8 +942,13 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) const {
|
||||
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
||||
QVector<QUuid> result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
hifi::qt::blockingInvokeMethod(this, "findOverlays", Q_RETURN_ARG(QVector<QUuid>, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||
int checked = 0;
|
||||
|
|
|
@ -25,8 +25,9 @@
|
|||
#include <PointerEvent.h>
|
||||
|
||||
#include "Overlay.h"
|
||||
#include "OverlayPanel.h"
|
||||
|
||||
#include "PanelAttachable.h"
|
||||
#include "OverlayPanel.h"
|
||||
|
||||
class PickRay;
|
||||
|
||||
|
@ -41,6 +42,8 @@ Q_DECLARE_METATYPE(OverlayPropertyResult);
|
|||
QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value);
|
||||
void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value);
|
||||
|
||||
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
||||
|
||||
/**jsdoc
|
||||
* @typedef Overlays.RayToOverlayIntersectionResult
|
||||
* @property {bool} intersects True if the PickRay intersected with a 3D overlay.
|
||||
|
@ -51,10 +54,9 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
|
|||
*/
|
||||
class RayToOverlayIntersectionResult {
|
||||
public:
|
||||
RayToOverlayIntersectionResult();
|
||||
bool intersects;
|
||||
OverlayID overlayID;
|
||||
float distance;
|
||||
bool intersects { false };
|
||||
OverlayID overlayID { UNKNOWN_OVERLAY_ID };
|
||||
float distance { 0 };
|
||||
BoxFace face;
|
||||
glm::vec3 surfaceNormal;
|
||||
glm::vec3 intersection;
|
||||
|
@ -77,8 +79,6 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
|
|||
* @namespace Overlays
|
||||
*/
|
||||
|
||||
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
||||
|
||||
class Overlays : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -94,11 +94,13 @@ public:
|
|||
void enable();
|
||||
|
||||
Overlay::Pointer getOverlay(OverlayID id) const;
|
||||
#if OVERLAY_PANELS
|
||||
OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; }
|
||||
#endif
|
||||
|
||||
/// adds an overlay that's already been created
|
||||
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||
OverlayID addOverlay(Overlay::Pointer overlay);
|
||||
OverlayID addOverlay(const Overlay::Pointer& overlay);
|
||||
|
||||
bool mousePressEvent(QMouseEvent* event);
|
||||
bool mouseDoublePressEvent(QMouseEvent* event);
|
||||
|
@ -156,7 +158,7 @@ public slots:
|
|||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of.
|
||||
* @return {string} The type of the overlay if found, otherwise the empty string.
|
||||
*/
|
||||
QString getOverlayType(OverlayID overlayId) const;
|
||||
QString getOverlayType(OverlayID overlayId);
|
||||
|
||||
/**jsdoc
|
||||
* Get the overlay Script object.
|
||||
|
@ -215,7 +217,7 @@ public slots:
|
|||
* @param {float} radius search radius
|
||||
* @return {Overlays.OverlayID[]} list of overlays withing the radius
|
||||
*/
|
||||
QVector<QUuid> findOverlays(const glm::vec3& center, float radius) const;
|
||||
QVector<QUuid> findOverlays(const glm::vec3& center, float radius);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an overlay's assets have been loaded. For example, if the
|
||||
|
@ -237,7 +239,7 @@ public slots:
|
|||
* @param {string} The string to measure.
|
||||
* @return {Vec2} The size of the text.
|
||||
*/
|
||||
QSizeF textSize(OverlayID id, const QString& text) const;
|
||||
QSizeF textSize(OverlayID id, const QString& text);
|
||||
|
||||
/**jsdoc
|
||||
* Get the width of the virtual 2D HUD.
|
||||
|
@ -245,7 +247,7 @@ public slots:
|
|||
* @function Overlays.width
|
||||
* @return {float} The width of the 2D HUD.
|
||||
*/
|
||||
float width() const;
|
||||
float width();
|
||||
|
||||
/**jsdoc
|
||||
* Get the height of the virtual 2D HUD.
|
||||
|
@ -253,11 +255,12 @@ public slots:
|
|||
* @function Overlays.height
|
||||
* @return {float} The height of the 2D HUD.
|
||||
*/
|
||||
float height() const;
|
||||
float height();
|
||||
|
||||
/// return true if there is an overlay with that id else false
|
||||
bool isAddedOverlay(OverlayID id);
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
OverlayID getParentPanel(OverlayID childId) const;
|
||||
void setParentPanel(OverlayID childId, OverlayID panelId);
|
||||
|
||||
|
@ -279,6 +282,8 @@ public slots:
|
|||
/// return true if there is a panel with that id else false
|
||||
bool isAddedPanel(OverlayID id) { return _panels.contains(id); }
|
||||
|
||||
#endif
|
||||
|
||||
void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
@ -287,7 +292,7 @@ public slots:
|
|||
void sendHoverOverOverlay(OverlayID id, PointerEvent event);
|
||||
void sendHoverLeaveOverlay(OverlayID id, PointerEvent event);
|
||||
|
||||
OverlayID getKeyboardFocusOverlay() const;
|
||||
OverlayID getKeyboardFocusOverlay();
|
||||
void setKeyboardFocusOverlay(OverlayID id);
|
||||
|
||||
signals:
|
||||
|
@ -316,13 +321,15 @@ private:
|
|||
|
||||
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
|
||||
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
|
||||
#if OVERLAY_PANELS
|
||||
QMap<OverlayID, OverlayPanel::Pointer> _panels;
|
||||
#endif
|
||||
QList<Overlay::Pointer> _overlaysToDelete;
|
||||
unsigned int _stackOrder { 1 };
|
||||
|
||||
QReadWriteLock _lock;
|
||||
QReadWriteLock _deleteLock;
|
||||
#if OVERLAY_PANELS
|
||||
QScriptEngine* _scriptEngine;
|
||||
#endif
|
||||
bool _enabled = true;
|
||||
|
||||
PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
|
||||
|
@ -331,7 +338,7 @@ private:
|
|||
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
|
||||
RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
||||
Q_INVOKABLE RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
|
|
@ -16,11 +16,15 @@
|
|||
#include "OverlayPanel.h"
|
||||
|
||||
bool PanelAttachable::getParentVisible() const {
|
||||
#if OVERLAY_PANELS
|
||||
if (getParentPanel()) {
|
||||
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
QVariant PanelAttachable::getProperty(const QString& property) {
|
||||
|
@ -61,11 +65,13 @@ void PanelAttachable::applyTransformTo(Transform& transform, bool force) {
|
|||
if (force || usecTimestampNow() > _transformExpiry) {
|
||||
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
|
||||
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
|
||||
#if OVERLAY_PANELS
|
||||
if (getParentPanel()) {
|
||||
getParentPanel()->applyTransformTo(transform, true);
|
||||
transform.postTranslate(getOffsetPosition());
|
||||
transform.postRotate(getOffsetRotation());
|
||||
transform.postScale(getOffsetScale());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#ifndef hifi_PanelAttachable_h
|
||||
#define hifi_PanelAttachable_h
|
||||
|
||||
#define OVERLAY_PANELS 0
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
@ -39,18 +41,21 @@
|
|||
#include <QUuid>
|
||||
|
||||
class OverlayPanel;
|
||||
|
||||
class PanelAttachable {
|
||||
public:
|
||||
// getters
|
||||
#if OVERLAY_PANELS
|
||||
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
|
||||
#endif
|
||||
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
|
||||
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
|
||||
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
|
||||
bool getParentVisible() const;
|
||||
|
||||
// setters
|
||||
#if OVERLAY_PANELS
|
||||
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
|
||||
#endif
|
||||
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
|
||||
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
|
||||
void setOffsetScale(float scale) { _offset.setScale(scale); }
|
||||
|
@ -66,7 +71,9 @@ protected:
|
|||
quint64 _transformExpiry = 0;
|
||||
|
||||
private:
|
||||
#if OVERLAY_PANELS
|
||||
std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
|
||||
#endif
|
||||
Transform _offset;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,21 +32,20 @@ QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay)
|
|||
}
|
||||
|
||||
void QmlOverlay::buildQmlElement(const QUrl& url) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "buildQmlElement", Q_ARG(QUrl, url));
|
||||
return;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->returnFromUiThread([=] {
|
||||
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
|
||||
// Create a shared ptr with a custom deleter lambda, that calls deleteLater
|
||||
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
|
||||
if (ptr) {
|
||||
ptr->deleteLater();
|
||||
}
|
||||
});
|
||||
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
|
||||
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
|
||||
// Create a shared ptr with a custom deleter lambda, that calls deleteLater
|
||||
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
|
||||
if (ptr) {
|
||||
ptr->deleteLater();
|
||||
}
|
||||
});
|
||||
while (!_qmlElement) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
return QVariant();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -55,20 +54,23 @@ QmlOverlay::~QmlOverlay() {
|
|||
}
|
||||
|
||||
void QmlOverlay::setProperties(const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
|
||||
return;
|
||||
}
|
||||
|
||||
Overlay2D::setProperties(properties);
|
||||
auto bounds = _bounds;
|
||||
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([weakQmlElement, bounds, properties] {
|
||||
// check to see if qmlElement still exists
|
||||
auto qmlElement = weakQmlElement.lock();
|
||||
if (qmlElement) {
|
||||
qmlElement->setX(bounds.left());
|
||||
qmlElement->setY(bounds.top());
|
||||
qmlElement->setWidth(bounds.width());
|
||||
qmlElement->setHeight(bounds.height());
|
||||
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
|
||||
}
|
||||
});
|
||||
// check to see if qmlElement still exists
|
||||
auto qmlElement = weakQmlElement.lock();
|
||||
if (qmlElement) {
|
||||
qmlElement->setX(bounds.left());
|
||||
qmlElement->setY(bounds.top());
|
||||
qmlElement->setWidth(bounds.width());
|
||||
qmlElement->setHeight(bounds.height());
|
||||
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
|
||||
}
|
||||
}
|
||||
|
||||
void QmlOverlay::render(RenderArgs* args) {
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
void render(RenderArgs* args) override;
|
||||
|
||||
private:
|
||||
void buildQmlElement(const QUrl& url);
|
||||
Q_INVOKABLE void buildQmlElement(const QUrl& url);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<QQuickItem> _qmlElement;
|
||||
|
|
|
@ -259,10 +259,21 @@ void AudioClient::customDeleter() {
|
|||
void AudioClient::cleanupBeforeQuit() {
|
||||
// FIXME: this should be put in customDeleter, but there is still a reference to this when it is called,
|
||||
// so this must be explicitly, synchronously stopped
|
||||
static ConditionalGuard guard;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
// This will likely be called from the main thread, but we don't want to do blocking queued calls
|
||||
// from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait
|
||||
// for completion
|
||||
// The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection
|
||||
// in the code
|
||||
QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
|
||||
guard.wait();
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
_checkDevicesTimer->stop();
|
||||
guard.trigger();
|
||||
}
|
||||
|
||||
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
|
||||
|
@ -1859,7 +1870,7 @@ void AudioClient::startThread() {
|
|||
}
|
||||
|
||||
void AudioClient::setInputVolume(float volume) {
|
||||
if (_audioInput && volume != _audioInput->volume()) {
|
||||
if (_audioInput && volume != (float)_audioInput->volume()) {
|
||||
_audioInput->setVolume(volume);
|
||||
emit inputVolumeChanged(_audioInput->volume());
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
static const char* const URL_PROPERTY = "source";
|
||||
|
@ -21,39 +22,51 @@ static const char* const SCRIPT_PROPERTY = "scriptUrl";
|
|||
// Method called by Qt scripts to create a new web window in the overlay
|
||||
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
auto properties = parseArguments(context);
|
||||
QmlWebWindowClass* retVal { nullptr };
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([&] {
|
||||
retVal = new QmlWebWindowClass();
|
||||
retVal->initQml(properties);
|
||||
}, true);
|
||||
QmlWebWindowClass* retVal = new QmlWebWindowClass();
|
||||
Q_ASSERT(retVal);
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
retVal->moveToThread(qApp->thread());
|
||||
QMetaObject::invokeMethod(retVal, "initQml", Q_ARG(QVariantMap, properties));
|
||||
} else {
|
||||
retVal->initQml(properties);
|
||||
}
|
||||
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
|
||||
return engine->newQObject(retVal);
|
||||
}
|
||||
|
||||
QString QmlWebWindowClass::getURL() const {
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant();
|
||||
}
|
||||
return _qmlWindow->property(URL_PROPERTY);
|
||||
});
|
||||
return result.toString();
|
||||
QString QmlWebWindowClass::getURL() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result = false;
|
||||
hifi::qt::blockingInvokeMethod(this, "getURL", Q_RETURN_ARG(QString, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return _qmlWindow->property(URL_PROPERTY).toString();
|
||||
}
|
||||
|
||||
void QmlWebWindowClass::setURL(const QString& urlString) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(URL_PROPERTY, urlString);
|
||||
}
|
||||
});
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setURL", Q_ARG(QString, urlString));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(URL_PROPERTY, urlString);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWebWindowClass::setScriptURL(const QString& script) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
|
||||
}
|
||||
});
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setScriptURL", Q_ARG(QString, script));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
QString getURL() const;
|
||||
QString getURL();
|
||||
void setURL(const QString& url);
|
||||
void setScriptURL(const QString& script);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
static const char* const SOURCE_PROPERTY = "source";
|
||||
|
@ -73,13 +74,15 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
|
|||
// Method called by Qt scripts to create a new web window in the overlay
|
||||
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
auto properties = parseArguments(context);
|
||||
QmlWindowClass* retVal { nullptr };
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([&] {
|
||||
retVal = new QmlWindowClass();
|
||||
retVal->initQml(properties);
|
||||
}, true);
|
||||
QmlWindowClass* retVal = new QmlWindowClass();
|
||||
Q_ASSERT(retVal);
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
retVal->moveToThread(qApp->thread());
|
||||
hifi::qt::blockingInvokeMethod(retVal, "initQml", Q_ARG(QVariantMap, properties));
|
||||
} else {
|
||||
retVal->initQml(properties);
|
||||
}
|
||||
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
|
||||
return engine->newQObject(retVal);
|
||||
}
|
||||
|
@ -214,49 +217,64 @@ QQuickItem* QmlWindowClass::asQuickItem() const {
|
|||
}
|
||||
|
||||
void QmlWindowClass::setVisible(bool visible) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setVisible", Q_ARG(bool, visible));
|
||||
return;
|
||||
}
|
||||
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
if (_toolWindow) {
|
||||
// For tool window tabs we special case visibility as a function call on the tab parent
|
||||
// The tool window itself has special logic based on whether any tabs are visible
|
||||
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
|
||||
} else {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
|
||||
});
|
||||
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool QmlWindowClass::isVisible() const {
|
||||
bool QmlWindowClass::isVisible() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
hifi::qt::blockingInvokeMethod(this, "isVisible", Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// The tool window itself has special logic based on whether any tabs are enabled
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant::fromValue(false);
|
||||
}
|
||||
if (_toolWindow) {
|
||||
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
|
||||
} else {
|
||||
return QVariant::fromValue(asQuickItem()->isVisible());
|
||||
}
|
||||
}).toBool();
|
||||
if (_qmlWindow.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_toolWindow) {
|
||||
return dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled();
|
||||
}
|
||||
|
||||
return asQuickItem()->isVisible();
|
||||
}
|
||||
|
||||
glm::vec2 QmlWindowClass::getPosition() const {
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant(QPointF(0, 0));
|
||||
}
|
||||
return asQuickItem()->position();
|
||||
});
|
||||
return toGlm(result.toPointF());
|
||||
glm::vec2 QmlWindowClass::getPosition() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
vec2 result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getPosition", Q_RETURN_ARG(vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return toGlm(asQuickItem()->position());
|
||||
}
|
||||
|
||||
|
||||
void QmlWindowClass::setPosition(const glm::vec2& position) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||
}
|
||||
});
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(vec2, position));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWindowClass::setPosition(int x, int y) {
|
||||
|
@ -268,23 +286,29 @@ glm::vec2 toGlm(const QSizeF& size) {
|
|||
return glm::vec2(size.width(), size.height());
|
||||
}
|
||||
|
||||
glm::vec2 QmlWindowClass::getSize() const {
|
||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||
if (_qmlWindow.isNull()) {
|
||||
return QVariant(QSizeF(0, 0));
|
||||
}
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
return QSizeF(targetWindow->width(), targetWindow->height());
|
||||
});
|
||||
return toGlm(result.toSizeF());
|
||||
glm::vec2 QmlWindowClass::getSize() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
vec2 result;
|
||||
hifi::qt::blockingInvokeMethod(this, "getSize", Q_RETURN_ARG(vec2, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_qmlWindow.isNull()) {
|
||||
return {};
|
||||
}
|
||||
QQuickItem* targetWindow = asQuickItem();
|
||||
return vec2(targetWindow->width(), targetWindow->height());
|
||||
}
|
||||
|
||||
void QmlWindowClass::setSize(const glm::vec2& size) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||
}
|
||||
});
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setSize", Q_ARG(vec2, size));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWindowClass::setSize(int width, int height) {
|
||||
|
@ -292,28 +316,33 @@ void QmlWindowClass::setSize(int width, int height) {
|
|||
}
|
||||
|
||||
void QmlWindowClass::setTitle(const QString& title) {
|
||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setProperty(TITLE_PROPERTY, title);
|
||||
}
|
||||
});
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setTitle", Q_ARG(QString, title));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlWindow.isNull()) {
|
||||
asQuickItem()->setProperty(TITLE_PROPERTY, title);
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWindowClass::close() {
|
||||
if (_qmlWindow) {
|
||||
if (_toolWindow) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
auto toolWindow = offscreenUi->getToolWindow();
|
||||
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
|
||||
Q_ARG(QVariant, _source));
|
||||
Q_ASSERT(invokeResult);
|
||||
});
|
||||
} else {
|
||||
_qmlWindow->deleteLater();
|
||||
}
|
||||
_qmlWindow = nullptr;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "close");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_toolWindow) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto toolWindow = offscreenUi->getToolWindow();
|
||||
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
|
||||
Q_ARG(QVariant, _source));
|
||||
Q_ASSERT(invokeResult);
|
||||
return;
|
||||
} else if (_qmlWindow) {
|
||||
_qmlWindow->deleteLater();
|
||||
}
|
||||
_qmlWindow = nullptr;
|
||||
}
|
||||
|
||||
void QmlWindowClass::hasMoved(QVector2D position) {
|
||||
|
@ -325,10 +354,13 @@ void QmlWindowClass::hasClosed() {
|
|||
}
|
||||
|
||||
void QmlWindowClass::raise() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "raise");
|
||||
return;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
if (!_qmlWindow.isNull()) {
|
||||
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
|
||||
}
|
||||
});
|
||||
if (_qmlWindow) {
|
||||
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,18 +31,18 @@ public:
|
|||
QmlWindowClass();
|
||||
~QmlWindowClass();
|
||||
|
||||
virtual void initQml(QVariantMap properties);
|
||||
Q_INVOKABLE virtual void initQml(QVariantMap properties);
|
||||
QQuickItem* asQuickItem() const;
|
||||
|
||||
public slots:
|
||||
bool isVisible() const;
|
||||
bool isVisible();
|
||||
void setVisible(bool visible);
|
||||
|
||||
glm::vec2 getPosition() const;
|
||||
glm::vec2 getPosition();
|
||||
void setPosition(const glm::vec2& position);
|
||||
void setPosition(int x, int y);
|
||||
|
||||
glm::vec2 getSize() const;
|
||||
glm::vec2 getSize();
|
||||
void setSize(const glm::vec2& size);
|
||||
void setSize(int width, int height);
|
||||
void setTitle(const QString& title);
|
||||
|
|
|
@ -887,28 +887,6 @@ QQmlContext* OffscreenQmlSurface::getSurfaceContext() {
|
|||
return _qmlContext;
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "executeOnUiThread", blocking ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||
Q_ARG(std::function<void()>, function));
|
||||
return;
|
||||
}
|
||||
|
||||
function();
|
||||
}
|
||||
|
||||
QVariant OffscreenQmlSurface::returnFromUiThread(std::function<QVariant()> function) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
hifi::qt::blockingInvokeMethod(this, "returnFromUiThread",
|
||||
Q_RETURN_ARG(QVariant, result),
|
||||
Q_ARG(std::function<QVariant()>, function));
|
||||
return result;
|
||||
}
|
||||
|
||||
return function();
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::focusDestroyed(QObject *obj) {
|
||||
_currentFocusItem = nullptr;
|
||||
}
|
||||
|
|
|
@ -55,10 +55,6 @@ public:
|
|||
return load(QUrl(qmlSourceFile), f);
|
||||
}
|
||||
void clearCache();
|
||||
|
||||
Q_INVOKABLE void executeOnUiThread(std::function<void()> function, bool blocking = false);
|
||||
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
|
||||
|
||||
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
|
||||
// Optional values for event handling
|
||||
void setProxyWindow(QWindow* window);
|
||||
|
|
|
@ -214,20 +214,18 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
|
|||
|
||||
// create new desktop window
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
auto tabletRootWindow = new TabletRootWindow();
|
||||
tabletRootWindow->initQml(QVariantMap());
|
||||
auto quickItem = tabletRootWindow->asQuickItem();
|
||||
_desktopWindow = tabletRootWindow;
|
||||
QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
|
||||
auto tabletRootWindow = new TabletRootWindow();
|
||||
tabletRootWindow->initQml(QVariantMap());
|
||||
auto quickItem = tabletRootWindow->asQuickItem();
|
||||
_desktopWindow = tabletRootWindow;
|
||||
QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
|
||||
|
||||
QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));
|
||||
QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));
|
||||
|
||||
QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
|
||||
QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
|
||||
|
||||
// forward qml surface events to interface js
|
||||
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
|
||||
});
|
||||
// forward qml surface events to interface js
|
||||
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
|
||||
} else {
|
||||
_state = State::Home;
|
||||
removeButtonsFromToolbar();
|
||||
|
|
Loading…
Reference in a new issue