Working on web entities

This commit is contained in:
Brad Davis 2015-05-12 14:39:54 -07:00
parent 2185cf1883
commit 745182a963
13 changed files with 465 additions and 100 deletions

View file

@ -137,6 +137,7 @@ var toolBar = (function () {
newSphereButton, newSphereButton,
newLightButton, newLightButton,
newTextButton, newTextButton,
newWebButton,
newZoneButton, newZoneButton,
browseMarketplaceButton; browseMarketplaceButton;
@ -204,6 +205,16 @@ var toolBar = (function () {
alpha: 0.9, alpha: 0.9,
visible: false visible: false
}); });
newWebButton = toolBar.addTool({
imageURL: toolIconUrl + "add-text.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: 0.9,
visible: false
});
newZoneButton = toolBar.addTool({ newZoneButton = toolBar.addTool({
imageURL: toolIconUrl + "zonecube_text.svg", imageURL: toolIconUrl + "zonecube_text.svg",
subImage: { x: 0, y: 128, width: 128, height: 128 }, subImage: { x: 0, y: 128, width: 128, height: 128 },
@ -253,6 +264,7 @@ var toolBar = (function () {
toolBar.showTool(newSphereButton, doShow); toolBar.showTool(newSphereButton, doShow);
toolBar.showTool(newLightButton, doShow); toolBar.showTool(newLightButton, doShow);
toolBar.showTool(newTextButton, doShow); toolBar.showTool(newTextButton, doShow);
toolBar.showTool(newWebButton, doShow);
toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newZoneButton, doShow);
}; };
@ -425,6 +437,23 @@ var toolBar = (function () {
return true; return true;
} }
if (newWebButton === toolBar.clicked(clickedOverlay)) {
print("Web");
var position = getPositionToCreateEntity();
if (position.x > 0 && position.y > 0 && position.z > 0) {
placingEntityID = Entities.addEntity({
type: "Web",
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS),
dimensions: { x: 0.65, y: 0.3, z: 0.01 },
source: "http://www.slashdot.org",
});
} else {
print("Can't create box: Text would be out of bounds.");
}
return true;
}
if (newZoneButton === toolBar.clicked(clickedOverlay)) { if (newZoneButton === toolBar.clicked(clickedOverlay)) {
var position = getPositionToCreateEntity(); var position = getPositionToCreateEntity();

View file

@ -468,6 +468,20 @@
elModelAnimationSettings.value = properties.animationSettings; elModelAnimationSettings.value = properties.animationSettings;
elModelTextures.value = properties.textures; elModelTextures.value = properties.textures;
elModelOriginalTextures.value = properties.originalTextures; elModelOriginalTextures.value = properties.originalTextures;
} else if (properties.type == "Web") {
for (var i = 0; i < elTextSections.length; i++) {
elTextSections[i].style.display = 'block';
}
elTextText.value = properties.text;
elTextLineHeight.value = properties.lineHeight.toFixed(4);
elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
elTextTextColorRed.value = properties.textColor.red;
elTextTextColorGreen.value = properties.textColor.green;
elTextTextColorBlue.value = properties.textColor.blue;
elTextBackgroundColorRed.value = properties.backgroundColor.red;
elTextBackgroundColorGreen.value = properties.backgroundColor.green;
elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
} else if (properties.type == "Text") { } else if (properties.type == "Text") {
for (var i = 0; i < elTextSections.length; i++) { for (var i = 0; i < elTextSections.length; i++) {
elTextSections[i].style.display = 'block'; elTextSections[i].style.display = 'block';
@ -1019,6 +1033,12 @@
</div> </div>
</div> </div>
<div class="web-section property">
<div class="label">Source URL</div>
<div class="value">
<input type="text" id="property-source-url" class="url"></input>
</div>
</div>
<div class="model-section property"> <div class="model-section property">

View file

@ -35,6 +35,7 @@
#include "RenderableParticleEffectEntityItem.h" #include "RenderableParticleEffectEntityItem.h"
#include "RenderableSphereEntityItem.h" #include "RenderableSphereEntityItem.h"
#include "RenderableTextEntityItem.h" #include "RenderableTextEntityItem.h"
#include "RenderableWebEntityItem.h"
#include "RenderableZoneEntityItem.h" #include "RenderableZoneEntityItem.h"
#include "EntitiesRendererLogging.h" #include "EntitiesRendererLogging.h"
@ -57,6 +58,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory)

View file

@ -0,0 +1,59 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 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 "RenderableWebEntityItem.h"
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <DeferredLightingEffect.h>
#include <GeometryCache.h>
#include <PerfStat.h>
#include <TextRenderer.h>
#include "GLMHelpers.h"
const int FIXED_FONT_POINT_SIZE = 40;
EntityItem* RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new RenderableWebEntityItem(entityID, properties);
}
void RenderableWebEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
assert(getType() == EntityTypes::Web);
glm::vec3 position = getPosition();
glm::vec3 dimensions = getDimensions();
glm::vec3 halfDimensions = dimensions / 2.0f;
glm::quat rotation = getRotation();
float leftMargin = 0.1f;
float topMargin = 0.1f;
//qCDebug(entitytree) << "RenderableWebEntityItem::render() id:" << getEntityItemID() << "text:" << getText();
glPushMatrix();
{
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
float alpha = 1.0f; //getBackgroundAlpha();
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0);
// TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color
// used for a sphere, or box have the same look as those used on a text entity.
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram();
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, glm::vec4(0, 1, 0, alpha));
DependencyManager::get<DeferredLightingEffect>()->releaseSimpleProgram();
}
glPopMatrix();
}

View file

@ -0,0 +1,26 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 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_RenderableWebEntityItem_h
#define hifi_RenderableWebEntityItem_h
#include <WebEntityItem.h>
class RenderableWebEntityItem : public WebEntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableWebEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
WebEntityItem(entityItemID, properties)
{ }
virtual void render(RenderArgs* args);
};
#endif // hifi_RenderableWebEntityItem_h

View file

@ -52,6 +52,7 @@ class EntityItemProperties {
friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class WebEntityItem; // TODO....
public: public:
EntityItemProperties(); EntityItemProperties();
virtual ~EntityItemProperties(); virtual ~EntityItemProperties();

View file

@ -148,6 +148,10 @@ enum EntityPropertyList {
PROP_SKYBOX_URL = PROP_ANIMATION_FPS, PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX, PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX,
// Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other.
PROP_SOURCE_URL = PROP_MODEL_URL,
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
}; };

View file

@ -24,6 +24,7 @@
#include "ParticleEffectEntityItem.h" #include "ParticleEffectEntityItem.h"
#include "SphereEntityItem.h" #include "SphereEntityItem.h"
#include "TextEntityItem.h" #include "TextEntityItem.h"
#include "WebEntityItem.h"
#include "ZoneEntityItem.h" #include "ZoneEntityItem.h"
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap; QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
@ -36,6 +37,7 @@ const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown";
// Register Entity the default implementations of entity types here... // Register Entity the default implementations of entity types here...
REGISTER_ENTITY_TYPE(Model) REGISTER_ENTITY_TYPE(Model)
REGISTER_ENTITY_TYPE(Box) REGISTER_ENTITY_TYPE(Box)
REGISTER_ENTITY_TYPE(Web)
REGISTER_ENTITY_TYPE(Sphere) REGISTER_ENTITY_TYPE(Sphere)
REGISTER_ENTITY_TYPE(Light) REGISTER_ENTITY_TYPE(Light)
REGISTER_ENTITY_TYPE(Text) REGISTER_ENTITY_TYPE(Text)

View file

@ -30,12 +30,13 @@ class EntityTypes {
public: public:
typedef enum EntityType_t { typedef enum EntityType_t {
Unknown, Unknown,
Model,
Box, Box,
Sphere,
Light, Light,
Text, Model,
ParticleEffect, ParticleEffect,
Sphere,
Text,
Web,
Zone, Zone,
LAST = Zone LAST = Zone
} EntityType; } EntityType;

View file

@ -0,0 +1,145 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 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 "WebEntityItem.h"
#include <glm/gtx/transform.hpp>
#include <QDebug>
#include <ByteCountCoding.h>
#include <PlaneShape.h>
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "EntitiesLogging.h"
const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com");
EntityItem* WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = new WebEntityItem(entityID, properties);
return result;
}
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID)
{
_type = EntityTypes::Web;
_created = properties.getCreated();
setProperties(properties);
}
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
void WebEntityItem::setDimensions(const glm::vec3& value) {
// NOTE: Web Entities always have a "depth" of 1cm.
_dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH);
}
EntityItemProperties WebEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getSource);
return properties;
}
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(modelURL, setSource);
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - getLastEdited();
qCDebug(entities) << "WebEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
}
setLastEdited(properties._lastEdited);
}
return somethingChanged;
}
int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY_STRING(PROP_SOURCE_URL, setSource);
return bytesRead;
}
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_SOURCE_URL;
return requestedProperties;
}
void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, appendValue, _source);
}
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {
RayIntersectionInfo rayInfo;
rayInfo._rayStart = origin;
rayInfo._rayDirection = direction;
rayInfo._rayLength = std::numeric_limits<float>::max();
PlaneShape plane;
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
plane.setNormal(normal);
plane.setPoint(getPosition()); // the position is definitely a point on our plane
bool intersects = plane.findRayIntersection(rayInfo);
if (intersects) {
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
// now we know the point the ray hit our plane
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = getDimensions();
glm::vec3 registrationPoint = getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
intersects = entityFrameBox.contains(entityFrameHitAt);
}
if (intersects) {
distance = rayInfo._hitDistance;
}
return intersects;
}

View file

@ -0,0 +1,59 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 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_WebEntityItem_h
#define hifi_WebEntityItem_h
#include "EntityItem.h"
class WebEntityItem : public EntityItem {
public:
static const QString DEFAULT_SOURCE_URL;
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
WebEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value);
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual bool setProperties(const EntityItemProperties& properties);
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const;
void setSourceUrl(const QString& value) { _sourceUrl = value; }
const QString& getSource() const { return _sourceUrl; }
protected:
QString _sourceUrl;
};
#endif // hifi_WebEntityItem_h

View file

@ -24,30 +24,10 @@ Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
// achieve. // achieve.
static const int SMALL_INTERVAL = 5; static const int SMALL_INTERVAL = 5;
class OffscreenUiRoot : public QQuickItem { OffscreenQmlSurface::OffscreenQmlSurface() {
Q_OBJECT
public:
OffscreenUiRoot(QQuickItem* parent = 0);
Q_INVOKABLE void information(const QString& title, const QString& text);
Q_INVOKABLE void loadChild(const QUrl& url) {
DependencyManager::get<OffscreenUi>()->load(url);
}
};
OffscreenUiRoot::OffscreenUiRoot(QQuickItem* parent) : QQuickItem(parent) {
} }
void OffscreenUiRoot::information(const QString& title, const QString& text) { OffscreenQmlSurface::~OffscreenQmlSurface() {
OffscreenUi::information(title, text);
}
OffscreenUi::OffscreenUi() {
::qmlRegisterType<OffscreenUiRoot>("Hifi", 1, 0, "Root");
}
OffscreenUi::~OffscreenUi() {
// Make sure the context is current while doing cleanup. Note that we use the // Make sure the context is current while doing cleanup. Note that we use the
// offscreen surface here because passing 'this' at this point is not safe: the // offscreen surface here because passing 'this' at this point is not safe: the
// underlying platform window may already be destroyed. To avoid all the trouble, use // underlying platform window may already be destroyed. To avoid all the trouble, use
@ -65,7 +45,7 @@ OffscreenUi::~OffscreenUi() {
doneCurrent(); doneCurrent();
} }
void OffscreenUi::create(QOpenGLContext* shareContext) { void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
OffscreenGlCanvas::create(shareContext); OffscreenGlCanvas::create(shareContext);
makeCurrent(); makeCurrent();
@ -87,13 +67,13 @@ void OffscreenUi::create(QOpenGLContext* shareContext) {
// a timer with a small interval is used to get better performance. // a timer with a small interval is used to get better performance.
_updateTimer.setSingleShot(true); _updateTimer.setSingleShot(true);
_updateTimer.setInterval(SMALL_INTERVAL); _updateTimer.setInterval(SMALL_INTERVAL);
connect(&_updateTimer, &QTimer::timeout, this, &OffscreenUi::updateQuick); connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
// Now hook up the signals. For simplicy we don't differentiate between // Now hook up the signals. For simplicy we don't differentiate between
// renderRequested (only render is needed, no sync) and sceneChanged (polish and sync // renderRequested (only render is needed, no sync) and sceneChanged (polish and sync
// is needed too). // is needed too).
connect(_renderControl, &QQuickRenderControl::renderRequested, this, &OffscreenUi::requestRender); connect(_renderControl, &QQuickRenderControl::renderRequested, this, &OffscreenQmlSurface::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &OffscreenUi::requestUpdate); connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &OffscreenQmlSurface::requestUpdate);
#ifdef DEBUG #ifdef DEBUG
connect(_quickWindow, &QQuickWindow::focusObjectChanged, [this]{ connect(_quickWindow, &QQuickWindow::focusObjectChanged, [this]{
@ -110,11 +90,7 @@ void OffscreenUi::create(QOpenGLContext* shareContext) {
_renderControl->initialize(&_context); _renderControl->initialize(&_context);
} }
void OffscreenUi::addImportPath(const QString& path) { void OffscreenQmlSurface::resize(const QSize& newSize) {
_qmlEngine->addImportPath(path);
}
void OffscreenUi::resize(const QSize& newSize) {
makeCurrent(); makeCurrent();
qreal pixelRatio = _renderControl->_renderWindow ? _renderControl->_renderWindow->devicePixelRatio() : 1.0; qreal pixelRatio = _renderControl->_renderWindow ? _renderControl->_renderWindow->devicePixelRatio() : 1.0;
@ -141,15 +117,15 @@ void OffscreenUi::resize(const QSize& newSize) {
doneCurrent(); doneCurrent();
} }
QQuickItem* OffscreenUi::getRootItem() { QQuickItem* OffscreenQmlSurface::getRootItem() {
return _rootItem; return _rootItem;
} }
void OffscreenUi::setBaseUrl(const QUrl& baseUrl) { void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
_qmlEngine->setBaseUrl(baseUrl); _qmlEngine->setBaseUrl(baseUrl);
} }
QObject* OffscreenUi::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) { QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
_qmlComponent->loadUrl(qmlSource); _qmlComponent->loadUrl(qmlSource);
if (_qmlComponent->isLoading()) { if (_qmlComponent->isLoading()) {
connect(_qmlComponent, &QQmlComponent::statusChanged, this, connect(_qmlComponent, &QQmlComponent::statusChanged, this,
@ -162,20 +138,20 @@ QObject* OffscreenUi::load(const QUrl& qmlSource, std::function<void(QQmlContext
return finishQmlLoad(f); return finishQmlLoad(f);
} }
void OffscreenUi::requestUpdate() { void OffscreenQmlSurface::requestUpdate() {
_polish = true; _polish = true;
if (!_updateTimer.isActive()) { if (!_updateTimer.isActive()) {
_updateTimer.start(); _updateTimer.start();
} }
} }
void OffscreenUi::requestRender() { void OffscreenQmlSurface::requestRender() {
if (!_updateTimer.isActive()) { if (!_updateTimer.isActive()) {
_updateTimer.start(); _updateTimer.start();
} }
} }
QObject* OffscreenUi::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) { QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f) {
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0); disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0);
if (_qmlComponent->isError()) { if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors(); QList<QQmlError> errorList = _qmlComponent->errors();
@ -232,7 +208,7 @@ QObject* OffscreenUi::finishQmlLoad(std::function<void(QQmlContext*, QObject*)>
} }
void OffscreenUi::updateQuick() { void OffscreenQmlSurface::updateQuick() {
if (_paused) { if (_paused) {
return; return;
} }
@ -274,7 +250,7 @@ void OffscreenUi::updateQuick() {
emit textureUpdated(fbo->texture()); emit textureUpdated(fbo->texture());
} }
QPointF OffscreenUi::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) { QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) {
vec2 sourceSize; vec2 sourceSize;
if (dynamic_cast<QWidget*>(sourceObject)) { if (dynamic_cast<QWidget*>(sourceObject)) {
sourceSize = toGlm(((QWidget*)sourceObject)->size()); sourceSize = toGlm(((QWidget*)sourceObject)->size());
@ -297,7 +273,7 @@ QPointF OffscreenUi::mapWindowToUi(const QPointF& sourcePosition, QObject* sourc
bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { bool OffscreenUi::shouldSwallowShortcut(QEvent* event) {
Q_ASSERT(event->type() == QEvent::ShortcutOverride); Q_ASSERT(event->type() == QEvent::ShortcutOverride);
QObject* focusObject = _quickWindow->focusObject(); QObject* focusObject = _quickWindow->focusObject();
if (focusObject != _quickWindow && focusObject != _rootItem) { if (focusObject != _quickWindow && focusObject != getRootItem()) {
//qDebug() << "Swallowed shortcut " << static_cast<QKeyEvent*>(event)->key(); //qDebug() << "Swallowed shortcut " << static_cast<QKeyEvent*>(event)->key();
event->accept(); event->accept();
return true; return true;
@ -310,7 +286,7 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) {
// Event handling customization // Event handling customization
// //
bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* event) {
// Only intercept events while we're in an active state // Only intercept events while we're in an active state
if (_paused) { if (_paused) {
return false; return false;
@ -389,37 +365,65 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
return false; return false;
} }
void OffscreenUi::lockTexture(int texture) { void OffscreenQmlSurface::lockTexture(int texture) {
_fboCache.lockTexture(texture); _fboCache.lockTexture(texture);
} }
void OffscreenUi::releaseTexture(int texture) { void OffscreenQmlSurface::releaseTexture(int texture) {
_fboCache.releaseTexture(texture); _fboCache.releaseTexture(texture);
} }
void OffscreenUi::pause() { void OffscreenQmlSurface::pause() {
_paused = true; _paused = true;
} }
void OffscreenUi::resume() { void OffscreenQmlSurface::resume() {
_paused = false; _paused = false;
requestRender(); requestRender();
} }
bool OffscreenUi::isPaused() const { bool OffscreenQmlSurface::isPaused() const {
return _paused; return _paused;
} }
void OffscreenUi::setProxyWindow(QWindow* window) { void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
_renderControl->_renderWindow = window; _renderControl->_renderWindow = window;
} }
class OffscreenUiRoot : public QQuickItem {
Q_OBJECT
public:
OffscreenUiRoot(QQuickItem* parent = 0);
Q_INVOKABLE void information(const QString& title, const QString& text);
Q_INVOKABLE void loadChild(const QUrl& url) {
DependencyManager::get<OffscreenUi>()->load(url);
}
};
OffscreenUiRoot::OffscreenUiRoot(QQuickItem* parent) : QQuickItem(parent) {
}
void OffscreenUiRoot::information(const QString& title, const QString& text) {
OffscreenUi::information(title, text);
}
OffscreenUi::OffscreenUi() {
::qmlRegisterType<OffscreenUiRoot>("Hifi", 1, 0, "Root");
}
OffscreenUi::~OffscreenUi() {
}
void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) { void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name); QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
// First load? // First load?
if (!item) { if (!item) {
load(url, f); load(url, f);
item = _rootItem->findChild<QQuickItem*>(name); item = getRootItem()->findChild<QQuickItem*>(name);
} }
if (item) { if (item) {
item->setEnabled(true); item->setEnabled(true);
@ -427,11 +431,11 @@ void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(
} }
void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) { void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name); QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
// First load? // First load?
if (!item) { if (!item) {
load(url, f); load(url, f);
item = _rootItem->findChild<QQuickItem*>(name); item = getRootItem()->findChild<QQuickItem*>(name);
} }
if (item) { if (item) {
item->setEnabled(!item->isEnabled()); item->setEnabled(!item->isEnabled());

View file

@ -96,9 +96,9 @@ private:
offscreenUi->load(QML, f); \ offscreenUi->load(QML, f); \
} }
class OffscreenUi : public OffscreenGlCanvas, public Dependency { class OffscreenQmlSurface : public OffscreenGlCanvas {
Q_OBJECT Q_OBJECT
protected:
class QMyQuickRenderControl : public QQuickRenderControl { class QMyQuickRenderControl : public QQuickRenderControl {
protected: protected:
QWindow* renderWindow(QPoint* offset) Q_DECL_OVERRIDE{ QWindow* renderWindow(QPoint* offset) Q_DECL_OVERRIDE{
@ -113,36 +113,76 @@ class OffscreenUi : public OffscreenGlCanvas, public Dependency {
private: private:
QWindow* _renderWindow{ nullptr }; QWindow* _renderWindow{ nullptr };
friend class OffscreenUi; friend class OffscreenQmlSurface;
}; };
public: public:
OffscreenQmlSurface();
virtual ~OffscreenQmlSurface();
using MouseTranslator = std::function<QPointF(const QPointF&)>; using MouseTranslator = std::function<QPointF(const QPointF&)>;
OffscreenUi();
virtual ~OffscreenUi();
void create(QOpenGLContext* context); void create(QOpenGLContext* context);
void resize(const QSize& size); void resize(const QSize& size);
QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}); QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) { QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
return load(QUrl(qmlSourceFile), f); return load(QUrl(qmlSourceFile), f);
} }
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}); // Optional values for event handling
void setBaseUrl(const QUrl& baseUrl);
void addImportPath(const QString& path);
//QQmlContext* getQmlContext();
QQuickItem* getRootItem();
void pause();
void resume();
bool isPaused() const;
void setProxyWindow(QWindow* window); void setProxyWindow(QWindow* window);
bool shouldSwallowShortcut(QEvent* event);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
void setMouseTranslator(MouseTranslator mouseTranslator) { void setMouseTranslator(MouseTranslator mouseTranslator) {
_mouseTranslator = mouseTranslator; _mouseTranslator = mouseTranslator;
} }
void pause();
void resume();
bool isPaused() const;
void setBaseUrl(const QUrl& baseUrl);
QQuickItem* getRootItem();
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
signals:
void textureUpdated(GLuint texture);
public slots:
void requestUpdate();
void requestRender();
void lockTexture(int texture);
void releaseTexture(int texture);
private:
QObject* finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
private slots:
void updateQuick();
protected:
QQuickWindow* _quickWindow{ nullptr };
private:
QMyQuickRenderControl* _renderControl{ new QMyQuickRenderControl };
QQmlEngine* _qmlEngine{ nullptr };
QQmlComponent* _qmlComponent{ nullptr };
QQuickItem* _rootItem{ nullptr };
QTimer _updateTimer;
FboCache _fboCache;
bool _polish{ true };
bool _paused{ true };
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } };
};
class OffscreenUi : public OffscreenQmlSurface, public Dependency {
Q_OBJECT
public:
OffscreenUi();
virtual ~OffscreenUi();
void show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
void toggle(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
bool shouldSwallowShortcut(QEvent* event);
// Messagebox replacement functions // Messagebox replacement functions
using ButtonCallback = std::function<void(QMessageBox::StandardButton)>; using ButtonCallback = std::function<void(QMessageBox::StandardButton)>;
@ -168,33 +208,6 @@ public:
static void critical(const QString& title, const QString& text, static void critical(const QString& title, const QString& text,
ButtonCallback callback = NO_OP_CALLBACK, ButtonCallback callback = NO_OP_CALLBACK,
QMessageBox::StandardButtons buttons = QMessageBox::Ok); QMessageBox::StandardButtons buttons = QMessageBox::Ok);
private:
QObject* finishQmlLoad(std::function<void(QQmlContext*, QObject*)> f);
private slots:
void updateQuick();
public slots:
void requestUpdate();
void requestRender();
void lockTexture(int texture);
void releaseTexture(int texture);
signals:
void textureUpdated(GLuint texture);
private:
QMyQuickRenderControl* _renderControl{ new QMyQuickRenderControl };
QQuickWindow* _quickWindow{ nullptr };
QQmlEngine* _qmlEngine{ nullptr };
QQmlComponent* _qmlComponent{ nullptr };
QQuickItem* _rootItem{ nullptr };
QTimer _updateTimer;
FboCache _fboCache;
bool _polish{ true };
bool _paused{ true };
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } };
}; };
#endif #endif