← Back to team overview

ubuntu-sdk-team team mailing list archive

[Merge] lp:~bzoltan/ubuntu-ui-toolkit/Landing-2017-03-06 into lp:ubuntu-ui-toolkit

 

Zoltan Balogh has proposed merging lp:~bzoltan/ubuntu-ui-toolkit/Landing-2017-03-06 into lp:ubuntu-ui-toolkit.

Commit message:
landing

Requested reviews:
  Zoltan Balogh (bzoltan)

For more details, see:
https://code.launchpad.net/~bzoltan/ubuntu-ui-toolkit/Landing-2017-03-06/+merge/319093

landing
-- 
Your team Ubuntu SDK team is subscribed to branch lp:ubuntu-ui-toolkit.
=== modified file 'components.api'
--- components.api	2017-01-26 11:59:45 +0000
+++ components.api	2017-03-06 16:02:52 +0000
@@ -731,6 +731,7 @@
 Ubuntu.Components.OptionSelector 1.3: Empty
     property bool colourImage
     property double containerHeight
+    readonly property int count
     property bool currentlyExpanded
     property Component delegate
     property bool expanded

=== modified file 'debian/changelog'
--- debian/changelog	2017-01-30 11:30:04 +0000
+++ debian/changelog	2017-03-06 16:02:52 +0000
@@ -1,3 +1,43 @@
+ubuntu-ui-toolkit (1.3.2184+17.04) UNRELEASED; urgency=medium
+
+  [ Adnane Belmadiaf ]
+  * Add support for CheckBox label when set
+  * Add more tests for checkbox. Fixes LP: #1333228, LP: #1442851.
+  * Make sure we resets partial text input from the input method.
+    Fixes LP: #1630872.
+  * Fix ListItem title/subtitle alignment & elide in RTL mode. 
+    Fixes LP: #1665944.
+  * Fix PageHeader title alignment & elide in RTL mode. Fixes LP: #1665942.
+  * Fix UbuntuListView docs. Fixes LP: #1486940.
+  * Add count prop to OptionSelector. Fixes LP: #1341559.
+  * Replace the hardcoded color with a theme color. Fixes LP: #1664758.
+  * Fix the import statement for Menu, MenuBar and MenuGroup. 
+    Fixes LP: #1660604.
+
+  [ Albert Astals Cid ]
+  * Fix memory leak in UCStyleHints
+  * ThinDivider: Anchors can't be null, set to undefined
+  * No need to call UCUnits::resolveResource to learn we just need to load it
+    normally because the fact that we already loaded it normally means we need
+    to load it normally. Fixes LP: #1558663.
+  * Fix conditional jump or move depends on uninitialised value(s)
+
+  [ Christian Dywan ]
+  * Initialize UCApplication with no side effects. Fixes LP: #1662868.
+  * Unit test case insensitive FilterBehavior.pattern. Fixes LP: #1663924.
+  * ListItemPopover delegate should (in)visible as per action Bug 1662220: 
+    [ListItem] Popupmenu shows traces of disabled actions
+
+  [ Olivier Tilloy ]
+  * Check that window is not null before accessing its activeFocusItem property.
+    Fixes LP: #1664620.
+
+  [ Lukáš Tinkl ]
+  * Unbreak the startup race between unity8/qtmir and UITK trying talk to 
+    content-hub. Fixes LP: #1663106.
+
+ -- Zoltán Balogh <zoltan@xxxxxxxxx>  Mon, 06 Mar 2017 17:51:04 +0200
+
 ubuntu-ui-toolkit (1.3.2166+17.04.20170130) zesty; urgency=medium
 
   [ Andrea Bernabei ]

=== modified file 'examples/ubuntu-ui-toolkit-gallery/Toggles.qml'
--- examples/ubuntu-ui-toolkit-gallery/Toggles.qml	2015-04-25 08:18:45 +0000
+++ examples/ubuntu-ui-toolkit-gallery/Toggles.qml	2017-03-06 16:02:52 +0000
@@ -55,6 +55,49 @@
                 checked: true
             }
         }
+
+        TemplateRow {
+            title: i18n.tr("Checkbox with label")
+
+            CheckBox {
+                objectName: "checkbox_checked_lbl"
+                checked: true
+                text: "This a checkbox label"
+            }
+        }
+
+        TemplateRow {
+            title: i18n.tr("Disabled checkbox with label")
+
+            CheckBox {
+                objectName: "checkbox_disabled_checked_lbl"
+                checked: true
+                enabled: false
+                text: "This a checkbox label"
+            }
+        }
+
+        TemplateRow {
+            title: i18n.tr("Disabled checkbox with label")
+
+            CheckBox {
+                objectName: "checkbox_disabled_checked_lbl"
+                checked: false
+                enabled: false
+                text: "This a checkbox label"
+            }
+        }
+
+        TemplateRow {
+            title: i18n.tr("Checkbox with multiline label")
+
+            CheckBox {
+                objectName: "checkbox_checked_lbl"
+                checked: true
+                text: "This is a checkbox with a built-in label spanning several lines that won't be ellipsized but increase in height instead"
+                width: parent.width
+            }
+        }
     }
 
 

=== modified file 'src/UbuntuToolkit/menu.cpp'
--- src/UbuntuToolkit/menu.cpp	2016-09-09 18:16:57 +0000
+++ src/UbuntuToolkit/menu.cpp	2017-03-06 16:02:52 +0000
@@ -303,7 +303,7 @@
 
 /*!
  * \qmltype Menu
- * \inqmlmodule Ubuntu.Components
+ * \inqmlmodule Ubuntu.Components.Labs
  * \ingroup ubuntu
  * \brief Menu defines a context menu or submenu structure of a MenuBar
  *
@@ -311,6 +311,7 @@
  * \qml
  * import QtQuick 2.4
  * import Ubuntu.Components 1.3
+ * import Ubuntu.Components.Labs 1.0
  * Menu {
  *     text: "&File"
  *

=== modified file 'src/UbuntuToolkit/menubar.cpp'
--- src/UbuntuToolkit/menubar.cpp	2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/menubar.cpp	2017-03-06 16:02:52 +0000
@@ -104,7 +104,7 @@
 
 /*!
  * \qmltype MenuBar
- * \inqmlmodule Ubuntu.Components 1.3
+ * \inqmlmodule Ubuntu.Components.Labs
  * \ingroup ubuntu
  * \brief MenuBar defines an application menu bar structure
  *
@@ -112,6 +112,7 @@
  * \qml
  * import QtQuick 2.4
  * import Ubuntu.Components 1.3
+ * import Ubuntu.Components.Labs 1.0
  * MainView {
  *     MenuBar {
  *         Menu {

=== modified file 'src/UbuntuToolkit/menugroup.cpp'
--- src/UbuntuToolkit/menugroup.cpp	2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/menugroup.cpp	2017-03-06 16:02:52 +0000
@@ -23,7 +23,7 @@
 
 /*!
  * \qmltype MenuGroup
- * \inqmlmodule Ubuntu.Components
+ * \inqmlmodule Ubuntu.Components.Labs
  * \ingroup ubuntu
  * \brief Logical list of items for a menu.
  *
@@ -31,6 +31,7 @@
  * \qml
  * import QtQuick 2.4
  * import Ubuntu.Components 1.3
+ * import Ubuntu.Components.Labs 1.0
  * Menu {
  *     text: "Edit"
  *

=== modified file 'src/UbuntuToolkit/privates/uccontenthub.cpp'
--- src/UbuntuToolkit/privates/uccontenthub.cpp	2017-01-26 14:29:30 +0000
+++ src/UbuntuToolkit/privates/uccontenthub.cpp	2017-03-06 16:02:52 +0000
@@ -21,8 +21,10 @@
 #include <QtCore/QLoggingCategory>
 #include <QtCore/QMimeData>
 #include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
 #include <QtDBus/QDBusInterface>
 #include <QtDBus/QDBusReply>
+#include <QtDBus/QDBusServiceWatcher>
 #include <QtQuick/QQuickItem>
 
 Q_LOGGING_CATEGORY(ucContentHub, "ubuntu.components.UCContentHub", QtMsgType::QtWarningMsg)
@@ -40,11 +42,17 @@
 UT_NAMESPACE_BEGIN
 
 UCContentHub::UCContentHub(QObject *parent)
-    : QObject(parent),
-      m_dbusIface(0),
-      m_contentHubIface(0),
-      m_canPaste(false),
-      m_targetItem(0)
+    : QObject(parent)
+{
+    if (QDBusConnection::sessionBus().interface()->isServiceRegistered(contentHubService)) { // content hub already running
+        init();
+    } else {
+        m_watcher = new QDBusServiceWatcher(contentHubService, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration, this);
+        connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, &UCContentHub::init);
+    }
+}
+
+void UCContentHub::init()
 {
     m_dbusIface = new QDBusInterface(dbusService,
                                      dbusObjectPath,
@@ -76,24 +84,13 @@
             SLOT(onPasteboardChanged())
         );
 
-        m_canPaste = checkPasteFormats();
-    }
-}
-
-UCContentHub::~UCContentHub()
-{
-    if (m_dbusIface) {
-        delete m_dbusIface;
-    }
-
-    if (m_contentHubIface) {
-        delete m_contentHubIface;
+        onPasteboardChanged();
     }
 }
 
 void UCContentHub::requestPaste(QQuickItem *targetItem)
 {
-    if (!m_contentHubIface->isValid()) {
+    if (!m_contentHubIface || !m_contentHubIface->isValid()) {
         CONTENT_HUB_TRACE("Invalid Content Hub DBusInterface");
         return;
     }
@@ -106,12 +103,12 @@
     m_contentHubIface->call(QStringLiteral("RequestPasteByAppId"), appProfile);
 }
 
-bool UCContentHub::canPaste()
+bool UCContentHub::canPaste() const
 {
     return m_canPaste;
 }
 
-void UCContentHub::onPasteSelected(QString appId, QByteArray mimedata, bool pasteAsRichText)
+void UCContentHub::onPasteSelected(const QString &appId, const QByteArray &mimedata, bool pasteAsRichText)
 {
     if (getAppProfile() != appId) {
         return;
@@ -138,15 +135,39 @@
 
 void UCContentHub::onPasteboardChanged()
 {
-    if (checkPasteFormats() != m_canPaste) {
-        m_canPaste = !m_canPaste;
+    if (!m_contentHubIface || !m_contentHubIface->isValid()) {
+        CONTENT_HUB_TRACE("Invalid Content Hub DBusInterface");
+        return;
+    }
+
+    QDBusPendingCall pcall = m_contentHubIface->asyncCall(QStringLiteral("PasteFormats"));
+    QDBusPendingCallWatcher * watcher = new QDBusPendingCallWatcher(pcall, this);
+    connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * call) {
+        QDBusPendingReply<QStringList> reply = *call;
+        call->deleteLater();
+        if (reply.isValid()) {
+            // TODO: ContentHub clipboard keeps a list of all available paste formats.
+            // Probably apps could make use of this information to check if a specific
+            // data type is available, instead of only checking if list is empty or not.
+            // (LP: #1657111)
+            setCanPaste(!reply.value().isEmpty());
+        } else {
+            CONTENT_HUB_TRACE("Invalid return from DBus call PasteFormats");
+        }
+    });
+}
+
+void UCContentHub::setCanPaste(bool value)
+{
+    if (value != m_canPaste) {
+        m_canPaste = value;
         Q_EMIT canPasteChanged();
     }
 }
 
-QString UCContentHub::getAppProfile()
+QString UCContentHub::getAppProfile() const
 {
-    if (!m_dbusIface->isValid()) {
+    if (!m_dbusIface || !m_dbusIface->isValid()) {
         CONTENT_HUB_TRACE("Invalid DBus DBusInterface");
         return QString();
     }
@@ -194,25 +215,4 @@
     return mimeData;
 }
 
-bool UCContentHub::checkPasteFormats()
-{
-    if (!m_contentHubIface->isValid()) {
-        CONTENT_HUB_TRACE("Invalid Content Hub DBusInterface");
-        return false;
-    }
-
-    QDBusReply<QStringList> reply = m_contentHubIface->call(QStringLiteral("PasteFormats"));
-    if (reply.isValid()) {
-        // TODO: ContentHub clipboard keeps a list of all available paste formats.
-        // Probably apps could make use of this information to check if a specific
-        // data type is available, instead of only checking if list is empty or not.
-        // (LP: #1657111)
-        return !reply.value().isEmpty();
-    } else {
-        CONTENT_HUB_TRACE("Invalid return from DBus call PasteFormats");
-    }
-
-    return false;
-}
-
 UT_NAMESPACE_END

=== modified file 'src/UbuntuToolkit/privates/uccontenthub_p.h'
--- src/UbuntuToolkit/privates/uccontenthub_p.h	2017-01-17 19:30:01 +0000
+++ src/UbuntuToolkit/privates/uccontenthub_p.h	2017-03-06 16:02:52 +0000
@@ -25,6 +25,7 @@
 
 class QMimeData;
 class QDBusInterface;
+class QDBusServiceWatcher;
 class QQuickItem;
 
 UT_NAMESPACE_BEGIN
@@ -32,35 +33,37 @@
 class UBUNTUTOOLKIT_EXPORT UCContentHub : public QObject
 {
     Q_OBJECT
-
+    friend class tst_UCContentHub;
     Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged)
 
 public:
-    UCContentHub(QObject* parent = 0);
-    ~UCContentHub();
+    UCContentHub(QObject* parent = nullptr);
+    ~UCContentHub() = default;
 
     Q_INVOKABLE void requestPaste(QQuickItem *targetItem);
 
-    bool canPaste();
-    QString getAppProfile();
+    bool canPaste() const;
+    QString getAppProfile() const;
     QMimeData* deserializeMimeData(const QByteArray &serializedMimeData);
 
 Q_SIGNALS:
     void pasteSelected(QQuickItem *targetItem, const QString &data);
     void canPasteChanged();
 
-public Q_SLOTS:
-    void onPasteSelected(QString appId, QByteArray mimedata, bool pasteAsRichText);
+private Q_SLOTS:
+    void init();
+    void onPasteSelected(const QString &appId, const QByteArray &mimedata, bool pasteAsRichText);
     void onPasteboardChanged();
 
 private:
-    bool checkPasteFormats();
-
-    QDBusInterface *m_dbusIface;
-    QDBusInterface *m_contentHubIface;
-
-    bool m_canPaste;
-    QQuickItem *m_targetItem;
+    void setCanPaste(bool value);
+    QDBusInterface *m_dbusIface{nullptr};
+    QDBusInterface *m_contentHubIface{nullptr};
+
+    bool m_canPaste{false};
+    QQuickItem *m_targetItem{nullptr};
+
+    QDBusServiceWatcher * m_watcher{nullptr};
 };
 
 UT_NAMESPACE_END

=== modified file 'src/UbuntuToolkit/sortfiltermodel.cpp'
--- src/UbuntuToolkit/sortfiltermodel.cpp	2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/sortfiltermodel.cpp	2017-03-06 16:02:52 +0000
@@ -52,7 +52,8 @@
  *         }
  *         ListElement {
  *             title: "Elephants Dream"
- *             producer: "Blender"
+ *             // lowercase b
+ *             producer: "blender"
  *         }
  *         ListElement {
  *             title: "Big Buck Bunny"
@@ -69,7 +70,8 @@
  *         sortCaseSensitivity: Qt.CaseInsensitive
  *
  *         filter.property: "producer"
- *         filter.pattern: /blender/
+ *         // case insensitive matches
+ *         filter.pattern: /blender/i
  *     }
  *
  *     ListView {
@@ -154,6 +156,7 @@
  *     \li /possible/ matches anywhere in a word, so both "impossible" and "possible".
  *     \li /^sign/ matches "sign". But not "assignment" because ^ means start.
  *     \li /vest$/ matches "safety vest" and "vest" but not "vested".
+ *     \li /bar/i matches "bar", "Bar" or "BAR" regardless of case.
  * \endlist
  *
  * For more advanced uses it's recommended to read up on Javascript regular expressions.

=== modified file 'src/UbuntuToolkit/ucapplication.cpp'
--- src/UbuntuToolkit/ucapplication.cpp	2017-01-12 15:20:31 +0000
+++ src/UbuntuToolkit/ucapplication.cpp	2017-03-06 16:02:52 +0000
@@ -39,8 +39,6 @@
 UCApplication::UCApplication(QObject* parent) : QObject(parent), m_context(0)
                                                                , m_inputMethod(QGuiApplication::inputMethod())
 {
-    // Unset organization by default to skip an extra folder component
-    QCoreApplication::setOrganizationName(QStringLiteral(""));
     // Make sure we receive application name changes from C++ modules
     connect(QCoreApplication::instance(), &QCoreApplication::applicationNameChanged,
             this, &UCApplication::applicationNameChanged);
@@ -70,7 +68,6 @@
        to how Unity uses it to distinguish running applications.
      */
     QCoreApplication::setApplicationName(applicationName);
-    QCoreApplication::setOrganizationName(QCoreApplication::organizationName());
     /*
        Ensure that LocalStorage and WebKit use the same location
        Docs are ambiguous: in practise applicationName is ignored by default

=== modified file 'src/UbuntuToolkit/ucmainwindow.cpp'
--- src/UbuntuToolkit/ucmainwindow.cpp	2017-01-18 17:23:48 +0000
+++ src/UbuntuToolkit/ucmainwindow.cpp	2017-03-06 16:02:52 +0000
@@ -163,11 +163,7 @@
         return;
 
     d->m_applicationName = applicationName;
-
-    if (applicationName != QStringLiteral("")) {
-        UbuntuI18n::instance()->setDomain(applicationName);
-        UCApplication::instance()->setApplicationName(applicationName);
-    }
+    updateApplication(applicationName, d->m_organizationName);
     Q_EMIT applicationNameChanged(applicationName);
 }
 
@@ -192,11 +188,17 @@
         return;
 
     d->m_organizationName = organizationName;
+    updateApplication(d->m_applicationName, organizationName);
+    Q_EMIT organizationNameChanged(organizationName);
+}
 
-    if (organizationName != QStringLiteral("")) {
+void UCMainWindow::updateApplication(QString applicationName, QString organizationName)
+{
+    if (applicationName != QStringLiteral("")) {
+        UbuntuI18n::instance()->setDomain(applicationName);
         QCoreApplication::setOrganizationName(organizationName);
+        UCApplication::instance()->setApplicationName(applicationName);
     }
-    Q_EMIT organizationNameChanged(organizationName);
 }
 
 /*!

=== modified file 'src/UbuntuToolkit/ucmainwindow_p.h'
--- src/UbuntuToolkit/ucmainwindow_p.h	2017-01-16 16:52:57 +0000
+++ src/UbuntuToolkit/ucmainwindow_p.h	2017-03-06 16:02:52 +0000
@@ -76,6 +76,8 @@
 #endif
 
 private:
+    void updateApplication(QString applicationName, QString organizationName);
+
     Q_DECLARE_PRIVATE(UCMainWindow)
 };
 

=== modified file 'src/UbuntuToolkit/ucqquickimageextension.cpp'
--- src/UbuntuToolkit/ucqquickimageextension.cpp	2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/ucqquickimageextension.cpp	2017-03-06 16:02:52 +0000
@@ -22,7 +22,13 @@
 #include <QtCore/QFile>
 #include <QtCore/QFileInfo>
 #include <QtGui/QGuiApplication>
+#include <QtQuick/private/qquickitem_p.h>
 #include <QtQuick/private/qquickimagebase_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
+
+#define foreach Q_FOREACH
+#include <QtQml/private/qqmlengine_p.h>
+#undef foreach
 
 #include "ucunits_p.h"
 
@@ -61,11 +67,35 @@
     return m_source;
 }
 
+
 void UCQQuickImageExtension::setSource(const QUrl& url)
 {
     if (url != m_source) {
         m_source = url;
-        reloadSource();
+        // We need to wait until the component is complete
+        // so that m_image->sourceSize() is actually valid
+        if (QQuickItemPrivate::get(m_image)->componentComplete) {
+            reloadSource();
+        } else {
+            // This is a bit convoluted but i couldn't find a better way to get notified of when
+            // the image actually finishes constructing.
+            // Since what we're interested in reloadSource() is the image having the sourceSize set,
+            // what we do is connect to the sourceSizeChanged signal.
+            // The problem is that this signal isn't fired if the Image {} doesn't have sourceSize set
+            // so we tell the engine to fire the sourceSizeChanged signal when the image finishes constructing
+            // This way if the Image {} has a sourceSize set the lambda gets called because of it
+            // and if there's no sourceSize set the lambda gets called because we registered the finalize callback
+
+            connect(m_image, &QQuickImageBase::sourceSizeChanged,
+                             this,
+                             [&] {
+                                 QObject::disconnect(m_image, &QQuickImageBase::sourceSizeChanged, this, nullptr);
+                                 reloadSource();
+                            });
+
+            QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(m_image));
+            engPriv->registerFinalizeCallback(m_image, m_image->metaObject()->indexOfSignal("sourceSizeChanged()"));
+        }
     }
 }
 
@@ -80,6 +110,25 @@
         return;
     }
 
+    // If the url we're trying to load is already in the cache and
+    // the devicePixelRatio is 1, we save calling UCUnits::resolveResource
+    // and just set that image directly.
+    // UCUnits::resolveResource is not cheap (does a stat on disk)
+    if (qFuzzyCompare(qGuiApp->devicePixelRatio(), (qreal)1.0)) {
+        QSize ss = m_image->sourceSize();
+        if (ss.isNull() && m_image->image().isNull()) {
+            // For some reason QQuickImage returns 0x0 as sourceSize
+            // when the sourceSize is not set (and the image has not yet been loaded)
+            // so set it back to -1x-1
+            ss = QSize(-1, -1);
+        }
+
+        if (QQuickPixmap::isCached(m_source, ss)) {
+            m_image->setSource(m_source);
+            return;
+        }
+    }
+
     QString resolved = UCUnits::instance()->resolveResource(m_source);
 
     if (resolved.isEmpty()) {
@@ -98,7 +147,9 @@
             || selectedFilePath.endsWith(QStringLiteral(".svgz"))) {
             // Take care to pass the original fragment
             QUrl selectedFileUrl(QUrl::fromLocalFile(selectedFilePath));
-            selectedFileUrl.setFragment(fragment);
+            if (m_source.hasFragment()) {
+                selectedFileUrl.setFragment(fragment);
+            }
             m_image->setSource(selectedFileUrl);
         } else {
             // Need to scale the pixel-based image to suit the devicePixelRatio setting ourselves.

=== modified file 'src/UbuntuToolkit/ucqquickimageextension_p.h'
--- src/UbuntuToolkit/ucqquickimageextension_p.h	2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucqquickimageextension_p.h	2017-03-06 16:02:52 +0000
@@ -19,6 +19,7 @@
 #ifndef UCQQUICKIMAGEEXTENSION_P_H
 #define UCQQUICKIMAGEEXTENSION_P_H
 
+#include <QtCore/QEvent>
 #include <QtCore/QByteArray>
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>

=== modified file 'src/UbuntuToolkit/ucslotslayout.cpp'
--- src/UbuntuToolkit/ucslotslayout.cpp	2016-10-07 14:33:47 +0000
+++ src/UbuntuToolkit/ucslotslayout.cpp	2017-03-06 16:02:52 +0000
@@ -32,6 +32,7 @@
     : QQuickItemPrivate()
     , mainSlot(Q_NULLPTR)
     , m_parentItem(Q_NULLPTR)
+    , mainSlotHeight(0)
     , maxSlotsHeight(0)
     , _q_cachedHeight(-1)
     , maxNumberOfLeadingSlots(1)

=== modified file 'src/UbuntuToolkit/ucstylehints.cpp'
--- src/UbuntuToolkit/ucstylehints.cpp	2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/ucstylehints.cpp	2017-03-06 16:02:52 +0000
@@ -273,6 +273,7 @@
 
         newBinding->setTarget(change->property());
         PropertyChange::setBinding(change, newBinding);
+        m_propertyBackup << change;
     }
 }
 

=== modified file 'src/imports/Components/1.3/ListItemPopover.qml'
--- src/imports/Components/1.3/ListItemPopover.qml	2015-07-01 12:06:34 +0000
+++ src/imports/Components/1.3/ListItemPopover.qml	2017-03-06 16:02:52 +0000
@@ -24,6 +24,7 @@
     contentWidth: units.gu(25)
 
     delegate: ListItem {
+        visible: action.visible
         contentItem.anchors {
              leftMargin: units.gu(2)
              rightMargin: units.gu(2)
@@ -35,7 +36,7 @@
         Label {
             anchors.verticalCenter: parent.verticalCenter
             text: action.text
-            color: '#5D5D5D'
+            color: theme.palette.normal.backgroundText
         }
 
         onClicked: popover.hide()

=== modified file 'src/imports/Components/1.3/OptionSelector.qml'
--- src/imports/Components/1.3/OptionSelector.qml	2016-08-22 10:18:57 +0000
+++ src/imports/Components/1.3/OptionSelector.qml	2017-03-06 16:02:52 +0000
@@ -155,6 +155,12 @@
     }
 
     /*!
+      \qmlproperty int count
+      This property holds the number of items in the OptionSelector.
+     */
+    property alias count: list.count
+
+    /*!
       \qmlproperty int selectedIndex
       The index of the currently selected element in our list.
      */

=== modified file 'src/imports/Components/1.3/TextField.qml'
--- src/imports/Components/1.3/TextField.qml	2017-01-18 13:00:07 +0000
+++ src/imports/Components/1.3/TextField.qml	2017-03-06 16:02:52 +0000
@@ -988,7 +988,12 @@
             name: control.hasClearButton && !control.readOnly ? "edit-clear" : ""
         }
 
-        onClicked: editor.text = ""
+        onClicked: {
+            //FIXME: Invoke editor.clear() once the SDK moves to Qt 5.7
+            // http://doc.qt.io/qt-5/qml-qtquick-textinput.html#clear-method
+            editor.text = "";
+            Qt.inputMethod.reset();
+        }
     }
 
     // hint text

=== modified file 'src/imports/Components/1.3/UbuntuListView.qml'
--- src/imports/Components/1.3/UbuntuListView.qml	2016-10-06 15:02:31 +0000
+++ src/imports/Components/1.3/UbuntuListView.qml	2017-03-06 16:02:52 +0000
@@ -79,6 +79,7 @@
 
       \qml
       import QtQuick 2.4
+      import QtQuick.XmlListModel 2.0
       import Ubuntu.Components 1.3
 
       UbuntuListView {

=== modified file 'src/imports/Components/ListItems/1.3/LabelVisual.qml'
--- src/imports/Components/ListItems/1.3/LabelVisual.qml	2016-01-27 13:58:29 +0000
+++ src/imports/Components/ListItems/1.3/LabelVisual.qml	2017-03-06 16:02:52 +0000
@@ -30,7 +30,8 @@
         return item.parent.hasOwnProperty("pointerTarget") || label.isInsideOverlay(item.parent)
     }
 
-    elide: Text.ElideRight
+    horizontalAlignment: Qt.application.layoutDirection == Qt.RightToLeft ? Text.AlignRight: Text.AlignLeft
+    elide: Qt.application.layoutDirection == Qt.RightToLeft ? Text.ElideLeft: Text.ElideRight
     color: selected
            ? theme.palette.selected.backgroundText
            : (secondary

=== modified file 'src/imports/Components/ListItems/1.3/ThinDivider.qml'
--- src/imports/Components/ListItems/1.3/ThinDivider.qml	2015-07-05 21:04:55 +0000
+++ src/imports/Components/ListItems/1.3/ThinDivider.qml	2017-03-06 16:02:52 +0000
@@ -47,8 +47,8 @@
 */
 Rectangle {
     anchors {
-        left: (parent) ? parent.left : null
-        right: (parent) ? parent.right : null
+        left: (parent) ? parent.left : undefined
+        right: (parent) ? parent.right : undefined
     }
     height: (visible) ? units.dp(2) : 0
     // a private property to catch theme background color change

=== modified file 'src/imports/Components/Popups/1.3/popupUtils.js'
--- src/imports/Components/Popups/1.3/popupUtils.js	2017-01-16 17:03:26 +0000
+++ src/imports/Components/Popups/1.3/popupUtils.js	2017-03-06 16:02:52 +0000
@@ -72,7 +72,7 @@
 
     var popupObject;
     // If there's an active item, save it so we can restore it later
-    var prevFocusItem = (typeof window !== "undefined") ? window.activeFocusItem : null;
+    var prevFocusItem = (typeof window !== "undefined") && window ? window.activeFocusItem : null;
     if (params !== undefined) {
         popupObject = popupComponent.createObject(rootObject, params);
     } else {

=== modified file 'src/imports/Components/Themes/Ambiance/1.3/CheckBoxStyle.qml'
--- src/imports/Components/Themes/Ambiance/1.3/CheckBoxStyle.qml	2016-01-27 15:17:56 +0000
+++ src/imports/Components/Themes/Ambiance/1.3/CheckBoxStyle.qml	2017-03-06 16:02:52 +0000
@@ -57,7 +57,7 @@
     property real iconPadding: units.gu(0.4)
 
     implicitWidth: units.gu(2)
-    implicitHeight: units.gu(2)
+    implicitHeight: Math.max(checkBoxLbl.contentHeight, units.gu(2))
 
     FocusShape {
     }
@@ -65,10 +65,12 @@
     UbuntuShape {
         id: background
         anchors {
-            fill: parent
             margins: checkBoxStyle.backgroundPadding
         }
-        property real iconSize: Math.min(width, height) - 2*checkBoxStyle.iconPadding
+        width: units.gu(2)
+        height: units.gu(2)
+
+        property real iconSize: units.gu(2) - 2*checkBoxStyle.iconPadding
 
         Icon {
             color: checkBoxStyle.iconColor
@@ -164,4 +166,16 @@
             }
         ]
     }
+
+    Label {
+        id: checkBoxLbl
+        text: styledItem.text
+        anchors.left: background.right
+        anchors.leftMargin: units.gu(1)
+        anchors.right: parent.right
+        height: parent.height
+        enabled: styledItem.enabled
+        visible: styledItem.text
+        wrapMode: Text.WordWrap
+    }
 }

=== modified file 'src/imports/Components/Themes/Ambiance/1.3/PageHeaderStyle.qml'
--- src/imports/Components/Themes/Ambiance/1.3/PageHeaderStyle.qml	2016-09-19 07:24:45 +0000
+++ src/imports/Components/Themes/Ambiance/1.3/PageHeaderStyle.qml	2017-03-06 16:02:52 +0000
@@ -121,6 +121,7 @@
                 id: titleLabel
                 objectName: "header_title_label"
                 text: styledItem.title
+                horizontalAlignment: Qt.application.layoutDirection == Qt.RightToLeft ? Text.AlignRight: Text.AlignLeft
 
                 anchors {
                     left: parent.left
@@ -131,7 +132,7 @@
                 color: pageHeaderStyle.foregroundColor
                 font.weight: pageHeaderStyle.fontWeight
                 textSize: pageHeaderStyle.textSize
-                elide: Text.ElideRight
+                elide: Qt.application.layoutDirection == Qt.RightToLeft ? Text.ElideLeft: Text.ElideRight
             }
         }
     }

=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/popups.py'
--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/popups.py	2016-08-12 11:42:43 +0000
+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/popups.py	2017-03-06 16:02:52 +0000
@@ -79,6 +79,8 @@
         name = introspection.get_classname_from_path(path)
         if name == b'OverflowPanel':
             return True
+        if name == b'ListItemPopover':
+            return True
 
         return False
 

=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.ListItemTestCase.qml'
--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.ListItemTestCase.qml	2015-03-03 13:20:06 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.ListItemTestCase.qml	2017-03-06 16:02:52 +0000
@@ -15,7 +15,7 @@
  */
 
 import QtQuick 2.0
-import Ubuntu.Components 1.2
+import Ubuntu.Components 1.3
 
 MainView {
     width: units.gu(48)
@@ -25,7 +25,10 @@
     Page {
         id: testPage
         objectName: "test_page"
-        title: listView.ViewItems.selectMode ? "In selection mode" : "No action triggered"
+        header: PageHeader {
+            objectName: 'test_header'
+            title: listView.ViewItems.selectMode ? "In selection mode" : "No action triggered"
+        }
         ListView {
             id: listView
             objectName: "test_view"
@@ -40,6 +43,10 @@
                             iconName: "delete"
                             objectName: 'delete_action'
                             onTriggered: testPage.title = objectName + " action triggered";
+                        },
+                        Action {
+                            visible: false
+                            objectName: 'invisible_action'
                         }
                     ]
                 }

=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.py'
--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.py	2015-09-22 15:56:35 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_listitem.py	2017-03-06 16:02:52 +0000
@@ -17,6 +17,7 @@
 import os
 
 import ubuntuuitoolkit
+from autopilot.introspection import dbus
 from ubuntuuitoolkit import tests
 
 
@@ -34,7 +35,9 @@
             'UCListItem', objectName='listitem0')
         self.test_page = self.main_view.select_single(
             objectName='test_page')
-        self.assertEqual(self.test_page.title, 'No action triggered')
+        header = self.test_page.select_single(
+            'PageHeader', objectName='test_header')
+        self.assertEqual(header.title, 'No action triggered')
 
     def test_trigger_delete(self):
         self.test_listitem.trigger_leading_action('delete_action')
@@ -96,3 +99,12 @@
             'UCListItem', objectName='listitem3')
         listItem3.toggle_selected()
         self.assertTrue(listItem3.selected)
+
+    def test_popover(self):
+        self.pointing_device.click_object(self.test_listitem, button=3)
+        popover = self.main_view.wait_select_single(
+            'ListItemPopover', objectName='listItemContextMenu')
+        self.assertRaises(
+            ubuntuuitoolkit._custom_proxy_objects._common.ToolkitException,
+            popover.click_action_button, 'invisible_action')
+        popover.click_action_button('delete_action')

=== modified file 'tests/unit/components/tst_sortfiltermodel.qml'
--- tests/unit/components/tst_sortfiltermodel.qml	2015-03-03 13:20:06 +0000
+++ tests/unit/components/tst_sortfiltermodel.qml	2017-03-06 16:02:52 +0000
@@ -25,7 +25,7 @@
         id: things
         ListElement { foo: "pub"; alpha: "bee"; num: 200 }
         ListElement { foo: "den"; alpha: "cow"; num: 300 }
-        ListElement { foo: "bar"; alpha: "ant"; num: 100 }
+        ListElement { foo: "Bar"; alpha: "ant"; num: 100 }
     }
 
     SortFilterModel {
@@ -72,6 +72,13 @@
         filter.pattern: /e/
     }
 
+    SortFilterModel {
+        id: caseSensitivity
+        model: things
+        filter.property: "foo"
+        filter.pattern: /bar/i
+    }
+
     function test_passthrough() {
         compare(unmodified.count, things.count)
     }
@@ -84,7 +91,7 @@
         compare(alphabetic.get(2).alpha, "cow")
 
         // Ensure different columns work also
-        compare(alphaSecond.get(0).foo, "bar")
+        compare(alphaSecond.get(0).foo, "Bar")
 
         // Descending
         compare(alphabeticRe.sort.order, Qt.DescendingOrder)
@@ -98,7 +105,7 @@
 
         // Changing roles
         alphabetic.sort.property = "foo"
-        compare(alphabetic.get(0).foo, "bar")
+        compare(alphabetic.get(0).foo, "Bar")
         compare(alphabetic.get(1).foo, "den")
         compare(alphabetic.get(2).foo, "pub")
         // Sanity check
@@ -120,4 +127,8 @@
         compare(bee.count, 1)
         compare(bee.get(0).alpha, "bee")
     }
+
+    function test_case_sensitivity() {
+        compare(caseSensitivity.get(0).foo, "Bar")
+    }
 }

=== modified file 'tests/unit/contenthub/tst_contenthub.cpp'
--- tests/unit/contenthub/tst_contenthub.cpp	2017-01-26 16:02:48 +0000
+++ tests/unit/contenthub/tst_contenthub.cpp	2017-03-06 16:02:52 +0000
@@ -68,12 +68,10 @@
     tst_UCContentHub() {}
 
 private:
-    MockContentService *mockService;
-    UCContentHub *contentHub;
-    QSignalSpy *pasteRequestedSpy;
-    QSignalSpy *pasteSelectedSpy;
-
-    const int testTimeout = 5000;
+    MockContentService *mockService{nullptr};
+    UCContentHub *contentHub{nullptr};
+    QSignalSpy *pasteRequestedSpy{nullptr};
+    QSignalSpy *pasteSelectedSpy{nullptr};
 
     const QString dummyAppId = "DummyAppId";
 
@@ -143,11 +141,11 @@
         QDBusConnection connection = QDBusConnection::sessionBus();
         connection.registerObject("/", mockService);
         connection.registerService("com.ubuntu.content.dbus.Service");
-        pasteRequestedSpy = new QSignalSpy(mockService, SIGNAL(PasteRequested()));
+        pasteRequestedSpy = new QSignalSpy(mockService, &MockContentService::PasteRequested);
 
         qRegisterMetaType<QQuickItem*>();
         contentHub = new UCContentHub();
-        pasteSelectedSpy = new QSignalSpy(contentHub, SIGNAL(pasteSelected(QQuickItem*, const QString&)));
+        pasteSelectedSpy = new QSignalSpy(contentHub, &UCContentHub::pasteSelected);
     }
 
     void cleanupTestCase()
@@ -155,6 +153,7 @@
         delete pasteRequestedSpy;
         delete pasteSelectedSpy;
         delete contentHub;
+        delete mockService;
     }
 
     void cleanup()
@@ -187,8 +186,7 @@
         QMimeData textPaste;
         textPaste.setText(sampleHtml);
         contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(textPaste), false);
-        pasteSelectedSpy->wait(testTimeout);
-        QCOMPARE(pasteSelectedSpy->count(), 1);
+        QTRY_COMPARE(pasteSelectedSpy->count(), 1);
         QList<QVariant> args = pasteSelectedSpy->takeFirst();
         QVERIFY(args.at(1).toString() == textPaste.text());
     }
@@ -198,8 +196,7 @@
         QMimeData htmlPaste;
         htmlPaste.setHtml(sampleHtml);
         contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(htmlPaste), false);
-        pasteSelectedSpy->wait(testTimeout);
-        QCOMPARE(pasteSelectedSpy->count(), 1);
+        QTRY_COMPARE(pasteSelectedSpy->count(), 1);
         QList<QVariant> args = pasteSelectedSpy->takeFirst();
         QVERIFY(args.at(1).toString() == htmlPaste.text());
     }
@@ -209,8 +206,7 @@
         QMimeData htmlPaste;
         htmlPaste.setHtml(sampleHtml);
         contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(htmlPaste), true);
-        pasteSelectedSpy->wait(testTimeout);
-        QCOMPARE(pasteSelectedSpy->count(), 1);
+        QTRY_COMPARE(pasteSelectedSpy->count(), 1);
         QList<QVariant> args = pasteSelectedSpy->takeFirst();
         QVERIFY(args.at(1).toString() == htmlPaste.html());
     }
@@ -220,8 +216,7 @@
         QMimeData textPaste;
         textPaste.setText(sampleText);
         contentHub->onPasteSelected(dummyAppId, serializeMimeData(textPaste), false);
-        pasteSelectedSpy->wait(testTimeout);
-        QCOMPARE(pasteSelectedSpy->count(), 0);
+        QTRY_COMPARE(pasteSelectedSpy->count(), 0);
     }
 
     void test_KeyboardShortcutOnTextField()
@@ -230,10 +225,10 @@
         testCase->rootObject()->forceActiveFocus();
         QQuickItem *textField = testCase->findItem<QQuickItem*>("textField");
         QTest::keyClick(textField->window(), Qt::Key_Tab);
-        QTRY_COMPARE_WITH_TIMEOUT(textField->property("activeFocus").toBool(), true, testTimeout);
+        QTRY_COMPARE(textField->property("activeFocus").toBool(), true);
+        QTRY_COMPARE(contentHub->canPaste(), true);
         QTest::keyClick(textField->window(), Qt::Key_V, Qt::ControlModifier|Qt::ShiftModifier);
-        pasteRequestedSpy->wait(testTimeout);
-        QCOMPARE(pasteRequestedSpy->count(), 1);
+        QTRY_COMPARE(pasteRequestedSpy->count(), 1);
     }
     
     void test_KeyboardShortcutOnTextArea()
@@ -242,10 +237,10 @@
         testCase->rootObject()->forceActiveFocus();
         QQuickItem *textArea = testCase->findItem<QQuickItem*>("textArea");
         QTest::keyClick(textArea->window(), Qt::Key_Tab);
-        QTRY_COMPARE_WITH_TIMEOUT(textArea->property("activeFocus").toBool(), true, testTimeout);
+        QTRY_COMPARE(textArea->property("activeFocus").toBool(), true);
+        QTRY_COMPARE(contentHub->canPaste(), true);
         QTest::keyClick(textArea->window(), Qt::Key_V, Qt::ControlModifier|Qt::ShiftModifier);
-        pasteRequestedSpy->wait(testTimeout);
-        QCOMPARE(pasteRequestedSpy->count(), 1);
+        QTRY_COMPARE(pasteRequestedSpy->count(), 1);
     }
 };
 

=== modified file 'tests/unit/mainwindow/VisualRoot.qml'
--- tests/unit/mainwindow/VisualRoot.qml	2017-01-16 14:18:21 +0000
+++ tests/unit/mainwindow/VisualRoot.qml	2017-03-06 16:02:52 +0000
@@ -20,7 +20,6 @@
 
 MainWindow {
     objectName: "visualRoot"
-    applicationName: "org.gnu.wildebeest"
     visualRoot: myRoot
 
     Rectangle {

=== modified file 'tests/unit/mainwindow/tst_mainwindow.cpp'
--- tests/unit/mainwindow/tst_mainwindow.cpp	2017-01-16 17:44:23 +0000
+++ tests/unit/mainwindow/tst_mainwindow.cpp	2017-03-06 16:02:52 +0000
@@ -33,10 +33,7 @@
 #include <QtCore/QCryptographicHash>
 #include <QtCore/QSettings>
 
-#include <QtQuick/QQuickItem>
-#include <QtQuick/QQuickView>
 #include <QtGui/QGuiApplication>
-#include <QtQml/QQmlEngine>
 #include <QtQml/QQmlContext>
 #include <QtQml/QQmlComponent>
 
@@ -103,18 +100,22 @@
 
 private Q_SLOTS:
 
-    void initTestCase()
-    {
-    }
-
-    void cleanupTestCase()
-    {
+    void cleanup()
+    {
+        // Delete engine, and thereby also the UCApplication instance
+        QObject* engine(UCApplication::instance()->parent());
+        delete engine;
+        QCoreApplication::setApplicationName(QStringLiteral(""));
+        QCoreApplication::setOrganizationName(QStringLiteral(""));
     }
 
     // Note: tests/unit/mainview13 contains the UCApplication bits
 
     void testCase_AppName()
     {
+        // Sanity check: no name set yet
+        QCOMPARE(QStringLiteral("mainwindow"), QCoreApplication::applicationName());
+
         QString applicationName("org.gnu.wildebeest");
         QQuickWindow *mainWindow(loadTest("AppName.qml"));
         QVERIFY(mainWindow);
@@ -125,6 +126,9 @@
 
     void testCase_OrganizationName()
     {
+        // Sanity check: no organization set yet
+        QCOMPARE(QStringLiteral(""), QCoreApplication::organizationName());
+
         QString applicationName("tv.island.pacific");
         QString organizationName("pacifist");
         QQuickWindow *mainWindow(loadTest("OrganizationName.qml"));
@@ -135,9 +139,28 @@
         QCOMPARE(organizationName, QCoreApplication::organizationName());
     }
 
+    void testCase_NoOrganizationName()
+    {
+        // Sanity check: no organization set yet
+        QCOMPARE(QStringLiteral(""), QCoreApplication::organizationName());
+
+        // Custom values set outside of QML
+        QString applicationName("com.example.random");
+        QString organizationName(QStringLiteral("Example"));
+        QCoreApplication::setApplicationName(applicationName);
+        QCoreApplication::setOrganizationName(organizationName);
+
+        QQuickWindow *mainWindow(loadTest("VisualRoot.qml"));
+        QVERIFY(mainWindow);
+        // MainView shouldn't interfere as applicationName wasn't set in QML
+        QCOMPARE(QStringLiteral(""), mainWindow->property("applicationName").toString());
+        QCOMPARE(applicationName, QCoreApplication::applicationName());
+        QCOMPARE(QStringLiteral(""), mainWindow->property("organizationName").toString());
+        QCOMPARE(organizationName, QCoreApplication::organizationName());
+    }
+
     void testCase_VisualRoot()
     {
-        QString applicationName("tv.island.pacific");
         QQuickWindow *mainWindow(loadTest("VisualRoot.qml"));
         QVERIFY(mainWindow);
         QQuickItem* myLabel(testItem(mainWindow, "myLabel"));

=== removed file 'tests/unit/qquick_image_extension/borderInName.sci'
--- tests/unit/qquick_image_extension/borderInName.sci	2013-05-06 16:33:54 +0000
+++ tests/unit/qquick_image_extension/borderInName.sci	1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-source: "borderInName.png"
-border.left: 9
-border.right: 2
-border.top: 9 
-border.bottom: 0
-horizontalTileMode: Stretch
-verticalTileMode: Stretch

=== added directory 'tests/unit/qquick_image_extension/data'
=== added file 'tests/unit/qquick_image_extension/data/borderInName.sci'
--- tests/unit/qquick_image_extension/data/borderInName.sci	1970-01-01 00:00:00 +0000
+++ tests/unit/qquick_image_extension/data/borderInName.sci	2017-03-06 16:02:52 +0000
@@ -0,0 +1,7 @@
+source: "borderInName.png"
+border.left: 9
+border.right: 2
+border.top: 9 
+border.bottom: 0
+horizontalTileMode: Stretch
+verticalTileMode: Stretch

=== added file 'tests/unit/qquick_image_extension/data/face.png'
Binary files tests/unit/qquick_image_extension/data/face.png	1970-01-01 00:00:00 +0000 and tests/unit/qquick_image_extension/data/face.png	2017-03-06 16:02:52 +0000 differ
=== added file 'tests/unit/qquick_image_extension/data/hundred_faces.qml'
--- tests/unit/qquick_image_extension/data/hundred_faces.qml	1970-01-01 00:00:00 +0000
+++ tests/unit/qquick_image_extension/data/hundred_faces.qml	2017-03-06 16:02:52 +0000
@@ -0,0 +1,13 @@
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Repeater {
+    width: 400
+    height: 600
+
+    model: 100
+    delegate: Image {
+        source: "face.png"
+    }
+}

=== added file 'tests/unit/qquick_image_extension/data/test@xxxxxx'
--- tests/unit/qquick_image_extension/data/test@xxxxxx	1970-01-01 00:00:00 +0000
+++ tests/unit/qquick_image_extension/data/test@xxxxxx	2017-03-06 16:02:52 +0000
@@ -0,0 +1,7 @@
+source: "test@xxxxxx"
+border.left: 9
+border.right: 2
+border.top: 9 
+border.bottom: 0
+horizontalTileMode: Stretch
+verticalTileMode: Stretch

=== modified file 'tests/unit/qquick_image_extension/qquick_image_extension.pro'
--- tests/unit/qquick_image_extension/qquick_image_extension.pro	2016-05-31 09:02:35 +0000
+++ tests/unit/qquick_image_extension/qquick_image_extension.pro	2017-03-06 16:02:52 +0000
@@ -1,3 +1,3 @@
 include(../test-include.pri)
 SOURCES += tst_qquick_image_extension.cpp
-QT += quick-private
+QT += core-private quick-private

=== removed file 'tests/unit/qquick_image_extension/test@xxxxxx'
--- tests/unit/qquick_image_extension/test@xxxxxx	2013-05-06 16:33:54 +0000
+++ tests/unit/qquick_image_extension/test@xxxxxx	1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-source: "test@xxxxxx"
-border.left: 9
-border.right: 2
-border.top: 9 
-border.bottom: 0
-horizontalTileMode: Stretch
-verticalTileMode: Stretch

=== modified file 'tests/unit/qquick_image_extension/tst_qquick_image_extension.cpp'
--- tests/unit/qquick_image_extension/tst_qquick_image_extension.cpp	2016-09-09 17:49:07 +0000
+++ tests/unit/qquick_image_extension/tst_qquick_image_extension.cpp	2017-03-06 16:02:52 +0000
@@ -23,6 +23,8 @@
 #define protected public
 #include <UbuntuToolkit/private/ucqquickimageextension_p.h>
 #undef protected
+#include <QtCore/private/qabstractfileengine_p.h>
+#include <QQuickView>
 
 UT_USE_NAMESPACE
 
@@ -32,6 +34,20 @@
     return QDir::temp().entryList(nameFilters, QDir::Files).count();
 }
 
+int nFaces = 0;
+
+class DummyFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+    QAbstractFileEngine *create(const QString &fileName) const
+    {
+        if (fileName.endsWith("/face.png"))
+            nFaces++;
+
+        return nullptr;
+    }
+};
+
 class tst_UCQQuickImageExtension : public QObject
 {
     Q_OBJECT
@@ -78,7 +94,7 @@
 
     void rewriteContainsBorderInName() {
         UCQQuickImageExtension image;
-        QString sciFilePath = "borderInName.sci";
+        QString sciFilePath = "data/borderInName.sci";
         QString result;
 
         QTextStream resultStream(&result);
@@ -86,7 +102,7 @@
 
         QString expected;
         QTextStream expectedStream(&expected);
-        expectedStream << "source: \"image://scaling/1/./borderInName.png\"" << endl;
+        expectedStream << "source: \"image://scaling/1/data/borderInName.png\"" << endl;
         expectedStream << "border.left: 9" << endl;
         expectedStream << "border.right: 2" << endl;
         expectedStream << "border.top: 9" << endl;
@@ -105,7 +121,7 @@
         QQuickImageBase baseImage;
         UCQQuickImageExtension* image1 = new UCQQuickImageExtension(&baseImage);
         UCQQuickImageExtension* image2 = new UCQQuickImageExtension(&baseImage);
-        QUrl sciFileUrl = QUrl::fromLocalFile("./test.sci");
+        QUrl sciFileUrl = QUrl::fromLocalFile("./data/test.sci");
 
         unsigned int initialNumberOfSciFiles = numberOfTemporarySciFiles();
 
@@ -124,6 +140,25 @@
         delete image2;
         QCOMPARE(numberOfTemporarySciFiles(), initialNumberOfSciFiles + 1);
     }
+
+    void onlyOneStatRepeatedImage() {
+        DummyFileEngineHandler handler;
+
+        QQuickView view(QUrl::fromLocalFile("./data/hundred_faces.qml"));
+
+        // Wait until the app is idle
+        bool idle = false;
+        while (!idle) {
+            QEventLoop l;
+            idle = !l.processEvents();
+        }
+        // We have actually loaded the image
+        QVERIFY(nFaces > 0);
+        // The number at the moment of writing this test is 4, but we
+        // can't know what Qt does internally, so i'm going for a relatively safe <10
+        // the important part is that is not 100 times (i.e. one for every Image item)
+        QVERIFY(nFaces < 10);
+    }
 };
 
 QTEST_MAIN(tst_UCQQuickImageExtension)

=== modified file 'tests/unit/visual/tst_optionselector.13.qml'
--- tests/unit/visual/tst_optionselector.13.qml	2016-09-19 11:13:19 +0000
+++ tests/unit/visual/tst_optionselector.13.qml	2017-03-06 16:02:52 +0000
@@ -183,8 +183,17 @@
         function test_custom_data() {
             return test_arrow_select_data()
         }
+
         function test_custom(data) {
             test_arrow_select(data, customSelector, 2);
         }
+
+        function test_count() {
+            var selector = defaultSelector;
+            sections.selectedIndex = 0;
+            selector.forceActiveFocus();
+            waitForRendering(selector);
+            compare(selector.count, customModel.count);
+        }
     }
 }

=== modified file 'tests/unit/visual/tst_toggles.13.qml'
--- tests/unit/visual/tst_toggles.13.qml	2016-06-15 13:46:51 +0000
+++ tests/unit/visual/tst_toggles.13.qml	2017-03-06 16:02:52 +0000
@@ -39,6 +39,23 @@
             property bool checkedNow: true
             onClicked: checkedNow = checked
         }
+
+        CheckBox {
+            id: testCheckLbl
+            checked: true
+            text: "This a checkbox label"
+            property bool checkedNow: true
+            onClicked: checkedNow = checked
+        }
+
+        CheckBox {
+            id: testCheckLblDisabled
+            checked: false
+            enabled: false
+            text: "This a checkbox label"
+            property bool checkedNow: false
+            onClicked: checkedNow = checked
+        }
     }
 
     UbuntuTestCase {
@@ -75,6 +92,37 @@
             clickedSpy.wait(400);
             compare(data.testItem.checkedNow, data.testItem.checked);
         }
+
+
+        function test_toggle_lbl_checked_data() {
+            return [
+                {tag: "CheckBox", testItem: testCheckLbl, mouse: true},
+            ];
+        }
+
+        function test_toggle_lbl_checked(data) {
+            data.testItem.checkedNow = data.testItem.checked;
+            data.testItem.forceActiveFocus();
+            clickedSpy.target = data.testItem;
+            mouseClick(data.testItem, centerOf(data.testItem).x, centerOf(data.testItem).y);
+            clickedSpy.wait(400);
+            compare(data.testItem.checkedNow, data.testItem.checked);
+        }
+
+        function test_toggle_lbl_checked_disabled_data() {
+            return [
+                {tag: "CheckBox", testItem: testCheckLblDisabled, mouse: true},
+            ];
+        }
+
+        function test_toggle_lbl_checked_disabled(data) {
+            data.testItem.checkedNow = data.testItem.checked;
+            data.testItem.forceActiveFocus();
+            clickedSpy.target = data.testItem;
+            mouseClick(data.testItem, centerOf(data.testItem).x, centerOf(data.testItem).y);
+            compare(clickedSpy.count, 0);
+            compare(data.testItem.checkedNow, data.testItem.checked);
+        }
     }
 }
 


References