ubuntu-sdk-team team mailing list archive
-
ubuntu-sdk-team team
-
Mailing list archive
-
Message #00541
[Merge] lp:~bzoltan/ubuntu-ui-toolkit/landing-2017-01-30 into lp:ubuntu-ui-toolkit
Zoltan Balogh has proposed merging lp:~bzoltan/ubuntu-ui-toolkit/landing-2017-01-30 into lp:ubuntu-ui-toolkit.
Commit message:
Landing 17.01.30
Requested reviews:
Zoltan Balogh (bzoltan)
For more details, see:
https://code.launchpad.net/~bzoltan/ubuntu-ui-toolkit/landing-2017-01-30/+merge/315882
Landing 17.01.30
--
Your team Ubuntu SDK team is subscribed to branch lp:ubuntu-ui-toolkit.
=== modified file 'components.api'
--- components.api 2016-09-17 05:48:25 +0000
+++ components.api 2017-01-30 11:36:29 +0000
@@ -1416,6 +1416,7 @@
function var cut()
function var deselect()
function var insert(var position, var text)
+ function var append(var text)
function var positionAt(var x, var y)
function var isRightToLeft(var start, var end)
function var moveCursorSelection(var position, var mode)
@@ -1619,6 +1620,8 @@
property bool opened
property Item pageStack
Ubuntu.Components.Styles.ToolbarStyle 1.3: Item
+ property color backgroundColor
+ readonly property ActionItemProperties buttons
property Component defaultDelegate
Ubuntu.Components.Type: Enum
Bool
=== modified file 'debian/changelog'
--- debian/changelog 2017-01-10 06:35:08 +0000
+++ debian/changelog 2017-01-30 11:36:29 +0000
@@ -1,3 +1,51 @@
+ubuntu-ui-toolkit (1.3.2166+17.04) UNRELEASED; urgency=medium
+
+ [ Andrea Bernabei ]
+ * ListItemLayout documentation: fix typo in color usage of a code example.
+ Fixes LP: #1561861
+
+ [ Zsombor Egri ]
+ * Get tst_listitem_focus_13 back alive. Fixes LP: #1624331.
+
+ [ Arthur Mello ]
+ * Add support for interacting with Content Hub Clipboard UI via DBus calls.
+ Fixes LP: #1563440.
+
+ [ Daniel d'Andrada ]
+ * If GRID_UNIT_PX set, always use it. It should override scaling information
+ from the QPA.
+
+ [ Adnane Belmadiaf ]
+ * Expose append method in TextArea. Fixes LP: #1658121.
+
+ [ Tim Peeters ]
+ * Toolbar visual refresh to scroll the icons instead of using an overflow
+ panel. Fixes LP: #1558018.
+ * Add missing dependency for the UITK gallery. Fixes lLP: #1640135
+
+ [ Sergio Cazzolato ]
+ * This change is done to remove the dependencies with upstart in the tests.
+ To start apps with env vars is gonna be used the launch_test_application
+ method provided by autopilot in its test base class which is a wrapper for
+ the ubuntu-app-launch tools (API). This change removes all the dependencies
+ with upstart, so all the projects which are using the initctl api and
+ fixtures will be impacted. An update in the documentation or in the blog is
+ gonna be done also to communicate this change to the developers.
+
+ [ Christian Dywan ]
+ * Add visualRoot property to MainWindow. Fixes LP: #1504551, LP: #1656857.
+ * Add python3-debian to control.gles and harden package-sorting.sh.
+ * Add organizationName property to MainWindow
+ * Add MainWindow to Ubuntu.Components.Labs.
+ Fixes LP: #1573118, LP: #1587431, LP: #1647415
+
+ [ Albert Astals Cid ]
+ * Labs is at 1.0.
+ * You can't save the focus after the popup/dialog has been created, at that
+ stage the dialog has focus already, you need to do it before creating it.
+
+ -- Zoltán Balogh <zoltan@xxxxxxxxx> Mon, 30 Jan 2017 12:49:05 +0200
+
ubuntu-ui-toolkit (1.3.2151+17.04.20161223build1~3) zesty; urgency=medium
* No change rebuild
=== modified file 'debian/control'
--- debian/control 2016-11-21 12:16:49 +0000
+++ debian/control 2017-01-30 11:36:29 +0000
@@ -353,6 +353,7 @@
Section: devel
Architecture: any
Depends: qml-module-qt-labs-folderlistmodel,
+ qml-module-qtqml-models2,
qml-module-qtquick-xmllistmodel,
qml-module-ubuntu-components (= ${binary:Version}) | qml-module-ubuntu-components-gles,
ubuntu-ui-toolkit-theme (= ${binary:Version}),
=== modified file 'debian/control.gles'
--- debian/control.gles 2016-11-21 12:16:49 +0000
+++ debian/control.gles 2017-01-30 11:36:29 +0000
@@ -37,6 +37,7 @@
lsb-release,
pep8,
python-autopilot (>= 1.4),
+ python3-debian,
python3-sphinx,
python3:any,
python:any,
=== added file 'examples/ubuntu-ui-toolkit-gallery/Toolbars.qml'
--- examples/ubuntu-ui-toolkit-gallery/Toolbars.qml 1970-01-01 00:00:00 +0000
+++ examples/ubuntu-ui-toolkit-gallery/Toolbars.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Template {
+ objectName: "toolbarTemplate"
+ id: toolbarTemplate
+
+ property list<Action> actionList: [
+ Action {
+ iconName: "alarm-clock"
+ text: "Tick tock"
+ },
+ Action {
+ iconName: "appointment"
+ text: "Date"
+ },
+ Action {
+ iconName: "attachment"
+ text: "Attach"
+ },
+ Action {
+ iconName: "contact"
+ text: "Contact"
+ },
+ Action {
+ iconName: "like"
+ text: "Like"
+ },
+ Action {
+ iconName: "lock"
+ text: "Lock"
+ }
+ ]
+
+ property list<Action> longActionList: [
+ Action {
+ iconName: "alarm-clock"
+ text: "Tick tock"
+ onTriggered: print("tock")
+ },
+ Action {
+ iconName: "appointment"
+ text: "Date"
+ onTriggered: print("date")
+ },
+ Action {
+ iconName: "attachment"
+ text: "Attach"
+ onTriggered: print("attach")
+ },
+ Action {
+ iconName: "contact"
+ text: "Contact"
+ onTriggered: print("contact")
+ },
+ Action {
+ iconName: "like"
+ text: "Like"
+ onTriggered: print("+1")
+ },
+ Action {
+ iconName: "lock"
+ text: "Lock"
+ onTriggered: print("lock")
+ },
+ Action {
+ iconName: "camcorder"
+ text: "Camera"
+ onTriggered: print("cam")
+ },
+ Action {
+ iconName: "location"
+ text: "Location"
+ onTriggered: print("loc")
+ enabled: false
+ },
+ Action {
+ iconName: "message"
+ text: "Message"
+ onTriggered: print("msg")
+ },
+ Action {
+ iconName: "livetv"
+ text: "Television"
+ onTriggered: print("tv")
+ },
+ Action {
+ iconName: "lock-broken"
+ text: "Unlock"
+ onTriggered: print("unlock")
+ },
+ Action {
+ iconName: "compose"
+ text: "Edit"
+ onTriggered: print("edit")
+ },
+ Action {
+ iconName: "contact-new"
+ text: "Add user"
+ onTriggered: print("useradd")
+ },
+ Action {
+ iconName: "crop"
+ text: "Crop"
+ onTriggered: print("crop")
+ },
+ Action {
+ iconName: "edit-cut"
+ text: "Cut"
+ onTriggered: print("cut")
+ },
+ Action {
+ iconName: "image-quality"
+ text: "Image quality"
+ onTriggered: print("image quality")
+ },
+ Action {
+ iconName: "import"
+ text: "Import"
+ onTriggered: print("import")
+ },
+ Action {
+ iconName: "history"
+ text: "History"
+ onTriggered: print("history")
+ },
+ Action {
+ iconName: "media-eject"
+ text: "Eject"
+ onTriggered: print("eject")
+ }
+ ]
+
+ property list<Action> deleteActionList: [
+ Action {
+ iconName: "delete"
+ text: "Delete"
+ onTriggered: print("delete")
+ }
+ ]
+
+ header: PageHeader {
+ id: h
+ title: toolbarTemplate.title
+ property bool longList: false
+ extension: Toolbar {
+ id: headerToolbar
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ leadingActionBar.actions: deleteActionList
+ trailingActionBar.actions: h.longList ? longActionList : actionList
+ }
+ trailingActionBar.actions: [
+ Action {
+ iconName: h.longList ? "remove" : "add"
+ text: h.longList ? "less" : "more"
+ onTriggered: {
+ h.longList = !h.longList;
+ }
+ }
+ ]
+ }
+
+ TemplateSection {
+ title: "Toolbar"
+ className: "Toolbar"
+
+ TemplateRow {
+ width: parent.width
+ title: i18n.tr("Enabled")
+ Toolbar {
+ width: parent.width
+ trailingActionBar.actions: actionList
+ leadingActionBar.actions: deleteActionList
+ }
+ }
+ TemplateRow {
+ title: i18n.tr("Disabled")
+ Toolbar {
+ width: parent.width
+ trailingActionBar.actions: actionList
+ leadingActionBar.actions: deleteActionList
+ enabled: false
+ }
+ }
+ }
+
+ TemplateSection {
+ title: "Scrollable toolbar"
+ className: "Toolbar"
+
+ TemplateRow {
+ title: i18n.tr("Enabled")
+ Toolbar {
+ width: parent.width
+ trailingActionBar.actions: longActionList
+ leadingActionBar.actions: deleteActionList
+ }
+ }
+ TemplateRow {
+ title: i18n.tr("Disabled")
+ Toolbar {
+ width: parent.width
+ trailingActionBar.actions: longActionList
+ leadingActionBar.actions: deleteActionList
+ enabled: false
+ }
+ }
+ }
+
+ TemplateSection {
+ title: "Colored"
+ className: "ActionBarStyle"
+
+ TemplateRow {
+ title: "Green"
+ Toolbar {
+ width: parent.width
+ trailingActionBar.actions: longActionList
+ leadingActionBar.actions: deleteActionList
+ StyleHints {
+ ignoreUnknownProperties: false
+ backgroundColor: UbuntuColors.green
+ buttons {
+ foregroundColor: "white"
+ disabledForegroundColor: UbuntuColors.silk
+ pressedBackgroundColor: UbuntuColors.ash
+ disabledBackgroundColor: UbuntuColors.slate
+ }
+ }
+ }
+ }
+ }
+}
=== modified file 'examples/ubuntu-ui-toolkit-gallery/WidgetsModel.qml'
--- examples/ubuntu-ui-toolkit-gallery/WidgetsModel.qml 2016-01-20 20:15:42 +0000
+++ examples/ubuntu-ui-toolkit-gallery/WidgetsModel.qml 2017-01-30 11:36:29 +0000
@@ -143,6 +143,12 @@
source: "Toggles.qml"
}
ListElement {
+ objectName: "toolbarsElement"
+ label: "Toolbar"
+ source: "Toolbars.qml"
+ }
+
+ ListElement {
objectName: "ubuntuListViewElement"
label: "Ubuntu ListView"
source: "UbuntuListViews.qml"
=== modified file 'src/UbuntuToolkit/UbuntuToolkit.pro'
--- src/UbuntuToolkit/UbuntuToolkit.pro 2016-11-04 09:37:32 +0000
+++ src/UbuntuToolkit/UbuntuToolkit.pro 2017-01-30 11:36:29 +0000
@@ -52,6 +52,7 @@
$$PWD/privates/listviewextensions_p.h \
$$PWD/privates/splitviewhandler_p.h \
$$PWD/privates/threelabelsslot_p.h \
+ $$PWD/privates/uccontenthub_p.h \
$$PWD/privates/ucpagewrapper_p.h \
$$PWD/privates/ucpagewrapper_p_p.h \
$$PWD/privates/ucpagewrapperincubator_p.h \
@@ -106,6 +107,8 @@
$$PWD/uclistitemstyle_p.h \
$$PWD/ucmainviewbase_p.h \
$$PWD/ucmainviewbase_p_p.h \
+ $$PWD/ucmainwindow_p.h \
+ $$PWD/ucmainwindow_p_p.h \
$$PWD/ucmargins_p.h \
$$PWD/ucmathutils_p.h \
$$PWD/ucmouse_p.h \
@@ -163,6 +166,7 @@
$$PWD/privates/listviewextensions.cpp \
$$PWD/privates/splitviewhandler.cpp \
$$PWD/privates/threelabelsslot_p.cpp \
+ $$PWD/privates/uccontenthub.cpp \
$$PWD/privates/ucpagewrapper.cpp \
$$PWD/privates/ucpagewrapperincubator.cpp \
$$PWD/privates/ucscrollbarutils.cpp \
@@ -203,6 +207,7 @@
$$PWD/uclistitemlayout.cpp \
$$PWD/uclistitemstyle.cpp \
$$PWD/ucmainviewbase.cpp \
+ $$PWD/ucmainwindow.cpp \
$$PWD/ucmathutils.cpp \
$$PWD/ucmousefilters.cpp \
$$PWD/ucpagetreenode.cpp \
=== added file 'src/UbuntuToolkit/privates/uccontenthub.cpp'
--- src/UbuntuToolkit/privates/uccontenthub.cpp 1970-01-01 00:00:00 +0000
+++ src/UbuntuToolkit/privates/uccontenthub.cpp 2017-01-30 11:36:29 +0000
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Arthur Mello <arthur.mello@xxxxxxxxxxxxx>
+ */
+
+#include "privates/uccontenthub_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QMimeData>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusReply>
+#include <QtQuick/QQuickItem>
+
+Q_LOGGING_CATEGORY(ucContentHub, "ubuntu.components.UCContentHub", QtMsgType::QtWarningMsg)
+
+#define CONTENT_HUB_TRACE(params) qCDebug(ucContentHub) << params
+
+static const QString contentHubService = QStringLiteral("com.ubuntu.content.dbus.Service");
+static const QString contentHubObjectPath = QStringLiteral("/");
+static const QString contentHubInterface = QStringLiteral("com.ubuntu.content.dbus.Service");
+
+static const QString dbusService = QStringLiteral("org.freedesktop.DBus");
+static const QString dbusObjectPath = QStringLiteral("/org/freedesktop/DBus");
+static const QString dbusInterface = QStringLiteral("org.freedesktop.DBus");
+
+UT_NAMESPACE_BEGIN
+
+UCContentHub::UCContentHub(QObject *parent)
+ : QObject(parent),
+ m_dbusIface(0),
+ m_contentHubIface(0),
+ m_canPaste(false),
+ m_targetItem(0)
+{
+ m_dbusIface = new QDBusInterface(dbusService,
+ dbusObjectPath,
+ dbusInterface,
+ QDBusConnection::sessionBus(),
+ this);
+
+ m_contentHubIface = new QDBusInterface(contentHubService,
+ contentHubObjectPath,
+ contentHubInterface,
+ QDBusConnection::sessionBus(),
+ this);
+ if (m_contentHubIface->isValid()) {
+ m_contentHubIface->connection().connect(
+ contentHubService,
+ contentHubObjectPath,
+ contentHubInterface,
+ QStringLiteral("PasteSelected"),
+ this,
+ SLOT(onPasteSelected(QString, QByteArray, bool))
+ );
+
+ m_contentHubIface->connection().connect(
+ contentHubService,
+ contentHubObjectPath,
+ contentHubInterface,
+ QStringLiteral("PasteboardChanged"),
+ this,
+ SLOT(onPasteboardChanged())
+ );
+
+ m_canPaste = checkPasteFormats();
+ }
+}
+
+UCContentHub::~UCContentHub()
+{
+ if (m_dbusIface) {
+ delete m_dbusIface;
+ }
+
+ if (m_contentHubIface) {
+ delete m_contentHubIface;
+ }
+}
+
+void UCContentHub::requestPaste(QQuickItem *targetItem)
+{
+ if (!m_contentHubIface->isValid()) {
+ CONTENT_HUB_TRACE("Invalid Content Hub DBusInterface");
+ return;
+ }
+
+ m_targetItem = targetItem;
+
+ QString appProfile = getAppProfile();
+ CONTENT_HUB_TRACE("AppArmor profile:" << appProfile);
+
+ m_contentHubIface->call(QStringLiteral("RequestPasteByAppId"), appProfile);
+}
+
+bool UCContentHub::canPaste()
+{
+ return m_canPaste;
+}
+
+void UCContentHub::onPasteSelected(QString appId, QByteArray mimedata, bool pasteAsRichText)
+{
+ if (getAppProfile() != appId) {
+ return;
+ }
+
+ if (mimedata.isNull()) {
+ CONTENT_HUB_TRACE("onPasteSelected: Invalid MimeData received");
+ return;
+ }
+
+ QMimeData* deserialized = deserializeMimeData(mimedata);
+ if (deserialized->hasImage()) {
+ if (deserialized->imageData().toByteArray().isEmpty()) {
+ Q_EMIT pasteSelected(m_targetItem, deserialized->html());
+ } else {
+ Q_EMIT pasteSelected(m_targetItem, deserialized->imageData().toString());
+ }
+ } else if (deserialized->hasHtml() && pasteAsRichText) {
+ Q_EMIT pasteSelected(m_targetItem, deserialized->html());
+ } else {
+ Q_EMIT pasteSelected(m_targetItem, deserialized->text());
+ }
+}
+
+void UCContentHub::onPasteboardChanged()
+{
+ if (checkPasteFormats() != m_canPaste) {
+ m_canPaste = !m_canPaste;
+ Q_EMIT canPasteChanged();
+ }
+}
+
+QString UCContentHub::getAppProfile()
+{
+ if (!m_dbusIface->isValid()) {
+ CONTENT_HUB_TRACE("Invalid DBus DBusInterface");
+ return QString();
+ }
+
+ QDBusReply<QString> reply = m_dbusIface->call(QStringLiteral("GetConnectionAppArmorSecurityContext"), QDBusConnection::sessionBus().baseService());
+ if (reply.isValid()) {
+ return reply.value();
+ }
+
+ return QString();
+}
+
+QMimeData* UCContentHub::deserializeMimeData(const QByteArray &serializedMimeData)
+{
+ int maxFormatsCount = 16;
+
+ if (static_cast<std::size_t>(serializedMimeData.size()) < sizeof(int)) {
+ // Data is invalid
+ return nullptr;
+ }
+
+ QMimeData *mimeData = new QMimeData;
+
+ const char* const buffer = serializedMimeData.constData();
+ const int* const header = reinterpret_cast<const int*>(serializedMimeData.constData());
+
+ const int count = qMin(header[0], maxFormatsCount);
+
+ for (int i = 0; i < count; i++) {
+ const int formatOffset = header[i*4+1];
+ const int formatSize = header[i*4+2];
+ const int dataOffset = header[i*4+3];
+ const int dataSize = header[i*4+4];
+
+ if (formatOffset + formatSize <= serializedMimeData.size()
+ && dataOffset + dataSize <= serializedMimeData.size()) {
+
+ QString mimeType = QString::fromLatin1(&buffer[formatOffset], formatSize);
+ QByteArray mimeDataBytes(&buffer[dataOffset], dataSize);
+
+ mimeData->setData(mimeType, mimeDataBytes);
+ }
+ }
+
+ 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
=== added file 'src/UbuntuToolkit/privates/uccontenthub_p.h'
--- src/UbuntuToolkit/privates/uccontenthub_p.h 1970-01-01 00:00:00 +0000
+++ src/UbuntuToolkit/privates/uccontenthub_p.h 2017-01-30 11:36:29 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Arthur Mello <arthur.mello@xxxxxxxxxxxxx>
+ */
+
+#ifndef UCCONTENTHUB_P_H
+#define UCCONTENTHUB_P_H
+
+#include <QtCore/QObject>
+
+#include <UbuntuToolkit/ubuntutoolkitglobal.h>
+
+class QMimeData;
+class QDBusInterface;
+class QQuickItem;
+
+UT_NAMESPACE_BEGIN
+
+class UBUNTUTOOLKIT_EXPORT UCContentHub : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged)
+
+public:
+ UCContentHub(QObject* parent = 0);
+ ~UCContentHub();
+
+ Q_INVOKABLE void requestPaste(QQuickItem *targetItem);
+
+ bool canPaste();
+ QString getAppProfile();
+ 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);
+ void onPasteboardChanged();
+
+private:
+ bool checkPasteFormats();
+
+ QDBusInterface *m_dbusIface;
+ QDBusInterface *m_contentHubIface;
+
+ bool m_canPaste;
+ QQuickItem *m_targetItem;
+};
+
+UT_NAMESPACE_END
+
+#endif // UCCONTENTHUB_P_H
=== modified file 'src/UbuntuToolkit/quickutils.cpp'
--- src/UbuntuToolkit/quickutils.cpp 2016-11-11 06:57:56 +0000
+++ src/UbuntuToolkit/quickutils.cpp 2017-01-30 11:36:29 +0000
@@ -29,6 +29,8 @@
#include <QtQuick/private/qquicktextedit_p.h>
#include <QtSystemInfo/QInputInfoManager>
+#include <UbuntuToolkit/private/ucmainwindow_p.h>
+
UT_NAMESPACE_BEGIN
QuickUtils *QuickUtils::m_instance = nullptr;
@@ -170,11 +172,18 @@
* \internal
* Returns the root item of a given item. In case there is a QQuickWindow (Window)
* found in the hierarchy, the function will return the contentItem of the window.
+ * If the root item is a \b MainWindow, the visualRoot property will be respected.
*/
QQuickItem *QuickUtils::rootItem(QObject *object)
{
// make sure we have the m_rootView updated
lookupQuickView();
+
+ UCMainWindow *mainWindow(qobject_cast<UCMainWindow*>(m_rootWindow));
+ if (mainWindow && mainWindow->visualRoot()) {
+ return mainWindow->visualRoot();
+ }
+
if (!object) {
return m_rootView ? m_rootView->rootObject() : (m_rootWindow ? m_rootWindow->contentItem() : 0);
}
=== modified file 'src/UbuntuToolkit/ubuntutoolkitmodule.cpp'
--- src/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-09-29 10:19:06 +0000
+++ src/UbuntuToolkit/ubuntutoolkitmodule.cpp 2017-01-30 11:36:29 +0000
@@ -39,11 +39,13 @@
#include "inversemouseareatype_p.h"
#include "listener_p.h"
#include "livetimer_p.h"
+#include "ucmainwindow_p.h"
#include "menu_p.h"
#include "menubar_p.h"
#include "menugroup_p.h"
#include "privates/appheaderbase_p.h"
#include "privates/frame_p.h"
+#include "privates/uccontenthub_p.h"
#include "privates/ucpagewrapper_p.h"
#include "privates/ucscrollbarutils_p.h"
#include "qquickclipboard_p.h"
@@ -262,6 +264,8 @@
qmlRegisterType<UCAppHeaderBase>(privateUri, 1, 3, "AppHeaderBase");
qmlRegisterType<Tree>(privateUri, 1, 3, "Tree");
+ qmlRegisterSimpleSingletonType<UCContentHub>(privateUri, 1, 3, "UCContentHub");
+
//FIXME: move to a more generic location, i.e StyledItem or QuickUtils
qmlRegisterSimpleSingletonType<UCScrollbarUtils>(privateUri, 1, 3, "PrivateScrollbarUtils");
@@ -439,6 +443,7 @@
qmlRegisterType<SplitView>(uri, 1, 0, "SplitView");
qmlRegisterType<SplitViewLayout>(uri, 1, 0, "SplitViewLayout");
qmlRegisterType<ViewColumn>(uri, 1, 0, "ViewColumn");
+ qmlRegisterType<UCMainWindow>(uri, 1, 0, "MainWindow");
qmlRegisterType<Menu>(uri, 1, 0, "Menu");
qmlRegisterType<MenuBar>(uri, 1, 0, "MenuBar");
qmlRegisterType<MenuGroup>(uri, 1, 0, "MenuGroup");
=== modified file 'src/UbuntuToolkit/ucapplication.cpp'
--- src/UbuntuToolkit/ucapplication.cpp 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucapplication.cpp 2017-01-30 11:36:29 +0000
@@ -39,6 +39,8 @@
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);
@@ -68,8 +70,7 @@
to how Unity uses it to distinguish running applications.
*/
QCoreApplication::setApplicationName(applicationName);
- // Unset organization to skip an extra folder component
- QCoreApplication::setOrganizationName(QString());
+ 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/uclistitemlayout.cpp'
--- src/UbuntuToolkit/uclistitemlayout.cpp 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/uclistitemlayout.cpp 2017-01-30 11:36:29 +0000
@@ -139,7 +139,7 @@
ListItemLayout {
id: layout
title.text: "Hello..."
- title.color: UbuntuColors.Orange
+ title.color: UbuntuColors.orange
subtitle.text: "...world!"
Rectangle {
=== modified file 'src/UbuntuToolkit/ucmainviewbase.cpp'
--- src/UbuntuToolkit/ucmainviewbase.cpp 2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/ucmainviewbase.cpp 2017-01-30 11:36:29 +0000
@@ -145,6 +145,7 @@
void UCMainViewBase::setApplicationName(QString applicationName)
{
Q_D(UCMainViewBase);
+
if (d->m_applicationName == applicationName)
return;
=== added file 'src/UbuntuToolkit/ucmainwindow.cpp'
--- src/UbuntuToolkit/ucmainwindow.cpp 1970-01-01 00:00:00 +0000
+++ src/UbuntuToolkit/ucmainwindow.cpp 2017-01-30 11:36:29 +0000
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2016-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ucmainwindow_p_p.h"
+
+#include <QtCore/QCoreApplication>
+
+#include "ucactionmanager_p.h"
+#include "ucactioncontext_p.h"
+#include "ucapplication_p.h"
+#include "uctheme_p.h"
+#include "i18n_p.h"
+#include "quickutils_p.h"
+
+UT_NAMESPACE_BEGIN
+
+/*!
+ \internal
+ \qmlabstract MainWindow
+ \inqmlmodule Ubuntu.Components.Labs
+ \ingroup ubuntu
+ \brief MainWindow is an alternate window-based root Item providing \l units
+ and \l i18n as native properties, an \ actionContext and an \l applicationName.
+ Unlike \l MainView there is no built-in header.
+
+ The simplest way to use a MainWindow is to include a single \l Page object:
+ \qml
+ import QtQuick 2.4
+ import Ubuntu.Components 1.3
+ import Ubuntu.Components.Labs 1.0
+
+ MainWindow {
+ minimumWidth: units.gu(48)
+ minimumHeight: units.gu(60)
+
+ Page {
+ anchors.fill: parent
+ header: PageHeader {
+ title: "Simple page"
+ }
+ Button {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: pageHeader.bottom
+ topMargin: units.gu(5)
+ }
+ width: units.gu(15)
+ text: "Push me"
+ onClicked: print("Click!")
+ }
+ }
+ }
+ \endqml
+ Anchors need to be set, there's no automatic fill like with \l MainView.
+
+ Do not include multiple Pages directly, but use \l AdaptivePageLayout
+ inside MainWindow to navigate between several Pages.
+
+ If the \l Page inside the MainWindow includes a Flickable, set the flickable property of
+ the PageHeader to automatically hide and show the header when the user scrolls up or down:
+ \qml
+ import QtQuick 2.4
+ import Ubuntu.Components 1.3
+ import Ubuntu.Components.Labs 1.0
+
+ MainWindow {
+ minimumWidth: units.gu(48)
+ maximumHeight: units.gu(60)
+
+ Page {
+ anchors.fill: parent
+ header: PageHeader {
+ title: "Page with Flickable"
+ flickable: myFlickable
+ }
+
+ Flickable {
+ id: myFlickable
+ anchors.fill: parent
+ contentHeight: column.height
+
+ Column {
+ id: column
+ Repeater {
+ model: 100
+ Label {
+ text: "line "+index
+ }
+ }
+ }
+ }
+ }
+ }
+ \endqml
+ The same header behavior is automatic when using a ListView instead of a Flickable in the above
+ example.
+
+ The examples above show how to include a single \l Page inside a MainWindow, but more
+ advanced application structures are possible using \l AdaptivePageLayout.
+**/
+UCMainWindowPrivate::UCMainWindowPrivate()
+ : m_actionContext(nullptr),
+ m_units(nullptr)
+{
+}
+
+void UCMainWindowPrivate::init()
+{
+ Q_Q(UCMainWindow);
+
+ //need to init here because the q pointer is null in constructor
+ m_actionContext = new UCPopupContext(q);
+
+ m_actionContext->setObjectName(QStringLiteral("RootContext"));
+ m_actionContext->setActive(true);
+}
+
+UCMainWindow::UCMainWindow(QWindow *parent)
+ : QQuickWindow(*(new UCMainWindowPrivate), parent)
+{
+ d_func()->init();
+
+ QObject::connect(UbuntuI18n::instance(this), SIGNAL(domainChanged()),
+ this, SIGNAL(i18nChanged()));
+ QObject::connect(UbuntuI18n::instance(this), SIGNAL(languageChanged()),
+ this, SIGNAL(i18nChanged()));
+}
+
+/*!
+ \qmlproperty string MainWindow::applicationName
+
+ The property holds the application's name, which must be the same as the
+ desktop file's name.
+ The name also sets the name of the QCoreApplication and defaults for data
+ and cache folders that work on the desktop and under confinement, as well as
+ the default gettext domain.
+ C++ code that writes files may use QStandardPaths::writableLocation with
+ QStandardPaths::DataLocation or QStandardPaths::CacheLocation.
+*/
+QString UCMainWindow::applicationName() const
+{
+ return d_func()->m_applicationName;
+}
+
+void UCMainWindow::setApplicationName(QString applicationName)
+{
+ Q_D(UCMainWindow);
+
+ if (d->m_applicationName == applicationName)
+ return;
+
+ d->m_applicationName = applicationName;
+
+ if (applicationName != QStringLiteral("")) {
+ UbuntuI18n::instance()->setDomain(applicationName);
+ UCApplication::instance()->setApplicationName(applicationName);
+ }
+ Q_EMIT applicationNameChanged(applicationName);
+}
+
+/*!
+ \qmlproperty string MainWindow::organizationName
+
+ The property holds the optional name of the organization. If set, data
+ folders reside in a subfolder of the organizationName. By default no
+ organizationName is set.
+*/
+QString UCMainWindow::organizationName() const
+{
+ return d_func()->m_organizationName;
+}
+
+
+void UCMainWindow::setOrganizationName(QString organizationName)
+{
+ Q_D(UCMainWindow);
+
+ if (d->m_organizationName == organizationName)
+ return;
+
+ d->m_organizationName = organizationName;
+
+ if (organizationName != QStringLiteral("")) {
+ QCoreApplication::setOrganizationName(organizationName);
+ }
+ Q_EMIT organizationNameChanged(organizationName);
+}
+
+/*!
+ \qmlproperty Units MainWindow::units
+
+ Grid units for this particular window - unlike the global context property
+ by the same name.
+*/
+UCUnits* UCMainWindow::units()
+{
+ Q_D(UCMainWindow);
+
+ if (!d->m_units) {
+ d->m_units = new UCUnits(this);
+ QObject::connect(d->m_units, SIGNAL(gridUnitChanged()),
+ this, SIGNAL(unitsChanged()));
+ }
+ return d->m_units;
+}
+
+/*!
+ \qmlproperty Units MainWindow::i18n
+
+ The property holds its breath for documentation.
+*/
+UbuntuI18n* UCMainWindow::i18n() const
+{
+ return UbuntuI18n::instance();
+}
+
+/*!
+ \qmlproperty ActionContext MainWindow::actionContext
+ \readonly
+ \since Ubuntu.Components 1.3
+ The action context of the MainWindow.
+ */
+UCPopupContext *UCMainWindow::actionContext() const
+{
+ return d_func()->m_actionContext;
+}
+
+/*!
+ \qmlproperty Item MainWindow::visualRoot
+
+ If set, the property holds the window's visual root, as opposed to root item
+ of the scene.
+ Popups (popovers, dialogs, menus) opened with popupUtils.open() will be
+ children of the visual root, or the root item otherwise.
+ Popups shown via show() may respectively reparent to the visual root, if it
+ is set, or the root item otherwise, if reparentToRootItem is set.
+*/
+QQuickItem *UCMainWindow::visualRoot() const
+{
+ return d_func()->m_visualRoot;
+}
+
+void UCMainWindow::setVisualRoot(QQuickItem *visualRoot)
+{
+ Q_D(UCMainWindow);
+
+ if (d->m_visualRoot == visualRoot)
+ return;
+
+ d->m_visualRoot = visualRoot;
+ Q_EMIT visualRootChanged(visualRoot);
+}
+
+UT_NAMESPACE_END
+
+#include "moc_ucmainwindow_p.cpp"
=== added file 'src/UbuntuToolkit/ucmainwindow_p.h'
--- src/UbuntuToolkit/ucmainwindow_p.h 1970-01-01 00:00:00 +0000
+++ src/UbuntuToolkit/ucmainwindow_p.h 2017-01-30 11:36:29 +0000
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UCMAINWINDOW_P_H
+#define UCMAINWINDOW_P_H
+
+#include <QtQuick/QQuickWindow>
+
+#include <UbuntuToolkit/private/i18n_p.h>
+#include <UbuntuToolkit/private/ucunits_p.h>
+
+UT_NAMESPACE_BEGIN
+
+class UCMainWindowPrivate;
+class UCPopupContext;
+class UCAction;
+
+class UBUNTUTOOLKIT_EXPORT UCMainWindow : public QQuickWindow
+{
+ Q_OBJECT
+ Q_PROPERTY(QString applicationName READ applicationName WRITE setApplicationName NOTIFY applicationNameChanged)
+ Q_PROPERTY(QString organizationName READ organizationName WRITE setOrganizationName NOTIFY organizationNameChanged)
+#ifndef Q_QDOC
+ Q_PROPERTY(UT_PREPEND_NAMESPACE(UCUnits)* units READ units NOTIFY unitsChanged)
+ Q_PROPERTY(UT_PREPEND_NAMESPACE(UbuntuI18n)* i18n READ i18n NOTIFY i18nChanged)
+ Q_PROPERTY(UT_PREPEND_NAMESPACE(UCPopupContext)* actionContext READ actionContext NOTIFY actionContextChanged)
+ Q_PROPERTY(UT_PREPEND_NAMESPACE(QQuickItem)* visualRoot READ visualRoot WRITE setVisualRoot NOTIFY visualRootChanged)
+#else
+ Q_PROPERTY(UCUnits* units READ units NOTIFY unitsChanged)
+ Q_PROPERTY(UbuntuI18n* i18n READ i18n NOTIFY i18nChanged)
+ Q_PROPERTY(UCPopupContext* actionContext READ actionContext NOTIFY actionContextChanged)
+ Q_PROPERTY(QQuickItem* visualRoot READ visualRoot WRITE setVisualRoot NOTIFY visualRootChanged)
+#endif
+
+public:
+ UCMainWindow(QWindow *parent = nullptr);
+
+
+ QString applicationName() const;
+ void setApplicationName(QString applicationName);
+ QString organizationName() const;
+ void setOrganizationName(QString organizationName);
+
+ UCUnits* units();
+ UbuntuI18n* i18n() const;
+
+ UCPopupContext* actionContext() const;
+
+ QQuickItem* visualRoot() const;
+ void setVisualRoot(QQuickItem*);
+
+Q_SIGNALS:
+ void applicationNameChanged(QString applicationName);
+ void organizationNameChanged(QString applicationName);
+ void i18nChanged();
+ void unitsChanged();
+#ifndef Q_QDOC
+ void actionContextChanged(UT_PREPEND_NAMESPACE(UCPopupContext)* actionContext);
+ void visualRootChanged(UT_PREPEND_NAMESPACE(QQuickItem)* visualRoot);
+#else
+ void actionContextChanged(UCPopupContext* actionContext);
+ void visualRootChanged(QQuickItem* visualRoot);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(UCMainWindow)
+};
+
+UT_NAMESPACE_END
+
+#endif // UCMAINWINDOW_P_H
=== added file 'src/UbuntuToolkit/ucmainwindow_p_p.h'
--- src/UbuntuToolkit/ucmainwindow_p_p.h 1970-01-01 00:00:00 +0000
+++ src/UbuntuToolkit/ucmainwindow_p_p.h 2017-01-30 11:36:29 +0000
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UCMAINWINDOW_P_P_H
+#define UCMAINWINDOW_P_P_H
+
+#include <UbuntuToolkit/private/ucmainwindow_p.h>
+
+#include <QtQml/QQmlProperty>
+
+#include <QtQuick/private/qquickwindow_p.h>
+
+UT_NAMESPACE_BEGIN
+
+class UCMainWindow;
+class UCPopupContext;
+
+class UCMainWindowPrivate : public QQuickWindowPrivate
+{
+ Q_DECLARE_PUBLIC(UCMainWindow)
+
+public:
+ UCMainWindowPrivate();
+ void init();
+
+ QString m_applicationName;
+ QString m_organizationName;
+ UCPopupContext* m_actionContext = nullptr;
+ UCUnits* m_units = nullptr;
+ QQuickItem* m_visualRoot = nullptr;
+
+};
+
+UT_NAMESPACE_END
+
+#endif // UCMAINWINDOW_P_P_H
=== modified file 'src/UbuntuToolkit/ucunits.cpp'
--- src/UbuntuToolkit/ucunits.cpp 2016-09-12 09:03:50 +0000
+++ src/UbuntuToolkit/ucunits.cpp 2017-01-30 11:36:29 +0000
@@ -97,23 +97,54 @@
* isolated from Qt's own scaling concept.
*/
+UCUnits::UCUnits(QWindow *parent) :
+ QObject(parent),
+ m_devicePixelRatio(parent->devicePixelRatio())
+{
+ m_gridUnit = getenvFloat(ENV_GRID_UNIT_PX, DEFAULT_GRID_UNIT_PX * m_devicePixelRatio);
+
+ if (!qEnvironmentVariableIsSet(ENV_GRID_UNIT_PX)) {
+ QObject::connect(parent, &QWindow::screenChanged,
+ this, &UCUnits::screenChanged);
+ m_screen = parent->screen();
+ if (m_screen)
+ QObject::connect(m_screen, &QScreen::physicalDotsPerInchChanged,
+ this, &UCUnits::devicePixelRatioChanged);
+ }
+}
+
+void UCUnits::screenChanged(QScreen *screen)
+{
+ if (m_screen)
+ QObject::disconnect(m_screen, &QScreen::physicalDotsPerInchChanged,
+ this, &UCUnits::devicePixelRatioChanged);
+ m_screen = screen;
+ QObject::connect(m_screen, &QScreen::physicalDotsPerInchChanged,
+ this, &UCUnits::devicePixelRatioChanged);
+ m_devicePixelRatio = screen->devicePixelRatio();
+ setGridUnit(DEFAULT_GRID_UNIT_PX * m_devicePixelRatio);
+}
+
+void UCUnits::devicePixelRatioChanged(qreal dpi)
+{
+ m_devicePixelRatio = dpi;
+ setGridUnit(DEFAULT_GRID_UNIT_PX * m_devicePixelRatio);
+}
+
UCUnits *UCUnits::m_units = nullptr;
UCUnits::UCUnits(QObject *parent) :
QObject(parent),
m_devicePixelRatio(qGuiApp->devicePixelRatio())
{
- // If GRID_UNIT_PX set, always use it. If not, 1GU := DEFAULT_GRID_UNIT_PX * m_devicePixelRatio
- if (qEnvironmentVariableIsSet(ENV_GRID_UNIT_PX)) {
- m_gridUnit = getenvFloat(ENV_GRID_UNIT_PX, DEFAULT_GRID_UNIT_PX);
- } else {
- m_gridUnit = DEFAULT_GRID_UNIT_PX * m_devicePixelRatio;
- }
+ m_gridUnit = getenvFloat(ENV_GRID_UNIT_PX, DEFAULT_GRID_UNIT_PX * m_devicePixelRatio);
- auto nativeInterface = qGuiApp->platformNativeInterface();
- if (nativeInterface) {
- QObject::connect(nativeInterface, &QPlatformNativeInterface::windowPropertyChanged,
- this, &UCUnits::windowPropertyChanged);
+ if (!qEnvironmentVariableIsSet(ENV_GRID_UNIT_PX)) {
+ auto nativeInterface = qGuiApp->platformNativeInterface();
+ if (nativeInterface) {
+ QObject::connect(nativeInterface, &QPlatformNativeInterface::windowPropertyChanged,
+ this, &UCUnits::windowPropertyChanged);
+ }
}
}
=== modified file 'src/UbuntuToolkit/ucunits_p.h'
--- src/UbuntuToolkit/ucunits_p.h 2016-09-09 17:49:07 +0000
+++ src/UbuntuToolkit/ucunits_p.h 2017-01-30 11:36:29 +0000
@@ -23,6 +23,7 @@
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QUrl>
+#include <QtGui/QWindow>
#include <UbuntuToolkit/ubuntutoolkitglobal.h>
@@ -48,6 +49,7 @@
}
explicit UCUnits(QObject *parent = 0);
+ explicit UCUnits(QWindow *parent);
~UCUnits();
Q_INVOKABLE float dp(float value);
Q_INVOKABLE float gu(float value);
@@ -68,10 +70,13 @@
private Q_SLOTS:
void windowPropertyChanged(QPlatformWindow *window, const QString &propertyName);
+ void screenChanged(QScreen *screen);
+ void devicePixelRatioChanged(qreal dpi);
private:
static UCUnits *m_units;
float m_devicePixelRatio;
+ QScreen *m_screen;
float m_gridUnit;
};
=== modified file 'src/imports/Components/1.3/TextArea.qml'
--- src/imports/Components/1.3/TextArea.qml 2016-11-10 15:36:49 +0000
+++ src/imports/Components/1.3/TextArea.qml 2017-01-30 11:36:29 +0000
@@ -17,6 +17,7 @@
import QtQuick 2.4
import Ubuntu.Components 1.3 as Ubuntu
import Ubuntu.Components.Popups 1.3
+import Ubuntu.Components.Private 1.3 as Private
/*!
\qmltype TextArea
@@ -581,6 +582,16 @@
}
/*!
+ Appends a new paragraph with text to the end of the TextArea.
+
+ In order to append without inserting a new paragraph, call TextArea.insert(TextArea.length, text) instead.
+ */
+ function append(text)
+ {
+ editor.append(text);
+ }
+
+ /*!
Returns the text position closest to pixel position (x, y).
Position 0 is before the first character, position 1 is after the first
@@ -736,6 +747,15 @@
editor.linkActivated.connect(control.linkActivated);
}
+ Connections {
+ target: Private.UCContentHub
+ onPasteSelected: {
+ if (targetItem === control) {
+ control.paste(data)
+ }
+ }
+ }
+
// activation area on mouse click
// the editor activates automatically when pressed in the editor control,
// however that one can be slightly spaced to the main control area
@@ -806,6 +826,11 @@
control.paste("\n");
}
event.accepted = true;
+ } else if ((event.key === Qt.Key_V) && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)){
+ if (Private.UCContentHub.canPaste) {
+ Private.UCContentHub.requestPaste(control);
+ event.accepted = true;
+ }
} else {
event.accepted = false;
}
=== modified file 'src/imports/Components/1.3/TextField.qml'
--- src/imports/Components/1.3/TextField.qml 2016-09-20 07:23:24 +0000
+++ src/imports/Components/1.3/TextField.qml 2017-01-30 11:36:29 +0000
@@ -17,6 +17,7 @@
import QtQuick 2.4
import Ubuntu.Components 1.3 as Ubuntu
import Ubuntu.Components.Popups 1.3
+import Ubuntu.Components.Private 1.3 as Private
/*!
\qmltype TextField
@@ -637,7 +638,6 @@
control.triggered(control.text)
}
-
/*!
Copies the currently selected text to the system clipboard.
*/
@@ -834,6 +834,28 @@
// internals
+ Connections {
+ target: Private.UCContentHub
+ onPasteSelected: {
+ if (targetItem === control) {
+ control.paste(data)
+ }
+ }
+ }
+
+ Keys.onPressed: {
+ if (readOnly)
+ return;
+ if ((event.key === Qt.Key_V) && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)){
+ if (Private.UCContentHub.canPaste) {
+ Private.UCContentHub.requestPaste(control);
+ event.accepted = true;
+ }
+ } else {
+ event.accepted = false;
+ }
+ }
+
// Overload focus mechanics to avoid bubbling up of focus from children
activeFocusOnPress: true
=== modified file 'src/imports/Components/1.3/TextInputPopover.qml'
--- src/imports/Components/1.3/TextInputPopover.qml 2015-12-14 15:15:46 +0000
+++ src/imports/Components/1.3/TextInputPopover.qml 2017-01-30 11:36:29 +0000
@@ -17,6 +17,7 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
import Ubuntu.Components.Popups 1.3
+import Ubuntu.Components.Private 1.3 as Private
Popover {
id: popover
@@ -54,12 +55,12 @@
}
},
Action {
- text: i18n.dtr('ubuntu-ui-toolkit', "Paste")
+ text: i18n.dtr('ubuntu-ui-toolkit', "Paste...")
iconName: "edit-paste"
- enabled: target && target.canPaste
+ enabled: target && Private.UCContentHub.canPaste
onTriggered: {
PopupUtils.close(popover);
- target.paste();
+ Private.UCContentHub.requestPaste(target);
}
}
]
=== modified file 'src/imports/Components/1.3/Toolbar.qml'
--- src/imports/Components/1.3/Toolbar.qml 2016-05-25 12:48:10 +0000
+++ src/imports/Components/1.3/Toolbar.qml 2017-01-30 11:36:29 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Canonical Ltd.
+ * Copyright 2016 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -127,6 +127,17 @@
print("WARNING: Toolbar with more than one leading actions is not supported.");
}
}
+ StyleHints {
+ backgroundColor: "transparent" // background is drawn by the toolbarStyle
+ buttons {
+ foregroundColor: toolbar.__styleInstance.buttons.foregroundColor
+ pressedForegroundColor: toolbar.__styleInstance.buttons.pressedForegroundColor
+ disabledForegroundColor: toolbar.__styleInstance.buttons.disabledForegroundColor
+ backgroundColor: toolbar.__styleInstance.buttons.backgroundColor
+ pressedBackgroundColor: toolbar.__styleInstance.buttons.pressedBackgroundColor
+ disabledBackgroundColor: toolbar.__styleInstance.buttons.disabledBackgroundColor
+ }
+ }
}
/*!
@@ -150,19 +161,29 @@
readonly property alias trailingActionBar: trailing
ActionBar {
id: trailing
+ styleName: "ScrollingActionBarStyle"
anchors {
+ left: leading.right
right: parent.right
top: parent.top
bottom: parent.bottom
+ leftMargin: units.gu(1)
rightMargin: units.gu(1)
}
- numberOfSlots: 8
delegate: toolbar.__styleInstance.defaultDelegate
- Component.onCompleted: {
- if (actions && actions.length > 8) {
- print("WARNING: Toolbar with more than one leading actions is not supported.");
+ StyleHints {
+ backgroundColor: "transparent" // background is drawn by the toolbarStyle
+ buttons {
+ foregroundColor: toolbar.__styleInstance.buttons.foregroundColor
+ pressedForegroundColor: toolbar.__styleInstance.buttons.pressedForegroundColor
+ disabledForegroundColor: toolbar.__styleInstance.buttons.disabledForegroundColor
+ backgroundColor: toolbar.__styleInstance.buttons.backgroundColor
+ pressedBackgroundColor: toolbar.__styleInstance.buttons.pressedBackgroundColor
+ disabledBackgroundColor: toolbar.__styleInstance.buttons.disabledBackgroundColor
+ }
+ scrollButtons {
+ backgroundColor: toolbar.__styleInstance.backgroundColor // must be opaque to hide the icon buttons
}
}
-
}
}
=== modified file 'src/imports/Components/1.3/UbuntuListView.qml'
--- src/imports/Components/1.3/UbuntuListView.qml 2016-09-22 17:39:59 +0000
+++ src/imports/Components/1.3/UbuntuListView.qml 2017-01-30 11:36:29 +0000
@@ -199,10 +199,10 @@
color: root.activeFocus
? theme.palette.focused.background
: theme.palette.selected.background
- width: root.currentItem.width
- height: root.currentItem.height
+ width: root.currentItem ? root.currentItem.width : 0
+ height: root.currentItem ? root.currentItem.height : 0
// FIXME: use opacity yet, until we fix the palette's disabled.background color
- opacity: root.currentItem.enabled ? 1.0 : 0.5
+ opacity: root.currentItem && root.currentItem.enabled ? 1.0 : 0.5
}
highlightMoveDuration: 50
}
=== modified file 'src/imports/Components/Popups/1.3/PopupBase.qml'
--- src/imports/Components/Popups/1.3/PopupBase.qml 2016-04-13 19:32:20 +0000
+++ src/imports/Components/Popups/1.3/PopupBase.qml 2017-01-30 11:36:29 +0000
@@ -119,6 +119,14 @@
/*!
\internal
+ The function saves the active focus for later.
+ */
+ function __setPreviousActiveFocusItem(item) {
+ stateWrapper.prevFocus = item;
+ }
+
+ /*!
+ \internal
Foreground component excluded from InverseMouseArea
*/
property Item __foreground
@@ -177,30 +185,14 @@
/*! \internal */
onParentChanged: stateWrapper.rootItem = QuickUtils.rootItem(popupBase)
Component.onCompleted: {
- stateWrapper.saveActiveFocus();
stateWrapper.rootItem = QuickUtils.rootItem(popupBase);
}
Item {
id: stateWrapper
property Item rootItem: QuickUtils.rootItem(popupBase)
-
- property bool windowIsValid: typeof window != "undefined"
property Item prevFocus
- function saveActiveFocus() {
- // 'window' context property is exposed to QML after component completion
- // before rendering is complete, therefore a simple 'if (window)' check is
- // not enough.
- if (windowIsValid) {
- prevFocus = window.activeFocusItem;
- windowIsValidChanged.disconnect(saveActiveFocus);
- } else {
- // connect the function so we can save the original focus item
- windowIsValidChanged.connect(saveActiveFocus);
- }
- }
-
function restoreActiveFocus() {
if (prevFocus) {
if (prevFocus.hasOwnProperty("requestFocus")) {
=== modified file 'src/imports/Components/Popups/1.3/popupUtils.js'
--- src/imports/Components/Popups/1.3/popupUtils.js 2016-09-15 15:53:27 +0000
+++ src/imports/Components/Popups/1.3/popupUtils.js 2017-01-30 11:36:29 +0000
@@ -32,7 +32,12 @@
\a caller should be given when a \l ComposerSheet or \l Dialog is specified using a URL
and opened inside a \b Window. If not, the application's root item will be the dismiss area.
- Returns a popop object, which can be closed using \l close.
+ Returns a popup object, which can be closed using \l close.
+
+ Note that popups created from a file or component will be created as children
+ of the root item. If that's not a good choice, a \b MainWindow used as the
+ root item can set the \b visualRoot property an arbitrary \b Item that acts
+ as the parent of all popups.
\qml
import Ubuntu.Components 1.3
@@ -66,6 +71,8 @@
}
var popupObject;
+ // If there's an active item, save it so we can restore it later
+ var prevFocusItem = (typeof window !== "undefined") ? window.activeFocusItem : null;
if (params !== undefined) {
popupObject = popupComponent.createObject(rootObject, params);
} else {
@@ -75,8 +82,11 @@
print(popupComponent.errorString().slice(0, -1));
print("PopupUtils.open(): Failed to create the popup object.");
return;
- } else if (popupObject.hasOwnProperty("caller") && caller)
+ } else if (popupObject.hasOwnProperty("caller") && caller) {
popupObject.caller = caller;
+ } else if (popupObject.hasOwnProperty("__setPreviousActiveFocusItem")) {
+ popupObject.__setPreviousActiveFocusItem(prevFocusItem);
+ }
// if caller is specified, connect its cleanup to the popup's close
// so popups will be removed together with the caller.
=== modified file 'src/imports/Components/Styles/1.3/ToolbarStyle.qml'
--- src/imports/Components/Styles/1.3/ToolbarStyle.qml 2015-12-02 13:02:44 +0000
+++ src/imports/Components/Styles/1.3/ToolbarStyle.qml 2017-01-30 11:36:29 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Canonical Ltd.
+ * Copyright 2016 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -25,6 +25,16 @@
*/
Item {
/*!
+ The color of the background of the action bar.
+ */
+ property color backgroundColor
+
+ /*!
+ Configuration of the colors of the action buttons in the action bar.
+ */
+ readonly property ActionItemProperties buttons: ActionItemProperties { }
+
+ /*!
The default action delegate if the styled item does
not provide a different delegate.
*/
=== modified file 'src/imports/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml'
--- src/imports/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml 2016-10-03 14:07:38 +0000
+++ src/imports/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml 2017-01-30 11:36:29 +0000
@@ -113,7 +113,11 @@
model: listViewContainer.visibleActions
highlight: Rectangle {
- color: theme.palette.focused.background
+ color: "transparent"
+ border {
+ color: theme.palette.selected.focus
+ width: 2
+ }
visible: actionsListView.activeFocus
width: actionsListView.currentItem.width
height: actionsListView.currentItem.height
=== modified file 'src/imports/Components/Themes/Ambiance/1.3/ToolbarStyle.qml'
--- src/imports/Components/Themes/Ambiance/1.3/ToolbarStyle.qml 2016-09-19 07:24:45 +0000
+++ src/imports/Components/Themes/Ambiance/1.3/ToolbarStyle.qml 2017-01-30 11:36:29 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Canonical Ltd.
+ * Copyright 2016 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -24,16 +24,49 @@
// reduce toolbar height on phone in landscape orientation:
implicitHeight: Screen.height > units.gu(50) ? units.gu(4) : units.gu(3)
+ backgroundColor: theme.palette.normal.background
+ buttons {
+ foregroundColor: theme.palette.normal.backgroundText
+ pressedForegroundColor: buttons.foregroundColor
+ disabledForegroundColor: theme.palette.disabled.backgroundText
+ backgroundColor: "transparent" // background is already colored
+ pressedBackgroundColor: theme.palette.highlighted.background
+ disabledBackgroundColor: buttons.backgroundColor
+ }
+
/*!
The default action delegate if the styled item does
not provide a delegate.
*/
- defaultDelegate: AbstractButton {
- style: IconButtonStyle { }
- objectName: action.objectName + "_button"
- height: parent ? parent.height : undefined
+ defaultDelegate: ListItem {
width: units.gu(4)
- action: modelData
- activeFocusOnTab: true
+ height: toolbarStyle.height
+ enabled: modelData.enabled
+ objectName: modelData.objectName + "_button"
+ onClicked: button.trigger()
+ AbstractButton {
+ id: button
+ anchors.fill: parent
+ style: IconButtonStyle {
+ foregroundColor: button.pressed ?
+ toolbarStyle.buttons.pressedForegroundColor :
+ button.enabled ?
+ toolbarStyle.buttons.foregroundColor :
+ toolbarStyle.buttons.disabledForegroundColor
+ backgroundColor: button.pressed ?
+ toolbarStyle.buttons.pressedBackgroundColor :
+ button.enabled ?
+ toolbarStyle.buttons.backgroundColor :
+ toolbarStyle.buttons.disabledBackgroundColor
+ }
+ action: modelData
+ activeFocusOnTab: false
+ }
+ divider.visible: false
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: toolbarStyle.backgroundColor
}
}
=== modified file 'tests/autopilot/ubuntuuitoolkit/__init__.py'
--- tests/autopilot/ubuntuuitoolkit/__init__.py 2016-04-21 03:23:24 +0000
+++ tests/autopilot/ubuntuuitoolkit/__init__.py 2017-01-30 11:36:29 +0000
@@ -18,7 +18,6 @@
from ubuntuuitoolkit import (
base,
- environment,
fixture_setup,
tests,
ubuntu_scenarios
@@ -59,7 +58,6 @@
'ActionBar',
'check_autopilot_version',
'CheckBox',
- 'environment',
'fixture_setup',
'get_keyboard',
'get_pointing_device',
=== removed file 'tests/autopilot/ubuntuuitoolkit/environment.py'
--- tests/autopilot/ubuntuuitoolkit/environment.py 2014-08-28 19:05:23 +0000
+++ tests/autopilot/ubuntuuitoolkit/environment.py 1970-01-01 00:00:00 +0000
@@ -1,68 +0,0 @@
-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
-#
-# Copyright (C) 2014 Canonical Ltd.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; version 3.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import logging
-import subprocess
-
-from autopilot import logging as autopilot_logging
-
-
-logger = logging.getLogger(__name__)
-
-
-def is_initctl_env_var_set(variable, global_=False):
- """Check True if an initctl environment variable is set.
-
- :param variable: The name of the variable to check.
- :param global: if True, the method will operate on the global environment
- table. Default is False.
- :return: True if the variable is set. False otherwise.
-
- """
- try:
- get_initctl_env_var(variable, global_)
- return True
- except subprocess.CalledProcessError:
- return False
-
-
-def get_initctl_env_var(variable, global_=False):
- """Return the value of an initctl environment variable."""
- command = ['/sbin/initctl', 'get-env', variable]
- if global_:
- command += ['--global']
- output = subprocess.check_output(
- command, stderr=subprocess.STDOUT, universal_newlines=True)
- return output.rstrip()
-
-
-@autopilot_logging.log_action(logger.info)
-def set_initctl_env_var(variable, value, global_=False):
- """Set the value of an initctl environment variable."""
- command = ['/sbin/initctl', 'set-env', '%s=%s' % (variable, value)]
- if global_:
- command += ['--global']
- subprocess.call(command, stderr=subprocess.STDOUT, universal_newlines=True)
-
-
-@autopilot_logging.log_action(logger.info)
-def unset_initctl_env_var(variable, global_=False):
- """Remove an initctl environment variable."""
- command = ['/sbin/initctl', 'unset-env', variable]
- if global_:
- command += ['--global']
- subprocess.call(
- command, stderr=subprocess.STDOUT, universal_newlines=True)
=== modified file 'tests/autopilot/ubuntuuitoolkit/fixture_setup.py'
--- tests/autopilot/ubuntuuitoolkit/fixture_setup.py 2015-07-09 21:55:03 +0000
+++ tests/autopilot/ubuntuuitoolkit/fixture_setup.py 2017-01-30 11:36:29 +0000
@@ -26,7 +26,7 @@
from autopilot import display
from gi.repository import Gio
-from ubuntuuitoolkit import base, environment
+from ubuntuuitoolkit import base
DEFAULT_QML_FILE_CONTENTS = ("""
@@ -156,45 +156,6 @@
url_dispatcher_file_path, shell=True)
-class InitctlEnvironmentVariable(fixtures.Fixture):
- """Set the value of initctl environment variables."""
-
- def __init__(self, global_=False, **kwargs):
- super().__init__()
- # Added one level of indirection to be able to spy the calls to
- # environment during tests.
- self.environment = environment
- self.variables = kwargs
- self.global_ = global_
-
- def setUp(self):
- super().setUp()
- for variable, value in self.variables.items():
- self._add_variable_cleanup(variable)
- if value is None:
- self.environment.unset_initctl_env_var(
- variable, global_=self.global_)
- else:
- self.environment.set_initctl_env_var(
- variable, value, global_=self.global_)
-
- def _add_variable_cleanup(self, variable):
- if self.environment.is_initctl_env_var_set(
- variable, global_=self.global_):
- original_value = self.environment.get_initctl_env_var(
- variable, global_=self.global_)
- self.addCleanup(
- self.environment.set_initctl_env_var,
- variable,
- original_value,
- global_=self.global_)
- else:
- self.addCleanup(
- self.environment.unset_initctl_env_var,
- variable,
- global_=self.global_)
-
-
class FakeHome(fixtures.Fixture):
# We copy the Xauthority file to allow executions using XVFB. If it is not
@@ -210,10 +171,6 @@
self.directory = self._make_directory_if_not_specified()
if self.should_copy_xauthority_file:
self._copy_xauthority_file(self.directory)
- # We patch both environment variables so it works on the desktop and on
- # the phone.
- self.useFixture(
- InitctlEnvironmentVariable(HOME=self.directory))
self.useFixture(
fixtures.EnvironmentVariable('HOME', newvalue=self.directory))
=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/__init__.py'
--- tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2015-12-09 14:21:53 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2017-01-30 11:36:29 +0000
@@ -16,6 +16,7 @@
"""Ubuntu UI Toolkit autopilot tests."""
+import fixtures
import os
import tempfile
@@ -106,9 +107,7 @@
desktop_file_name = os.path.basename(
fake_application.desktop_file_path)
application_name, _ = os.path.splitext(desktop_file_name)
- self.app = self.launch_upstart_application(
- application_name,
- emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)
+ self.app = self.launch_test_application(application_name)
def use_local_modules(self, local_modules_path):
env_vars = [
@@ -116,10 +115,9 @@
'QML2_IMPORT_PATH',
'UBUNTU_UI_TOOLKIT_THEMES_PATH'
]
- kwargs = {'global_': True}
for env in env_vars:
- kwargs[env] = local_modules_path
- self.useFixture(fixture_setup.InitctlEnvironmentVariable(**kwargs))
+ self.useFixture(fixtures.EnvironmentVariable(env,
+ local_modules_path))
class QMLStringAppTestCase(UbuntuUIToolkitWithFakeAppRunningTestCase):
=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/components/test_units.py'
--- tests/autopilot/ubuntuuitoolkit/tests/components/test_units.py 2015-04-14 21:02:06 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/components/test_units.py 2017-01-30 11:36:29 +0000
@@ -23,10 +23,7 @@
import fixtures
-from ubuntuuitoolkit import (
- fixture_setup,
- units
-)
+from ubuntuuitoolkit import units
logger = logging.getLogger(__name__)
@@ -52,8 +49,6 @@
def setUp(self):
self.useFixture(fixtures.EnvironmentVariable(
'GRID_UNIT_PX', self.grid_unit_px))
- self.useFixture(fixture_setup.InitctlEnvironmentVariable(
- global_=True, GRID_UNIT_PX=self.grid_unit_px))
super().setUp()
def test_gu(self):
=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/gallery/__init__.py'
--- tests/autopilot/ubuntuuitoolkit/tests/gallery/__init__.py 2016-08-10 14:05:48 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/gallery/__init__.py 2017-01-30 11:36:29 +0000
@@ -17,7 +17,6 @@
"""Tests for the Ubuntu UI Toolkit Gallery"""
import os
-import shutil
from autopilot.matchers import Eventually
from testtools.matchers import Equals
=== removed file 'tests/autopilot/ubuntuuitoolkit/tests/test_environment.py'
--- tests/autopilot/ubuntuuitoolkit/tests/test_environment.py 2014-08-29 16:12:02 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/test_environment.py 1970-01-01 00:00:00 +0000
@@ -1,89 +0,0 @@
-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
-#
-# Copyright (C) 2014 Canonical Ltd.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; version 3.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import uuid
-
-import testtools
-
-from ubuntuuitoolkit import environment
-
-
-class InitctlEnvironmentVariableTestCase(testtools.TestCase):
-
- def test_is_environment_variable_set_with_unset_variable(self):
- """Test that is_initctl_env_var_set returns False for unset vars."""
- variable = 'I do not exist {}'.format(uuid.uuid1())
- self.assertFalse(environment.is_initctl_env_var_set(variable))
-
- def test_is_environment_variable_set_with_set_variable(self):
- """Test that is_initctl_env_var_set returns True for existing vars."""
- variable = 'Test variable to set {}'.format(uuid.uuid1())
- self.addCleanup(environment.unset_initctl_env_var, variable)
-
- environment.set_initctl_env_var(variable, 'dummy')
-
- self.assertTrue(environment.is_initctl_env_var_set(variable))
-
- def test_get_environment_variable(self):
- """Test that get_initctl_env_var returns the right value."""
- variable = 'Test variable to get {}'.format(uuid.uuid1())
- self.addCleanup(environment.unset_initctl_env_var, variable)
- environment.set_initctl_env_var(variable, 'test value')
-
- self.assertEqual(
- 'test value', environment.get_initctl_env_var(variable))
-
- def test_unset_environment_variable(self):
- """Test that unset_initctl_env_var removes the variable."""
- variable = 'Test variable to unset {}'.format(uuid.uuid1())
- environment.set_initctl_env_var(variable, 'dummy')
-
- environment.unset_initctl_env_var(variable)
-
- self.assertFalse(environment.is_initctl_env_var_set(variable))
-
- def test_unset_environment_variable_with_unset_variable(self):
- """Test that unset_initctl_env_var does nothing with unset var."""
- variable = 'I do not exist {}'.format(uuid.uuid1())
-
- environment.unset_initctl_env_var(variable)
-
- self.assertFalse(environment.is_initctl_env_var_set(variable))
-
- def test_is_global_environment_variable_set_with_unset_variable(self):
- """Test is_initctl_env_var_set returns False for unset global vars."""
- variable = 'I do not exist global {}'.format(uuid.uuid1())
-
- self.assertFalse(environment.is_initctl_env_var_set(
- variable, global_=True))
-
- def test_get_global_environment_variable(self):
- """Test that get_initctl_env_var returns the right global value."""
- variable = 'Test variable to get {}'.format(uuid.uuid1())
- self.addCleanup(
- environment.unset_initctl_env_var, variable, global_=True)
- environment.set_initctl_env_var(variable, 'test value', global_=True)
-
- self.assertEqual(
- 'test value',
- environment.get_initctl_env_var(variable, global_=True))
-
- def test_unset_global_environment_variable(self):
- """Test that unset_initctl_env_var removes the global variable."""
- variable = 'Test variable to unset {}'.format(uuid.uuid1())
-
- environment.set_initctl_env_var(variable, 'dummy', global_=True)
- environment.unset_initctl_env_var(variable, global_=True)
=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py'
--- tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py 2015-07-09 21:55:03 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/test_fixture_setup.py 2017-01-30 11:36:29 +0000
@@ -19,18 +19,16 @@
import tempfile
from unittest import mock
-import testscenarios
import testtools
from autopilot import (
display,
- introspection,
platform,
testcase as autopilot_testcase
)
from autopilot.matchers import Eventually
from testtools.matchers import Contains, Equals, FileExists, Not
-from ubuntuuitoolkit import base, environment, fixture_setup, tests
+from ubuntuuitoolkit import base, fixture_setup, tests
class FakeApplicationTestCase(testtools.TestCase):
@@ -190,9 +188,6 @@
url_dispatcher_protocols=['testprotocol'])
self.useFixture(fake_application)
- self.useFixture(fixture_setup.InitctlEnvironmentVariable(
- QT_LOAD_TESTABILITY=1))
-
self.addCleanup(
subprocess.check_output,
['ubuntu-app-stop', fake_application.application_name])
@@ -202,135 +197,11 @@
pid = int(subprocess.check_output(
['ubuntu-app-pid', fake_application.application_name]).strip())
-
- application = introspection.get_proxy_object_for_existing_process(
- pid=pid)
-
- # We can select a component from the application.
- application.select_single('Label', objectName='testLabel')
-
-
-class InitctlEnvironmentVariableTestCase(testscenarios.TestWithScenarios):
-
- scenarios = [
- ('global_variable', {'global_': True}),
- ('local_variable', {'global_': False})
- ]
-
- def set_original_value(self, value):
- self.addCleanup(
- environment.unset_initctl_env_var, 'testenvvarforfixture',
- global_=self.global_)
- environment.set_initctl_env_var('testenvvarforfixture',
- value, global_=self.global_)
-
- def create_fixture(self, value):
- self.initctl_env_var = fixture_setup.InitctlEnvironmentVariable(
- testenvvarforfixture=value, global_=self.global_)
-
- def assertValueIs(self, expected_value):
- self.assertEqual(
- expected_value,
- environment.get_initctl_env_var(
- 'testenvvarforfixture', global_=self.global_))
-
- def assertVariableIsNotSet(self):
- self.assertFalse(
- environment.is_initctl_env_var_set(
- 'testenvvarforfixture', global_=self.global_))
-
- def assertTestIsSuccessful(self, expected_value, test_name):
- result = testtools.TestResult()
-
- class TestWithInitctlEnvVar(testtools.TestCase):
- def setUp(inner):
- super().setUp()
- inner.useFixture(self.initctl_env_var)
-
- def test_value_set(inner):
- self.assertValueIs(expected_value)
-
- def test_variable_not_set(inner):
- self.assertVariableIsNotSet()
-
- TestWithInitctlEnvVar(test_name).run(result)
- self.assertTrue(
- result.wasSuccessful(), 'Failed to set the environment variable.')
-
- def test_use_initctl_environment_variable_to_set_unexisting_variable(self):
- """Test the initctl env var fixture when the var is unset.
-
- During the test, the new value must be in place.
- After the test, the variable must be unset again.
-
- """
- self.create_fixture('test value')
- self.assertTestIsSuccessful('test value', 'test_value_set')
- self.assertVariableIsNotSet()
-
- def test_use_initctl_environment_variable_to_set_existing_variable(self):
- """Test the initctl env var fixture when the var is unset.
-
- During the test, the new value must be in place.
- After the test, the old value must be set again.
-
- """
- self.set_original_value('original test value')
- self.create_fixture('new test value')
- self.assertTestIsSuccessful('new test value', 'test_value_set')
- self.assertValueIs('original test value')
-
- def test_use_initctl_environment_variable_to_unset_existing_variable(self):
- """Test the initctl env var fixture to unset a variable.
-
- During the test, the variable must be unset.
- After the test, the old value must be set again.
-
- """
- self.set_original_value('original test value')
- self.create_fixture(None)
- self.assertTestIsSuccessful(None, 'test_variable_not_set')
- self.assertValueIs('original test value',)
-
- def test_use_initctl_environment_variable_to_unset_nonexisting_variable(
- self):
- """Test the initctl env var fixture to unset a variable.
-
- During the test, the variable must be unset.
- After the test, the variable must remain unset.
-
- """
- self.create_fixture(None)
- self.assertTestIsSuccessful(None, 'test_variable_not_set')
- self.assertVariableIsNotSet()
+ self.assertGreater(pid, 0)
class FakeHomeTestCase(testtools.TestCase):
- def test_fake_home_fixture_patches_initctl_env_var(self):
- original_home = environment.get_initctl_env_var('HOME')
- fake_home = original_home + 'fake'
- result = testtools.TestResult()
-
- def inner_test():
- class TestWithFakeHome(testtools.TestCase):
- def test_it(self):
- fake_home_fixture = fixture_setup.FakeHome(fake_home)
- fake_home_fixture.should_copy_xauthority_file = False
- self.useFixture(fake_home_fixture)
- self.assertEqual(
- fake_home, environment.get_initctl_env_var('HOME'))
- return TestWithFakeHome('test_it')
-
- inner_test().run(result)
-
- self.assertTrue(
- result.wasSuccessful(),
- 'Failed to fake the home: {}'.format(result.errors))
- self.assertEqual(
- original_home,
- environment.get_initctl_env_var('HOME'))
-
def test_fake_home_fixture_patches_env_var(self):
original_home = os.environ.get('HOME')
fake_home = tempfile.gettempdir()
=== renamed file 'tests/autopilot/ubuntuuitoolkit/tests/test_launcher.window.qml' => 'tests/autopilot/ubuntuuitoolkit/tests/test_launcher.mainwindow.qml'
--- tests/autopilot/ubuntuuitoolkit/tests/test_launcher.window.qml 2016-07-12 12:05:11 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/test_launcher.mainwindow.qml 2017-01-30 11:36:29 +0000
@@ -17,23 +17,30 @@
import QtQuick 2.4
import QtQuick.Window 2.2 // Not Ubuntu.Test
import Ubuntu.Components 1.3
+import Ubuntu.Components.Labs 1.3
-Window {
- title: "Hello World"
+MainWindow {
+ title: i18n.tr("Hello World")
minimumWidth: units.gu(30)
minimumHeight: units.gu(50)
maximumWidth: units.gu(90)
maximumHeight: units.gu(120)
- MainView {
+
+ Item {
+ anchors.fill: parent
objectName: "mainView"
Page {
- title: "Launcher/Window"
+ anchors.fill: parent
+ header: PageHeader {
+ title: "Launcher/Window"
+ }
Column {
+ anchors.top: parent.header.bottom
Label {
objectName: "label"
- text: "Lorem ipsum dolor sit amet"
+ text: i18n.tr("Lorem ipsum dolor sit amet")
width: units.gu(25)
height: units.gu(25)
}
=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_launcher.py'
--- tests/autopilot/ubuntuuitoolkit/tests/test_launcher.py 2016-07-12 12:05:11 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/test_launcher.py 2017-01-30 11:36:29 +0000
@@ -49,6 +49,18 @@
Eventually(Equals("Lorem ipsum dolor sit amet")))
+class LauncherMainWindowTestCase(tests.QMLFileAppTestCase):
+ path = os.path.abspath(__file__)
+ dir_path = os.path.dirname(path)
+ test_qml_file_path = os.path.join(
+ dir_path, 'test_launcher.mainwindow.qml')
+
+ def test_window_root_item(self):
+ label = self.main_view.select_single(objectName="label")
+ self.assertThat(label.text,
+ Eventually(Equals("Lorem ipsum dolor sit amet")))
+
+
class LauncherQtTestTestCase(tests.QMLFileAppTestCase):
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
=== added file 'tests/autopilot/ubuntuuitoolkit/tests/test_launcher.window.qml'
--- tests/autopilot/ubuntuuitoolkit/tests/test_launcher.window.qml 1970-01-01 00:00:00 +0000
+++ tests/autopilot/ubuntuuitoolkit/tests/test_launcher.window.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014-2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import QtQuick.Window 2.2 // Not Ubuntu.Test
+import Ubuntu.Components 1.3
+
+Window {
+ title: "Hello World"
+ minimumWidth: units.gu(30)
+ minimumHeight: units.gu(50)
+ maximumWidth: units.gu(90)
+ maximumHeight: units.gu(120)
+ MainView {
+ objectName: "mainView"
+
+ Page {
+ title: "Launcher/Window"
+
+ Column {
+ Label {
+ objectName: "label"
+ text: "Lorem ipsum dolor sit amet"
+ width: units.gu(25)
+ height: units.gu(25)
+ }
+ }
+ }
+ }
+}
=== modified file 'tests/autopilot/ubuntuuitoolkit/units.py'
--- tests/autopilot/ubuntuuitoolkit/units.py 2015-02-07 05:03:06 +0000
+++ tests/autopilot/ubuntuuitoolkit/units.py 2017-01-30 11:36:29 +0000
@@ -18,18 +18,12 @@
import os
-from ubuntuuitoolkit import environment
-
-
ENV_GRID_UNIT_PX = 'GRID_UNIT_PX'
DEFAULT_GRID_UNIT_PX = 8
def get_grid_unit():
grid_unit_px = os.environ.get(ENV_GRID_UNIT_PX, None)
- if not grid_unit_px and environment.is_initctl_env_var_set(
- ENV_GRID_UNIT_PX):
- grid_unit_px = environment.get_initctl_env_var(ENV_GRID_UNIT_PX)
return float(grid_unit_px or DEFAULT_GRID_UNIT_PX)
=== modified file 'tests/license/checklicense.sh'
--- tests/license/checklicense.sh 2015-11-09 15:13:21 +0000
+++ tests/license/checklicense.sh 2017-01-30 11:36:29 +0000
@@ -18,7 +18,7 @@
include_files="\.(c(c|pp|xx)?|h(h|pp|xx)?|p(l|m)|php|py(|x)|java|js|vala|qml)$"
exclude_dirs="(3rd_party|qrc_|moc_|_build|include)"
-allowed_licenses="(Canonical|Android|Google|Digia)"
+allowed_licenses="(Canonical|Android|Google|Digia|Qt Company Ltd)"
issues_count=`licensecheck --noconf -r * --copyright -m -c $include_files -i $exclude_dirs | egrep -v $allowed_licenses | wc -l`
if [ $issues_count -eq 0 ]; then
=== modified file 'tests/packaging-sorting.sh'
--- tests/packaging-sorting.sh 2016-05-02 08:37:47 +0000
+++ tests/packaging-sorting.sh 2017-01-30 11:36:29 +0000
@@ -15,28 +15,30 @@
#
# Author: Timo Jyrinki <timo.jyrinki@xxxxxxxxxxxxx>
+# Get the current script directory (compatible with Bash and ZSH)
+SCRIPT_DIR=`dirname ${BASH_SOURCE[0]-$0}`
+SCRIPT_DIR=`cd $SCRIPT_DIR && pwd`
+
+SRC=$SCRIPT_DIR/..
# Ensure packaging has gone through wrap-and-sort command
-
-if [ ! -f "/usr/bin/wrap-and-sort" ] ; then
- echo "Please install 'devscripts' package"
- exit 1
-fi
-
-cd $(dirname $0)
tmpdir=$(mktemp -d)
-cp -a ../debian $tmpdir
-
+cp -a $SRC/debian $tmpdir
wrap-and-sort -a -t -d $tmpdir/debian/
-# Verify control.gles which otherwise isn't picked up
-wrap-and-sort -a -t -d $tmpdir/debian/ -f $tmpdir/debian/control.gles
+[ $? == 2 ] && exit 2
+# Note: control.gles may be moved in gles builds
+if [ -f $tmpdir/debian/control.gles ] ; then
+ # Verify control.gles which otherwise isn't picked up
+ wrap-and-sort -a -t -d $tmpdir/debian/ -f $tmpdir/debian/control.gles
+fi
+[ $? == 2 ] && exit 2
-diff -urN ../debian $tmpdir/debian
+diff -urN $SRC/debian $tmpdir/debian
if [ $? == 1 ] ; then
echo
echo
echo "*******************************************************"
- echo "Please run 'wrap-and-sort -a -t' to clean up packaging."
+ echo "Please run 'wrap-and-sort -a -t; wrap-and-sort -a -t -f debian/control.gles' to clean up packaging."
echo "*******************************************************"
echo
exit 1
=== added directory 'tests/unit/contenthub'
=== added file 'tests/unit/contenthub/TextAreaPaste.qml'
--- tests/unit/contenthub/TextAreaPaste.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/contenthub/TextAreaPaste.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Item {
+ width: units.gu(20)
+ height: units.gu(20)
+
+ TextArea {
+ id: textArea
+ objectName: "textArea"
+ width: parent.width
+ height: units.gu(10)
+ focus: true
+ }
+}
+
=== added file 'tests/unit/contenthub/TextFieldPaste.qml'
--- tests/unit/contenthub/TextFieldPaste.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/contenthub/TextFieldPaste.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+Item {
+ width: units.gu(20)
+ height: units.gu(20)
+
+ TextField {
+ id: textField
+ objectName: "textField"
+ width: parent.width
+ focus: true
+ }
+}
+
=== added file 'tests/unit/contenthub/com.ubuntu.content.MockService.xml'
--- tests/unit/contenthub/com.ubuntu.content.MockService.xml 1970-01-01 00:00:00 +0000
+++ tests/unit/contenthub/com.ubuntu.content.MockService.xml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,17 @@
+<node>
+ <interface name="com.ubuntu.content.dbus.Service">
+ <method name="RequestPasteByAppId">
+ <arg name="appId" type="s" direction="in" />
+ </method>
+ <method name="PasteFormats">
+ <arg name="formats" type="as" direction="out" />
+ </method>
+ <signal name="PasteSelected">
+ <arg name="appId" type="s" />
+ <arg name="mimedata" type="ay" />
+ <arg name="outputAsHtml" type="b" />
+ </signal>
+ <signal name="PasteboardChanged">
+ </signal>
+ </interface>
+</node>
=== added file 'tests/unit/contenthub/contenthub.pro'
--- tests/unit/contenthub/contenthub.pro 1970-01-01 00:00:00 +0000
+++ tests/unit/contenthub/contenthub.pro 2017-01-30 11:36:29 +0000
@@ -0,0 +1,5 @@
+include(../test-include-x11.pri)
+QT += dbus gui
+SOURCES += tst_contenthub.cpp
+OTHER_FILES += TextAreaPaste.qml TextFieldPaste.qml
+DBUS_ADAPTORS += com.ubuntu.content.MockService.xml
=== added file 'tests/unit/contenthub/tst_contenthub.cpp'
--- tests/unit/contenthub/tst_contenthub.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit/contenthub/tst_contenthub.cpp 2017-01-30 11:36:29 +0000
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Arthur Mello <arthur.mello@xxxxxxxxxxxxx>
+ */
+
+#include "mockservice_adaptor.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMimeData>
+#include <QtDBus/QDBusConnection>
+#include <QtTest/QTest>
+#include <QtTest/QSignalSpy>
+#include <QtQuick/QQuickItem>
+#include <UbuntuToolkit/ubuntutoolkitmodule.h>
+#include <UbuntuToolkit/private/uccontenthub_p.h>
+
+#include "uctestcase.h"
+
+UT_USE_NAMESPACE
+
+class MockContentService : public QObject
+{
+ Q_OBJECT
+
+public:
+ MockContentService() {}
+ ~MockContentService() {}
+
+public Q_SLOTS:
+ void RequestPasteByAppId(const QString &appId)
+ {
+ Q_UNUSED(appId);
+ Q_EMIT PasteRequested();
+ }
+
+ QStringList PasteFormats()
+ {
+ QStringList formats;
+ formats << "text/plain" << "text/html" << "image/jpeg";
+ return formats;
+ }
+
+Q_SIGNALS:
+ void PasteSelected(const QString&, const QByteArray&, bool);
+ void PasteboardChanged();
+
+ void PasteRequested();
+};
+
+class tst_UCContentHub : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_UCContentHub() {}
+
+private:
+ MockContentService *mockService;
+ UCContentHub *contentHub;
+ QSignalSpy *pasteRequestedSpy;
+ QSignalSpy *pasteSelectedSpy;
+
+ const int testTimeout = 5000;
+
+ const QString dummyAppId = "DummyAppId";
+
+ const QString sampleText = "TextData";
+ const QString sampleHtml = "<html><body><p>HtmlTest</p></body></html>";
+
+ // Following serialize code is the same as used by content-hub
+ QByteArray serializeMimeData(const QMimeData &mimeData)
+ {
+ /*
+ Data format:
+ number of mime types (sizeof(int))
+ data layout ((4 * sizeof(int)) * number of mime types)
+ mime type string offset (sizeof(int))
+ mime type string size (sizeof(int))
+ data offset (sizeof(int))
+ data size (sizeof(int))
+ data (n bytes)
+ */
+
+ const int maxFormatsCount = 16;
+ const int maxBufferSize = 4 * 1024 * 1024; // 4 Mb
+
+ const QStringList formats = mimeData.formats();
+ const int formatCount = qMin(formats.size(), maxFormatsCount);
+ const int headerSize = sizeof(int) + (formatCount * 4 * sizeof(int));
+ int bufferSize = headerSize;
+
+ for (int i = 0; i < formatCount; i++)
+ bufferSize += formats[i].size() + mimeData.data(formats[i]).size();
+
+ QByteArray serializedMimeData;
+ if (bufferSize <= maxBufferSize) {
+ // Serialize data.
+ serializedMimeData.resize(bufferSize);
+ {
+ char *buffer = serializedMimeData.data();
+ int* header = reinterpret_cast<int*>(serializedMimeData.data());
+ int offset = headerSize;
+ header[0] = formatCount;
+ for (int i = 0; i < formatCount; i++) {
+ const QByteArray data = mimeData.data(formats[i]);
+ const int formatOffset = offset;
+ const int formatSize = formats[i].size();
+ const int dataOffset = offset + formatSize;
+ const int dataSize = data.size();
+ memcpy(&buffer[formatOffset], formats[i].toLatin1().data(), formatSize);
+ memcpy(&buffer[dataOffset], data.data(), dataSize);
+ header[i*4+1] = formatOffset;
+ header[i*4+2] = formatSize;
+ header[i*4+3] = dataOffset;
+ header[i*4+4] = dataSize;
+ offset += formatSize + dataSize;
+ }
+ }
+ }
+
+ return serializedMimeData;
+ }
+
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ mockService = new MockContentService();
+ new ServiceAdaptor(mockService);
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ connection.registerObject("/", mockService);
+ connection.registerService("com.ubuntu.content.dbus.Service");
+ pasteRequestedSpy = new QSignalSpy(mockService, SIGNAL(PasteRequested()));
+
+ qRegisterMetaType<QQuickItem*>();
+ contentHub = new UCContentHub();
+ pasteSelectedSpy = new QSignalSpy(contentHub, SIGNAL(pasteSelected(QQuickItem*, const QString&)));
+ }
+
+ void cleanupTestCase()
+ {
+ delete pasteRequestedSpy;
+ delete pasteSelectedSpy;
+ delete contentHub;
+ }
+
+ void cleanup()
+ {
+ pasteRequestedSpy->clear();
+ pasteSelectedSpy->clear();
+ }
+
+ void test_DeserializeTextMimeData()
+ {
+ QMimeData textMimeData;
+ textMimeData.setText(sampleText);
+ QMimeData *deserialized = contentHub->deserializeMimeData(serializeMimeData(textMimeData));
+ QCOMPARE(deserialized->formats().size(), 1);
+ QVERIFY(deserialized->hasText());
+ QVERIFY(deserialized->text() == sampleText);
+ }
+
+ void test_DeserializeHtmlMimeData()
+ {
+ QMimeData htmlMimeData;
+ htmlMimeData.setHtml(sampleHtml);
+ QMimeData *deserialized = contentHub->deserializeMimeData(serializeMimeData(htmlMimeData));
+ QVERIFY(deserialized->hasHtml());
+ QVERIFY(deserialized->html() == sampleHtml);
+ }
+
+ void test_TextPasteSelected()
+ {
+ QMimeData textPaste;
+ textPaste.setText(sampleHtml);
+ contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(textPaste), false);
+ pasteSelectedSpy->wait(testTimeout);
+ QCOMPARE(pasteSelectedSpy->count(), 1);
+ QList<QVariant> args = pasteSelectedSpy->takeFirst();
+ QVERIFY(args.at(1).toString() == textPaste.text());
+ }
+
+ void test_HtmlPasteSelectedAsText()
+ {
+ QMimeData htmlPaste;
+ htmlPaste.setHtml(sampleHtml);
+ contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(htmlPaste), false);
+ pasteSelectedSpy->wait(testTimeout);
+ QCOMPARE(pasteSelectedSpy->count(), 1);
+ QList<QVariant> args = pasteSelectedSpy->takeFirst();
+ QVERIFY(args.at(1).toString() == htmlPaste.text());
+ }
+
+ void test_HtmlPasteSelectedAsRichText()
+ {
+ QMimeData htmlPaste;
+ htmlPaste.setHtml(sampleHtml);
+ contentHub->onPasteSelected(contentHub->getAppProfile(), serializeMimeData(htmlPaste), true);
+ pasteSelectedSpy->wait(testTimeout);
+ QCOMPARE(pasteSelectedSpy->count(), 1);
+ QList<QVariant> args = pasteSelectedSpy->takeFirst();
+ QVERIFY(args.at(1).toString() == htmlPaste.html());
+ }
+
+ void test_PasteFromAnotherAppId()
+ {
+ QMimeData textPaste;
+ textPaste.setText(sampleText);
+ contentHub->onPasteSelected(dummyAppId, serializeMimeData(textPaste), false);
+ pasteSelectedSpy->wait(testTimeout);
+ QCOMPARE(pasteSelectedSpy->count(), 0);
+ }
+
+ void test_KeyboardShortcutOnTextField()
+ {
+ QScopedPointer<UbuntuTestCase> testCase(new UbuntuTestCase("TextFieldPaste.qml"));
+ 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);
+ QTest::keyClick(textField->window(), Qt::Key_V, Qt::ControlModifier|Qt::ShiftModifier);
+ pasteRequestedSpy->wait(testTimeout);
+ QCOMPARE(pasteRequestedSpy->count(), 1);
+ }
+
+ void test_KeyboardShortcutOnTextArea()
+ {
+ QScopedPointer<UbuntuTestCase> testCase(new UbuntuTestCase("TextAreaPaste.qml"));
+ 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);
+ QTest::keyClick(textArea->window(), Qt::Key_V, Qt::ControlModifier|Qt::ShiftModifier);
+ pasteRequestedSpy->wait(testTimeout);
+ QCOMPARE(pasteRequestedSpy->count(), 1);
+ }
+};
+
+QTEST_MAIN(tst_UCContentHub)
+
+#include "tst_contenthub.moc"
=== added directory 'tests/unit/mainwindow'
=== added file 'tests/unit/mainwindow/AppName.qml'
--- tests/unit/mainwindow/AppName.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/mainwindow/AppName.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Labs 1.0
+
+MainWindow {
+ objectName: "appName"
+ applicationName: "org.gnu.wildebeest"
+
+ Label {
+ text: "Lorem ipsum dolor sit amet"
+ }
+}
=== added file 'tests/unit/mainwindow/OrganizationName.qml'
--- tests/unit/mainwindow/OrganizationName.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/mainwindow/OrganizationName.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Labs 1.0
+
+MainWindow {
+ objectName: "appName"
+ applicationName: "tv.island.pacific"
+ organizationName: "pacifist"
+
+ Label {
+ text: "Lorem ipsum dolor sit amet"
+ }
+}
=== added file 'tests/unit/mainwindow/VisualRoot.qml'
--- tests/unit/mainwindow/VisualRoot.qml 1970-01-01 00:00:00 +0000
+++ tests/unit/mainwindow/VisualRoot.qml 2017-01-30 11:36:29 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013-2017 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Components.Labs 1.0
+
+MainWindow {
+ objectName: "visualRoot"
+ applicationName: "org.gnu.wildebeest"
+ visualRoot: myRoot
+
+ Rectangle {
+ id: myRoot
+ objectName: "myRoot"
+ anchors.fill: parent
+
+ Label {
+ objectName: "myLabel"
+ text: "Lorem ipsum dolor sit amet"
+ }
+ }
+}
=== added file 'tests/unit/mainwindow/mainwindow.pro'
--- tests/unit/mainwindow/mainwindow.pro 1970-01-01 00:00:00 +0000
+++ tests/unit/mainwindow/mainwindow.pro 2017-01-30 11:36:29 +0000
@@ -0,0 +1,5 @@
+CONFIG += custom_qpa # needed by test to set device pixel ratio correctly
+include(../test-include.pri)
+
+QT += gui
+SOURCES += tst_mainwindow.cpp
=== added file 'tests/unit/mainwindow/tst_mainwindow.cpp'
--- tests/unit/mainwindow/tst_mainwindow.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit/mainwindow/tst_mainwindow.cpp 2017-01-30 11:36:29 +0000
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2012-2016 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Christian Dywan <christian.dywan@xxxxxxxxxxxxx>
+ */
+
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QProcessEnvironment>
+#include <QtCore/QDebug>
+#include <QtTest/QTest>
+#include <QtTest/QSignalSpy>
+#include <QtCore/QCoreApplication>
+#include <QtQml/QQmlEngine>
+#include <QtQuick/QQuickView>
+#include <QtQuick/QQuickItem>
+#include <QtCore/QThread>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QSettings>
+
+#include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickView>
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlContext>
+#include <QtQml/QQmlComponent>
+
+#include <UbuntuToolkit/ubuntutoolkitmodule.h>
+#include <UbuntuToolkit/private/quickutils_p.h>
+#include <UbuntuToolkit/private/ucapplication_p.h>
+#include <UbuntuToolkit/private/ucmainwindow_p.h>
+#include <UbuntuToolkit/private/ucunits_p.h>
+
+UT_USE_NAMESPACE
+
+class tst_MainWindow : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_MainWindow()
+ {
+ }
+
+ QQuickWindow *loadTest(const QString &document)
+ {
+ // Can't use UbuntuTestCase: We need a Window root item
+ QPointer<QQmlEngine> engine(new QQmlEngine());
+ QString modulePath(UBUNTU_QML_IMPORT_PATH);
+ if (!QDir(modulePath).exists()) {
+ qWarning("'%s' doesn't exist", qPrintable(modulePath));
+ return 0;
+ }
+ engine->addImportPath(modulePath);
+ UbuntuToolkitModule::initializeContextProperties(engine);
+ QPointer<QQmlComponent> component(new QQmlComponent(engine));
+ component->loadUrl(QUrl::fromLocalFile(document), QQmlComponent::Asynchronous);
+ while (component->isLoading())
+ QCoreApplication::processEvents();
+ QObject *toplevel(component->create());
+ if (component->errorString() != "") {
+ qWarning("%s", qPrintable(component->errorString()));
+ return 0;
+ }
+ QQuickWindow* window(qobject_cast<QQuickWindow *>(toplevel));
+ if (window)
+ engine->setIncubationController(window->incubationController());
+ else {
+ QQuickItem *rootItem = qobject_cast<QQuickItem *>(toplevel);
+ if (rootItem) {
+ QQuickView *view(new QQuickView(engine, 0));
+ window = view;
+ view->setResizeMode(QQuickView::SizeRootObjectToView);
+ view->setContent(document, component, rootItem);
+ }
+ }
+ return window;
+ }
+
+ QQuickItem *testItem(QObject *that, const QString &identifier)
+ {
+ if (that->property(identifier.toLocal8Bit()).isValid())
+ return that->property(identifier.toLocal8Bit()).value<QQuickItem*>();
+
+ QList<QQuickItem*> children = that->findChildren<QQuickItem*>(identifier);
+ return (children.count() > 0) ? children[0] : 0;
+ }
+
+private Q_SLOTS:
+
+ void initTestCase()
+ {
+ }
+
+ void cleanupTestCase()
+ {
+ }
+
+ // Note: tests/unit/mainview13 contains the UCApplication bits
+
+ void testCase_AppName()
+ {
+ QString applicationName("org.gnu.wildebeest");
+ QQuickWindow *mainWindow(loadTest("AppName.qml"));
+ QVERIFY(mainWindow);
+ QCOMPARE(applicationName, mainWindow->property("applicationName").toString());
+ QCOMPARE(applicationName, QCoreApplication::applicationName());
+ QCOMPARE(QString(""), QCoreApplication::organizationName());
+ }
+
+ void testCase_OrganizationName()
+ {
+ QString applicationName("tv.island.pacific");
+ QString organizationName("pacifist");
+ QQuickWindow *mainWindow(loadTest("OrganizationName.qml"));
+ QVERIFY(mainWindow);
+ QCOMPARE(applicationName, mainWindow->property("applicationName").toString());
+ QCOMPARE(applicationName, QCoreApplication::applicationName());
+ QCOMPARE(organizationName, 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"));
+ QQuickItem* visualRoot(QuickUtils::instance()->rootItem(myLabel));
+ QQuickItem* myRoot(testItem(mainWindow, "myRoot"));
+ QCOMPARE(visualRoot, myRoot);
+ }
+};
+
+QTEST_MAIN(tst_MainWindow)
+
+#include "tst_mainwindow.moc"
=== modified file 'tests/unit/unit.pro'
--- tests/unit/unit.pro 2016-09-30 05:39:57 +0000
+++ tests/unit/unit.pro 2017-01-30 11:36:29 +0000
@@ -41,10 +41,12 @@
performance \
mainview11 \
mainview13 \
+ mainwindow \
i18n \
arguments \
argument \
alarms \
theme \
quickutils \
- tree
+ tree \
+ contenthub
=== modified file 'tests/unit/visual/tst_focus.13.qml'
--- tests/unit/visual/tst_focus.13.qml 2016-09-19 16:40:49 +0000
+++ tests/unit/visual/tst_focus.13.qml 2017-01-30 11:36:29 +0000
@@ -414,13 +414,13 @@
component: dialogComponent,
item: null,
foreground_name: "dialogForeground",
- bug: "1569979"
+ bug: "" // this is not buggy
},
{ tag: "Popover component",
component: popoverComponent,
item: null,
foreground_name: "popover_foreground",
- bug: "" // this is the only case without a bug
+ bug: "" // this is not buggy
},
{ tag: "Dialog item",
component: null,
=== renamed file 'tests/unit/visual/FIXME-QT56_listitem_focus.13.qml' => 'tests/unit/visual/tst_listitem_focus.13.qml'
=== modified file 'tests/unit/visual/tst_popups_dialog.13.qml'
--- tests/unit/visual/tst_popups_dialog.13.qml 2016-06-15 13:46:51 +0000
+++ tests/unit/visual/tst_popups_dialog.13.qml 2017-01-30 11:36:29 +0000
@@ -47,6 +47,21 @@
keyClick(Qt.Key_Escape);
tryCompare(test, "dialogDestroyed", true, 500, "Dialog not destroyed");
}
+
+ function test_focus_restore_ondismiss_dialog() {
+ pressMe.forceActiveFocus();
+
+ tryCompare(window, "activeFocusItem", pressMe);
+
+ var dlg = PopupUtils.open(dialog);
+ waitForRendering(dlg);
+
+ tryCompare(window, "activeFocusItem", dlg.button);
+
+ keyClick(Qt.Key_Escape);
+
+ tryCompare(window, "activeFocusItem", pressMe);
+ }
}
Component {
@@ -54,10 +69,13 @@
Dialog {
id: ahojDialog
title: "Ahoj"
+ property alias button: closeButton
Button {
+ id: closeButton
text: "Close"
onClicked: PopupUtils.close(ahojDialog)
+ focus: true
}
}
}
References