mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #6963 from jherico/qmlOverlays
Make all 2D overlays QML-based
This commit is contained in:
commit
8efe15cb21
19 changed files with 413 additions and 495 deletions
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
78
interface/resources/qml/hifi/overlays/ImageOverlay.qml
Normal file
78
interface/resources/qml/hifi/overlays/ImageOverlay.qml
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
interface/resources/qml/hifi/overlays/Overlay.qml
Normal file
30
interface/resources/qml/hifi/overlays/Overlay.qml
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
interface/resources/qml/hifi/overlays/RectangleOverlay.qml
Normal file
38
interface/resources/qml/hifi/overlays/RectangleOverlay.qml
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
interface/resources/qml/hifi/overlays/TextOverlay.qml
Normal file
54
interface/resources/qml/hifi/overlays/TextOverlay.qml
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
74
interface/src/ui/overlays/QmlOverlay.cpp
Normal file
74
interface/src/ui/overlays/QmlOverlay.cpp
Normal 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);
|
||||
}
|
||||
}
|
41
interface/src/ui/overlays/QmlOverlay.h
Normal file
41
interface/src/ui/overlays/QmlOverlay.h
Normal 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
|
21
interface/src/ui/overlays/RectangleOverlay.cpp
Normal file
21
interface/src/ui/overlays/RectangleOverlay.cpp
Normal 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);
|
||||
}
|
27
interface/src/ui/overlays/RectangleOverlay.h
Normal file
27
interface/src/ui/overlays/RectangleOverlay.h
Normal 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
|
|
@ -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) {}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue