Add Window attached property on Item
authorShawn Rutledge <shawn.rutledge@digia.com>
Thu, 7 Aug 2014 11:57:50 +0000 (13:57 +0200)
committerJ-P Nurmi <jpnurmi@digia.com>
Fri, 8 Aug 2014 17:28:34 +0000 (19:28 +0200)
An Item sometimes needs to know a few things about the window
in which it is being displayed; this attached property can expose
them without needing to go up the heirarchy to find the window.
Instead of adding the QQuickWindow pointer as a property on Item
as in 8f49f50a169db85401eb37daf4fe3a0fc3280603, having an attached
property means that it will not be found by introspection; and
it solves the problem that Window is in the QtQuick.Window module:
you must import the module to use the attached property, instead
of having access to a pointer whose type might not be defined
if you didn't import it.  The Window attached property is created
on-demand (so the memory cost adds up if you use it in too many
places); the tradeoff is that it can exist even when the item
is not yet being shown in a window, so bindings at startup work.
The API is purposely incomplete compared to that in QQuickWindow
so that we can introduce what is needed in a controlled fasion
over time.  For now we know of use cases for visibility, active
and activeFocusItem.

[ChangeLog][QtQuick][Window] Added Item.Window attached property

Change-Id: I649404cbd1383326678aa2144f790b2f2542dbbc
Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
src/quick/items/items.pri
src/quick/items/qquickwindow.cpp
src/quick/items/qquickwindow.h
src/quick/items/qquickwindowattached.cpp [new file with mode: 0644]
src/quick/items/qquickwindowattached_p.h [new file with mode: 0644]
src/quick/items/qquickwindowmodule.cpp
tests/auto/quick/qquickwindow/data/windowattached.qml [new file with mode: 0644]
tests/auto/quick/qquickwindow/tst_qquickwindow.cpp

index 042ff80..87e2212 100644 (file)
@@ -73,6 +73,7 @@ HEADERS += \
     $$PWD/qquickitemview_p_p.h \
     $$PWD/qquickitemviewtransition_p.h \
     $$PWD/qquickscreen_p.h \
+    $$PWD/qquickwindowattached_p.h \
     $$PWD/qquickwindowmodule_p.h \
     $$PWD/qquickframebufferobject.h \
     $$PWD/qquickitemgrabresult.h \
@@ -129,6 +130,7 @@ SOURCES += \
     $$PWD/qquickitemviewtransition.cpp \
     $$PWD/qquickwindowmodule.cpp \
     $$PWD/qquickscreen.cpp \
+    $$PWD/qquickwindowattached.cpp \
     $$PWD/qquickframebufferobject.cpp \
     $$PWD/qquickitemgrabresult.cpp \
     $$PWD/qquickrendercontrol.cpp
index eb97dde..1640b58 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "qquickwindow.h"
 #include "qquickwindow_p.h"
+#include "qquickwindowattached_p.h"
 
 #include "qquickitem.h"
 #include "qquickitem_p.h"
@@ -3850,6 +3851,18 @@ bool QQuickWindow::glslIsCoreProfile() const
  */
 
 /*!
+    \qmlattachedproperty QWindow::Visibility Window::visibility
+    \since 5.4
+
+    This attached property holds whether whether the window is currently shown
+    in the windowing system as normal, minimized, maximized, fullscreen or
+    hidden. The Window attached property can be attached to any Item. If the
+    item is not shown in any window, the value will be \l {QWindow::}{Hidden}.
+
+    \sa visible, visibility
+*/
+
+/*!
     \qmlproperty Qt::ScreenOrientation Window::contentOrientation
 
     This is a hint to the window manager in case it needs to display
@@ -3896,6 +3909,15 @@ bool QQuickWindow::glslIsCoreProfile() const
  */
 
 /*!
+    \qmlattachedproperty Item Window::activeFocusItem
+    \since 5.4
+
+    This attached property holds the item which currently has active focus or
+    \c null if there is no item with active focus. The Window attached property
+    can be attached to any Item.
+*/
+
+/*!
     \qmlproperty Window::active
     \since 5.1
 
@@ -3905,6 +3927,26 @@ bool QQuickWindow::glslIsCoreProfile() const
  */
 
 /*!
+    \qmlattachedproperty bool Window::active
+    \since 5.4
+
+    This attached property tells whether the window is active. The Window
+    attached property can be attached to any Item.
+
+    Here is an example which changes a label to show the active state of the
+    window in which it is shown:
+
+    \qml
+    import QtQuick 2.4
+    import QtQuick.Window 2.2
+
+    Text {
+        text: Window.active ? "active" : "inactive"
+    }
+    \endqml
+*/
+
+/*!
     \qmlmethod QtQuick::Window::requestActivate()
     \since 5.1
 
@@ -3983,6 +4025,11 @@ void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage)
     d->renderJobMutex.unlock();
 }
 
+QQuickWindowAttached *QQuickWindow::qmlAttachedProperties(QObject *object)
+{
+    return new QQuickWindowAttached(object);
+}
+
 void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
 {
     renderJobMutex.lock();
index 6353f6a..04a89b1 100644 (file)
@@ -47,6 +47,7 @@
 #include <QtGui/qopengl.h>
 #include <QtGui/qwindow.h>
 #include <QtGui/qevent.h>
+#include <qqml.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -55,6 +56,7 @@ class QQuickItem;
 class QSGTexture;
 class QInputMethodEvent;
 class QQuickWindowPrivate;
+class QQuickWindowAttached;
 class QOpenGLFramebufferObject;
 class QQmlIncubationController;
 class QInputMethodEvent;
@@ -156,6 +158,8 @@ public:
 
     void scheduleRenderJob(QRunnable *job, RenderStage schedule);
 
+    static QQuickWindowAttached *qmlAttachedProperties(QObject *object);
+
 Q_SIGNALS:
     void frameSwapped();
     Q_REVISION(2) void openglContextCreated(QOpenGLContext *context);
@@ -220,6 +224,7 @@ private:
 QT_END_NAMESPACE
 
 Q_DECLARE_METATYPE(QQuickWindow *)
+QML_DECLARE_TYPEINFO(QQuickWindow, QML_HAS_ATTACHED_PROPERTIES)
 
 #endif // QQUICKWINDOW_H
 
diff --git a/src/quick/items/qquickwindowattached.cpp b/src/quick/items/qquickwindowattached.cpp
new file mode 100644 (file)
index 0000000..666b21f
--- /dev/null
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickwindow.h"
+#include "qquickitem.h"
+#include "qquickwindowattached_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// QDoc comments must go in qquickwindow.cpp to avoid overwriting the Window docs
+
+QQuickWindowAttached::QQuickWindowAttached(QObject* attachee)
+    : QObject(attachee)
+    , m_window(NULL)
+{
+    m_attachee = qobject_cast<QQuickItem*>(attachee);
+    if (m_attachee && m_attachee->window()) // It might not be in a window yet
+        windowChanged(m_attachee->window());
+    if (m_attachee)
+        connect(m_attachee, &QQuickItem::windowChanged, this, &QQuickWindowAttached::windowChanged);
+}
+
+QWindow::Visibility QQuickWindowAttached::visibility() const
+{
+    return (m_window ? m_window->visibility() : QWindow::Hidden);
+}
+
+bool QQuickWindowAttached::isActive() const
+{
+    return (m_window ? m_window->isActive() : false);
+}
+
+QQuickItem *QQuickWindowAttached::activeFocusItem() const
+{
+    return (m_window ? m_window->activeFocusItem() : Q_NULLPTR);
+}
+
+void QQuickWindowAttached::windowChanged(QQuickWindow *window)
+{
+    if (window != m_window) {
+        QQuickWindow* oldWindow = m_window;
+        m_window = window;
+
+        if (oldWindow)
+            oldWindow->disconnect(this);
+
+        if (!window)
+            return; // No values to get, therefore nothing to emit
+
+        if (!oldWindow || window->visibility() != oldWindow->visibility())
+            emit visibilityChanged();
+        if (!oldWindow || window->isActive() != oldWindow->isActive())
+            emit activeChanged();
+        if (!oldWindow || window->activeFocusItem() != oldWindow->activeFocusItem())
+            emit activeFocusItemChanged();
+
+        // QQuickWindowQmlImpl::visibilityChanged also exists, and window might even
+        // be QQuickWindowQmlImpl, but that's not what we are connecting to.
+        // So this is actual window state rather than a buffered or as-requested one.
+        // If we used the metaobject connect syntax there would be a warning:
+        // QMetaObjectPrivate::indexOfSignalRelative - QMetaObject::indexOfSignal:
+        // signal visibilityChanged(QWindow::Visibility) from QQuickWindow redefined in QQuickWindowQmlImpl
+        connect(window, &QQuickWindow::visibilityChanged,
+                this, &QQuickWindowAttached::visibilityChanged);
+        connect(window, &QQuickWindow::activeChanged,
+                this, &QQuickWindowAttached::activeChanged);
+        connect(window, &QQuickWindow::activeFocusItemChanged,
+                this, &QQuickWindowAttached::activeFocusItemChanged);
+    }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h
new file mode 100644 (file)
index 0000000..007ff00
--- /dev/null
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWINDOW_ATTACHED_P_H
+#define QQUICKWINDOW_ATTACHED_P_H
+
+#include <qqml.h>
+#include <QWindow>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickWindow;
+
+class Q_AUTOTEST_EXPORT QQuickWindowAttached : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QWindow::Visibility visibility READ visibility NOTIFY visibilityChanged)
+    Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
+    Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged)
+
+public:
+    QQuickWindowAttached(QObject* attachee);
+
+    QWindow::Visibility visibility() const;
+    bool isActive() const;
+    QQuickItem* activeFocusItem() const;
+
+Q_SIGNALS:
+
+    void visibilityChanged();
+    void activeChanged();
+    void activeFocusItemChanged();
+
+protected Q_SLOTS:
+    void windowChanged(QQuickWindow*);
+
+private:
+    QQuickWindow* m_window;
+    QQuickItem* m_attachee;
+};
+
+QT_END_NAMESPACE
+
+#endif
index 51053ea..f472d5b 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 
 #include "qquickwindowmodule_p.h"
+#include "qquickwindowattached_p.h"
 #include "qquickscreen_p.h"
 #include "qquickview_p.h"
 #include <QtQuick/QQuickWindow>
@@ -173,6 +174,10 @@ void QQuickWindowModule::defineModule()
 {
     const char uri[] = "QtQuick.Window";
 
+    // Since Window is both an attached property and a createable type,
+    // the attached property declaration must come first so that it can
+    // be overridden below.
+    qmlRegisterUncreatableType<QQuickWindow>(uri, 2, 2, "Window", QQuickWindow::tr("Window is available via attached properties"));
     qmlRegisterType<QQuickWindow>(uri, 2, 0, "Window");
     qmlRegisterRevision<QWindow,1>(uri, 2, 1);
     qmlRegisterRevision<QWindow,2>(uri, 2, 2);
diff --git a/tests/auto/quick/qquickwindow/data/windowattached.qml b/tests/auto/quick/qquickwindow/data/windowattached.qml
new file mode 100644 (file)
index 0000000..e000d5c
--- /dev/null
@@ -0,0 +1,25 @@
+import QtQuick 2.4
+import QtQuick.Window 2.2
+
+Rectangle {
+    id: root
+    width: 100
+    height: 100
+    property bool windowActive: root.Window.active
+    Text {
+        objectName: "rectangleWindowText"
+        anchors.centerIn: parent
+        text: (windowActive ? "active" : "inactive") + "\nvisibility: " + root.Window.visibility
+    }
+
+    property Window extraWindow: Window {
+        objectName: "extraWindow"
+        title: "extra window"
+        visible: true
+        Text {
+            objectName: "extraWindowText"
+            anchors.centerIn: parent
+            text: (extraWindow.active ? "active" : "inactive") + "\nvisibility: " + Window.visibility
+        }
+    }
+}
index 7b23d14..b8e36b0 100644 (file)
@@ -43,6 +43,7 @@
 #include <QDebug>
 #include <QTouchEvent>
 #include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickView>
 #include <QtQuick/QQuickWindow>
 #include <QtQml/QQmlEngine>
 #include <QtQml/QQmlComponent>
@@ -368,6 +369,8 @@ private slots:
     void defaultSurfaceFormat();
     void glslVersion();
 
+    void attachedProperty();
+
     void testRenderJob();
 
 private:
@@ -1974,6 +1977,24 @@ void tst_qquickwindow::glslVersion()
     }
 }
 
+void tst_qquickwindow::attachedProperty()
+{
+    QQuickView view(testFileUrl("windowattached.qml"));
+    view.show();
+    view.requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
+    QVERIFY(view.rootObject()->property("windowActive").toBool());
+
+    QQuickWindow *innerWindow = view.rootObject()->findChild<QQuickWindow*>("extraWindow");
+    QVERIFY(innerWindow);
+    innerWindow->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(innerWindow));
+
+    QQuickText *text = view.rootObject()->findChild<QQuickText*>("extraWindowText");
+    QVERIFY(text);
+    QCOMPARE(text->text(), QLatin1String("active\nvisibility: 2"));
+}
+
 class RenderJob : public QRunnable
 {
 public: