diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml index b261743434..2ad2b75553 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml @@ -66,7 +66,7 @@ Item { source: "image://security/securityImage"; cache: false; onVisibleChanged: { - commerce.getSecurityImage(); + Commerce.getSecurityImage(); } } Item { @@ -194,7 +194,7 @@ Item { securityImageSubmitButton.text = "Submitting..."; securityImageSubmitButton.enabled = false; var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex()) - commerce.chooseSecurityImage(securityImagePath); + Commerce.chooseSecurityImage(securityImagePath); } } } diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index 1508367318..4d443fb97c 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -23,11 +23,26 @@ Item { property double sortOrder: 100 property int stableOrder: 0 property var tabletRoot; + property var flickable: null + property var gridView: null + + property int buttonIndex: -1 + width: 129 height: 129 signal clicked() + Connections { + target: flickable + onMovingChanged: { + //when flick/move started, and hover is on, clean hove state + if (flickable.moving && tabletButton.state.indexOf("hover") !== -1) { + tabletButton.state = (tabletButton.isActive) ? "active state" : "base state"; + } + } + } + function changeProperty(key, value) { tabletButton[key] = value; } @@ -121,8 +136,10 @@ Item { anchors.fill: parent hoverEnabled: true enabled: true - preventStealing: true + preventStealing: false onClicked: { + gridView.currentIndex = buttonIndex + if (tabletButton.inDebugMode) { if (tabletButton.isActive) { tabletButton.isActive = false; @@ -130,12 +147,15 @@ Item { tabletButton.isActive = true; } } + tabletButton.clicked(); if (tabletRoot) { Tablet.playSound(TabletEnums.ButtonClick); } } + onEntered: { + gridView.currentIndex = buttonIndex tabletButton.isEntered = true; Tablet.playSound(TabletEnums.ButtonHover); diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 3f9451436c..e934f18ab6 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -1,7 +1,10 @@ -import QtQuick 2.5 +import QtQuick 2.7 +import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import QtQuick.Layouts 1.3 +import TabletScriptingInterface 1.0 + import "." import "../../styles-uit" import "../audio" as HifiAudio @@ -10,7 +13,11 @@ Item { id: tablet objectName: "tablet" property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system"); - + + property var currentGridItems: null + + focus: true + Rectangle { id: bgTopBar height: 90 @@ -85,7 +92,6 @@ Item { Rectangle { id: bgMain - clip: true gradient: Gradient { GradientStop { position: 0 @@ -102,55 +108,186 @@ Item { anchors.left: parent.left anchors.top: bgTopBar.bottom - GridView { - id: flickable - anchors.top: parent.top - anchors.topMargin: 15 - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - width: cellWidth * 3 - cellHeight: 145 - cellWidth: 145 - model: tabletProxy.buttons - delegate: Item { - width: flickable.cellWidth - height: flickable.cellHeight - property var proxy: modelData - - TabletButton { - id: tabletButton - anchors.centerIn: parent - onClicked: modelData.clicked() - state: wrapper.GridView.isCurrentItem ? "hover state" : "base state" + SwipeView { + id: swipeView + clip: false + currentIndex: -1 + property int previousIndex: -1 + Repeater { + id: pageRepeater + model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) + onItemAdded: { + item.proxyModel.sourceModel = tabletProxy.buttons; + item.proxyModel.pageIndex = index; } - Connections { - target: modelData; - onPropertiesChanged: { - updateProperties(); + delegate: Item { + id: page + property TabletButtonsProxyModel proxyModel: TabletButtonsProxyModel {} + + GridView { + id: gridView + keyNavigationEnabled: false + highlightFollowsCurrentItem: false + property int previousGridIndex: -1 + anchors { + fill: parent + topMargin: 20 + leftMargin: 30 + rightMargin: 30 + bottomMargin: 0 + } + + function setButtonState(buttonIndex, buttonstate) { + if (buttonIndex < 0 || gridView.contentItem === undefined + || gridView.contentItem.children.length - 1 < buttonIndex) { + return; + } + var itemat = gridView.contentItem.children[buttonIndex].children[0]; + if (itemat.isActive) { + itemat.state = "active state"; + } else { + itemat.state = buttonstate; + } + } + + onCurrentIndexChanged: { + setButtonState(previousGridIndex, "base state"); + setButtonState(currentIndex, "hover state"); + previousGridIndex = currentIndex + } + + cellWidth: width/3 + cellHeight: cellWidth + flow: GridView.LeftToRight + model: page.proxyModel + + delegate: Item { + id: wrapper + width: gridView.cellWidth + height: gridView.cellHeight + + property var proxy: modelData + + TabletButton { + id: tabletButton + anchors.centerIn: parent + gridView: wrapper.GridView.view + buttonIndex: page.proxyModel.buttonIndex(uuid); + flickable: swipeView.contentItem; + onClicked: modelData.clicked() + } + + Connections { + target: modelData; + onPropertiesChanged: { + updateProperties(); + } + } + + Component.onCompleted: updateProperties() + + function updateProperties() { + var keys = Object.keys(modelData.properties).forEach(function (key) { + if (tabletButton[key] !== modelData.properties[key]) { + tabletButton[key] = modelData.properties[key]; + } + }); + } + } } } + } - Component.onCompleted: updateProperties() - - function updateProperties() { - var keys = Object.keys(modelData.properties).forEach(function (key) { - if (tabletButton[key] !== modelData.properties[key]) { - tabletButton[key] = modelData.properties[key]; - } - }); + onCurrentIndexChanged: { + if (swipeView.currentIndex < 0 + || swipeView.itemAt(swipeView.currentIndex) === null + || swipeView.itemAt(swipeView.currentIndex).children[0] === null) { + return; } + + currentGridItems = swipeView.itemAt(swipeView.currentIndex).children[0]; + + currentGridItems.currentIndex = (previousIndex > swipeView.currentIndex ? currentGridItems.count - 1 : 0); + previousIndex = currentIndex; + } + + hoverEnabled: true + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: pageIndicator.top + } + } + + PageIndicator { + id: pageIndicator + currentIndex: swipeView.currentIndex + + delegate: Item { + width: 15 + height: 15 + + Rectangle { + anchors.centerIn: parent + opacity: index === pageIndicator.currentIndex ? 0.95 : 0.45 + implicitWidth: index === pageIndicator.currentIndex ? 15 : 10 + implicitHeight: implicitWidth + radius: width/2 + color: "white" + Behavior on opacity { + OpacityAnimator { + duration: 100 + } + } + } + } + + interactive: false + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + count: swipeView.count + } + } + + Component.onCompleted: { + focus = true; + forceActiveFocus(); + } + + Keys.onRightPressed: { + if (!currentGridItems) { + return; + } + + var index = currentGridItems.currentIndex; + currentGridItems.moveCurrentIndexRight(); + if (index === currentGridItems.count - 1 && index === currentGridItems.currentIndex) { + if (swipeView.currentIndex < swipeView.count - 1) { + swipeView.incrementCurrentIndex(); } } } - Keys.onRightPressed: flickable.moveCurrentIndexRight(); - Keys.onLeftPressed: flickable.moveCurrentIndexLeft(); - Keys.onDownPressed: flickable.moveCurrentIndexDown(); - Keys.onUpPressed: flickable.moveCurrentIndexUp(); + Keys.onLeftPressed: { + if (!currentGridItems) { + return; + } + + var index = currentGridItems.currentIndex; + currentGridItems.moveCurrentIndexLeft(); + if (index === 0 && index === currentGridItems.currentIndex) { + if (swipeView.currentIndex > 0) { + swipeView.decrementCurrentIndex(); + } + } + } + Keys.onDownPressed: currentGridItems.moveCurrentIndexDown(); + Keys.onUpPressed: currentGridItems.moveCurrentIndexUp(); Keys.onReturnPressed: { - if (flickable.currentItem) { - flickable.currentItem.proxy.clicked(); + if (currentGridItems.currentItem) { + currentGridItems.currentItem.proxy.clicked(); if (tabletRoot) { tabletRoot.playButtonClickSound(); } diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 6922ca87b7..84a6fe1da4 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -212,8 +212,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { * parentID set, otherwise the same value as rotation. * @property {boolean} isSolid=false - Synonyms: solid, isFilled, * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, @@ -241,7 +239,7 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "localRotation" || property == "localOrientation") { return quatToVariant(getLocalOrientation()); } - if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled" || property == "filed") { + if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled") { return _isSolid; } if (property == "isWire" || property == "wire") { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 0e793fef21..c69ec1ce84 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -92,8 +92,48 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) { endResetModel(); } +TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) { +} + +int TabletButtonsProxyModel::pageIndex() const { + return _pageIndex; +} + +int TabletButtonsProxyModel::buttonIndex(const QString &uuid) { + if (!sourceModel() || _pageIndex < 0) { + return -1; + } + TabletButtonListModel* model = static_cast(sourceModel()); + for (int i = 0; i < model->rowCount(); i++) { + TabletButtonProxy* bproxy = model->data(model->index(i), ButtonProxyRole).value(); + if (bproxy && bproxy->getUuid().toString().contains(uuid)) { + return i - (_pageIndex*TabletScriptingInterface::ButtonsOnPage); + } + } + return -1; +} + +void TabletButtonsProxyModel::setPageIndex(int pageIndex) +{ + if (_pageIndex == pageIndex) + return; + + _pageIndex = pageIndex; + invalidateFilter(); + emit pageIndexChanged(_pageIndex); +} + +bool TabletButtonsProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const { + Q_UNUSED(sourceParent); + return (sourceRow >= _pageIndex*TabletScriptingInterface::ButtonsOnPage + && sourceRow < (_pageIndex + 1)*TabletScriptingInterface::ButtonsOnPage); +} + TabletScriptingInterface::TabletScriptingInterface() { qmlRegisterType("TabletScriptingInterface", 1, 0, "TabletEnums"); + qmlRegisterType("TabletScriptingInterface", 1, 0, "TabletButtonsProxyModel"); } TabletScriptingInterface::~TabletScriptingInterface() { @@ -769,6 +809,7 @@ void TabletProxy::sendToQml(const QVariant& msg) { } + OffscreenQmlSurface* TabletProxy::getTabletSurface() { return _qmlOffscreenSurface; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index d24b3b6947..56e3ae257b 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,10 @@ public: enum TabletAudioEvents { ButtonClick, ButtonHover, TabletOpen, TabletHandsIn, TabletHandsOut, Last}; Q_ENUM(TabletAudioEvents) + //Different useful constants + enum TabletConstants { ButtonsColumnsOnPage = 3, ButtonsRowsOnPage = 4, ButtonsOnPage = 12 }; + Q_ENUM(TabletConstants) + TabletScriptingInterface(); virtual ~TabletScriptingInterface(); static const QString QML; @@ -118,6 +123,31 @@ protected: Q_DECLARE_METATYPE(TabletButtonListModel*); +class TabletButtonsProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + + Q_PROPERTY(int pageIndex READ pageIndex WRITE setPageIndex NOTIFY pageIndexChanged) +public: + TabletButtonsProxyModel(QObject* parent = 0); + int pageIndex() const; + Q_INVOKABLE int buttonIndex(const QString& uuid); + +public slots: + void setPageIndex(int pageIndex); + +signals: + void pageIndexChanged(int pageIndex); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +private: + int _pageIndex { -1 }; +}; + +Q_DECLARE_METATYPE(TabletButtonsProxyModel*); + /**jsdoc * @class TabletProxy * @property name {string} READ_ONLY: name of this tablet @@ -234,6 +264,7 @@ public: QQuickItem* getQmlMenu() const; TabletButtonListModel* getButtons() { return &_buttons; } + signals: /**jsdoc * Signaled when this tablet receives an event from the html/js embedded in the tablet