From 5d1ccfbccc2ecf2a69f284382812fac2e85e7eef Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Dec 2016 15:49:37 -0800 Subject: [PATCH 1/4] Tablet#removeButton now works properly --- .../resources/qml/hifi/tablet/Tablet.qml | 36 ++++++++++----- .../qml/hifi/tablet/TabletButton.qml | 1 + .../src/RenderableWebEntityItem.cpp | 7 +++ .../src/TabletScriptingInterface.cpp | 45 ++++++++----------- .../src/TabletScriptingInterface.h | 18 ++++---- 5 files changed, 61 insertions(+), 46 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 4ea1891737..05952c82f6 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -10,25 +10,41 @@ Item { width: 480 height: 720 + // used to look up a button by its uuid + function findButtonIndex(uuid) { + if (!uuid) { + return -1; + } + + for (var i in flowMain.children) { + var child = flowMain.children[i]; + if (child.uuid === uuid) { + return i; + } + } + return -1; + } + // called by C++ code when a button should be added to the tablet function addButtonProxy(properties) { var component = Qt.createComponent("TabletButton.qml"); var button = component.createObject(flowMain); - if (properties.icon) { - button.icon = properties.icon; - } - if (properties.color) { - button.color = properties.color; - } - if (properties.text) { - button.text = properties.text; - } + + // copy all properites to button + var keys = Object.keys(properties).forEach(function (key) { + button[key] = properties[key]; + }); + return button; } // called by C++ code when a button should be removed from the tablet function removeButtonProxy(properties) { - console.log("TABLET_UI_HACK: removeButtonProxy, NOT IMPLEMENTED!, properties = " + JSON.stringify(properties)); + var index = findButtonIndex(properties.uuid); + if (index < 0) { + console.log("Warning: Tablet.qml could not find button with uuid = " + properties.uuid); + } + flowMain.children[index].destroy(); } Rectangle { diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 92a7ee2865..bcd693a0a3 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -6,6 +6,7 @@ Item { property string color: "#1080B8" property string text: "EDIT" property string icon: "icons/edit-icon.svg" + property var uuid; width: 132 height: 132 diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index cba6842ba9..a29d2c0b8e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -361,6 +361,13 @@ void RenderableWebEntityItem::destroyWebSurface() { --_currentWebCount; QQuickItem* rootItem = _webSurface->getRootItem(); + + // TABLET_UI_HACK: move this to overlays as well! + if (rootItem && rootItem->objectName() == "tablet") { + auto tabletScriptingInterface = DependencyManager::get(); + tabletScriptingInterface->setQmlTablet("com.highfidelity.interface.tablet.system", nullptr); + } + if (rootItem) { QObject* obj = rootItem->findChild("webEngineView"); if (obj) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 1bcdfc6013..032d8ce671 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -12,7 +12,7 @@ QObject* TabletScriptingInterface::getTablet(const QString& tabletId) { - std::lock_guard guard(_tabletProxiesMutex); + std::lock_guard guard(_mutex); // look up tabletId in the map. auto iter = _tabletProxies.find(tabletId); @@ -36,7 +36,6 @@ void TabletScriptingInterface::setQmlTablet(QString tabletId, QQuickItem* qmlTab } } - // // TabletProxy // @@ -64,24 +63,27 @@ static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* return; } QObject::connect(qmlButton, SIGNAL(clicked()), buttonProxy, SLOT(clickedSlot())); + buttonProxy->setQmlButton(qobject_cast(qmlButton)); } void TabletProxy::setQmlTablet(QQuickItem* qmlTablet) { + std::lock_guard guard(_mutex); if (qmlTablet) { _qmlTablet = qmlTablet; - std::lock_guard guard(_tabletButtonProxiesMutex); for (auto& buttonProxy : _tabletButtonProxies) { addButtonProxyToQmlTablet(_qmlTablet, buttonProxy.data()); } } else { + for (auto& buttonProxy : _tabletButtonProxies) { + buttonProxy->setQmlButton(nullptr); + } _qmlTablet = nullptr; } - } QObject* TabletProxy::addButton(const QVariant& properties) { auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); - std::lock_guard guard(_tabletButtonProxiesMutex); + std::lock_guard guard(_mutex); _tabletButtonProxies.push_back(tabletButtonProxy); if (_qmlTablet) { addButtonProxyToQmlTablet(_qmlTablet, tabletButtonProxy.data()); @@ -90,11 +92,12 @@ QObject* TabletProxy::addButton(const QVariant& properties) { } void TabletProxy::removeButton(QObject* tabletButtonProxy) { - std::lock_guard guard(_tabletButtonProxiesMutex); + std::lock_guard guard(_mutex); auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy); if (iter != _tabletButtonProxies.end()) { if (_qmlTablet) { - QMetaObject::invokeMethod(_qmlTablet, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties())); + (*iter)->setQmlButton(nullptr); + QMetaObject::invokeMethod(_qmlTablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, (*iter)->getProperties())); } _tabletButtonProxies.erase(iter); } else { @@ -106,28 +109,18 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { // TabletButtonProxy // -TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : _properties(properties) { - ; +const QString UUID_KEY = "uuid"; + +TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : _uuid(QUuid::createUuid()), _properties(properties) { + // this is used to uniquely identify this button. + _properties[UUID_KEY] = _uuid; } -void TabletButtonProxy::setInitRequestHandler(const QScriptValue& handler) { - _initRequestHandler = handler; +void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { + std::lock_guard guard(_mutex); + _qmlButton = qmlButton; } -// TABLET_UI_HACK remove -/* -static QString IMAGE_URL_KEY = "imageUrl"; -static QString IMAGE_URL_DEFAULT = ""; - -QString TabletButtonProxy::getImageUrl() const { - std::lock_guard guard(_propertiesMutex); - return _properties.value(IMAGE_URL_KEY, IMAGE_URL_DEFAULT).toString(); -} - -void TabletButtonProxy::setImageUrl(QString imageUrl) { - std::lock_guard guard(_propertiesMutex); - _properties[IMAGE_URL_KEY] = imageUrl; -} -*/ +// TABLET_UI_HACK TODO: add property accessors, and forward property changes to the _qmlButton if present. #include "TabletScriptingInterface.moc" diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 13d7219306..262a496f1a 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -11,10 +11,11 @@ #include -#include +#include #include #include #include +#include #include @@ -39,7 +40,7 @@ public: void setQmlTablet(QString tabletId, QQuickItem* qmlTablet); protected: - std::mutex _tabletProxiesMutex; + std::mutex _mutex; std::map> _tabletProxies; }; @@ -73,7 +74,7 @@ public: QString getName() const { return _name; } protected: QString _name; - std::mutex _tabletButtonProxiesMutex; + std::mutex _mutex; std::vector> _tabletButtonProxies; QQuickItem* _qmlTablet { nullptr }; }; @@ -87,11 +88,7 @@ class TabletButtonProxy : public QObject { public: TabletButtonProxy(const QVariantMap& properties); - /**jsdoc - * @function TabletButtonProxy#setInitRequestHandler - * @param handler {Function} A function used by the system to request the current button state from JavaScript. - */ - Q_INVOKABLE void setInitRequestHandler(const QScriptValue& handler); + void setQmlButton(QQuickItem* qmlButton); const QVariantMap& getProperties() const { return _properties; } @@ -107,9 +104,10 @@ signals: void clicked(); protected: - mutable std::mutex _propertiesMutex; + QUuid _uuid; + mutable std::mutex _mutex; + QQuickItem* _qmlButton { nullptr }; QVariantMap _properties; - QScriptValue _initRequestHandler; }; #endif // hifi_TabletScriptingInterface_h From 0add5274acde04d5654c6bb11b44e543ff8defd9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Dec 2016 16:50:20 -0800 Subject: [PATCH 2/4] Add tabletTest.js to test adding and removing buttons from tablet UI --- scripts/developer/tests/tabletTest.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 scripts/developer/tests/tabletTest.js diff --git a/scripts/developer/tests/tabletTest.js b/scripts/developer/tests/tabletTest.js new file mode 100644 index 0000000000..6f101beb8b --- /dev/null +++ b/scripts/developer/tests/tabletTest.js @@ -0,0 +1,26 @@ +// +// tabletTest.js +// +// Created by Anthony J. Thibault on 2016-12-15 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Adds a BAM! button to the tablet ui. + +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); +var button = tablet.addButton({ + icon: "https://s3.amazonaws.com/hifi-public/tony/icons/hat-up.svg", + color: "#ff6f6f", + text: "BAM!!!" +}); + +button.clicked.connect(function () { + print("AJT: BAMM!!! CLICK from JS!"); +}); + +Script.scriptEnding.connect(function () { + tablet.removeButton(button); +}); From 5441d33a25497dc6bb88cc0d4d25d5f70cc6d061 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Dec 2016 18:17:14 -0800 Subject: [PATCH 3/4] Support for the ablity to change button properties from js. --- .../resources/qml/hifi/tablet/TabletButton.qml | 4 ++++ .../src/TabletScriptingInterface.cpp | 17 ++++++++++++++++- .../src/TabletScriptingInterface.h | 14 +++++++++++++- scripts/developer/tests/tabletTest.js | 12 ++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index bcd693a0a3..3c12130d2e 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -12,6 +12,10 @@ Item { signal clicked() + function changeProperty(key, value) { + tabletButton[key] = value; + } + Rectangle { id: buttonBg color: tabletButton.color diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 032d8ce671..d77c60e9e7 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -121,6 +121,21 @@ void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { _qmlButton = qmlButton; } -// TABLET_UI_HACK TODO: add property accessors, and forward property changes to the _qmlButton if present. +QVariantMap TabletButtonProxy::getProperties() const { + std::lock_guard guard(_mutex); + return _properties; +} + +void TabletButtonProxy::editProperties(QVariantMap properties) { + std::lock_guard guard(_mutex); + QVariantMap::const_iterator iter = properties.constBegin(); + while (iter != properties.constEnd()) { + _properties[iter.key()] = iter.value(); + if (_qmlButton) { + QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value())); + } + ++iter; + } +} #include "TabletScriptingInterface.moc" diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 262a496f1a..da796be174 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -90,7 +90,19 @@ public: void setQmlButton(QQuickItem* qmlButton); - const QVariantMap& getProperties() const { return _properties; } + /**jsdoc + * Returns the current value of this button's properties + * @function TabletButtonProxy#getProperties + * @returns {object} + */ + Q_INVOKABLE QVariantMap getProperties() const; + + /**jsdoc + * Replace the values of some of this button's properties + * @function TabletButtonProxy#editProperties + * @param properties {object} set of properties to change + */ + Q_INVOKABLE void editProperties(QVariantMap properties); public slots: void clickedSlot() { emit clicked(); } diff --git a/scripts/developer/tests/tabletTest.js b/scripts/developer/tests/tabletTest.js index 6f101beb8b..8840c249bb 100644 --- a/scripts/developer/tests/tabletTest.js +++ b/scripts/developer/tests/tabletTest.js @@ -17,6 +17,18 @@ var button = tablet.addButton({ text: "BAM!!!" }); +// change the color and name every second... +var colors = ["#ff6f6f", "#6fff6f", "#6f6fff"]; +var names = ["BAM!", "BAM!!", "BAM!!!"]; +var colorIndex = 0; +Script.setInterval(function () { + colorIndex = (colorIndex + 1) % colors.length; + button.editProperties({ + color: colors[colorIndex], + text: names[colorIndex] + }); +}, 1000); + button.clicked.connect(function () { print("AJT: BAMM!!! CLICK from JS!"); }); From 3610beb2cd66154f686a23fb44a570698f8a9848 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Dec 2016 09:57:12 -0800 Subject: [PATCH 4/4] bug-fix found in code review --- interface/resources/qml/hifi/tablet/Tablet.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 05952c82f6..7dedecc303 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -43,8 +43,9 @@ Item { var index = findButtonIndex(properties.uuid); if (index < 0) { console.log("Warning: Tablet.qml could not find button with uuid = " + properties.uuid); + } else { + flowMain.children[index].destroy(); } - flowMain.children[index].destroy(); } Rectangle {