1
0
Fork 0

Remote-canvas Image and font caches working again

Reconnecting and disconnecting also starting to work more smoothly
This commit is contained in:
James Turner 2017-11-03 13:56:43 +00:00
parent 67ab1c7162
commit 4bae38f994
22 changed files with 290 additions and 112 deletions

View file

@ -111,7 +111,9 @@ void ApplicationController::rebuildConfigData()
void ApplicationController::query()
{
qWarning() << Q_FUNC_INFO << m_host << m_port;
if (m_query) {
cancelQuery();
}
if (m_host.isEmpty() || (m_port == 0))
return;
@ -123,13 +125,31 @@ void ApplicationController::query()
queryUrl.setPath("/json/canvas/by-index");
queryUrl.setQuery("d=2");
QNetworkReply* reply = m_netAccess->get(QNetworkRequest(queryUrl));
connect(reply, &QNetworkReply::finished,
m_query = m_netAccess->get(QNetworkRequest(queryUrl));
connect(m_query, &QNetworkReply::finished,
this, &ApplicationController::onFinishedGetCanvasList);
setStatus(Querying);
}
void ApplicationController::cancelQuery()
{
setStatus(Idle);
if (m_query) {
m_query->abort();
m_query->deleteLater();
}
m_query = nullptr;
m_canvases.clear();
emit canvasListChanged();
}
void ApplicationController::clearQuery()
{
cancelQuery();
}
void ApplicationController::restoreConfig(int index)
{
QString path = m_configs.at(index).toMap().value("path").toString();
@ -215,7 +235,8 @@ QJsonObject jsonPropNodeFindChild(QJsonObject obj, QByteArray name)
void ApplicationController::onFinishedGetCanvasList()
{
m_canvases.clear();
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QNetworkReply* reply = m_query;
m_query = nullptr;
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
@ -272,8 +293,7 @@ QByteArray ApplicationController::saveState(QString name) const
void ApplicationController::restoreState(QByteArray bytes)
{
qDeleteAll(m_activeCanvases);
m_activeCanvases.clear();
clearConnections();
QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes);
QJsonObject json = jsonDoc.object();
@ -291,3 +311,12 @@ void ApplicationController::restoreState(QByteArray bytes)
emit activeCanvasesChanged();
}
void ApplicationController::clearConnections()
{
Q_FOREACH(auto c, m_activeCanvases) {
c->deleteLater();
}
m_activeCanvases.clear();
emit activeCanvasesChanged();
}

View file

@ -49,6 +49,8 @@ public:
Q_INVOKABLE void save(QString configName);
Q_INVOKABLE void query();
Q_INVOKABLE void cancelQuery();
Q_INVOKABLE void clearQuery();
Q_INVOKABLE void restoreConfig(int index);
@ -107,6 +109,7 @@ private:
void setStatus(Status newStatus);
void rebuildConfigData();
void clearConnections();
QByteArray saveState(QString name) const;
void restoreState(QByteArray bytes);
@ -118,6 +121,7 @@ private:
QNetworkAccessManager* m_netAccess;
Status m_status;
QVariantList m_configs;
QNetworkReply* m_query = nullptr;
};

View file

@ -28,6 +28,8 @@
#include <QNetworkReply>
#include "localprop.h"
#include "fgqcanvasfontcache.h"
#include "fgqcanvasimageloader.h"
CanvasConnection::CanvasConnection(QObject *parent) : QObject(parent)
{
@ -41,6 +43,7 @@ CanvasConnection::CanvasConnection(QObject *parent) : QObject(parent)
CanvasConnection::~CanvasConnection()
{
qDebug() << Q_FUNC_INFO;
disconnect(&m_webSocket, &QWebSocket::disconnected,
this, &CanvasConnection::onWebSocketClosed);
m_webSocket.close();
@ -145,19 +148,31 @@ LocalProp *CanvasConnection::propertyRoot() const
return m_localPropertyRoot.get();
}
FGQCanvasImageLoader *CanvasConnection::imageLoader() const
{
if (!m_imageLoader) {
m_imageLoader = new FGQCanvasImageLoader(m_netAccess, const_cast<CanvasConnection*>(this));
m_imageLoader->setHost(m_webSocketUrl.host(),
m_webSocketUrl.port());
}
return m_imageLoader;
}
FGQCanvasFontCache *CanvasConnection::fontCache() const
{
if (!m_fontCache) {
m_fontCache = new FGQCanvasFontCache(m_netAccess, const_cast<CanvasConnection*>(this));
m_fontCache->setHost(m_webSocketUrl.host(),
m_webSocketUrl.port());
}
return m_fontCache;
}
void CanvasConnection::onWebSocketConnected()
{
m_localPropertyRoot.reset(new LocalProp{nullptr, NameIndexTuple("")});
// ui->canvas->setRootProperty(m_localPropertyRoot);
#if 0
FGQCanvasFontCache::instance()->setHost(ui->hostName->text(),
ui->portEdit->text().toInt());
FGQCanvasImageLoader::instance()->setHost(ui->hostName->text(),
ui->portEdit->text().toInt());
#endif
setStatus(Connected);
}

View file

@ -28,6 +28,8 @@
class LocalProp;
class QNetworkAccessManager;
class FGQCanvasImageLoader;
class FGQCanvasFontCache;
class CanvasConnection : public QObject
{
@ -85,6 +87,10 @@ public:
return QString::fromUtf8(m_rootPropertyPath);
}
FGQCanvasImageLoader* imageLoader() const;
FGQCanvasFontCache* fontCache() const;
public Q_SLOTS:
void reconnect();
@ -126,6 +132,9 @@ private:
QHash<int, LocalProp*> idPropertyDict;
Status m_status = NotConnected;
QString m_rootPath;
mutable FGQCanvasImageLoader* m_imageLoader = nullptr;
mutable FGQCanvasFontCache* m_fontCache = nullptr;
};
#endif // CANVASCONNECTION_H

View file

@ -18,16 +18,17 @@
#include "canvasdisplay.h"
#include <QDebug>
#include <QQuickItem>
#include "canvasconnection.h"
#include "fgcanvasgroup.h"
#include "fgcanvaspaintcontext.h"
#include "canvasitem.h"
#include "localprop.h"
CanvasDisplay::CanvasDisplay(QQuickItem* parent) :
QQuickItem(parent)
{
setSize(QSizeF(400, 400));
qDebug() << "created a canvas display";
setFlag(ItemHasContents);
@ -35,7 +36,8 @@ CanvasDisplay::CanvasDisplay(QQuickItem* parent) :
CanvasDisplay::~CanvasDisplay()
{
qDebug() << Q_FUNC_INFO << "connection is" << m_connection;
delete m_rootItem;
}
void CanvasDisplay::updatePolish()
@ -43,6 +45,12 @@ void CanvasDisplay::updatePolish()
m_rootElement->polish();
}
void CanvasDisplay::geometryChanged(const QRectF &newGeometry, const QRectF &)
{
Q_UNUSED(newGeometry);
recomputeScaling();
}
void CanvasDisplay::setCanvas(CanvasConnection *canvas)
{
if (m_connection == canvas)
@ -50,31 +58,81 @@ void CanvasDisplay::setCanvas(CanvasConnection *canvas)
if (m_connection) {
disconnect(m_connection, nullptr, this, nullptr);
qDebug() << "deleting items";
delete m_rootItem;
qDebug() << "deleting elements";
m_rootElement.reset();
qDebug() << "done";
}
m_connection = canvas;
emit canvasChanged(m_connection);
// delete existing children
if (m_connection) {
// delete existing children
connect(m_connection, &CanvasConnection::statusChanged,
this, &CanvasDisplay::onConnectionStatusChanged);
connect(m_connection, &CanvasConnection::updated,
this, &CanvasDisplay::onConnectionUpdated);
connect(m_connection, &QObject::destroyed,
this, &CanvasDisplay::onConnectionDestroyed);
connect(m_connection, &CanvasConnection::statusChanged,
this, &CanvasDisplay::onConnectionStatusChanged);
connect(m_connection, &CanvasConnection::updated,
this, &CanvasDisplay::onConnectionUpdated);
onConnectionStatusChanged();
}
}
void CanvasDisplay::onConnectionDestroyed()
{
m_connection = nullptr;
emit canvasChanged(m_connection);
m_rootElement.reset();
}
void CanvasDisplay::onConnectionStatusChanged()
{
if (m_connection->status() == CanvasConnection::Connected) {
m_rootElement.reset(new FGCanvasGroup(nullptr, m_connection->propertyRoot()));
auto qq = m_rootElement->createQuickItem(this);
qq->setSize(QSizeF(400, 400));
// this is important to elements can discover their connection
// by walking their parent chain
m_rootElement->setParent(m_connection);
connect(m_rootElement.get(), &FGCanvasGroup::canvasSizeChanged,
this, &CanvasDisplay::onCanvasSizeChanged);
m_rootItem = m_rootElement->createQuickItem(this);
onCanvasSizeChanged();
}
}
void CanvasDisplay::onConnectionUpdated()
{
m_rootElement->polish();
update();
if (m_rootElement) {
m_rootElement->polish();
update();
}
}
void CanvasDisplay::onCanvasSizeChanged()
{
m_sourceSize = QSizeF(m_connection->propertyRoot()->value("size", 400).toDouble(),
m_connection->propertyRoot()->value("size[1]", 400).toDouble());
recomputeScaling();
}
void CanvasDisplay::recomputeScaling()
{
double xScaleFactor = width() / m_sourceSize.width();
double yScaleFactor = height() / m_sourceSize.height();
double finalScaleFactor = std::min(xScaleFactor, yScaleFactor);
setScale(finalScaleFactor);
}

View file

@ -23,7 +23,8 @@
#include <QQuickItem>
class CanvasConnection;
class FGCanvasElement;
class FGCanvasGroup;
class QQuickItem;
class CanvasDisplay : public QQuickItem
{
@ -51,14 +52,23 @@ void setCanvas(CanvasConnection* canvas);
protected:
void updatePolish() override;
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
private slots:
void onConnectionStatusChanged();
void onConnectionUpdated();
void onCanvasSizeChanged();
void onConnectionDestroyed();
private:
void recomputeScaling();
CanvasConnection* m_connection = nullptr;
std::unique_ptr<FGCanvasElement> m_rootElement;
std::unique_ptr<FGCanvasGroup> m_rootElement;
QQuickItem* m_rootItem = nullptr;
QSizeF m_sourceSize;
};
#endif // CANVASDISPLAY_H

View file

@ -21,6 +21,7 @@
#include "fgcanvaspaintcontext.h"
#include "fgcanvasgroup.h"
#include "canvasitem.h"
#include "canvasconnection.h"
#include <QDebug>
#include <QPainter>
@ -85,7 +86,10 @@ FGCanvasElement::FGCanvasElement(FGCanvasGroup* pr, LocalProp* prop) :
_parent(pr)
{
connect(prop->getOrCreateWithPath("visible"), &LocalProp::valueChanged,
[this](QVariant val) { _visible = val.toBool(); });
[this](QVariant val) {
_visible = val.toBool();
requestPolish();
});
connect(prop, &LocalProp::childAdded, this, &FGCanvasElement::onChildAdded);
connect(prop, &LocalProp::childRemoved, this, &FGCanvasElement::onChildRemoved);
@ -104,7 +108,13 @@ void FGCanvasElement::requestPolish()
void FGCanvasElement::polish()
{
if (!isVisible()) {
bool vis = isVisible();
auto qq = quickItem();
if (qq && (qq->isVisible() != vis)) {
qq->setVisible(vis);
}
if (!vis) {
return;
}
@ -113,8 +123,8 @@ void FGCanvasElement::polish()
_clipDirty = false;
}
if (quickItem()) {
quickItem()->setTransform(combinedTransform());
if (qq) {
qq->setTransform(combinedTransform());
}
if (_styleDirty) {
@ -218,6 +228,13 @@ const FGCanvasGroup *FGCanvasElement::parentGroup() const
return _parent;
}
CanvasConnection *FGCanvasElement::connection() const
{
if (_parent)
return _parent->connection();
return qobject_cast<CanvasConnection*>(parent());
}
bool FGCanvasElement::onChildAdded(LocalProp *prop)
{
const QByteArray nm = prop->name();

View file

@ -30,6 +30,7 @@ class FGCanvasPaintContext;
class FGCanvasGroup;
class CanvasItem;
class QQuickItem;
class CanvasConnection;
class FGCanvasElement : public QObject
{
@ -47,6 +48,8 @@ public:
const FGCanvasGroup* parentGroup() const;
CanvasConnection* connection() const;
static bool isStyleProperty(QByteArray name);
LocalProp* property() const;

View file

@ -67,7 +67,6 @@ unsigned int FGCanvasGroup::indexOfChild(const FGCanvasElement *e) const
CanvasItem *FGCanvasGroup::createQuickItem(QQuickItem *parent)
{
qDebug() << Q_FUNC_INFO;
_quick = new CanvasItem(parent);
for (auto e : _children) {
@ -135,7 +134,12 @@ bool FGCanvasGroup::onChildAdded(LocalProp *prop)
if (isRootGroup) {
// ignore all of these, handled by the enclosing canvas view
if ((nm == "view") || (nm == "size") || nm.startsWith("status") || (nm == "name") || (nm == "mipmapping") || (nm == "placement")) {
if (nm == "size") {
connect(prop, &LocalProp::valueChanged, this, &FGCanvasGroup::canvasSizeChanged);
return true;
}
if ((nm == "view") || nm.startsWith("status") || (nm == "name") || (nm == "mipmapping") || (nm == "placement")) {
return true;
}
}

View file

@ -29,6 +29,7 @@ signals:
void childAdded();
void childRemoved(int index);
void canvasSizeChanged();
protected:
virtual void doPaint(FGCanvasPaintContext* context) const override;

View file

@ -49,7 +49,6 @@ public:
: CanvasItem(parent)
{
setFlag(ItemHasContents);
qDebug() << Q_FUNC_INFO;
}
void setPath(QPainterPath pp)

View file

@ -26,6 +26,7 @@
#include "localprop.h"
#include "fgqcanvasfontcache.h"
#include "canvasitem.h"
#include "canvasconnection.h"
static QQmlComponent* static_textComponent = nullptr;
@ -33,10 +34,6 @@ FGCanvasText::FGCanvasText(FGCanvasGroup* pr, LocalProp* prop) :
FGCanvasElement(pr, prop),
_metrics(QFont())
{
// this signal fires infrequently enough, it's simpler just to have
// all texts watch it.
connect(FGQCanvasFontCache::instance(), &FGQCanvasFontCache::fontLoaded,
this, &FGCanvasText::onFontLoaded);
}
CanvasItem *FGCanvasText::createQuickItem(QQuickItem *parent)
@ -191,6 +188,8 @@ void FGCanvasText::onFontLoaded(QByteArray name)
return; // not our font
}
auto fontCache = connection()->fontCache();
disconnect(fontCache, &FGQCanvasFontCache::fontLoaded, this, &FGCanvasText::onFontLoaded);
markFontDirty();
}
@ -198,9 +197,12 @@ void FGCanvasText::rebuildFont() const
{
QByteArray fontName = getCascadedStyle("font", QString()).toByteArray();
bool ok;
QFont f = FGQCanvasFontCache::instance()->fontForName(fontName, &ok);
auto fontCache = connection()->fontCache();
QFont f = fontCache->fontForName(fontName, &ok);
if (!ok) {
// wait for the correct font
connect(fontCache, &FGQCanvasFontCache::fontLoaded, this, &FGCanvasText::onFontLoaded);
return;
}
const int pixelSize = getCascadedStyle("character-size", 16).toInt();

View file

@ -55,20 +55,6 @@ QFont FGQCanvasFontCache::fontForName(QByteArray name, bool* ok)
return QFont(); // default font
}
static FGQCanvasFontCache* s_instance = nullptr;
void FGQCanvasFontCache::initialise(QNetworkAccessManager *nam)
{
Q_ASSERT(s_instance == nullptr);
s_instance = new FGQCanvasFontCache(nam);
}
FGQCanvasFontCache *FGQCanvasFontCache::instance()
{
return s_instance;
}
void FGQCanvasFontCache::setHost(QString hostName, int portNumber)
{
m_hostName = hostName;

View file

@ -34,9 +34,6 @@ public:
QFont fontForName(QByteArray name, bool* ok = nullptr);
static void initialise(QNetworkAccessManager* nam);
static FGQCanvasFontCache* instance();
void setHost(QString hostName, int portNumber);
signals:
void fontLoaded(QByteArray name);

View file

@ -25,6 +25,7 @@
#include "localprop.h"
#include "fgqcanvasimageloader.h"
#include "canvasitem.h"
#include "canvasconnection.h"
static QQmlComponent* static_imageComponent = nullptr;
@ -37,10 +38,12 @@ FGQCanvasImage::FGQCanvasImage(FGCanvasGroup* pr, LocalProp* prop) :
void FGQCanvasImage::setEngine(QQmlEngine *engine)
{
static_imageComponent = new QQmlComponent(engine, QUrl("image.qml"));
qDebug() << static_imageComponent->errorString();
if (!static_imageComponent || !static_imageComponent->errors().empty()) {
qWarning() << static_imageComponent->errorString();
}
}
void FGQCanvasImage::doPaint(FGCanvasPaintContext *context) const
void FGQCanvasImage::doPolish()
{
if (_imageDirty) {
rebuildImage();
@ -50,7 +53,10 @@ void FGQCanvasImage::doPaint(FGCanvasPaintContext *context) const
if (_sourceRectDirty) {
recomputeSourceRect();
}
}
void FGQCanvasImage::doPaint(FGCanvasPaintContext *context) const
{
QRectF dstRect(0.0, 0.0, _destSize.width(), _destSize.height());
context->painter()->drawPixmap(dstRect, _image, _sourceRect);
}
@ -75,18 +81,19 @@ bool FGQCanvasImage::onChildAdded(LocalProp *prop)
return true;
}
qDebug() << "image saw child:" << prop->name();
return false;
}
void FGQCanvasImage::markImageDirty()
{
_imageDirty = true;
requestPolish();
}
void FGQCanvasImage::markSourceDirty()
{
_sourceRectDirty = true;
requestPolish();
}
void FGQCanvasImage::recomputeSourceRect() const
@ -118,15 +125,16 @@ void FGQCanvasImage::recomputeSourceRect() const
void FGQCanvasImage::rebuildImage() const
{
QByteArray file = _propertyRoot->value("file", QByteArray()).toByteArray();
auto loader = connection()->imageLoader();
if (!file.isEmpty()) {
_image = FGQCanvasImageLoader::instance()->getImage(file);
_image = loader->getImage(file);
if (_image.isNull()) {
// get notified when the image loads
FGQCanvasImageLoader::instance()->connectToImageLoaded(file,
const_cast<FGQCanvasImage*>(this),
SLOT(markImageDirty()));
loader->connectToImageLoaded(file,
const_cast<FGQCanvasImage*>(this),
SLOT(markImageDirty()));
} else {
// loaded image ok!
}

View file

@ -39,6 +39,7 @@ protected:
virtual void markStyleDirty() override;
void doPolish() override;
private slots:
void markImageDirty();
void markSourceDirty();

View file

@ -20,8 +20,6 @@
#include <QDebug>
#include <QNetworkAccessManager>
static FGQCanvasImageLoader* static_instance = nullptr;
class TransferSignalHolder : public QObject
{
Q_OBJECT
@ -31,8 +29,9 @@ signals:
void trigger();
};
FGQCanvasImageLoader::FGQCanvasImageLoader(QNetworkAccessManager* dl)
: m_downloader(dl)
FGQCanvasImageLoader::FGQCanvasImageLoader(QNetworkAccessManager* dl, QObject* pr)
: QObject(pr)
, m_downloader(dl)
{
}
@ -58,17 +57,6 @@ void FGQCanvasImageLoader::onDownloadFinished()
reply->deleteLater();
}
FGQCanvasImageLoader *FGQCanvasImageLoader::instance()
{
return static_instance;
}
void FGQCanvasImageLoader::initialise(QNetworkAccessManager *dl)
{
Q_ASSERT(static_instance == nullptr);
static_instance = new FGQCanvasImageLoader(dl);
}
void FGQCanvasImageLoader::setHost(QString hostName, int portNumber)
{
m_hostName = hostName;

View file

@ -29,9 +29,8 @@ class FGQCanvasImageLoader : public QObject
{
Q_OBJECT
public:
static FGQCanvasImageLoader* instance();
FGQCanvasImageLoader(QNetworkAccessManager* dl, QObject* pr = nullptr);
static void initialise(QNetworkAccessManager* dl);
void setHost(QString hostName, int portNumber);
@ -49,7 +48,6 @@ signals:
private:
explicit FGQCanvasImageLoader(QNetworkAccessManager* dl);
void onDownloadFinished();

View file

@ -22,8 +22,6 @@
#include "fgcanvastext.h"
#include "fgqcanvasimage.h"
#include "fgqcanvasfontcache.h"
#include "fgqcanvasimageloader.h"
#include "canvasitem.h"
#include "applicationcontroller.h"
#include "canvasdisplay.h"
@ -40,9 +38,6 @@ int main(int argc, char *argv[])
ApplicationController appController;
// load saved history on the app controller?
FGQCanvasFontCache::initialise(appController.netAccess());
FGQCanvasImageLoader::initialise(appController.netAccess());
qmlRegisterType<CanvasItem>("FlightGear", 1, 0, "CanvasItem");
qmlRegisterType<CanvasDisplay>("FlightGear", 1, 0, "CanvasDisplay");
qmlRegisterUncreatableType<CanvasConnection>("FlightGear", 1, 0, "CanvasConnection", "Don't create me");

View file

@ -61,6 +61,31 @@ Item {
}
}
Button {
id: cancelButton
label: "Cancel"
anchors.right: parent.right
visible: (_application.status == FG.Application.Querying)
onClicked: {
_application.cancelQuery();
}
}
Button {
id: clearlButton
label: "Clear"
anchors.right: parent.right
visible: (_application.status == FG.Application.SuccessfulQuery) |
(_application.status == FG.Application.QueryFailed)
onClicked: {
_application.clearQuery();
}
}
}
}
@ -76,6 +101,7 @@ Item {
width: 300
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
visible: _application.canvases.length > 0
ListView {
id: canvasList

View file

@ -8,11 +8,15 @@ Item {
readonly property var centerPoint: Qt.point(width / 2, height / 2)
clip: true;
Component.onCompleted: {
width = canvas.size.width
height = canvas.size.height
x = canvas.center.x - (width / 2)
y = canvas.center.y - (height / 2)
if (canvas) {
width = canvas.size.width
height = canvas.size.height
x = canvas.center.x - (width / 2)
y = canvas.center.y - (height / 2)
}
}
function saveGeometry()
@ -26,11 +30,13 @@ Item {
anchors.fill: parent
onCanvasChanged: {
root.width = canvas.size.width
root.height = canvas.size.height
if (canvas) {
root.width = canvas.size.width
root.height = canvas.size.height
root.x = canvas.center.x - (root.width / 2)
root.y = canvas.center.y - (root.height / 2)
root.x = canvas.center.x - (root.width / 2)
root.y = canvas.center.y - (root.height / 2)
}
}
}
@ -39,8 +45,8 @@ Item {
border.color: "orange"
color: "transparent"
anchors.centerIn: parent
width: parent.width + 2
height: parent.height + 2
width: parent.width
height: parent.height
MouseArea {
anchors.fill: parent
@ -110,7 +116,4 @@ Item {
}
}
}

View file

@ -60,13 +60,43 @@ Item {
width: parent.width
height:delegateFrame.height + 8
Rectangle {
id: delegateBackFrame
color: "#1f1f1f"
width: delegateFrame.width
height: delegateFrame.height
Button {
id: deleteButton
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 8
label: "Delete"
onClicked: {
}
}
Button {
anchors.verticalCenter: parent.verticalCenter
anchors.right: deleteButton.left
anchors.rightMargin: 8
label: "Save"
onClicked: {
}
}
}
Rectangle {
id: delegateFrame
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
// anchors.horizontalCenter: parent.horizontalCenter
height: configLabel.implicitHeight + 20
opacity: 1.0
color: "#3f3f3f"
Text {
@ -83,19 +113,14 @@ Item {
onClicked: {
_application.restoreConfig(model.index)
}
drag.target: delegateFrame
drag.axis: Drag.XAxis
drag.minimumX: -delegateFrame.width
drag.maximumX: 0
}
Button {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 8
label: "X"
width: height
onClicked: {
}
}
} // of visible rect
} // of delegate item