on xenial help sign in
Add Preview for new generation scopes.
commit62e07dd9411c219d687ae379112e694c49f17294 authorMichał Sawicz <michal.sawicz@canonical.com> dateFri 7 Feb 2014, 10:46 (13 years ago) parente44a20355dc2dd
11 files changed+572
qml/Dash/Previews/Preview.qml+103 qml/Dash/Previews/PreviewWidget.qml+2 qml/Dash/Previews/PreviewWidgetFactory.qml+64 tests/mocks/Unity/CMakeLists.txt+1 tests/mocks/Unity/fake_unity_plugin.cpp+5 tests/mocks/Unity/scopes-ng/fake_preview_model.cpp+96 tests/mocks/Unity/scopes-ng/fake_preview_model.h+68 tests/qmltests/CMakeLists.txt+2 tests/qmltests/Dash/Previews/MockPreviewWidget.qml+27 tests/qmltests/Dash/Previews/tst_Preview.qml+112 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+92
qml/Dash/Previews/Preview.qmlnew file+103
@@ -0,0 +1,103 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+/*! \brief This component constructs the Preview UI.
21+ *
22+ * Currently it displays all the widgets in a flickable column.
23+ */
24+
25+Item {
26+ id: root
27+
28+ /*! \brief Model containing preview widgets.
29+ *
30+ * The model should expose "widgetId", "type" and "properties" roles, as well as
31+ * have a triggered(QString widgetId, QString actionId, QVariantMap data) method,
32+ * that's called when actions are executed in widgets.
33+ */
34+ property var previewModel
35+
36+ //! \brief Should be set to true if this preview is currently displayed.
37+ property bool isCurrent: false
38+
39+ clip: true
40+
41+ Connections {
42+ target: shell.applicationManager
43+ onMainStageFocusedApplicationChanged: {
44+ root.close();
45+ }
46+ onSideStageFocusedApplicationChanged: {
47+ root.close();
48+ }
49+ }
50+
51+ onPreviewModelChanged: processingMouseArea.enabled = false
52+
53+ MouseArea {
54+ anchors.fill: parent
55+ }
56+
57+ Row {
58+ id: row
59+
60+ spacing: units.gu(1)
61+ anchors { fill: parent; margins: spacing }
62+
63+ Repeater {
64+ model: 1
65+
66+ delegate: ListView {
67+ id: column
68+ anchors { top: parent.top; bottom: parent.bottom }
69+ width: row.width
70+ spacing: row.spacing
71+ bottomMargin: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
72+
73+ model: previewModel
74+
75+ Behavior on contentY { UbuntuNumberAnimation { } }
76+
77+ delegate: PreviewWidgetFactory {
78+ widgetId: model.widgetId
79+ widgetType: model.type
80+ widgetData: model.properties
81+ isCurrentPreview: root.isCurrent
82+ anchors { left: parent.left; right: parent.right }
83+
84+ onTriggered: {
85+ processingMouseArea.enabled = true;
86+ previewModel.triggered(widgetId, actionId, data);
87+ }
88+
89+ onFocusChanged: if (focus) column.positionViewAtIndex(index, ListView.Contain)
90+
91+ onHeightChanged: if (focus) column.positionViewAtIndex(index, ListView.Contain)
92+ }
93+ }
94+ }
95+ }
96+
97+ MouseArea {
98+ id: processingMouseArea
99+ objectName: "processingMouseArea"
100+ anchors.fill: parent
101+ enabled: false
102+ }
103+}
qml/Dash/Previews/PreviewWidget.qml+2
@@ -34,4 +34,6 @@ Item {
3434 * \param data Optional widget-specific data sent to the scope.
3535 */
3636 signal triggered(string widgetId, string actionId, var data)
37+
38+ objectName: widgetId
3739}
qml/Dash/Previews/PreviewWidgetFactory.qmlnew file+64
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+import QtQuick 2.0
19+
20+//! \brief This component loads the widgets based on widgetData["type"].
21+
22+Loader {
23+ id: root
24+
25+ //! Identifier of the widget.
26+ property string widgetId: ""
27+
28+ //! Type of the widget to display.
29+ property string widgetType: ""
30+
31+ //! Widget data, forwarded to the widget as is.
32+ property var widgetData: null
33+
34+ //! Set to true if the parent preview is displayed.
35+ property bool isCurrentPreview: false
36+
37+ //! Triggered signal forwarded from the widgets.
38+ signal triggered(string widgetId, string actionId, var data)
39+
40+ source: widgetSource
41+
42+ //! \cond private
43+ property url widgetSource: {
44+ switch (widgetType) {
45+ case "audio": return "PreviewAudioPlayback.qml";
46+ case "text": return "PreviewTextSummary.qml";
47+ case "gallery": return "PreviewImageGallery.qml";
48+ case "actions": return "PreviewActions.qml";
49+ default: return "";
50+ }
51+ }
52+ //! \endcond
53+
54+ onLoaded: {
55+ item.widgetId = Qt.binding(function() { return root.widgetId } )
56+ item.widgetData = Qt.binding(function() { return root.widgetData } )
57+ item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
58+ }
59+
60+ Connections {
61+ target: root.item
62+ onTriggered: root.triggered(widgetId, actionId, data)
63+ }
64+}
tests/mocks/Unity/CMakeLists.txt+1
@@ -25,6 +25,7 @@ set(UnityQML_SOURCES
2525 fake_scope.cpp
2626 fake_scopes.cpp
2727 fake_categories.cpp
28+ scopes-ng/fake_preview_model.cpp
2829 fake_unity_plugin.cpp
2930)
3031
tests/mocks/Unity/fake_unity_plugin.cpp+5
@@ -25,6 +25,9 @@
2525#include "fake_preview.h"
2626#include "categoryresults.h"
2727
28+// scopes-ng
29+#include "scopes-ng/fake_preview_model.h"
30+
2831// External
2932#include <glib-object.h>
3033
@@ -44,4 +47,6 @@ void FakeUnityPlugin::registerTypes(const char *uri)
4447 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
4548 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
4649 qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
50+
51+ qmlRegisterType<scopes_ng::PreviewModel>(uri, 0, 1, "FakePreviewModel");
4752}
tests/mocks/Unity/scopes-ng/fake_preview_model.cppnew file+96
@@ -0,0 +1,96 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Michał Sawicz <michal.sawicz@canonical.com>
6+ * Michal Hruby <michal.hruby@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// self
22+#include "fake_preview_model.h"
23+
24+// Qt
25+#include <QVariantMap>
26+
27+namespace scopes_ng
28+{
29+
30+struct PreviewData
31+{
32+ QString id;
33+ QString type;
34+ QVariantMap data;
35+
36+ PreviewData(QString const& id_, QString const& type_, QVariantMap const& data_): id(id_), type(type_), data(data_)
37+ {
38+ }
39+};
40+
41+PreviewModel::PreviewModel(QObject* parent) : QAbstractListModel(parent)
42+{
43+ m_roles[Roles::RoleWidgetId] = "widgetId";
44+ m_roles[Roles::RoleType] = "type";
45+ m_roles[Roles::RoleProperties] = "properties";
46+
47+ populateWidgets();
48+}
49+
50+void PreviewModel::populateWidgets()
51+{
52+ beginResetModel();
53+ m_previewWidgets.clear();
54+ for (int i = 0; i <= 20; i++) {
55+ // FIXME: the API will expose nicer getters soon, use those!
56+ QVariantMap attributes;
57+ attributes["text"] = QVariant::fromValue(QString("Widget %1").arg(i));
58+ attributes["title"] = QVariant::fromValue(QString("Title %1").arg(i));
59+ PreviewData* preview_data = new PreviewData(QString("widget-%1").arg(i), QString("text"), attributes);
60+ m_previewWidgets.append(QSharedPointer<PreviewData>(preview_data));
61+ }
62+ endResetModel();
63+
64+}
65+
66+void PreviewModel::triggered(QString widgetId, QString actionId, QVariantMap data)
67+{
68+ Q_EMIT actionTriggered(widgetId, actionId, data);
69+}
70+
71+QHash<int, QByteArray> PreviewModel::roleNames() const
72+{
73+ return m_roles;
74+}
75+
76+int PreviewModel::rowCount(const QModelIndex&) const
77+{
78+ return m_previewWidgets.size();
79+}
80+
81+QVariant PreviewModel::data(const QModelIndex& index, int role) const
82+{
83+ auto widget_data = m_previewWidgets.at(index.row());
84+ switch (role) {
85+ case RoleWidgetId:
86+ return widget_data->id;
87+ case RoleType:
88+ return widget_data->type;
89+ case RoleProperties:
90+ return widget_data->data;
91+ default:
92+ return QVariant();
93+ }
94+}
95+
96+} // namespace scopes_ng
tests/mocks/Unity/scopes-ng/fake_preview_model.hnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+
18+#ifndef NG_FAKE_PREVIEW_H
19+#define NG_FAKE_PREVIEW_H
20+
21+#include <QAbstractListModel>
22+#include <QSharedPointer>
23+#include <QVariantMap>
24+
25+namespace scopes_ng
26+{
27+
28+class PreviewData;
29+
30+class Q_DECL_EXPORT PreviewModel : public QAbstractListModel
31+{
32+ Q_OBJECT
33+
34+ Q_ENUMS(Roles)
35+
36+public:
37+ explicit PreviewModel(QObject* parent = 0);
38+
39+ enum Roles {
40+ RoleWidgetId,
41+ RoleType,
42+ RoleProperties
43+ };
44+
45+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
46+ QHash<int, QByteArray> roleNames() const override;
47+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
48+
49+Q_SIGNALS:
50+ void actionTriggered(QString widgetId, QString actionId, QVariantMap data);
51+
52+public Q_SLOTS:
53+ void triggered(QString widgetId, QString actionId, QVariantMap data);
54+
55+private:
56+ QHash<int, QByteArray> m_roles;
57+ QList<QSharedPointer<PreviewData>> m_previewWidgets;
58+
59+ void populateWidgets();
60+
61+};
62+
63+} // namespace scopes_ng
64+
65+
66+Q_DECLARE_METATYPE(scopes_ng::PreviewModel*)
67+
68+#endif // NG_FAKE_PREVIW_H
tests/qmltests/CMakeLists.txt+2
@@ -55,6 +55,8 @@ add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IM
5555add_qml_test(Dash/Apps AppPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
5656add_qml_test(Dash/Movie MoviePreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
5757add_qml_test(Dash/Music MusicPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
58+add_qml_test(Dash/Previews Preview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
59+add_qml_test(Dash/Previews PreviewWidgetFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
5860add_qml_test(Dash/Previews PreviewActions IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
5961add_qml_test(Dash/Previews PreviewAudioPlayback IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
6062add_qml_test(Dash/Previews PreviewImageGallery IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
tests/qmltests/Dash/Previews/MockPreviewWidget.qmlnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+import QtQuick 2.0
19+import "../../../../qml/Dash/Previews"
20+
21+PreviewWidget {
22+ objectName: "mockPreviewWidget"
23+
24+ function trigger() {
25+ triggered("mockWidget", "mockAction", {"mock": "data"})
26+ }
27+}
tests/qmltests/Dash/Previews/tst_Preview.qmlnew file+112
@@ -0,0 +1,112 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import "../../../../qml/Dash/Previews"
20+import Unity.Test 0.1 as UT
21+import Unity 0.1 as Unity
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(60)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ Item {
30+ id: shell
31+
32+ anchors.fill: parent
33+ property var applicationManager: null
34+ }
35+
36+ Unity.FakePreviewModel {
37+ id: mockPreviewModel
38+ }
39+
40+ Preview {
41+ id: preview
42+ anchors.fill: parent
43+
44+ previewModel: mockPreviewModel
45+ }
46+
47+ SignalSpy {
48+ id: triggeredSpy
49+ target: mockPreviewModel
50+ signalName: "actionTriggered"
51+ }
52+
53+ UT.UnityTestCase {
54+ name: "Preview"
55+ when: windowShown
56+
57+ function test_triggered() {
58+ waitForRendering(preview);
59+ var widget = findChild(preview, "widget-3");
60+
61+ compare(typeof widget, "object", "Could not find the widget object.");
62+
63+ widget.triggered(widget.widgetId, "mockAction", {"mock": "data"});
64+
65+ triggeredSpy.wait();
66+
67+ var args = triggeredSpy.signalArguments[0];
68+
69+ compare(args[0], "widget-3", "Widget id not passed correctly.");
70+ compare(args[1], "mockAction", "Action id not passed correctly.");
71+ compare(args[2]["mock"], "data", "Data not passed correctly.");
72+ }
73+
74+ function test_showProcessing() {
75+ waitForRendering(preview);
76+ var widget = findChild(preview, "widget-3");
77+ widget.triggered(widget.widgetId, "mockAction", {"mock": "data"});
78+
79+ var processing = findChild(preview, "processingMouseArea");
80+
81+ tryCompare(processing, "enabled", true);
82+
83+ preview.previewModelChanged();
84+
85+ tryCompare(processing, "enabled", false);
86+ }
87+
88+ function test_containOnFocus() {
89+ waitForRendering(preview);
90+ var widget = findChild(preview, "widget-13");
91+
92+ widget.forceActiveFocus();
93+
94+ var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
95+
96+ tryCompareFunction(function () { return bottomLeft.y <= preview.height }, true);
97+ }
98+
99+ function test_containOnGrow() {
100+ waitForRendering(preview);
101+ var widget = findChild(preview, "widget-13");
102+
103+ widget.forceActiveFocus();
104+ widget.height += 200;
105+
106+ tryCompareFunction(function () {
107+ var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
108+ return bottomLeft.y <= preview.height
109+ }, true);
110+ }
111+ }
112+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qmlnew file+92
@@ -0,0 +1,92 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import "../../../../qml/Dash/Previews"
20+import Unity.Test 0.1 as UT
21+
22+Rectangle {
23+ id: root
24+ width: units.gu(60)
25+ height: units.gu(80)
26+ color: Theme.palette.selected.background
27+
28+ PreviewWidgetFactory {
29+ id: factory
30+ anchors {
31+ left: parent.left
32+ right: parent.right
33+ }
34+ }
35+
36+ SignalSpy {
37+ id: triggeredSpy
38+ target: factory
39+ signalName: "triggered"
40+ }
41+
42+ UT.UnityTestCase {
43+ name: "PreviewWidgetFactory"
44+ when: windowShown
45+
46+ property Item mockWidget: findChild(factory, "mockPreviewWidget")
47+
48+ function cleanup() {
49+ factory.source = Qt.binding(function() { return factory.widgetSource });
50+ }
51+
52+ function test_previewData() {
53+ factory.source = Qt.resolvedUrl("MockPreviewWidget.qml");
54+
55+ verify(typeof mockWidget === "object", "Could not find the mock preview widget.");
56+
57+ tryCompare(mockWidget, "widgetData", factory.widgetData);
58+ }
59+
60+ function test_triggered() {
61+ factory.source = Qt.resolvedUrl("MockPreviewWidget.qml");
62+
63+ verify(typeof mockWidget === "object", "Could not find the mock preview widget.");
64+
65+ mockWidget.trigger();
66+
67+ triggeredSpy.wait();
68+
69+ var args = triggeredSpy.signalArguments[0];
70+
71+ compare(args[0], "mockWidget", "Widget id not passed correctly.");
72+ compare(args[1], "mockAction", "Action id not passed correctly.");
73+ compare(args[2]["mock"], "data", "Data not passed correctly.");
74+ }
75+
76+ function test_mapping_data() {
77+ return [
78+ { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml" },
79+ { tag: "Text", type: "text", source: "PreviewTextSummary.qml" },
80+ { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
81+ { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
82+ ];
83+ }
84+
85+ function test_mapping(data) {
86+ factory.widgetData = { type: data.type };
87+ factory.widgetType = data.type;
88+
89+ verify(("" + factory.source).indexOf(data.source) != -1);
90+ }
91+ }
92+}