mirror of
https://github.com/lubosz/overte.git
synced 2025-04-16 10:46:45 +02:00
Merge pull request #14711 from SamGondelman/NOverlays10
Case 20585: Add missing Web Overlay functionality to Web Entities
This commit is contained in:
commit
f651ad2f67
30 changed files with 768 additions and 482 deletions
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// Web3DOverlay.qml
|
||||
//
|
||||
// Created by Gabriel Calero & Cristian Duarte on Jun 22, 2018
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Item {
|
||||
|
||||
property string url
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#262626" }
|
||||
GradientStop { position: 1.0; color: "#000000" }
|
||||
}
|
||||
}
|
||||
|
||||
function shortUrl(url) {
|
||||
var hostBegin = url.indexOf("://");
|
||||
if (hostBegin > -1) {
|
||||
url = url.substring(hostBegin + 3);
|
||||
}
|
||||
|
||||
var portBegin = url.indexOf(":");
|
||||
if (portBegin > -1) {
|
||||
url = url.substring(0, portBegin);
|
||||
}
|
||||
|
||||
var pathBegin = url.indexOf("/");
|
||||
if (pathBegin > -1) {
|
||||
url = url.substring(0, pathBegin);
|
||||
}
|
||||
|
||||
if (url.length > 45) {
|
||||
url = url.substring(0, 45);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
Text {
|
||||
id: urlText
|
||||
text: shortUrl(url)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: 10
|
||||
font.family: "Cairo"
|
||||
font.weight: Font.DemiBold
|
||||
font.pointSize: 48
|
||||
fontSizeMode: Text.Fit
|
||||
color: "#FFFFFF"
|
||||
minimumPixelSize: 5
|
||||
}
|
||||
|
||||
Image {
|
||||
id: hand
|
||||
source: "../../../icons/hand.svg"
|
||||
width: 300
|
||||
height: 300
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 100
|
||||
anchors.rightMargin: 100
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// WebEntityView.qml
|
||||
//
|
||||
// Created by Kunal Gosar on 16 March 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import "."
|
||||
|
||||
WebView {
|
||||
viewProfile: FileTypeProfile;
|
||||
|
||||
urlTag: "noDownload=true";
|
||||
}
|
|
@ -58,7 +58,7 @@ Item {
|
|||
keyItem.state = "mouseOver";
|
||||
|
||||
var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY);
|
||||
var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
var pointerID = QmlSurface.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
|
||||
if (Pointers.isLeftHand(pointerID)) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand);
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
#include <trackers/EyeTracker.h>
|
||||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
#include <RenderableWebEntityItem.h>
|
||||
#include <model-networking/MaterialCache.h>
|
||||
#include "recording/ClipCache.h"
|
||||
|
||||
|
@ -2320,6 +2321,76 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * Vectors::UP;
|
||||
});
|
||||
|
||||
render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
bool isTablet = url == TabletScriptingInterface::QML;
|
||||
if (htmlContent) {
|
||||
webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(render::entities::WebEntityRenderer::QML);
|
||||
cachedWebSurface = true;
|
||||
auto rootItemLoadedFunctor = [url, webSurface] {
|
||||
webSurface->getRootItem()->setProperty(render::entities::WebEntityRenderer::URL_PROPERTY, url);
|
||||
};
|
||||
if (webSurface->getRootItem()) {
|
||||
rootItemLoadedFunctor();
|
||||
} else {
|
||||
QObject::connect(webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, rootItemLoadedFunctor);
|
||||
}
|
||||
} else {
|
||||
// FIXME: the tablet should use the OffscreenQmlSurfaceCache
|
||||
webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
});
|
||||
});
|
||||
auto rootItemLoadedFunctor = [webSurface, url, isTablet] {
|
||||
Application::setupQmlSurface(webSurface->getSurfaceContext(), isTablet || url == OVERLAY_LOGIN_DIALOG.toString());
|
||||
};
|
||||
if (webSurface->getRootItem()) {
|
||||
rootItemLoadedFunctor();
|
||||
} else {
|
||||
QObject::connect(webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, rootItemLoadedFunctor);
|
||||
}
|
||||
webSurface->load(url);
|
||||
cachedWebSurface = false;
|
||||
}
|
||||
const uint8_t DEFAULT_MAX_FPS = 10;
|
||||
const uint8_t TABLET_FPS = 90;
|
||||
webSurface->setMaxFps(isTablet ? TABLET_FPS : DEFAULT_MAX_FPS);
|
||||
});
|
||||
render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([this](QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
webSurface->pause();
|
||||
|
||||
for (auto& connection : connections) {
|
||||
QObject::disconnect(connection);
|
||||
}
|
||||
connections.clear();
|
||||
|
||||
// If the web surface was fetched out of the cache, release it back into the cache
|
||||
if (cachedWebSurface) {
|
||||
// If it's going back into the cache make sure to explicitly set the URL to a blank page
|
||||
// in order to stop any resource consumption or audio related to the page.
|
||||
if (rootItem) {
|
||||
rootItem->setProperty("url", "about:blank");
|
||||
}
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
if (offscreenCache) {
|
||||
offscreenCache->release(render::entities::WebEntityRenderer::QML, webSurface);
|
||||
}
|
||||
cachedWebSurface = false;
|
||||
}
|
||||
webSurface.reset();
|
||||
});
|
||||
|
||||
// Preload Tablet sounds
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
DependencyManager::get<Keyboard>()->createKeyboard();
|
||||
|
@ -3021,7 +3092,7 @@ void Application::initializeUi() {
|
|||
});
|
||||
|
||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||
offscreenSurfaceCache->reserve(render::entities::WebEntityRenderer::QML, 2);
|
||||
#endif
|
||||
|
||||
flushMenuUpdates();
|
||||
|
@ -3161,6 +3232,61 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) {
|
||||
surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||
surfaceContext->setContextProperty("Vec3", new Vec3());
|
||||
surfaceContext->setContextProperty("Quat", new Quat());
|
||||
surfaceContext->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
||||
if (setAdditionalContextProperties) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
|
||||
surfaceContext->setContextProperty("offscreenFlags", flags);
|
||||
surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
surfaceContext->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
surfaceContext->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
surfaceContext->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
|
||||
surfaceContext->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
|
||||
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
surfaceContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
surfaceContext->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
surfaceContext->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
surfaceContext->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
|
||||
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
|
||||
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
PerformanceTimer perfTimer("updateCamera");
|
||||
|
|
|
@ -589,6 +589,8 @@ private:
|
|||
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||
void toggleTabletUI(bool shouldOpen = false) const;
|
||||
|
||||
static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties);
|
||||
|
||||
MainWindow* _window;
|
||||
QElapsedTimer& _sessionRunTimer;
|
||||
|
||||
|
|
|
@ -64,20 +64,13 @@
|
|||
#include "AboutUtil.h"
|
||||
#include "ResourceRequestObserver.h"
|
||||
|
||||
#include <RenderableWebEntityItem.h>
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static const float METERS_TO_INCHES = 39.3701f;
|
||||
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
const QString Web3DOverlay::TYPE = "web3d";
|
||||
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||
|
||||
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([surface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete surface;
|
||||
});
|
||||
};
|
||||
|
||||
Web3DOverlay::Web3DOverlay() {
|
||||
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||
|
@ -87,34 +80,21 @@ Web3DOverlay::Web3DOverlay() {
|
|||
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface);
|
||||
connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface);
|
||||
connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface);
|
||||
|
||||
//need to be intialized before Tablet 1st open
|
||||
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
|
||||
_cachedWebSurface = true;
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
buildWebSurface(true);
|
||||
}
|
||||
|
||||
Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
|
||||
Billboard3DOverlay(Web3DOverlay),
|
||||
_url(Web3DOverlay->_url),
|
||||
_scriptURL(Web3DOverlay->_scriptURL),
|
||||
_dpi(Web3DOverlay->_dpi),
|
||||
_showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight)
|
||||
_dpi(Web3DOverlay->_dpi)
|
||||
{
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
Web3DOverlay::~Web3DOverlay() {
|
||||
disconnect(this, &Web3DOverlay::requestWebSurface, this, nullptr);
|
||||
disconnect(this, &Web3DOverlay::releaseWebSurface, this, nullptr);
|
||||
disconnect(this, &Web3DOverlay::resizeWebSurface, this, nullptr);
|
||||
|
||||
destroyWebSurface();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
|
@ -128,74 +108,22 @@ void Web3DOverlay::rebuildWebSurface() {
|
|||
}
|
||||
|
||||
void Web3DOverlay::destroyWebSurface() {
|
||||
if (!_webSurface) {
|
||||
return;
|
||||
if (_webSurface) {
|
||||
render::entities::WebEntityRenderer::releaseWebSurface(_webSurface, _cachedWebSurface, _connections);
|
||||
}
|
||||
|
||||
QQuickItem* rootItem = _webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
_webSurface->pause();
|
||||
|
||||
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
|
||||
// If the web surface was fetched out of the cache, release it back into the cache
|
||||
if (_cachedWebSurface) {
|
||||
// If it's going back into the cache make sure to explicitly set the URL to a blank page
|
||||
// in order to stop any resource consumption or audio related to the page.
|
||||
if (rootItem) {
|
||||
rootItem->setProperty("url", "about:blank");
|
||||
}
|
||||
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
|
||||
if (offscreenCache) {
|
||||
offscreenCache->release(QML, _webSurface);
|
||||
}
|
||||
_cachedWebSurface = false;
|
||||
}
|
||||
_webSurface.reset();
|
||||
}
|
||||
|
||||
void Web3DOverlay::buildWebSurface() {
|
||||
void Web3DOverlay::buildWebSurface(bool overrideWeb) {
|
||||
if (_webSurface) {
|
||||
return;
|
||||
}
|
||||
// FIXME the context save here is most likely unecessary since the QML surfaces now render
|
||||
// off the main thread, and all GL context work is done off the main thread (I *think*)
|
||||
gl::withSavedContext([&] {
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
if (_currentMaxFPS != _desiredMaxFPS) {
|
||||
setMaxFPS(_desiredMaxFPS);
|
||||
}
|
||||
|
||||
if (isWebContent()) {
|
||||
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
|
||||
_cachedWebSurface = true;
|
||||
_webSurface->getRootItem()->setProperty("url", _url);
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
} else {
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), qmlSurfaceDeleter);
|
||||
connect(_webSurface.data(), &hifi::qml::OffscreenSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
|
||||
setupQmlSurface(_url == TabletScriptingInterface::QML, _url == OVERLAY_LOGIN_DIALOG.toString());
|
||||
});
|
||||
_webSurface->load(_url);
|
||||
_cachedWebSurface = false;
|
||||
}
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition()));
|
||||
onResizeWebSurface();
|
||||
_webSurface->resume();
|
||||
});
|
||||
render::entities::WebEntityRenderer::acquireWebSurface(_url, overrideWeb || isWebContent(), _webSurface, _cachedWebSurface);
|
||||
onResizeWebSurface();
|
||||
_webSurface->resume();
|
||||
|
||||
QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
_connections.push_back(QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent));
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived));
|
||||
}
|
||||
|
||||
void Web3DOverlay::update(float deltatime) {
|
||||
|
@ -215,69 +143,8 @@ bool Web3DOverlay::isWebContent() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Web3DOverlay::setupQmlSurface(bool isTablet, bool isLoginDialog) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Preferences", DependencyManager::get<Preferences>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Vec3", new Vec3());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Quat", new Quat());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
||||
if (isTablet || isLoginDialog) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("KeyboardScriptingInterface", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
}
|
||||
if (isTablet) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto flags = tabletScriptingInterface->getFlags();
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
|
||||
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
|
||||
|
||||
// Override min fps for tablet UI, for silky smooth scrolling
|
||||
setMaxFPS(90);
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::setMaxFPS(uint8_t maxFPS) {
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces and the current rendering load)
|
||||
_desiredMaxFPS = maxFPS;
|
||||
if (_webSurface) {
|
||||
_webSurface->setMaxFps(_desiredMaxFPS);
|
||||
|
@ -298,21 +165,13 @@ void Web3DOverlay::onResizeWebSurface() {
|
|||
_webSurface->resize(QSize(dims.x, dims.y));
|
||||
}
|
||||
|
||||
unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
if (_webSurface) {
|
||||
return _webSurface->deviceIdByTouchPoint(x, y);
|
||||
} else {
|
||||
return PointerEvent::INVALID_POINTER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void Web3DOverlay::render(RenderArgs* args) {
|
||||
if (!_renderVisible || !getParentVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_webSurface) {
|
||||
emit requestWebSurface();
|
||||
emit requestWebSurface(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -499,11 +358,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
_desiredMaxFPS = maxFPS.toInt();
|
||||
}
|
||||
|
||||
auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"];
|
||||
if (showKeyboardFocusHighlight.isValid()) {
|
||||
_showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool();
|
||||
}
|
||||
|
||||
auto inputModeValue = properties["inputMode"];
|
||||
if (inputModeValue.isValid()) {
|
||||
QString inputModeStr = inputModeValue.toString();
|
||||
|
@ -566,8 +420,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
* @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms:
|
||||
* <code>scale</code>, <code>size</code>.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - If <code>true</code>, the Web overlay is highlighted when it has
|
||||
* keyboard focus.
|
||||
* @property {string} inputMode=Touch - The user input mode to use - either <code>"Touch"</code> or <code>"Mouse"</code>.
|
||||
*/
|
||||
QVariant Web3DOverlay::getProperty(const QString& property) {
|
||||
|
@ -583,9 +435,6 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "maxFPS") {
|
||||
return _desiredMaxFPS;
|
||||
}
|
||||
if (property == "showKeyboardFocusHighlight") {
|
||||
return _showKeyboardFocusHighlight;
|
||||
}
|
||||
|
||||
if (property == "inputMode") {
|
||||
if (_inputMode == Mouse) {
|
||||
|
@ -605,14 +454,14 @@ void Web3DOverlay::setURL(const QString& url) {
|
|||
if (wasWebContent && isWebContent()) {
|
||||
// If we're just targeting a new web URL, then switch to that without messing around
|
||||
// with the underlying QML
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
|
||||
_webSurface->getRootItem()->setProperty("url", _url);
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
_webSurface->getRootItem()->setProperty(render::entities::WebEntityRenderer::URL_PROPERTY, _url);
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
});
|
||||
} else {
|
||||
// If we're switching to or from web content, or between different QML content
|
||||
// we need to destroy and rebuild the entire QML surface
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
rebuildWebSurface();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ class Web3DOverlay : public Billboard3DOverlay {
|
|||
using Parent = Billboard3DOverlay;
|
||||
|
||||
public:
|
||||
|
||||
static const QString QML;
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
|
@ -59,12 +57,10 @@ public:
|
|||
Mouse
|
||||
};
|
||||
|
||||
void buildWebSurface();
|
||||
void buildWebSurface(bool overrideWeb = false);
|
||||
void destroyWebSurface();
|
||||
void onResizeWebSurface();
|
||||
|
||||
Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
|
@ -72,26 +68,23 @@ signals:
|
|||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
void resizeWebSurface();
|
||||
void requestWebSurface();
|
||||
void releaseWebSurface();
|
||||
void requestWebSurface(bool overrideWeb);
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
void setupQmlSurface(bool isTablet, bool isLoginDialog);
|
||||
void rebuildWebSurface();
|
||||
bool isWebContent() const;
|
||||
|
||||
InputMode _inputMode { Touch };
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface;
|
||||
bool _cachedWebSurface{ false };
|
||||
bool _cachedWebSurface { false };
|
||||
gpu::TexturePointer _texture;
|
||||
QString _url;
|
||||
QString _scriptURL;
|
||||
float _dpi { 30.0f };
|
||||
int _geometryId { 0 };
|
||||
bool _showKeyboardFocusHighlight { true };
|
||||
|
||||
QTouchDevice _touchDevice;
|
||||
|
||||
|
@ -99,6 +92,8 @@ private:
|
|||
uint8_t _currentMaxFPS { 0 };
|
||||
|
||||
bool _mayNeedResize { false };
|
||||
|
||||
std::vector<QMetaObject::Connection> _connections;
|
||||
};
|
||||
|
||||
#endif // hifi_Web3DOverlay_h
|
||||
|
|
|
@ -30,22 +30,26 @@
|
|||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
static const QString WEB_ENTITY_QML = "controls/WebEntityView.qml";
|
||||
const QString WebEntityRenderer::QML = "Web3DSurface.qml";
|
||||
const char* WebEntityRenderer::URL_PROPERTY = "url";
|
||||
|
||||
std::function<void(QString, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> WebEntityRenderer::_acquireWebSurfaceOperator = nullptr;
|
||||
std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> WebEntityRenderer::_releaseWebSurfaceOperator = nullptr;
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
const float METERS_TO_INCHES = 39.3701f;
|
||||
static uint32_t _currentWebCount{ 0 };
|
||||
// Don't allow more than 20 concurrent web views
|
||||
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
// If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer
|
||||
static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
static int DEFAULT_MAX_FPS = 10;
|
||||
static int YOUTUBE_MAX_FPS = 30;
|
||||
static uint8_t YOUTUBE_MAX_FPS = 30;
|
||||
|
||||
// Don't allow more than 20 concurrent web views
|
||||
static uint32_t _currentWebCount { 0 };
|
||||
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 20;
|
||||
|
||||
static QTouchDevice _touchDevice;
|
||||
static const char* URL_PROPERTY = "url";
|
||||
|
||||
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
|
||||
if (urlString.isEmpty()) {
|
||||
|
@ -71,13 +75,18 @@ WebEntityRenderer::WebEntityRenderer(const EntityItemPointer& entity) : Parent(e
|
|||
_touchDevice.setMaximumTouchPoints(4);
|
||||
});
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
|
||||
_texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||
_texture->setSource(__FUNCTION__);
|
||||
|
||||
_contentType = ContentType::HtmlContent;
|
||||
buildWebSurface(entity, "");
|
||||
|
||||
_timer.setInterval(MSECS_PER_SECOND);
|
||||
connect(&_timer, &QTimer::timeout, this, &WebEntityRenderer::onTimeout);
|
||||
}
|
||||
|
||||
void WebEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
|
||||
WebEntityRenderer::~WebEntityRenderer() {
|
||||
destroyWebSurface();
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -86,6 +95,11 @@ void WebEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity)
|
|||
}
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f;
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (_contextPosition != entity->getWorldPosition()) {
|
||||
return true;
|
||||
|
@ -101,11 +115,31 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
}
|
||||
}
|
||||
|
||||
if (_lastSourceUrl != entity->getSourceUrl()) {
|
||||
if (_color != entity->getColor()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_lastDPI != entity->getDPI()) {
|
||||
if (_alpha != entity->getAlpha()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_sourceURL != entity->getSourceUrl()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_dpi != entity->getDPI()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_scriptURL != entity->getScriptURL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_maxFPS != entity->getMaxFPS()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inputMode != entity->getInputMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -113,35 +147,25 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdate() const {
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
withReadLock([&] {
|
||||
webSurface = _webSurface;
|
||||
});
|
||||
if (!webSurface) {
|
||||
// If we have rendered recently, and there is no web surface, we're going to create one
|
||||
return true;
|
||||
}
|
||||
if (resultWithReadLock<bool>([this] {
|
||||
return !_webSurface;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parent::needsRenderUpdate();
|
||||
}
|
||||
|
||||
void WebEntityRenderer::onTimeout() {
|
||||
bool needsCheck = resultWithReadLock<bool>([&] {
|
||||
uint64_t lastRenderTime;
|
||||
if (!resultWithReadLock<bool>([&] {
|
||||
lastRenderTime = _lastRenderTime;
|
||||
return (_lastRenderTime != 0 && (bool)_webSurface);
|
||||
});
|
||||
|
||||
if (!needsCheck) {
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t interval;
|
||||
withReadLock([&] {
|
||||
interval = usecTimestampNow() - _lastRenderTime;
|
||||
});
|
||||
|
||||
if (interval > MAX_NO_RENDER_INTERVAL) {
|
||||
if (usecTimestampNow() - lastRenderTime > MAX_NO_RENDER_INTERVAL) {
|
||||
destroyWebSurface();
|
||||
}
|
||||
}
|
||||
|
@ -151,80 +175,100 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
// destroy the existing surface (because surfaces don't support changing the root
|
||||
// object, so subsequent loads of content just overlap the existing content
|
||||
bool urlChanged = false;
|
||||
auto newSourceURL = entity->getSourceUrl();
|
||||
{
|
||||
auto newSourceUrl = entity->getSourceUrl();
|
||||
auto newContentType = getContentType(newSourceUrl);
|
||||
auto currentContentType = ContentType::NoContent;
|
||||
auto newContentType = getContentType(newSourceURL);
|
||||
ContentType currentContentType;
|
||||
withReadLock([&] {
|
||||
urlChanged = _lastSourceUrl != newSourceUrl;
|
||||
urlChanged = _sourceURL != newSourceURL;
|
||||
currentContentType = _contentType;
|
||||
});
|
||||
|
||||
if (urlChanged) {
|
||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||
destroyWebSurface();
|
||||
// If we destroyed the surface, the URL change will be implicitly handled by the re-creation
|
||||
urlChanged = false;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_lastSourceUrl = newSourceUrl;
|
||||
_contentType = newContentType;
|
||||
});
|
||||
|
||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||
destroyWebSurface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
withWriteLock([&] {
|
||||
_inputMode = entity->getInputMode();
|
||||
_dpi = entity->getDPI();
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
|
||||
if (_contentType == ContentType::NoContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This work must be done on the main thread
|
||||
// If we couldn't create a new web surface, exit
|
||||
if (!hasWebSurface() && !buildWebSurface(entity)) {
|
||||
return;
|
||||
if (!_webSurface) {
|
||||
buildWebSurface(entity, newSourceURL);
|
||||
}
|
||||
|
||||
if (urlChanged && _contentType == ContentType::HtmlContent) {
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
|
||||
withWriteLock([&] {
|
||||
if (_contextPosition != entity->getWorldPosition()) {
|
||||
// update globalPosition
|
||||
_contextPosition = entity->getWorldPosition();
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
|
||||
if (_webSurface && _webSurface->getRootItem()) {
|
||||
if (_webSurface->getRootItem()) {
|
||||
if (_contentType == ContentType::HtmlContent && urlChanged) {
|
||||
_webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL);
|
||||
_sourceURL = newSourceURL;
|
||||
}
|
||||
|
||||
_lastDPI = entity->getDPI();
|
||||
{
|
||||
auto scriptURL = entity->getScriptURL();
|
||||
if (_scriptURL != scriptURL) {
|
||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
_scriptURL = scriptURL;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
{
|
||||
auto maxFPS = entity->getMaxFPS();
|
||||
if (_maxFPS != maxFPS) {
|
||||
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
||||
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the web entity
|
||||
if (QUrl(_sourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
||||
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
||||
} else {
|
||||
_webSurface->setMaxFps(maxFPS);
|
||||
}
|
||||
_maxFPS = maxFPS;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto contextPosition = entity->getWorldPosition();
|
||||
if (_contextPosition != contextPosition) {
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(contextPosition));
|
||||
_contextPosition = contextPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
withWriteLock([&] {
|
||||
glm::vec2 windowSize = getWindowSize(entity);
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("WebEntityRenderer::render");
|
||||
withWriteLock([&] {
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
});
|
||||
|
||||
#ifdef WANT_EXTRA_DEBUGGING
|
||||
{
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
|
||||
glm::vec4 cubeColor{ 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
DependencyManager::get<GeometryCache>()->renderWireCube(batch, 1.0f, cubeColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Try to update the texture
|
||||
{
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
|
@ -242,111 +286,61 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("WebEntityRenderer::render");
|
||||
static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f);
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
glm::vec4 color;
|
||||
withReadLock([&] {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
color = glm::vec4(toGlm(_color), _alpha * fadeRatio);
|
||||
batch.setModelTransform(_renderTransform);
|
||||
});
|
||||
batch.setResourceTexture(0, _texture);
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
|
||||
// Turn off jitter for these entities
|
||||
batch.pushProjectionJitter();
|
||||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
|
||||
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId);
|
||||
batch.popProjectionJitter();
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::hasWebSurface() {
|
||||
return (bool)_webSurface && _webSurface->getRootItem();
|
||||
}
|
||||
|
||||
static const auto WebSurfaceDeleter = [](OffscreenQmlSurface* webSurface) {
|
||||
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||
// if the application has already stopped its event loop, delete must be explicit
|
||||
delete webSurface;
|
||||
});
|
||||
};
|
||||
|
||||
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||
void WebEntityRenderer::buildWebSurface(const EntityItemPointer& entity, const QString& newSourceURL) {
|
||||
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
||||
qWarning() << "Too many concurrent web views to create new view";
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
++_currentWebCount;
|
||||
|
||||
// FIXME use the surface cache instead of explicit creation
|
||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), WebSurfaceDeleter);
|
||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||
// and the current rendering load)
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [](QQmlContext* surfaceContext) {
|
||||
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
|
||||
surfaceContext->setContextProperty("desktop", QVariant());
|
||||
// Let us interact with the keyboard
|
||||
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
});
|
||||
|
||||
// forward web events to EntityScriptingInterface
|
||||
auto entities = DependencyManager::get<EntityScriptingInterface>();
|
||||
const EntityItemID entityItemID = entity->getID();
|
||||
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, [=](const QVariant& message) {
|
||||
emit entities->webEventReceived(entityItemID, message);
|
||||
});
|
||||
|
||||
if (_contentType == ContentType::HtmlContent) {
|
||||
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
||||
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
|
||||
// web entity
|
||||
if (QUrl(_lastSourceUrl).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
||||
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
||||
} else {
|
||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
}
|
||||
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||
});
|
||||
} else if (_contentType == ContentType::QmlContent) {
|
||||
_webSurface->load(_lastSourceUrl);
|
||||
}
|
||||
WebEntityRenderer::acquireWebSurface(newSourceURL, _contentType == ContentType::HtmlContent, _webSurface, _cachedWebSurface);
|
||||
_fadeStartTime = usecTimestampNow();
|
||||
_webSurface->resume();
|
||||
|
||||
return _webSurface->getRootItem();
|
||||
_connections.push_back(QObject::connect(this, &WebEntityRenderer::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent));
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &WebEntityRenderer::webEventReceived));
|
||||
const EntityItemID entityItemID = entity->getID();
|
||||
_connections.push_back(QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, [entityItemID](const QVariant& message) {
|
||||
emit DependencyManager::get<EntityScriptingInterface>()->webEventReceived(entityItemID, message);
|
||||
}));
|
||||
}
|
||||
|
||||
void WebEntityRenderer::destroyWebSurface() {
|
||||
QSharedPointer<OffscreenQmlSurface> webSurface;
|
||||
ContentType contentType{ ContentType::NoContent };
|
||||
ContentType contentType = ContentType::NoContent;
|
||||
withWriteLock([&] {
|
||||
webSurface.swap(_webSurface);
|
||||
std::swap(contentType, _contentType);
|
||||
_contentType = contentType;
|
||||
});
|
||||
|
||||
if (webSurface) {
|
||||
--_currentWebCount;
|
||||
QQuickItem* rootItem = webSurface->getRootItem();
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
|
||||
if (rootItem && contentType == ContentType::HtmlContent) {
|
||||
// stop loading
|
||||
QMetaObject::invokeMethod(rootItem, "stop");
|
||||
}
|
||||
|
||||
webSurface->pause();
|
||||
webSurface.reset();
|
||||
WebEntityRenderer::releaseWebSurface(webSurface, _cachedWebSurface, _connections);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) const {
|
||||
glm::vec2 dims = glm::vec2(entity->getScaledDimensions());
|
||||
dims *= METERS_TO_INCHES * _lastDPI;
|
||||
dims *= METERS_TO_INCHES * _dpi;
|
||||
|
||||
// ensure no side is never larger then MAX_WINDOW_SIZE
|
||||
float max = (dims.x > dims.y) ? dims.x : dims.y;
|
||||
|
@ -358,29 +352,84 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con
|
|||
}
|
||||
|
||||
void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
if (_inputMode == WebInputMode::MOUSE) {
|
||||
handlePointerEvent(event);
|
||||
} else if (_webSurface) {
|
||||
qDebug() << "boop5" << this << _webSurface << _webSurface->getRootItem();
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->hoverBeginEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
if (_inputMode == WebInputMode::MOUSE) {
|
||||
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
|
||||
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
|
||||
handlePointerEvent(endEvent);
|
||||
// QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited.
|
||||
PointerEvent endMoveEvent(PointerEvent::Move, event.getID());
|
||||
handlePointerEvent(endMoveEvent);
|
||||
} else if (_webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->hoverEndEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) {
|
||||
if (_inputMode == WebInputMode::TOUCH) {
|
||||
handlePointerEventAsTouch(event);
|
||||
} else {
|
||||
handlePointerEventAsMouse(event);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEventAsTouch(const PointerEvent& event) {
|
||||
if (_webSurface) {
|
||||
PointerEvent webEvent = event;
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI));
|
||||
webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi));
|
||||
_webSurface->handlePointerEvent(webEvent, _touchDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void WebEntityRenderer::handlePointerEventAsMouse(const PointerEvent& event) {
|
||||
if (!_webSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
Qt::MouseButtons buttons = Qt::NoButton;
|
||||
if (event.getButtons() & PointerEvent::PrimaryButton) {
|
||||
buttons |= Qt::LeftButton;
|
||||
}
|
||||
|
||||
Qt::MouseButton button = Qt::NoButton;
|
||||
if (event.getButton() == PointerEvent::PrimaryButton) {
|
||||
button = Qt::LeftButton;
|
||||
}
|
||||
|
||||
QEvent::Type type;
|
||||
switch (event.getType()) {
|
||||
case PointerEvent::Press:
|
||||
type = QEvent::MouseButtonPress;
|
||||
break;
|
||||
case PointerEvent::Release:
|
||||
type = QEvent::MouseButtonRelease;
|
||||
break;
|
||||
case PointerEvent::Move:
|
||||
type = QEvent::MouseMove;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers());
|
||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
|
||||
}
|
||||
|
||||
void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) {
|
||||
if (_webSurface) {
|
||||
_webSurface->setProxyWindow(proxyWindow);
|
||||
|
@ -394,8 +443,6 @@ QObject* WebEntityRenderer::getEventHandler() {
|
|||
return _webSurface->getEventHandler();
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
||||
}
|
||||
|
||||
void WebEntityRenderer::emitScriptEvent(const QVariant& message) {
|
||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
|
||||
}
|
|
@ -24,13 +24,30 @@ class WebEntityRenderer : public TypedEntityRenderer<WebEntityItem> {
|
|||
|
||||
public:
|
||||
WebEntityRenderer(const EntityItemPointer& entity);
|
||||
~WebEntityRenderer();
|
||||
|
||||
Q_INVOKABLE void hoverEnterEntity(const PointerEvent& event);
|
||||
Q_INVOKABLE void hoverLeaveEntity(const PointerEvent& event);
|
||||
Q_INVOKABLE void handlePointerEvent(const PointerEvent& event);
|
||||
|
||||
static const QString QML;
|
||||
static const char* URL_PROPERTY;
|
||||
|
||||
static void setAcquireWebSurfaceOperator(std::function<void(const QString&, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> acquireWebSurfaceOperator) { _acquireWebSurfaceOperator = acquireWebSurfaceOperator; }
|
||||
static void acquireWebSurface(const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
|
||||
if (_acquireWebSurfaceOperator) {
|
||||
_acquireWebSurfaceOperator(url, htmlContent, webSurface, cachedWebSurface);
|
||||
}
|
||||
}
|
||||
|
||||
static void setReleaseWebSurfaceOperator(std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> releaseWebSurfaceOperator) { _releaseWebSurfaceOperator = releaseWebSurfaceOperator; }
|
||||
static void releaseWebSurface(QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface, std::vector<QMetaObject::Connection>& connections) {
|
||||
if (_releaseWebSurfaceOperator) {
|
||||
_releaseWebSurfaceOperator(webSurface, cachedWebSurface, connections);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
virtual bool needsRenderUpdate() const override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
|
@ -39,40 +56,59 @@ protected:
|
|||
|
||||
virtual bool wantsHandControllerPointerEvents() const override { return true; }
|
||||
virtual bool wantsKeyboardFocus() const override { return true; }
|
||||
|
||||
virtual void setProxyWindow(QWindow* proxyWindow) override;
|
||||
virtual QObject* getEventHandler() override;
|
||||
|
||||
void handlePointerEventAsTouch(const PointerEvent& event);
|
||||
void handlePointerEventAsMouse(const PointerEvent& event);
|
||||
|
||||
private:
|
||||
void onTimeout();
|
||||
bool buildWebSurface(const TypedEntityPointer& entity);
|
||||
void buildWebSurface(const EntityItemPointer& entity, const QString& newSourceURL);
|
||||
void destroyWebSurface();
|
||||
bool hasWebSurface();
|
||||
glm::vec2 getWindowSize(const TypedEntityPointer& entity) const;
|
||||
|
||||
|
||||
int _geometryId{ 0 };
|
||||
enum class ContentType {
|
||||
NoContent,
|
||||
HtmlContent,
|
||||
QmlContent
|
||||
};
|
||||
|
||||
static ContentType getContentType(const QString& urlString);
|
||||
ContentType _contentType { ContentType::NoContent };
|
||||
|
||||
ContentType _contentType{ ContentType::NoContent };
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface;
|
||||
glm::vec3 _contextPosition;
|
||||
QSharedPointer<OffscreenQmlSurface> _webSurface { nullptr };
|
||||
bool _cachedWebSurface { false };
|
||||
gpu::TexturePointer _texture;
|
||||
QString _lastSourceUrl;
|
||||
uint16_t _lastDPI;
|
||||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
|
||||
QString _sourceURL;
|
||||
uint16_t _dpi;
|
||||
QString _scriptURL;
|
||||
uint8_t _maxFPS;
|
||||
WebInputMode _inputMode;
|
||||
|
||||
glm::vec3 _contextPosition;
|
||||
|
||||
QTimer _timer;
|
||||
uint64_t _lastRenderTime { 0 };
|
||||
|
||||
std::vector<QMetaObject::Connection> _connections;
|
||||
|
||||
static std::function<void(QString, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> _acquireWebSurfaceOperator;
|
||||
static std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> _releaseWebSurfaceOperator;
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
signals:
|
||||
void scriptEventReceived(const QVariant& message);
|
||||
void webEventReceived(const QVariant& message);
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
||||
#if 0
|
||||
virtual void emitScriptEvent(const QVariant& message) override;
|
||||
#endif
|
||||
} }
|
||||
|
||||
#endif // hifi_RenderableWebEntityItem_h
|
||||
|
|
|
@ -97,6 +97,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
// requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA; // not sent over the wire
|
||||
requestedProperties += PROP_RENDER_LAYER;
|
||||
requestedProperties += PROP_PRIMITIVE_MODE;
|
||||
requestedProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
withReadLock([&] {
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
});
|
||||
|
@ -280,6 +281,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
// APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, getIsVisibleInSecondaryCamera()); // not sent over the wire
|
||||
APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)getRenderLayer());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)getPrimitiveMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection());
|
||||
withReadLock([&] {
|
||||
_grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -848,6 +850,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// READ_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire
|
||||
READ_ENTITY_PROPERTY(PROP_RENDER_LAYER, RenderLayer, setRenderLayer);
|
||||
READ_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection);
|
||||
withWriteLock([&] {
|
||||
int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
|
@ -1321,6 +1324,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(isVisibleInSecondaryCamera, isVisibleInSecondaryCamera);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderLayer, getRenderLayer);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(primitiveMode, getPrimitiveMode);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection);
|
||||
withReadLock([&] {
|
||||
_grabProperties.getProperties(properties);
|
||||
});
|
||||
|
@ -1467,6 +1471,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isVisibleInSecondaryCamera, setIsVisibleInSecondaryCamera);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderLayer, setRenderLayer);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(primitiveMode, setPrimitiveMode);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection);
|
||||
withWriteLock([&] {
|
||||
bool grabPropertiesChanged = _grabProperties.setProperties(properties);
|
||||
somethingChanged |= grabPropertiesChanged;
|
||||
|
@ -2984,6 +2989,18 @@ void EntityItem::setPrimitiveMode(PrimitiveMode value) {
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityItem::getIgnorePickIntersection() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _ignorePickIntersection;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setIgnorePickIntersection(bool value) {
|
||||
withWriteLock([&] {
|
||||
_ignorePickIntersection = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityItem::getCanCastShadow() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
|
|
|
@ -299,6 +299,9 @@ public:
|
|||
PrimitiveMode getPrimitiveMode() const;
|
||||
void setPrimitiveMode(PrimitiveMode value);
|
||||
|
||||
bool getIgnorePickIntersection() const;
|
||||
void setIgnorePickIntersection(bool value);
|
||||
|
||||
bool getCanCastShadow() const;
|
||||
void setCanCastShadow(bool value);
|
||||
|
||||
|
@ -630,6 +633,7 @@ protected:
|
|||
RenderLayer _renderLayer { RenderLayer::WORLD };
|
||||
PrimitiveMode _primitiveMode { PrimitiveMode::SOLID };
|
||||
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
|
||||
bool _ignorePickIntersection { false };
|
||||
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
|
||||
uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
|
||||
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };
|
||||
|
|
|
@ -404,6 +404,32 @@ void EntityItemProperties::setPrimitiveModeFromString(const QString& primitiveMo
|
|||
}
|
||||
}
|
||||
|
||||
QHash<QString, WebInputMode> stringToWebInputModeLookup;
|
||||
|
||||
void addWebInputMode(WebInputMode mode) {
|
||||
stringToWebInputModeLookup[WebInputModeHelpers::getNameForWebInputMode(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToWebInputModeLookup() {
|
||||
addWebInputMode(WebInputMode::TOUCH);
|
||||
addWebInputMode(WebInputMode::MOUSE);
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getInputModeAsString() const {
|
||||
return WebInputModeHelpers::getNameForWebInputMode(_inputMode);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setInputModeFromString(const QString& webInputMode) {
|
||||
if (stringToWebInputModeLookup.empty()) {
|
||||
buildStringToWebInputModeLookup();
|
||||
}
|
||||
auto webInputModeItr = stringToWebInputModeLookup.find(webInputMode.toLower());
|
||||
if (webInputModeItr != stringToWebInputModeLookup.end()) {
|
||||
_inputMode = webInputModeItr.value();
|
||||
_inputModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
|
@ -430,6 +456,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
CHECK_PROPERTY_CHANGE(PROP_RENDER_LAYER, renderLayer);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PRIMITIVE_MODE, primitiveMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection);
|
||||
changedProperties += _grab.getChangedProperties();
|
||||
|
||||
// Physics
|
||||
|
@ -584,6 +611,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
// Web
|
||||
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SCRIPT_URL, scriptURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS);
|
||||
CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode);
|
||||
|
||||
// Polyline
|
||||
CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints);
|
||||
|
@ -669,6 +699,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {boolean} isVisibleInSecondaryCamera=true - Whether or not the entity is rendered in the secondary camera. If <code>true</code> then the entity is rendered.
|
||||
* @property {RenderLayer} renderLayer="world" - In which layer this entity renders.
|
||||
* @property {PrimitiveMode} primitiveMode="solid" - How this entity's geometry is rendered.
|
||||
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the entity.
|
||||
*
|
||||
* @property {Vec3} position=0,0,0 - The position of the entity.
|
||||
* @property {Quat} rotation=0,0,0,1 - The orientation of the entity with respect to world coordinates.
|
||||
|
@ -1269,11 +1300,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* The entity has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
|
||||
* @typedef {object} Entities.EntityProperties-Web
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity.
|
||||
* @property {Color} color=255,255,255 - The color of the web surface.
|
||||
* @property {number} alpha=1 - The alpha of the web surface.
|
||||
* @property {string} sourceUrl="" - The URL of the Web page to display. This value does not change as you or others navigate
|
||||
* on the Web entity.
|
||||
* @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter
|
||||
* (multiply by 1 / 0.0254 = 39.3701) then multiply <code>dimensions.x</code> and <code>dimensions.y</code> by that value
|
||||
* you get the resolution in pixels.
|
||||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web content, in frames/second.
|
||||
* @property {WebInputMode} inputMode="touch" - The user input mode to use.
|
||||
* @example <caption>Create a Web entity displaying at 1920 x 1080 resolution.</caption>
|
||||
* var METERS_TO_INCHES = 39.3701;
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1482,6 +1518,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_RENDER_LAYER, renderLayer, getRenderLayerAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PRIMITIVE_MODE, primitiveMode, getPrimitiveModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection);
|
||||
_grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
// Physics
|
||||
|
@ -1669,8 +1706,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
|
||||
// Web only
|
||||
if (_type == EntityTypes::Web) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_URL, scriptURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString());
|
||||
}
|
||||
|
||||
// PolyVoxel only
|
||||
|
@ -1866,6 +1909,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(renderLayer, RenderLayer);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(primitiveMode, PrimitiveMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection);
|
||||
_grab.copyFromScriptValue(object, _defaultSettings);
|
||||
|
||||
// Physics
|
||||
|
@ -2025,6 +2069,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
// Web
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptURL, QString, setScriptURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode);
|
||||
|
||||
// Polyline
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints);
|
||||
|
@ -2140,6 +2187,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(isVisibleInSecondaryCamera);
|
||||
COPY_PROPERTY_IF_CHANGED(renderLayer);
|
||||
COPY_PROPERTY_IF_CHANGED(primitiveMode);
|
||||
COPY_PROPERTY_IF_CHANGED(ignorePickIntersection);
|
||||
_grab.merge(other._grab);
|
||||
|
||||
// Physics
|
||||
|
@ -2294,6 +2342,9 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
// Web
|
||||
COPY_PROPERTY_IF_CHANGED(sourceUrl);
|
||||
COPY_PROPERTY_IF_CHANGED(dpi);
|
||||
COPY_PROPERTY_IF_CHANGED(scriptURL);
|
||||
COPY_PROPERTY_IF_CHANGED(maxFPS);
|
||||
COPY_PROPERTY_IF_CHANGED(inputMode);
|
||||
|
||||
// Polyline
|
||||
COPY_PROPERTY_IF_CHANGED(linePoints);
|
||||
|
@ -2413,6 +2464,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer);
|
||||
ADD_PROPERTY_TO_MAP(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode);
|
||||
ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool);
|
||||
{ // Grab
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic);
|
||||
|
@ -2663,6 +2715,9 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
// Web
|
||||
ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode);
|
||||
|
||||
// Polyline
|
||||
ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector<vec3>);
|
||||
|
@ -2845,6 +2900,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
// APPEND_ENTITY_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, properties.getIsVisibleInSecondaryCamera()); // not sent over the wire
|
||||
APPEND_ENTITY_PROPERTY(PROP_RENDER_LAYER, (uint32_t)properties.getRenderLayer());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PRIMITIVE_MODE, (uint32_t)properties.getPrimitiveMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection());
|
||||
_staticGrab.setProperties(properties);
|
||||
_staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -3026,8 +3082,14 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, properties.getScriptURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Line) {
|
||||
|
@ -3289,6 +3351,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
// READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE_IN_SECONDARY_CAMERA, bool, setIsVisibleInSecondaryCamera); // not sent over the wire
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_LAYER, RenderLayer, setRenderLayer);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIMITIVE_MODE, PrimitiveMode, setPrimitiveMode);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection);
|
||||
properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
// Physics
|
||||
|
@ -3459,8 +3522,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_URL, QString, setScriptURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Line) {
|
||||
|
@ -3674,6 +3743,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_isVisibleInSecondaryCameraChanged = true;
|
||||
_renderLayerChanged = true;
|
||||
_primitiveModeChanged = true;
|
||||
_ignorePickIntersectionChanged = true;
|
||||
_grab.markAllChanged();
|
||||
|
||||
// Physics
|
||||
|
@ -3821,6 +3891,9 @@ void EntityItemProperties::markAllChanged() {
|
|||
// Web
|
||||
_sourceUrlChanged = true;
|
||||
_dpiChanged = true;
|
||||
_scriptURLChanged = true;
|
||||
_maxFPSChanged = true;
|
||||
_inputModeChanged = true;
|
||||
|
||||
// Polyline
|
||||
_linePointsChanged = true;
|
||||
|
@ -4053,6 +4126,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (primitiveModeChanged()) {
|
||||
out += "primitiveMode";
|
||||
}
|
||||
if (ignorePickIntersectionChanged()) {
|
||||
out += "ignorePickIntersection";
|
||||
}
|
||||
getGrab().listChangedProperties(out);
|
||||
|
||||
// Physics
|
||||
|
@ -4432,6 +4508,15 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (dpiChanged()) {
|
||||
out += "dpi";
|
||||
}
|
||||
if (scriptURLChanged()) {
|
||||
out += "scriptURL";
|
||||
}
|
||||
if (maxFPSChanged()) {
|
||||
out += "maxFPS";
|
||||
}
|
||||
if (inputModeChanged()) {
|
||||
out += "inputMode";
|
||||
}
|
||||
|
||||
// Polyline
|
||||
if (linePointsChanged()) {
|
||||
|
|
|
@ -30,29 +30,33 @@
|
|||
#include <ShapeInfo.h>
|
||||
#include <ColorUtils.h>
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
#include "EntityItemID.h"
|
||||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "EntityPropertyFlags.h"
|
||||
#include "EntityPsuedoPropertyFlags.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "SimulationOwner.h"
|
||||
|
||||
#include "TextEntityItem.h"
|
||||
#include "WebEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||
|
||||
|
@ -173,6 +177,7 @@ public:
|
|||
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_RENDER_LAYER, RenderLayer, renderLayer, RenderLayer, RenderLayer::WORLD);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PRIMITIVE_MODE, PrimitiveMode, primitiveMode, PrimitiveMode, PrimitiveMode::SOLID);
|
||||
DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false);
|
||||
DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup);
|
||||
|
||||
// Physics
|
||||
|
@ -325,8 +330,11 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
|
||||
|
||||
// Web
|
||||
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, WebEntityItem::DEFAULT_SOURCE_URL);
|
||||
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
|
||||
DEFINE_PROPERTY_REF(PROP_SCRIPT_URL, ScriptURL, scriptURL, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH);
|
||||
|
||||
// Polyline
|
||||
DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
|
||||
|
|
|
@ -41,6 +41,7 @@ enum EntityPropertyList {
|
|||
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire
|
||||
PROP_RENDER_LAYER,
|
||||
PROP_PRIMITIVE_MODE,
|
||||
PROP_IGNORE_PICK_INTERSECTION,
|
||||
// Grab
|
||||
PROP_GRAB_GRABBABLE,
|
||||
PROP_GRAB_KINEMATIC,
|
||||
|
@ -287,6 +288,9 @@ enum EntityPropertyList {
|
|||
// Web
|
||||
PROP_SOURCE_URL = PROP_DERIVED_0,
|
||||
PROP_DPI = PROP_DERIVED_1,
|
||||
PROP_SCRIPT_URL = PROP_DERIVED_2,
|
||||
PROP_MAX_FPS = PROP_DERIVED_3,
|
||||
PROP_INPUT_MODE = PROP_DERIVED_4,
|
||||
|
||||
// Polyline
|
||||
PROP_LINE_POINTS = PROP_DERIVED_0,
|
||||
|
|
|
@ -2707,7 +2707,6 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) {
|
|||
bool EntityTree::readFromMap(QVariantMap& map) {
|
||||
// These are needed to deal with older content (before adding inheritance modes)
|
||||
int contentVersion = map["Version"].toInt();
|
||||
bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes);
|
||||
|
||||
if (map.contains("Id")) {
|
||||
_persistID = map["Id"].toUuid();
|
||||
|
@ -2782,7 +2781,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
}
|
||||
|
||||
// Fix for older content not containing mode fields in the zones
|
||||
if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) {
|
||||
if (contentVersion < (int)EntityVersion::ZoneLightInheritModes && (properties.getType() == EntityTypes::EntityType::Zone)) {
|
||||
// The legacy version had no keylight mode - this is set to on
|
||||
properties.setKeyLightMode(COMPONENT_MODE_ENABLED);
|
||||
|
||||
|
|
|
@ -187,6 +187,10 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
|
|||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
EntityItemID entityID;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
if (entity->getIgnorePickIntersection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use simple line-sphere for broadphase check
|
||||
// (this is faster and more likely to cull results than the filter check below so we do it first)
|
||||
bool success;
|
||||
|
@ -327,6 +331,10 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
|
|||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
EntityItemID entityID;
|
||||
forEachEntity([&](EntityItemPointer entity) {
|
||||
if (entity->getIgnorePickIntersection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use simple line-sphere for broadphase check
|
||||
// (this is faster and more likely to cull results than the filter check below so we do it first)
|
||||
bool success;
|
||||
|
|
|
@ -232,6 +232,12 @@ void ShapeEntityItem::setAlpha(float alpha) {
|
|||
});
|
||||
}
|
||||
|
||||
float ShapeEntityItem::getAlpha() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||
const float MAX_FLAT_DIMENSION = 0.0001f;
|
||||
if ((_shape == entity::Shape::Circle || _shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) {
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
void setShape(const entity::Shape& shape);
|
||||
void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); }
|
||||
|
||||
float getAlpha() const { return _alpha; };
|
||||
float getAlpha() const;
|
||||
void setAlpha(float alpha);
|
||||
|
||||
glm::u8vec3 getColor() const;
|
||||
|
@ -102,9 +102,8 @@ public:
|
|||
std::shared_ptr<graphics::Material> getMaterial() { return _material; }
|
||||
|
||||
protected:
|
||||
|
||||
float _alpha { 1.0f };
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
entity::Shape _shape { entity::Shape::Sphere };
|
||||
|
||||
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
|
||||
const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com");
|
||||
const QString WebEntityItem::DEFAULT_SOURCE_URL = "http://www.google.com";
|
||||
const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10;
|
||||
|
||||
EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity(new WebEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
|
@ -31,20 +32,25 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent
|
|||
|
||||
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
_type = EntityTypes::Web;
|
||||
_dpi = ENTITY_ITEM_DEFAULT_DPI;
|
||||
}
|
||||
|
||||
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
|
||||
void WebEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||
// NOTE: Web Entities always have a "depth" of 1cm.
|
||||
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH));
|
||||
}
|
||||
|
||||
EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptURL, getScriptURL);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -52,8 +58,14 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptURL, setScriptURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -77,16 +89,28 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_URL, QString, setScriptURL);
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
requestedProperties += PROP_DPI;
|
||||
requestedProperties += PROP_SCRIPT_URL;
|
||||
requestedProperties += PROP_MAX_FPS;
|
||||
requestedProperties += PROP_INPUT_MODE;
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
|
@ -99,8 +123,14 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, _dpi);
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode());
|
||||
}
|
||||
|
||||
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
@ -157,6 +187,30 @@ bool WebEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, co
|
|||
}
|
||||
}
|
||||
|
||||
void WebEntityItem::setColor(const glm::u8vec3& value) {
|
||||
withWriteLock([&] {
|
||||
_color = value;
|
||||
});
|
||||
}
|
||||
|
||||
glm::u8vec3 WebEntityItem::getColor() const {
|
||||
return resultWithReadLock<glm::u8vec3>([&] {
|
||||
return _color;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setAlpha(float alpha) {
|
||||
withWriteLock([&] {
|
||||
_alpha = alpha;
|
||||
});
|
||||
}
|
||||
|
||||
float WebEntityItem::getAlpha() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setSourceUrl(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
if (_sourceUrl != value) {
|
||||
|
@ -172,17 +226,63 @@ void WebEntityItem::setSourceUrl(const QString& value) {
|
|||
}
|
||||
|
||||
QString WebEntityItem::getSourceUrl() const {
|
||||
QString result;
|
||||
withReadLock([&] {
|
||||
result = _sourceUrl;
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _sourceUrl;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void WebEntityItem::setDPI(uint16_t value) {
|
||||
_dpi = value;
|
||||
withWriteLock([&] {
|
||||
_dpi = value;
|
||||
});
|
||||
}
|
||||
|
||||
uint16_t WebEntityItem::getDPI() const {
|
||||
return _dpi;
|
||||
return resultWithReadLock<uint16_t>([&] {
|
||||
return _dpi;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setScriptURL(const QString& value) {
|
||||
withWriteLock([&] {
|
||||
if (_scriptURL != value) {
|
||||
auto newURL = QUrl::fromUserInput(value);
|
||||
|
||||
if (newURL.isValid()) {
|
||||
_scriptURL = newURL.toDisplayString();
|
||||
} else {
|
||||
qCDebug(entities) << "Clearing web entity source URL since" << value << "cannot be parsed to a valid URL.";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString WebEntityItem::getScriptURL() const {
|
||||
return resultWithReadLock<QString>([&] {
|
||||
return _scriptURL;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setMaxFPS(uint8_t value) {
|
||||
withWriteLock([&] {
|
||||
_maxFPS = value;
|
||||
});
|
||||
}
|
||||
|
||||
uint8_t WebEntityItem::getMaxFPS() const {
|
||||
return resultWithReadLock<uint8_t>([&] {
|
||||
return _maxFPS;
|
||||
});
|
||||
}
|
||||
|
||||
void WebEntityItem::setInputMode(const WebInputMode& value) {
|
||||
withWriteLock([&] {
|
||||
_inputMode = value;
|
||||
});
|
||||
}
|
||||
|
||||
WebInputMode WebEntityItem::getInputMode() const {
|
||||
return resultWithReadLock<WebInputMode>([&] {
|
||||
return _inputMode;
|
||||
});
|
||||
}
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
class WebEntityItem : public EntityItem {
|
||||
public:
|
||||
static const QString DEFAULT_SOURCE_URL;
|
||||
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
WebEntityItem(const EntityItemID& entityItemID);
|
||||
|
@ -54,15 +52,38 @@ public:
|
|||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
virtual void setSourceUrl(const QString& value);
|
||||
glm::u8vec3 getColor() const;
|
||||
void setColor(const glm::u8vec3& value);
|
||||
|
||||
float getAlpha() const;
|
||||
void setAlpha(float alpha);
|
||||
|
||||
static const QString DEFAULT_SOURCE_URL;
|
||||
void setSourceUrl(const QString& value);
|
||||
QString getSourceUrl() const;
|
||||
|
||||
void setDPI(uint16_t value);
|
||||
uint16_t getDPI() const;
|
||||
|
||||
void setScriptURL(const QString& value);
|
||||
QString getScriptURL() const;
|
||||
|
||||
static const uint8_t DEFAULT_MAX_FPS;
|
||||
void setMaxFPS(uint8_t value);
|
||||
uint8_t getMaxFPS() const;
|
||||
|
||||
void setInputMode(const WebInputMode& value);
|
||||
WebInputMode getInputMode() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
|
||||
QString _sourceUrl;
|
||||
uint16_t _dpi;
|
||||
QString _scriptURL;
|
||||
uint8_t _maxFPS;
|
||||
WebInputMode _inputMode;
|
||||
};
|
||||
|
||||
#endif // hifi_WebEntityItem_h
|
||||
|
|
|
@ -257,6 +257,7 @@ enum class EntityVersion : PacketVersion {
|
|||
UpdatedPolyLines,
|
||||
FixProtocolVersionBumpMismatch,
|
||||
MigrateOverlayRenderProperties,
|
||||
MissingWebEntityProperties,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "BillboardMode.h"
|
||||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElement.h"
|
||||
|
@ -267,6 +268,7 @@ public:
|
|||
static int unpackDataFromBytes(const unsigned char* dataBytes, BillboardMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, RenderLayer& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, PrimitiveMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, WebInputMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
|
||||
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);
|
||||
|
|
|
@ -66,6 +66,7 @@ void RenderEventHandler::onInitalize() {
|
|||
qFatal("Unable to make QML rendering context current on render thread");
|
||||
}
|
||||
_shared->initializeRenderControl(_canvas.getContext());
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void RenderEventHandler::resize() {
|
||||
|
@ -150,22 +151,24 @@ void RenderEventHandler::onRender() {
|
|||
}
|
||||
|
||||
void RenderEventHandler::onQuit() {
|
||||
if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) {
|
||||
qFatal("QML rendering context not current on render thread");
|
||||
}
|
||||
if (_initialized) {
|
||||
if (_canvas.getContext() != QOpenGLContextWrapper::currentContext()) {
|
||||
qFatal("QML rendering context not current on render thread");
|
||||
}
|
||||
|
||||
if (_depthStencil) {
|
||||
glDeleteRenderbuffers(1, &_depthStencil);
|
||||
_depthStencil = 0;
|
||||
}
|
||||
if (_depthStencil) {
|
||||
glDeleteRenderbuffers(1, &_depthStencil);
|
||||
_depthStencil = 0;
|
||||
}
|
||||
|
||||
if (_fbo) {
|
||||
glDeleteFramebuffers(1, &_fbo);
|
||||
_fbo = 0;
|
||||
}
|
||||
if (_fbo) {
|
||||
glDeleteFramebuffers(1, &_fbo);
|
||||
_fbo = 0;
|
||||
}
|
||||
|
||||
_shared->shutdownRendering(_canvas, _currentSize);
|
||||
_canvas.doneCurrent();
|
||||
_shared->shutdownRendering(_canvas, _currentSize);
|
||||
_canvas.doneCurrent();
|
||||
}
|
||||
_canvas.moveToThreadWithContext(qApp->thread());
|
||||
moveToThread(qApp->thread());
|
||||
QThread::currentThread()->quit();
|
||||
|
|
|
@ -53,6 +53,8 @@ private:
|
|||
|
||||
uint32_t _fbo{ 0 };
|
||||
uint32_t _depthStencil{ 0 };
|
||||
|
||||
bool _initialized { false };
|
||||
};
|
||||
|
||||
}}} // namespace hifi::qml::impl
|
||||
|
|
|
@ -500,6 +500,9 @@ void SharedObject::onTimer() {
|
|||
}
|
||||
|
||||
{
|
||||
if (_maxFps == 0) {
|
||||
return;
|
||||
}
|
||||
auto minRenderInterval = USECS_PER_SECOND / _maxFps;
|
||||
auto lastInterval = usecTimestampNow() - _lastRenderTime;
|
||||
// Don't exceed the framerate limit
|
||||
|
|
24
libraries/shared/src/WebInputMode.cpp
Normal file
24
libraries/shared/src/WebInputMode.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/9/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "WebInputMode.h"
|
||||
|
||||
const char* webInputModeNames[] = {
|
||||
"touch",
|
||||
"mouse"
|
||||
};
|
||||
|
||||
static const size_t WEB_INPUT_MODE_NAMES = (sizeof(webInputModeNames) / sizeof(webInputModeNames[0]));
|
||||
|
||||
QString WebInputModeHelpers::getNameForWebInputMode(WebInputMode mode) {
|
||||
if (((int)mode <= 0) || ((int)mode >= (int)WEB_INPUT_MODE_NAMES)) {
|
||||
mode = (WebInputMode)0;
|
||||
}
|
||||
|
||||
return webInputModeNames[(int)mode];
|
||||
}
|
39
libraries/shared/src/WebInputMode.h
Normal file
39
libraries/shared/src/WebInputMode.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/9/19.
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_WebInputMode_h
|
||||
#define hifi_WebInputMode_h
|
||||
|
||||
#include "QString"
|
||||
|
||||
/**jsdoc
|
||||
* <p>Controls how the web surface processed PointerEvents</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>touch</code></td><td>Events are processed as touch events.</td></tr>
|
||||
* <tr><td><code>mouse</code></td><td>Events are processed as mouse events.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} WebInputMode
|
||||
*/
|
||||
|
||||
enum class WebInputMode {
|
||||
TOUCH = 0,
|
||||
MOUSE,
|
||||
};
|
||||
|
||||
class WebInputModeHelpers {
|
||||
public:
|
||||
static QString getNameForWebInputMode(WebInputMode mode);
|
||||
};
|
||||
|
||||
#endif // hifi_WebInputMode_h
|
||||
|
|
@ -35,7 +35,7 @@ public:
|
|||
Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* targetOverride = nullptr);
|
||||
Q_INVOKABLE void lowerKeyboard();
|
||||
PointerEvent::EventType choosePointerEventType(QEvent::Type type);
|
||||
unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
signals:
|
||||
void focusObjectChanged(QObject* newFocus);
|
||||
|
|
Loading…
Reference in a new issue