From 73909837218f17d78b37f94d294b99c2a11bbf49 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 3 Jul 2018 16:49:49 +0200 Subject: [PATCH] introduce scriptable presentationMode instead of automatic mode switching windows --- interface/resources/qml/InteractiveWindow.qml | 191 ++++++++---------- interface/src/Application.cpp | 3 +- .../scripting/DesktopScriptingInterface.cpp | 8 + .../src/scripting/DesktopScriptingInterface.h | 23 ++- libraries/ui/src/InteractiveWindow.cpp | 56 ++++- libraries/ui/src/InteractiveWindow.h | 77 +++++-- 6 files changed, 219 insertions(+), 139 deletions(-) diff --git a/interface/resources/qml/InteractiveWindow.qml b/interface/resources/qml/InteractiveWindow.qml index 92c60c653b..d1e9284101 100644 --- a/interface/resources/qml/InteractiveWindow.qml +++ b/interface/resources/qml/InteractiveWindow.qml @@ -9,7 +9,6 @@ // import QtQuick 2.3 -import InteractiveWindowFlags 1.0 import "windows" as Windows import "controls" @@ -47,33 +46,9 @@ Windows.Window { property bool keyboardRaised: false; property bool punctuationMode: false; - readonly property int modeNotSet: 0; - readonly property int modeNative: 1; - readonly property int modeVirtual: 2; - - property int windowMode: modeNotSet; - - property bool forceNative: false; - property bool forceVirtual: false; - - property string windowModeText: getModeString(); - - function getModeString() { - switch (windowMode) { - case modeNotSet: - return "none"; - case modeNative: - return "native"; - case modeVirtual: - return "virtual"; - } - return "unknown"; - } - - onWindowModeChanged: { - windowModeText = getModeString(); - } + property int presentationMode: 0; + property var initialized: false; onSourceChanged: { if (dynamicContent) { dynamicContent.destroy(); @@ -88,117 +63,108 @@ Windows.Window { } function updateInteractiveWindowPositionForMode() { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { x = interactiveWindowPosition.x; y = interactiveWindowPosition.y; - } else if (windowMode === modeVirtual && nativeWindow) { + } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.x = interactiveWindowPosition.x; nativeWindow.y = interactiveWindowPosition.y; } } function updateInteractiveWindowSizeForMode() { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { width = interactiveWindowSize.width; height = interactiveWindowSize.height; - } else if (windowMode === modeVirtual && nativeWindow) { + } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.width = interactiveWindowSize.width; - nativeWindow.height = interactiveWindowSize.heigth; + nativeWindow.height = interactiveWindowSize.height; } } - function trySwitchWindowMode() { - if (windowMode !== modeVirtual && (HMD.active || (forceVirtual && !forceNative))) { - windowMode = modeVirtual; + function setupPresentationMode() { + console.warn(presentationMode); + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { if (nativeWindow) { nativeWindow.setVisible(false); } contentHolder.parent = root; updateInteractiveWindowPositionForMode(); shown = interactiveWindowVisible; - } else if (windowMode !== modeNative && (!HMD.active || (forceNative && !forceVirtual))) { - windowMode = modeNative; + } else if (presentationMode === Desktop.PresentationMode.NATIVE) { shown = false; if (nativeWindow) { contentHolder.parent = nativeWindow.contentItem; nativeWindow.setVisible(interactiveWindowVisible); updateInteractiveWindowPositionForMode(); } - } else if (windowMode === modeNotSet) { - console.error("windowMode should be set."); + } else if (presentationMode === modeNotSet) { + console.error("presentationMode should be set."); } } - - function displayModeChanged(isHMD) { - trySwitchWindowMode(); - } Component.onCompleted: { - HMD.displayModeChanged.connect(displayModeChanged); - - forceVirtual = (flags & InteractiveWindowFlags.ForceVirtual) === InteractiveWindowFlags.ForceVirtual; - forceNative = (flags & InteractiveWindowFlags.ForceNative) === InteractiveWindowFlags.ForceNative; x = interactiveWindowPosition.x; y = interactiveWindowPosition.y; width = interactiveWindowSize.width; height = interactiveWindowSize.height; - if (!forceVirtual || (forceVirtual && forceNative)) { - nativeWindow = Qt.createQmlObject(' - import QtQuick 2.3; - import QtQuick.Window 2.3; + nativeWindow = Qt.createQmlObject(' + import QtQuick 2.3; + import QtQuick.Window 2.3; - Window { - id: root; - Rectangle { - color: hifi.colors.baseGray - anchors.fill: parent - } - }', root, 'InteractiveWindow.qml->nativeWindow'); - nativeWindow.title = root.title; - var nativeWindowFlags = Qt.Window | - Qt.WindowTitleHint | - Qt.WindowSystemMenuHint | - Qt.WindowCloseButtonHint | - Qt.WindowMaximizeButtonHint | - Qt.WindowMinimizeButtonHint; - if ((flags & InteractiveWindowFlags.AlwaysOnTop) === InteractiveWindowFlags.AlwaysOnTop) { - nativeWindowFlags |= Qt.WindowStaysOnTopHint; - } - nativeWindow.flags = nativeWindowFlags; - - nativeWindow.x = interactiveWindowPosition.x; - nativeWindow.y = interactiveWindowPosition.y; - - nativeWindow.width = interactiveWindowSize.width; - nativeWindow.height = interactiveWindowSize.height; - - nativeWindow.xChanged.connect(function() { - if (windowMode === modeNative && nativeWindow.visible) { - interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y); + Window { + id: root; + Rectangle { + color: hifi.colors.baseGray + anchors.fill: parent } - }); - nativeWindow.yChanged.connect(function() { - if (windowMode === modeNative && nativeWindow.visible) { - interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y); - } - }); - - nativeWindow.widthChanged.connect(function() { - if (windowMode === modeNative && nativeWindow.visible) { - interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height); - } - }); - nativeWindow.heightChanged.connect(function() { - if (windowMode === modeNative && nativeWindow.visible) { - interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height); - } - }); + }', root, 'InteractiveWindow.qml->nativeWindow'); + nativeWindow.title = root.title; + var nativeWindowFlags = Qt.Window | + Qt.WindowTitleHint | + Qt.WindowSystemMenuHint | + Qt.WindowCloseButtonHint | + Qt.WindowMaximizeButtonHint | + Qt.WindowMinimizeButtonHint; + if ((flags & Desktop.ALWAYS_ON_TOP) === Desktop.ALWAYS_ON_TOP) { + nativeWindowFlags |= Qt.WindowStaysOnTopHint; } + nativeWindow.flags = nativeWindowFlags; + + nativeWindow.x = interactiveWindowPosition.x; + nativeWindow.y = interactiveWindowPosition.y; + + nativeWindow.width = interactiveWindowSize.width; + nativeWindow.height = interactiveWindowSize.height; + + nativeWindow.xChanged.connect(function() { + if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + interactiveWindowPosition = Qt.point(nativeWindow.x, interactiveWindowPosition.y); + } + }); + nativeWindow.yChanged.connect(function() { + if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, nativeWindow.y); + } + }); + + nativeWindow.widthChanged.connect(function() { + if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + interactiveWindowSize = Qt.size(nativeWindow.width, interactiveWindowSize.height); + } + }); + nativeWindow.heightChanged.connect(function() { + if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow.visible) { + interactiveWindowSize = Qt.size(interactiveWindowSize.width, nativeWindow.height); + } + }); // finally set the initial window mode: - trySwitchWindowMode(); + setupPresentationMode(); + + initialized = true; } // Handle message traffic from the script that launched us to the loaded QML @@ -214,9 +180,9 @@ Windows.Window { } function raiseWindow() { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { raise(); - } else if (windowMode === modeNative && nativeWindow) { + } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.raise(); } } @@ -231,9 +197,9 @@ Windows.Window { } onInteractiveWindowVisibleChanged: { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { shown = interactiveWindowVisible; - } else if (windowMode === modeNative && nativeWindow) { + } else if (presentationMode === Desktop.PresentationMode.NATIVE && nativeWindow) { nativeWindow.setVisible(interactiveWindowVisible); } } @@ -245,35 +211,42 @@ Windows.Window { } onXChanged: { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowPosition = Qt.point(x, interactiveWindowPosition.y); } } onYChanged: { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowPosition = Qt.point(interactiveWindowPosition.x, y); } } onWidthChanged: { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowSize = Qt.size(width, interactiveWindowSize.height); } } onHeightChanged: { - if (windowMode === modeVirtual) { + if (presentationMode === Desktop.PresentationMode.VIRTUAL) { interactiveWindowSize = Qt.size(interactiveWindowSize.width, height); } } - onInteractiveWindowPositionChanged: { - updateInteractiveWindowPositionForMode(); + onPresentationModeChanged: { + if (initialized) { + setupPresentationMode(); + } } - onInteractiveWindowSizeChanged: { - updateInteractiveWindowSizeForMode(); + onWindowClosed: { + // set invisible on close, to make it not re-appear unintended after switching PresentationMode + interactiveWindowVisible = false; + } + + onWindowDestroyed: { + console.warn("destroyed"); } Item { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7b2987b092..fba64e1e00 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2828,8 +2828,6 @@ void Application::initializeUi() { qmlRegisterType("Hifi", 1, 0, "Preference"); qmlRegisterType("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine"); - InteractiveWindowEnums::declareQML(); - { auto tabletScriptingInterface = DependencyManager::get(); tabletScriptingInterface->getTablet(SYSTEM_TABLET); @@ -2978,6 +2976,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Overlays", &_overlays); surfaceContext->setContextProperty("Window", DependencyManager::get().data()); + surfaceContext->setContextProperty("Desktop", DependencyManager::get().data()); surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index e326b765a9..bda06cda48 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -31,6 +31,14 @@ int DesktopScriptingInterface::getHeight() { return size.height(); } +QVariantMap DesktopScriptingInterface::getPresentationMode() { + static QVariantMap presentationModes { + { "VIRTUAL", Virtual }, + { "NATIVE", Native } + }; + return presentationModes; +} + void DesktopScriptingInterface::setHUDAlpha(float alpha) { qApp->getApplicationCompositor().setAlpha(alpha); } diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index 6003153ca7..ae0e8d5a4f 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -19,16 +19,20 @@ #include "InteractiveWindow.h" - +/**jsdoc + * @namespace Desktop + * + * @hifi-interface + * @hifi-client-entity + */ class DesktopScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(int width READ getWidth) // Physical width of screen(s) including task bars and system menus Q_PROPERTY(int height READ getHeight) // Physical height of screen(s) including task bars and system menus - Q_PROPERTY(int ForceNative READ flagForceNative) - Q_PROPERTY(int ForceVirtual READ flagForceVirtual) - Q_PROPERTY(int AlwaysOnTop READ flagAlwaysOnTop) - Q_PROPERTY(int CloseButtonHides READ flagCloseButtonHides) + Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL) + Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL) + Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL) public: Q_INVOKABLE void setHUDAlpha(float alpha); @@ -41,10 +45,11 @@ public: private: - int flagForceNative() { return ForceNative; } - int flagForceVirtual() { return ForceVirtual; } - int flagAlwaysOnTop() { return AlwaysOnTop; } - int flagCloseButtonHides() { return CloseButtonHides; } + static int flagAlwaysOnTop() { return AlwaysOnTop; } + static int flagCloseButtonHides() { return CloseButtonHides; } + + Q_INVOKABLE static QVariantMap getPresentationMode(); }; + #endif // hifi_DesktopScriptingInterface_h diff --git a/libraries/ui/src/InteractiveWindow.cpp b/libraries/ui/src/InteractiveWindow.cpp index cadd0080e7..ad74ff3720 100644 --- a/libraries/ui/src/InteractiveWindow.cpp +++ b/libraries/ui/src/InteractiveWindow.cpp @@ -32,7 +32,7 @@ static const char* const INTERACTIVE_WINDOW_SIZE_PROPERTY = "interactiveWindowSi static const char* const VISIBLE_PROPERTY = "visible"; static const char* const INTERACTIVE_WINDOW_VISIBLE_PROPERTY = "interactiveWindowVisible"; static const char* const EVENT_BRIDGE_PROPERTY = "eventBridge"; -static const char* const WINDOW_MODE_TEXT_PROPERTY = "windowModeText"; +static const char* const PRESENTATION_MODE_PROPERTY = "presentationMode"; static const QStringList KNOWN_SCHEMES = QStringList() << "http" << "https" << "file" << "about" << "atp" << "qrc"; @@ -60,6 +60,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap if (properties.contains(FLAGS_PROPERTY)) { object->setProperty(FLAGS_PROPERTY, properties[FLAGS_PROPERTY].toUInt()); } + if (properties.contains(PRESENTATION_MODE_PROPERTY)) { + object->setProperty(PRESENTATION_MODE_PROPERTY, properties[PRESENTATION_MODE_PROPERTY].toInt()); + } if (properties.contains(TITLE_PROPERTY)) { object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString()); } @@ -79,9 +82,10 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap connect(object, SIGNAL(interactiveWindowPositionChanged()), this, SIGNAL(positionChanged()), Qt::QueuedConnection); connect(object, SIGNAL(interactiveWindowSizeChanged()), this, SIGNAL(sizeChanged()), Qt::QueuedConnection); connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SIGNAL(visibleChanged()), Qt::QueuedConnection); - connect(object, SIGNAL(windowModeChanged()), this, SIGNAL(modeChanged()), Qt::QueuedConnection); + connect(object, SIGNAL(presentationModeChanged()), this, SIGNAL(presentationModeChanged()), Qt::QueuedConnection); connect(object, SIGNAL(titleChanged()), this, SIGNAL(titleChanged()), Qt::QueuedConnection); + QUrl sourceURL{ sourceUrl }; // If the passed URL doesn't correspond to a known scheme, assume it's a local file path if (!KNOWN_SCHEMES.contains(sourceURL.scheme(), Qt::CaseInsensitive)) { @@ -188,7 +192,7 @@ bool InteractiveWindow::isVisible() const { glm::vec2 InteractiveWindow::getPosition() const { if (QThread::currentThread() != thread()) { - vec2 result; + glm::vec2 result; BLOCKING_INVOKE_METHOD(const_cast(this), "getPosition", Q_RETURN_ARG(glm::vec2, result)); return result; } @@ -208,12 +212,13 @@ void InteractiveWindow::setPosition(const glm::vec2& position) { if (!_qmlWindow.isNull()) { _qmlWindow->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y)); + QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowPositionForMode", Qt::DirectConnection); } } glm::vec2 InteractiveWindow::getSize() const { if (QThread::currentThread() != thread()) { - vec2 result; + glm::vec2 result; BLOCKING_INVOKE_METHOD(const_cast(this), "getSize", Q_RETURN_ARG(glm::vec2, result)); return result; } @@ -232,18 +237,55 @@ void InteractiveWindow::setSize(const glm::vec2& size) { if (!_qmlWindow.isNull()) { _qmlWindow->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y)); + QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowSizeForMode", Qt::DirectConnection); } } -QString InteractiveWindow::getMode() const { +QString InteractiveWindow::getTitle() const { if (QThread::currentThread() != thread()) { QString result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getMode", Q_RETURN_ARG(QString, result)); + BLOCKING_INVOKE_METHOD(const_cast(this), "getTitle", Q_RETURN_ARG(QString, result)); return result; } if (_qmlWindow.isNull()) { return QString(); } - return _qmlWindow->property(WINDOW_MODE_TEXT_PROPERTY).toString(); + return _qmlWindow->property(TITLE_PROPERTY).toString(); +} + +void InteractiveWindow::setTitle(const QString& title) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setTitle", Q_ARG(const QString&, title)); + return; + } + + if (!_qmlWindow.isNull()) { + _qmlWindow->setProperty(TITLE_PROPERTY, title); + } +} + +int InteractiveWindow::getPresentationMode() const { + if (QThread::currentThread() != thread()) { + int result; + BLOCKING_INVOKE_METHOD(const_cast(this), "getPresentationMode", + Q_RETURN_ARG(int, result)); + return result; + } + + if (_qmlWindow.isNull()) { + return Virtual; + } + return _qmlWindow->property(PRESENTATION_MODE_PROPERTY).toInt(); +} + +void InteractiveWindow::setPresentationMode(int presentationMode) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setPresentationMode", Q_ARG(int, presentationMode)); + return; + } + + if (!_qmlWindow.isNull()) { + _qmlWindow->setProperty(PRESENTATION_MODE_PROPERTY, presentationMode); + } } diff --git a/libraries/ui/src/InteractiveWindow.h b/libraries/ui/src/InteractiveWindow.h index 1258ae6943..541ff2e140 100644 --- a/libraries/ui/src/InteractiveWindow.h +++ b/libraries/ui/src/InteractiveWindow.h @@ -19,33 +19,76 @@ #include #include +#include #include namespace InteractiveWindowEnums { Q_NAMESPACE enum InteractiveWindowFlags : uint8_t { - ForceNative = 1 << 0, - ForceVirtual = 1 << 1, - AlwaysOnTop = 1 << 2, - CloseButtonHides = 1 << 3 + AlwaysOnTop = 1 << 0, + CloseButtonHides = 1 << 1 }; Q_ENUM_NS(InteractiveWindowFlags); - inline void declareQML() { - qmlRegisterUncreatableMetaObject(staticMetaObject, "InteractiveWindowFlags", 1, 0, - "InteractiveWindowFlags", "Error: enums only"); - } + enum InteractiveWindowPresentationMode { + Virtual, + Native + }; + Q_ENUM_NS(InteractiveWindowPresentationMode); } using namespace InteractiveWindowEnums; +/**jsdoc + * @class InteractiveWindow + * @hideconstructor + * + * @hifi-interface + * @hifi-client-en + * + * @property {string} mode + */ class InteractiveWindow : public QObject { Q_OBJECT + + /**jsdoc + * title of the window + * + * @name InteractiveWindow#title + * @type string + * @default "InteractiveWindow" + */ + Q_PROPERTY(QString title READ getTitle WRITE setTitle) + + /**jsdoc + * window position on current desktop + * + * @name InteractiveWindow#position + * @type Vec2 + */ Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) + + /**jsdoc + * window size + * + * @name InteractiveWindow#size + * @type Vec2 + */ Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) + + /**jsdoc + * visibility of the window + * + * @name InteractiveWindow#visible + * @type boolean + * @default true + * @example + * // Toggle window visiblity; + * interactiveWindow.visible = !interactiveWindow.visible; + */ Q_PROPERTY(bool visible READ isVisible WRITE setVisible) - Q_PROPERTY(QString mode READ getMode) + Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode) public: InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties); @@ -54,6 +97,9 @@ public: private: // define property getters and setters as private to not expose them to the JS API + Q_INVOKABLE QString getTitle() const; + Q_INVOKABLE void setTitle(const QString& title); + Q_INVOKABLE glm::vec2 getPosition() const; Q_INVOKABLE void setPosition(const glm::vec2& position); @@ -63,7 +109,8 @@ private: Q_INVOKABLE void setVisible(bool visible); Q_INVOKABLE bool isVisible() const; - Q_INVOKABLE QString getMode() const; + Q_INVOKABLE void setPresentationMode(int presentationMode); + Q_INVOKABLE int getPresentationMode() const; public slots: @@ -123,10 +170,10 @@ signals: void sizeChanged(); /**jsdoc - * @function InteractiveWindow.modeChanged + * @function InteractiveWindow.presentationModeChanged * @returns {Signal} */ - void modeChanged(); + void presentationModeChanged(); /**jsdoc * @function InteractiveWindow.titleChanged @@ -134,6 +181,12 @@ signals: */ void titleChanged(); + /**jsdoc + * @function InteractiveWindow.closed + * @returns {Signal} + */ + void closed(); + /**jsdoc * @function InteractiveWindow.fromQml * @param {object} message