Merge pull request #6963 from jherico/qmlOverlays

Make all 2D overlays QML-based
This commit is contained in:
Brad Hefta-Gaub 2016-01-28 23:33:30 -08:00
commit 8efe15cb21
19 changed files with 413 additions and 495 deletions

View file

@ -236,18 +236,10 @@
"dimensions"
]);
ImageOverlay = generateOverlayClass(Overlay2D, "image", [
"subImage", "imageURL"
]);
Image3DOverlay = generateOverlayClass(Billboard3DOverlay, "image3d", [
"url", "subImage"
]);
TextOverlay = generateOverlayClass(Overlay2D, "text", [
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
]);
Text3DOverlay = generateOverlayClass(Billboard3DOverlay, "text3d", [
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
"rightMargin", "bottomMargin"

View file

@ -132,14 +132,15 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
this.y = y;
this.width = 0;
this.height = ToolBar.TITLE_BAR_HEIGHT;
this.back = this.back = Overlays.addOverlay("text", {
backgroundColor: { red: 255, green: 255, blue: 255 },
this.backAlpha = 1.0;
this.back = Overlays.addOverlay("rectangle", {
color: { red: 255, green: 255, blue: 255 },
x: this.x,
y: this.y,
radius: 4,
width: this.width,
height: this.height,
alpha: 1.0,
backgroundAlpha: 1.0,
alpha: this.backAlpha,
visible: false
});
this.spacing = [];
@ -246,10 +247,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
this.tools[tool].setAlpha(alpha);
}
if (this.back != null) {
Overlays.editOverlay(this.back, {
alpha: alpha,
backgroundAlpha: alpha
});
this.backAlpha = alpha;
Overlays.editOverlay(this.back, { alpha: alpha });
}
} else {
this.tools[tool].setAlpha(alpha);
@ -258,9 +257,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
this.setBack = function(color, alpha) {
if (color == null) {
Overlays.editOverlay(this.back, {
visible: false
});
Overlays.editOverlay(this.back, { visible: false });
} else {
Overlays.editOverlay(this.back, {
width: this.width +
@ -268,8 +265,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
height: this.height +
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
visible: true,
backgroundColor: color,
backgroundAlpha: alpha
color: color,
alpha: alpha
});
}
}
@ -339,12 +336,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
that.hover = function (enable) { // Can be overriden or extended by clients.
that.isHovering = enable;
if (that.back) {
if (enable) {
that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha');
}
Overlays.editOverlay(this.back, {
visible: enable,
backgroundAlpha: enable ? 0.5 : that.oldAlpha
alpha: enable ? 0.5 : that.backAlpha
});
}
};

View file

@ -1,23 +0,0 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
TextOverlayElement {
id: root
Rectangle {
color: root.backgroundColor
anchors.fill: parent
Text {
x: root.leftMargin
y: root.topMargin
id: text
objectName: "textElement"
text: root.text
color: root.textColor
font.family: root.fontFamily
font.pixelSize: root.fontSize
lineHeightMode: Text.FixedHeight
lineHeight: root.lineHeight
}
}
}

View file

@ -25,7 +25,7 @@ FocusScope {
readonly property alias zLevels: zLevels
QtObject {
id: zLevels;
readonly property real normal: 0
readonly property real normal: 1 // make windows always appear higher than QML overlays and other non-window controls.
readonly property real top: 2000
readonly property real modal: 4000
readonly property real menu: 8000

View file

@ -0,0 +1,78 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
Overlay {
id: root
Image {
id: image
property bool scaleFix: true;
property real xOffset: 0
property real yOffset: 0
property real imageScale: 1.0
property var resizer: Timer {
interval: 50
repeat: false
running: false
onTriggered: {
var targetAspect = root.width / root.height;
var sourceAspect = image.sourceSize.width / image.sourceSize.height;
if (sourceAspect <= targetAspect) {
if (root.width === image.sourceSize.width) {
return;
}
image.imageScale = root.width / image.sourceSize.width;
} else if (sourceAspect > targetAspect){
if (root.height === image.sourceSize.height) {
return;
}
image.imageScale = root.height / image.sourceSize.height;
}
image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale);
}
}
x: -1 * xOffset * imageScale
y: -1 * yOffset * imageScale
onSourceSizeChanged: {
if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) {
scaleFix = false;
resizer.start();
}
}
}
function updateSubImage(subImage) {
var keys = Object.keys(subImage);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = subImage[key];
switch (key) {
case "x": image.xOffset = value; break;
case "y": image.yOffset = value; break;
}
}
}
function updatePropertiesFromScript(properties) {
var keys = Object.keys(properties);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = properties[key];
switch (key) {
case "height": root.height = value; break;
case "width": root.width = value; break;
case "x": root.x = value; break;
case "y": root.y = value; break;
case "visible": root.visible = value; break;
case "alpha": root.opacity = value; break;
case "imageURL": image.source = value; break;
case "subImage": updateSubImage(value); break;
default: console.log("OVERLAY Unhandled image property " + key);
}
}
}
}

View file

@ -0,0 +1,30 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
Item {
id: root
clip: true
property int dumpDepth: 0;
function dumpObject(object) {
var keys = Object.keys(object);
var tabsString = "";
for (var j = 0; j < dumpDepth; ++j) {
tabsString = tabsString + "\t";
}
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = object[key];
console.log(tabsString + "OVERLAY Key " + key + " (" + typeof(value) + "): " + value);
if (typeof(value) === "object") {
++dumpDepth;
dumpObject(value)
--dumpDepth;
}
}
}
}

View file

@ -0,0 +1,38 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
Overlay {
id: root
Rectangle {
id: rectangle
anchors.fill: parent
color: "black"
}
function updatePropertiesFromScript(properties) {
var keys = Object.keys(properties);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = properties[key];
console.log("OVERLAY rectangle property " + key + " set to value " + value);
switch (key) {
case "height": root.height = value; break;
case "width": root.width = value; break;
case "x": root.x = value; break;
case "y": root.y = value; break;
case "visible": root.visible = value; break;
case "alpha": rectangle.color.a = value; break;
case "color": rectangle.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.color.a); break;
case "borderAlpha": rectangle.border.color.a = value; break;
case "borderColor": rectangle.border.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, rectangle.border.color.a); break;
case "borderWidth": rectangle.border.width = value; break;
case "radius": rectangle.radius = value; break;
default: console.log("OVERLAY Unhandled rectangle property " + key);
}
}
}
}

View file

@ -0,0 +1,54 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
Overlay {
id: root
clip: true
Rectangle {
id: background;
anchors.fill: parent
color: "#B2000000"
Text {
id: textField;
anchors { fill: parent; bottomMargin: textField.anchors.topMargin; rightMargin: textField.anchors.leftMargin; }
objectName: "textElement"
color: "white"
lineHeightMode: Text.FixedHeight
font.family: "Helvetica"
font.pixelSize: 18
lineHeight: 18
}
}
function updatePropertiesFromScript(properties) {
var keys = Object.keys(properties);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
var value = properties[key];
switch (key) {
case "height": root.height = value; break;
case "width": root.width = value; break;
case "x": root.x = value; break;
case "y": root.y = value; break;
case "visible": root.visible = value; break;
case "alpha": textField.color.a = value; break;
case "margin": textField.anchors.margins = value; break;
case "leftMargin": textField.anchors.leftMargin = value; break;
case "topMargin": textField.anchors.topMargin = value; break;
case "color": // fall through
case "textColor": textField.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, textField.color.a); break;
case "text": textField.text = value; break;
case "backgroundAlpha": background.color = Qt.rgba(background.color.r, background.color.g, background.color.b, value); break;
case "backgroundColor": background.color = Qt.rgba(value.red / 255, value.green / 255, value.blue / 255, background.color.a); break;
case "font": textField.font.pixelSize = value.size; break;
case "lineHeight": textField.lineHeight = value; break;
default:
console.log("OVERLAY text unhandled property " + key);
}
}
}
}

View file

@ -17,159 +17,15 @@
QString const ImageOverlay::TYPE = "image";
QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml"));
ImageOverlay::ImageOverlay() :
_imageURL(),
_renderImage(false),
_wantClipFromImage(false)
{
}
ImageOverlay::ImageOverlay()
: QmlOverlay(URL) { }
ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
Overlay2D(imageOverlay),
_imageURL(imageOverlay->_imageURL),
_textureImage(imageOverlay->_textureImage),
_texture(imageOverlay->_texture),
_fromImage(imageOverlay->_fromImage),
_renderImage(imageOverlay->_renderImage),
_wantClipFromImage(imageOverlay->_wantClipFromImage)
{
}
QmlOverlay(URL, imageOverlay) { }
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
void ImageOverlay::setImageURL(const QUrl& url) {
_imageURL = url;
if (url.isEmpty()) {
_isLoaded = true;
_renderImage = false;
_texture.clear();
} else {
_isLoaded = false;
_renderImage = true;
}
}
void ImageOverlay::render(RenderArgs* args) {
if (!_isLoaded && _renderImage) {
_isLoaded = true;
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
}
// If we are not visible or loaded, return. If we are trying to render an
// image but the texture hasn't loaded, return.
if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) {
return;
}
auto geometryCache = DependencyManager::get<GeometryCache>();
gpu::Batch& batch = *args->_batch;
geometryCache->useSimpleDrawPipeline(batch);
if (_renderImage) {
batch.setResourceTexture(0, _texture->getGPUTexture());
} else {
batch.setResourceTexture(0, args->_whiteTexture);
}
const float MAX_COLOR = 255.0f;
xColor color = getColor();
float alpha = getAlpha();
glm::vec4 quadColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
int left = _bounds.left();
int right = _bounds.right() + 1;
int top = _bounds.top();
int bottom = _bounds.bottom() + 1;
glm::vec2 topLeft(left, top);
glm::vec2 bottomRight(right, bottom);
batch.setModelTransform(Transform());
// if for some reason our image is not over 0 width or height, don't attempt to render the image
if (_renderImage) {
float imageWidth = _texture->getWidth();
float imageHeight = _texture->getHeight();
if (imageWidth > 0 && imageHeight > 0) {
QRect fromImage;
if (_wantClipFromImage) {
float scaleX = imageWidth / _texture->getOriginalWidth();
float scaleY = imageHeight / _texture->getOriginalHeight();
fromImage.setX(scaleX * _fromImage.x());
fromImage.setY(scaleY * _fromImage.y());
fromImage.setWidth(scaleX * _fromImage.width());
fromImage.setHeight(scaleY * _fromImage.height());
}
else {
fromImage.setX(0);
fromImage.setY(0);
fromImage.setWidth(imageWidth);
fromImage.setHeight(imageHeight);
}
float x = fromImage.x() / imageWidth;
float y = fromImage.y() / imageHeight;
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
float h = fromImage.height() / imageHeight;
glm::vec2 texCoordTopLeft(x, y);
glm::vec2 texCoordBottomRight(x + w, y + h);
glm::vec4 texcoordRect(texCoordTopLeft, w, h);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
} else {
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
}
} else {
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, quadColor);
}
}
void ImageOverlay::setProperties(const QScriptValue& properties) {
Overlay2D::setProperties(properties);
QScriptValue subImageBounds = properties.property("subImage");
if (subImageBounds.isValid()) {
QRect oldSubImageRect = _fromImage;
QRect subImageRect = _fromImage;
if (subImageBounds.property("x").isValid()) {
subImageRect.setX(subImageBounds.property("x").toVariant().toInt());
} else {
subImageRect.setX(oldSubImageRect.x());
}
if (subImageBounds.property("y").isValid()) {
subImageRect.setY(subImageBounds.property("y").toVariant().toInt());
} else {
subImageRect.setY(oldSubImageRect.y());
}
if (subImageBounds.property("width").isValid()) {
subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt());
} else {
subImageRect.setWidth(oldSubImageRect.width());
}
if (subImageBounds.property("height").isValid()) {
subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt());
} else {
subImageRect.setHeight(oldSubImageRect.height());
}
setClipFromSource(subImageRect);
}
QScriptValue imageURL = properties.property("imageURL");
if (imageURL.isValid()) {
setImageURL(imageURL.toVariant().toString());
}
}
QScriptValue ImageOverlay::getProperty(const QString& property) {
if (property == "subImage") {
return qRectToScriptValue(_scriptEngine, _fromImage);
}
if (property == "imageURL") {
return _imageURL.toString();
}
return Overlay2D::getProperty(property);
}
ImageOverlay* ImageOverlay::createClone() const {
return new ImageOverlay(this);
}

View file

@ -11,48 +11,22 @@
#ifndef hifi_ImageOverlay_h
#define hifi_ImageOverlay_h
// include this before QGLWidget, which includes an earlier version of OpenGL
#include <QImage>
#include <QRect>
#include <QUrl>
#include <TextureCache.h>
#include "QmlOverlay.h"
#include "Overlay2D.h"
class ImageOverlay : public Overlay2D {
class ImageOverlay : public QmlOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
static QUrl const URL;
ImageOverlay();
ImageOverlay(const ImageOverlay* imageOverlay);
virtual void render(RenderArgs* args);
// getters
const QRect& getClipFromSource() const { return _fromImage; }
const QUrl& getImageURL() const { return _imageURL; }
// setters
void setClipFromSource(const QRect& bounds) { _fromImage = bounds; _wantClipFromImage = true; }
void setImageURL(const QUrl& url);
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
virtual ImageOverlay* createClone() const;
private:
QUrl _imageURL;
QImage _textureImage;
NetworkTexturePointer _texture;
QRect _fromImage; // where from in the image to sample
bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle
bool _wantClipFromImage;
virtual ImageOverlay* createClone() const override;
};

View file

@ -39,6 +39,7 @@ public:
virtual void render(RenderArgs* args) = 0;
virtual AABox getBounds() const = 0;
virtual bool supportsGetProperty() const { return true; }
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);

View file

@ -30,6 +30,7 @@
#include "Sphere3DOverlay.h"
#include "Grid3DOverlay.h"
#include "TextOverlay.h"
#include "RectangleOverlay.h"
#include "Text3DOverlay.h"
#include "Web3DOverlay.h"
#include <QtQuick/QQuickWindow>
@ -175,6 +176,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
thisOverlay = std::make_shared<ModelOverlay>();
} else if (type == Web3DOverlay::TYPE) {
thisOverlay = std::make_shared<Web3DOverlay>();
} else if (type == RectangleOverlay::TYPE) {
thisOverlay = std::make_shared<RectangleOverlay>();
}
if (thisOverlay) {
@ -373,7 +376,7 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop
OverlayPropertyResult result;
Overlay::Pointer thisOverlay = getOverlay(id);
QReadLocker lock(&_lock);
if (thisOverlay) {
if (thisOverlay && thisOverlay->supportsGetProperty()) {
result.value = thisOverlay->getProperty(property);
}
return result;

View file

@ -0,0 +1,74 @@
//
// Created by Bradley Austin Davis on 2016/01/27
// Copyright 2013-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
//
#include "QmlOverlay.h"
#include <QQuickItem>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <GLMHelpers.h>
#include <OffscreenUi.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
#include "Application.h"
#include "text/FontFamilies.h"
QmlOverlay::QmlOverlay(const QUrl& url) {
buildQmlElement(url);
}
QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay)
: Overlay2D(textOverlay) {
buildQmlElement(url);
}
void QmlOverlay::buildQmlElement(const QUrl& url) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->returnFromUiThread([=] {
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
_qmlElement = dynamic_cast<QQuickItem*>(object);
});
while (!_qmlElement) {
qApp->processEvents();
}
return QVariant();
});
}
QmlOverlay::~QmlOverlay() {
if (_qmlElement) {
_qmlElement->deleteLater();
_qmlElement = nullptr;
}
}
void QmlOverlay::setProperties(const QScriptValue& properties) {
Overlay2D::setProperties(properties);
auto bounds = _bounds;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
_qmlElement->setX(bounds.left());
_qmlElement->setY(bounds.top());
_qmlElement->setWidth(bounds.width());
_qmlElement->setHeight(bounds.height());
});
QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Q_ARG(QVariant, properties.toVariant()));
}
void QmlOverlay::render(RenderArgs* args) {
if (!_qmlElement) {
return;
}
if (_visible != _qmlElement->isVisible()) {
_qmlElement->setVisible(_visible);
}
}

View file

@ -0,0 +1,41 @@
//
// Created by Bradley Austin Davis on 2016/01/27
// Copyright 2013-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
//
#ifndef hifi_QmlOverlay_h
#define hifi_QmlOverlay_h
#include <QString>
#include <SharedUtil.h>
#include "Overlay2D.h"
class QQuickItem;
class QmlOverlay : public Overlay2D {
Q_OBJECT
public:
QmlOverlay(const QUrl& url);
QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay);
~QmlOverlay();
// Cannot fetch properties from QML based overlays due to race conditions
bool supportsGetProperty() const override { return false; }
void setProperties(const QScriptValue& properties) override;
void render(RenderArgs* args) override;
private:
void buildQmlElement(const QUrl& url);
protected:
QQuickItem* _qmlElement{ nullptr };
};
#endif // hifi_QmlOverlay_h

View file

@ -0,0 +1,21 @@
//
// Created by Bradley Austin Davis on 2016/01/27
// Copyright 2013-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
//
#include "RectangleOverlay.h"
QString const RectangleOverlay::TYPE = "rectangle";
QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml"));
RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {}
RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay)
: QmlOverlay(URL, rectangleOverlay) { }
RectangleOverlay* RectangleOverlay::createClone() const {
return new RectangleOverlay(this);
}

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2016/01/27
// Copyright 2013-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
//
#ifndef hifi_RectangleOverlay_h
#define hifi_RectangleOverlay_h
#include "QmlOverlay.h"
class RectangleOverlay : public QmlOverlay {
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
static QUrl const URL;
RectangleOverlay();
RectangleOverlay(const RectangleOverlay* RectangleOverlay);
virtual RectangleOverlay* createClone() const;
};
#endif // hifi_RectangleOverlay_h

View file

@ -24,216 +24,21 @@
#include "Application.h"
#include "text/FontFamilies.h"
#define TEXT_OVERLAY_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name WRITE set##name NOTIFY name##Changed) \
public: \
type name() { return _##name; }; \
void set##name(const type& name) { \
if (name != _##name) { \
_##name = name; \
emit name##Changed(); \
} \
} \
private: \
type _##name{ initialValue };
class TextOverlayElement : public QQuickItem {
Q_OBJECT
HIFI_QML_DECL
private:
TEXT_OVERLAY_PROPERTY(QString, text, "")
TEXT_OVERLAY_PROPERTY(QString, fontFamily, SANS_FONT_FAMILY)
TEXT_OVERLAY_PROPERTY(QString, textColor, "#ffffffff")
TEXT_OVERLAY_PROPERTY(QString, backgroundColor, "#B2000000")
TEXT_OVERLAY_PROPERTY(qreal, fontSize, 18)
TEXT_OVERLAY_PROPERTY(qreal, lineHeight, 18)
TEXT_OVERLAY_PROPERTY(qreal, leftMargin, 0)
TEXT_OVERLAY_PROPERTY(qreal, topMargin, 0)
public:
TextOverlayElement(QQuickItem* parent = nullptr) : QQuickItem(parent) {
}
signals:
void textChanged();
void fontFamilyChanged();
void fontSizeChanged();
void lineHeightChanged();
void leftMarginChanged();
void topMarginChanged();
void textColorChanged();
void backgroundColorChanged();
};
HIFI_QML_DEF(TextOverlayElement)
QString toQmlColor(const glm::vec4& v) {
QString templat("#%1%2%3%4");
return templat.
arg((int)(v.a * 255), 2, 16, QChar('0')).
arg((int)(v.r * 255), 2, 16, QChar('0')).
arg((int)(v.g * 255), 2, 16, QChar('0')).
arg((int)(v.b * 255), 2, 16, QChar('0'));
}
QString const TextOverlay::TYPE = "text";
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
TextOverlay::TextOverlay() :
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
_leftMargin(DEFAULT_MARGIN),
_topMargin(DEFAULT_MARGIN),
_fontSize(DEFAULT_FONTSIZE)
{
qApp->postLambdaEvent([=] {
static std::once_flag once;
std::call_once(once, [] {
TextOverlayElement::registerType();
});
auto offscreenUi = DependencyManager::get<OffscreenUi>();
TextOverlayElement::show([=](QQmlContext* context, QObject* object) {
_qmlElement = static_cast<TextOverlayElement*>(object);
});
});
while (!_qmlElement) {
QThread::msleep(1);
}
TextOverlay::TextOverlay() : QmlOverlay(URL) { }
TextOverlay::TextOverlay(const TextOverlay* textOverlay)
: QmlOverlay(URL, textOverlay) {
}
TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
Overlay2D(textOverlay),
_text(textOverlay->_text),
_backgroundColor(textOverlay->_backgroundColor),
_backgroundAlpha(textOverlay->_backgroundAlpha),
_leftMargin(textOverlay->_leftMargin),
_topMargin(textOverlay->_topMargin),
_fontSize(textOverlay->_fontSize)
{
qApp->postLambdaEvent([=] {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
TextOverlayElement::show([this](QQmlContext* context, QObject* object) {
_qmlElement = static_cast<TextOverlayElement*>(object);
});
});
while (!_qmlElement) {
QThread::msleep(1);
}
}
TextOverlay::~TextOverlay() {
if (_qmlElement) {
_qmlElement->deleteLater();
}
}
xColor TextOverlay::getBackgroundColor() {
if (_colorPulse == 0.0f) {
return _backgroundColor;
}
float pulseLevel = updatePulse();
xColor result = _backgroundColor;
if (_colorPulse < 0.0f) {
result.red *= (1.0f - pulseLevel);
result.green *= (1.0f - pulseLevel);
result.blue *= (1.0f - pulseLevel);
} else {
result.red *= pulseLevel;
result.green *= pulseLevel;
result.blue *= pulseLevel;
}
return result;
}
void TextOverlay::render(RenderArgs* args) {
if (!_qmlElement) {
return;
}
if (_visible != _qmlElement->isVisible()) {
_qmlElement->setVisible(_visible);
}
}
void TextOverlay::setProperties(const QScriptValue& properties) {
Overlay2D::setProperties(properties);
_qmlElement->setX(_bounds.left());
_qmlElement->setY(_bounds.top());
_qmlElement->setWidth(_bounds.width());
_qmlElement->setHeight(_bounds.height());
_qmlElement->settextColor(toQmlColor(vec4(toGlm(_color), _alpha)));
QScriptValue font = properties.property("font");
if (font.isObject()) {
if (font.property("size").isValid()) {
setFontSize(font.property("size").toInt32());
}
QFont font(_qmlElement->fontFamily());
font.setPixelSize(_qmlElement->fontSize());
QFontMetrics fm(font);
_qmlElement->setlineHeight(fm.lineSpacing() * 1.2);
}
QScriptValue text = properties.property("text");
if (text.isValid()) {
setText(text.toVariant().toString());
}
QScriptValue backgroundColor = properties.property("backgroundColor");
if (backgroundColor.isValid()) {
QScriptValue red = backgroundColor.property("red");
QScriptValue green = backgroundColor.property("green");
QScriptValue blue = backgroundColor.property("blue");
if (red.isValid() && green.isValid() && blue.isValid()) {
_backgroundColor.red = red.toVariant().toInt();
_backgroundColor.green = green.toVariant().toInt();
_backgroundColor.blue = blue.toVariant().toInt();
}
}
if (properties.property("backgroundAlpha").isValid()) {
_backgroundAlpha = properties.property("backgroundAlpha").toVariant().toFloat();
}
_qmlElement->setbackgroundColor(toQmlColor(vec4(toGlm(_backgroundColor), _backgroundAlpha)));
if (properties.property("leftMargin").isValid()) {
setLeftMargin(properties.property("leftMargin").toVariant().toInt());
}
if (properties.property("topMargin").isValid()) {
setTopMargin(properties.property("topMargin").toVariant().toInt());
}
}
TextOverlay::~TextOverlay() { }
TextOverlay* TextOverlay::createClone() const {
return new TextOverlay(this);
}
QScriptValue TextOverlay::getProperty(const QString& property) {
if (property == "font") {
QScriptValue font = _scriptEngine->newObject();
font.setProperty("size", _fontSize);
return font;
}
if (property == "text") {
return _text;
}
if (property == "backgroundColor") {
return xColorToScriptValue(_scriptEngine, _backgroundColor);
}
if (property == "backgroundAlpha") {
return _backgroundAlpha;
}
if (property == "leftMargin") {
return _leftMargin;
}
if (property == "topMargin") {
return _topMargin;
}
return Overlay2D::getProperty(property);
}
QSizeF TextOverlay::textSize(const QString& text) const {
int lines = 1;
foreach(QChar c, text) {
@ -241,31 +46,15 @@ QSizeF TextOverlay::textSize(const QString& text) const {
++lines;
}
}
QFont font(_qmlElement->fontFamily());
font.setPixelSize(_qmlElement->fontSize());
QFont font(SANS_FONT_FAMILY);
font.setPixelSize(18);
QFontMetrics fm(font);
QSizeF result = QSizeF(fm.width(text), _qmlElement->lineHeight() * lines);
QSizeF result = QSizeF(fm.width(text), 18 * lines);
return result;
}
void TextOverlay::setFontSize(int fontSize) {
_fontSize = fontSize;
_qmlElement->setfontSize(fontSize);
}
void TextOverlay::setText(const QString& text) {
_text = text;
_qmlElement->settext(text);
}
void TextOverlay::setLeftMargin(int margin) {
_leftMargin = margin;
_qmlElement->setleftMargin(margin);
}
void TextOverlay::setTopMargin(int margin) {
_topMargin = margin;
_qmlElement->settopMargin(margin);
}
#include "TextOverlay.moc"
void TextOverlay::setTopMargin(float margin) {}
void TextOverlay::setLeftMargin(float margin) {}
void TextOverlay::setFontSize(float size) {}
void TextOverlay::setText(const QString& text) {}

View file

@ -11,59 +11,27 @@
#ifndef hifi_TextOverlay_h
#define hifi_TextOverlay_h
#include <QString>
#include "QmlOverlay.h"
#include <SharedUtil.h>
#include "Overlay2D.h"
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
const int DEFAULT_MARGIN = 10;
const int DEFAULT_FONTSIZE = 12;
const int DEFAULT_FONT_WEIGHT = 50;
class TextOverlayElement;
class TextOverlay : public Overlay2D {
Q_OBJECT
class TextOverlay : public QmlOverlay {
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
QString getType() const override { return TYPE; }
static QUrl const URL;
TextOverlay();
TextOverlay(const TextOverlay* textOverlay);
~TextOverlay();
virtual void render(RenderArgs* args);
// getters
const QString& getText() const { return _text; }
int getLeftMargin() const { return _leftMargin; }
int getTopMargin() const { return _topMargin; }
xColor getBackgroundColor();
float getBackgroundAlpha() const { return _backgroundAlpha; }
// setters
void setTopMargin(float margin);
void setLeftMargin(float margin);
void setFontSize(float size);
void setText(const QString& text);
void setLeftMargin(int margin);
void setTopMargin(int margin);
void setFontSize(int fontSize);
virtual void setProperties(const QScriptValue& properties);
virtual TextOverlay* createClone() const;
virtual QScriptValue getProperty(const QString& property);
TextOverlay* createClone() const;
QSizeF textSize(const QString& text) const; // Pixels
private:
TextOverlayElement* _qmlElement{ nullptr };
QString _text;
xColor _backgroundColor;
float _backgroundAlpha;
int _leftMargin;
int _topMargin;
int _fontSize;
};

View file

@ -27,5 +27,6 @@ DISTFILES += \
../../interface/resources/qml/windows/*.qml \
../../interface/resources/qml/hifi/*.qml \
../../interface/resources/qml/hifi/dialogs/*.qml \
../../interface/resources/qml/hifi/dialogs/preferences/*.qml
../../interface/resources/qml/hifi/dialogs/preferences/*.qml \
../../interface/resources/qml/hifi/overlays/*.qml