on xenial help sign in
Initial implementation of departments, needs polishing
commitf395e402981e81c4676cb27477dd5ca6271aa7f6 authorAlbert Astals <albert.astals@canonical.com> dateTue 27 May 2014, 13:32 (12 years ago) parentbf3016e
12 files changed+641−9
plugins/Dash/listviewwithpageheader.cpp+25−5 plugins/Dash/listviewwithpageheader.h+3 qml/Components/PageHeader.qml+12−3 qml/Dash/DashContent.qml+7−1 qml/Dash/DashDepartments.qml+161 qml/Dash/DashDepartmentsList.qml+169 tests/mocks/Unity/CMakeLists.txt+1 tests/mocks/Unity/fake_department.cpp+123 tests/mocks/Unity/fake_department.h+85 tests/mocks/Unity/fake_scope.cpp+35 tests/mocks/Unity/fake_scope.h+18 tests/mocks/Unity/fake_unity_plugin.cpp+2
plugins/Dash/listviewwithpageheader.cpp+25−5
@@ -251,6 +251,8 @@ void ListViewWithPageHeader::setHeader(QQuickItem *headerItem)
251251 if (m_headerItem) {
252252 m_headerItem->setParentItem(contentItem());
253253 m_headerItem->setZ(1);
254+ m_previousHeaderImplicitHeight = m_headerItem->implicitHeight();
255+ QQuickItemPrivate::get(m_headerItem)->addItemChangeListener(this, QQuickItemPrivate::ImplicitHeight);
254256 }
255257 qreal newHeaderHeight = m_headerItem ? m_headerItem->height() : 0;
256258 if (!m_visibleItems.isEmpty() && newHeaderHeight != oldHeaderHeight) {
@@ -489,7 +491,15 @@ void ListViewWithPageHeader::viewportMoved(Qt::Orientations orient)
489491
490492 QQuickFlickable::viewportMoved(orient);
491493// qDebug() << "ListViewWithPageHeader::viewportMoved" << contentY();
492− qreal diff = m_previousContentY - contentY();
494+ const qreal diff = m_previousContentY - contentY();
495+ adjustHeader(diff);
496+ m_previousContentY = contentY();
497+ layout();
498+ polish();
499+}
500+
501+void ListViewWithPageHeader::adjustHeader(qreal diff)
502+{
493503 const bool showHeaderAnimationRunning = m_contentYAnimation->isRunning() && contentYAnimationType == ContentYAnimationShowHeader;
494504 if (m_headerItem) {
495505 const auto oldHeaderItemShownHeight = m_headerItemShownHeight;
@@ -551,10 +561,6 @@ void ListViewWithPageHeader::viewportMoved(Qt::Orientations orient)
551561 adjustMinYExtent();
552562 }
553563 }
554−
555− m_previousContentY = contentY();
556− layout();
557− polish();
558564}
559565
560566void ListViewWithPageHeader::createDelegateModel()
@@ -1060,6 +1066,20 @@ void ListViewWithPageHeader::itemGeometryChanged(QQuickItem * /*item*/, const QR
10601066 }
10611067}
10621068
1069+void ListViewWithPageHeader::itemImplicitHeightChanged(QQuickItem *item)
1070+{
1071+ if (item == m_headerItem) {
1072+ const qreal diff = m_headerItem->implicitHeight() - m_previousHeaderImplicitHeight;
1073+ if (diff != 0) {
1074+ adjustHeader(diff);
1075+ m_previousHeaderImplicitHeight = m_headerItem->implicitHeight();
1076+ layout();
1077+ polish();
1078+ m_contentHeightDirty = true;
1079+ }
1080+ }
1081+}
1082+
10631083void ListViewWithPageHeader::headerHeightChanged(qreal newHeaderHeight, qreal oldHeaderHeight, qreal oldHeaderY)
10641084{
10651085 const qreal heightDiff = newHeaderHeight - oldHeaderHeight;
plugins/Dash/listviewwithpageheader.h+3
@@ -106,6 +106,7 @@ protected:
106106 void viewportMoved(Qt::Orientations orient);
107107 qreal minYExtent() const;
108108 void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
109+ void itemImplicitHeightChanged(QQuickItem *item) override;
109110 void updatePolish();
110111
111112private Q_SLOTS:
@@ -142,6 +143,7 @@ private:
142143 bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
143144 ListItem *createItem(int modelIndex, bool asynchronous);
144145
146+ void adjustHeader(qreal diff);
145147 void adjustMinYExtent();
146148 void updateClipItem();
147149 void headerHeightChanged(qreal newHeaderHeight, qreal oldHeaderHeight, qreal oldHeaderY);
@@ -174,6 +176,7 @@ private:
174176
175177 QQuickItem *m_headerItem;
176178 qreal m_previousContentY;
179+ qreal m_previousHeaderImplicitHeight;
177180 qreal m_headerItemShownHeight; // The height of header shown when the header is shown outside its topmost position
178181 // i.e. it's being shown after dragging down in the middle of the list
179182 enum ContentYAnimationType { ContentYAnimationShowHeader, ContentYAnimationMaximizeVisibleArea };
qml/Components/PageHeader.qml+12−3
@@ -27,12 +27,13 @@ Item {
2727 property ListModel searchHistory
2828 property Scope scope
2929 property alias childItem: itemContainer.children
30+ property alias afterLineChildItem: afterLineContainer.children
3031 property alias showBackButton: backButton.visible
3132
3233 signal backClicked
3334
34− height: units.gu(8.5)
35− implicitHeight: units.gu(8.5)
35+ height: header.height + units.gu(2) + afterLineContainer.height
36+ implicitHeight: header.height + units.gu(2) + afterLineContainer.height
3637
3738 function triggerSearch() {
3839 if (searchEntryEnabled) searchField.forceActiveFocus()
@@ -404,9 +405,17 @@ Item {
404405 top: header.bottom
405406 left: parent.left
406407 right: parent.right
407− bottom: parent.bottom
408+ bottom: afterLineContainer.top
408409 }
409410
410411 source: "graphics/PageHeaderBaseDivider.sci"
411412 }
413+
414+ Item {
415+ id: afterLineContainer
416+
417+ anchors.bottom: parent.bottom
418+ width: parent.width
419+ height: childrenRect.height
420+ }
412421}
qml/Dash/DashContent.qml+7−1
@@ -151,7 +151,7 @@ Item {
151151 item.previewListView = previewListView;
152152 item.scope = Qt.binding(function() { return scope })
153153 item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
154− item.tabBarHeight = dashPageHeader.implicitHeight;
154+ item.tabBarHeight = Qt.binding(function() { return dashPageHeader.implicitHeight; })
155155 dashContent.scopeLoaded(item.scope.id)
156156 }
157157 Connections {
@@ -217,6 +217,12 @@ Item {
217217 onTriggered: tabBar.selectionMode = false
218218 }
219219 }
220+
221+ afterLineChildItem: DashDepartments {
222+ scope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
223+ width: parent.width
224+ windowHeight: dashContent.height
225+ }
220226 }
221227 }
222228
qml/Dash/DashDepartments.qmlnew file+161
@@ -0,0 +1,161 @@
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.2
18+import Ubuntu.Components 0.1
19+
20+AbstractButton {
21+ id: root
22+
23+ property int windowHeight: 0
24+
25+ property var scope: null
26+
27+ property bool showList: false
28+
29+ readonly property var currentDepartment: scope && scope.hasDepartments ? scope.getDepartment(scope.currentDepartment) : null
30+
31+ // Are we drilling down the tree or up?
32+ property bool isGoingBack: false
33+
34+ visible: root.currentDepartment != null
35+
36+ height: visible ? units.gu(5) : 0
37+
38+ onClicked: {
39+ root.showList = !root.showList;
40+ }
41+
42+ Rectangle {
43+ color: "black"
44+ opacity: 0.3
45+ width: parent.width
46+ anchors.top: departmentListView.top
47+ height: windowHeight
48+ visible: showList
49+ }
50+
51+ Image {
52+ anchors {
53+ top: parent.top
54+ left: parent.left
55+ right: parent.right
56+ }
57+ fillMode: Image.Stretch
58+ source: "graphics/dash_divider_top_lightgrad.png"
59+ z: -1
60+ }
61+
62+ Image {
63+ anchors {
64+ bottom: parent.bottom
65+ left: parent.left
66+ right: parent.right
67+ }
68+ fillMode: Image.Stretch
69+ source: "graphics/dash_divider_top_darkgrad.png"
70+ z: -1
71+ }
72+
73+ Label {
74+ anchors.fill: parent
75+ anchors.margins: units.gu(2)
76+ verticalAlignment: Text.AlignVCenter
77+ text: root.currentDepartment ? root.currentDepartment.label : ""
78+ }
79+
80+ Image {
81+ anchors.verticalCenter: parent.verticalCenter
82+ anchors.right: parent.right
83+ anchors.rightMargin: units.gu(2)
84+ rotation: showList ? 180 : 0
85+ source: "image://theme/dropdown-menu"
86+ height: parent.height - units.gu(2)
87+ fillMode: Image.PreserveAspectFit
88+ }
89+
90+ // departmentListView is outside root
91+ ListView {
92+ id: departmentListView
93+ orientation: ListView.Horizontal
94+ interactive: false
95+ model: ListModel {
96+ id: departmentModel
97+ }
98+ width: root.width
99+ readonly property int maxHeight: units.gu(60)
100+ property int prevHeight: maxHeight
101+ height: currentItem ? currentItem.height : maxHeight
102+ onHeightChanged: {
103+ console.log("MOOO", height)
104+ prevHeight = height;
105+ }
106+ anchors.top: root.bottom
107+ delegate: DashDepartmentsList {
108+ visible: root.showList
109+ width: departmentListView.width
110+ height: department && department.loaded ? Math.min(implicitHeight, departmentListView.maxHeight) : departmentListView.prevHeight
111+ department: scope.getDepartment(departmentId)
112+ onEnterDepartment: {
113+ scope.loadDepartment(departmentId);
114+ // We only need to add a new item to the model
115+ // if we have children, otherwise just load it
116+ if (hasChildren) {
117+ isGoingBack = false;
118+ departmentModel.append({"departmentId": departmentId});
119+ departmentListView.currentIndex++;
120+ } else {
121+ showList = false;
122+ }
123+ }
124+ onGoBackToParentClicked: {
125+ scope.loadDepartment(department.parentId);
126+ isGoingBack = true;
127+ departmentModel.setProperty(departmentListView.currentIndex - 1, "departmentId", department.parentId);
128+ departmentListView.currentIndex--;
129+ }
130+ onAllDepartmentClicked: {
131+ showList = false;
132+ if (root.currentDepartment.count == 0) {
133+ // For leaves we have to go to the parent too
134+ scope.loadDepartment(root.currentDepartment.parentId);
135+ }
136+ }
137+ }
138+ onContentXChanged: {
139+ if (contentX == width * departmentListView.currentIndex) {
140+ if (isGoingBack) {
141+ departmentModel.remove(departmentListView.currentIndex + 1);
142+ } else {
143+ departmentModel.setProperty(departmentListView.currentIndex - 1, "departmentId", "");
144+ }
145+ }
146+ }
147+ }
148+
149+ InverseMouseArea {
150+ anchors.fill: departmentListView
151+ enabled: root.showList
152+ onClicked: root.showList = false
153+ }
154+
155+ onScopeChanged: {
156+ departmentModel.clear();
157+ if (scope && scope.hasDepartments) {
158+ departmentModel.append({"departmentId": scope.currentDepartment});
159+ }
160+ }
161+}
qml/Dash/DashDepartmentsList.qmlnew file+169
@@ -0,0 +1,169 @@
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.2
18+import Ubuntu.Components 0.1
19+
20+Item {
21+ id: root
22+ property var department: null
23+ signal enterDepartment(var departmentId, bool hasChildren)
24+ signal goBackToParentClicked()
25+ signal allDepartmentClicked()
26+
27+ readonly property int itemHeight: units.gu(5)
28+ implicitHeight: flickable.contentHeight
29+
30+ Rectangle {
31+ color: "white"
32+ anchors.fill: parent
33+ }
34+
35+ ActivityIndicator {
36+ id: loadingIndicator
37+ anchors.centerIn: parent
38+ running: !(department && department.loaded)
39+ }
40+ clip: true
41+
42+ Flickable {
43+ id: flickable
44+
45+ anchors.fill: parent
46+
47+ readonly property int nItems: department && department.loaded ? (department.count + (department.parentId != "" ? 2 : 0)) : 0
48+ contentHeight: nItems * root.itemHeight
49+ contentWidth: width
50+
51+ AbstractButton {
52+ id: backButton
53+ width: parent.width
54+ visible: department && department.parentId != ""
55+ height: itemHeight
56+
57+ onClicked: root.goBackToParentClicked();
58+
59+ Image {
60+ id: backImage
61+ anchors.verticalCenter: parent.verticalCenter
62+ anchors.left: parent.left
63+ anchors.leftMargin: units.gu(2)
64+ source: "image://theme/back"
65+ height: parent.height - units.gu(2)
66+ fillMode: Image.PreserveAspectFit
67+ }
68+
69+ Label {
70+ anchors.verticalCenter: parent.verticalCenter
71+ anchors.left: backImage.right
72+ text: department ? department.parentLabel : ""
73+ }
74+
75+ Rectangle {
76+ anchors.bottom: parent.bottom
77+ anchors.left: parent.left
78+ anchors.right: parent.right
79+ anchors.leftMargin: units.gu(2)
80+ anchors.rightMargin: units.gu(2)
81+ width: parent.width
82+ color: "gray"
83+ opacity: 0.2
84+ height: units.dp(1)
85+ }
86+ }
87+
88+ AbstractButton {
89+ id: allButton
90+ anchors.top: backButton.bottom
91+ width: parent.width
92+ visible: backButton.visible
93+ height: itemHeight
94+
95+ Label {
96+ anchors.verticalCenter: parent.verticalCenter
97+ anchors.left: parent.left
98+ anchors.leftMargin: units.gu(2)
99+ text: department ? department.allLabel : ""
100+ font.bold: true
101+ }
102+
103+ Rectangle {
104+ anchors.bottom: parent.bottom
105+ anchors.left: parent.left
106+ anchors.right: parent.right
107+ anchors.leftMargin: units.gu(2)
108+ anchors.rightMargin: units.gu(2)
109+ width: parent.width
110+ color: "grey"
111+ opacity: 0.2
112+ height: units.dp(1)
113+ }
114+
115+ onClicked: root.allDepartmentClicked();
116+ }
117+ // TODO Add line separator
118+
119+ Repeater {
120+ model: department && department.loaded ? department : null
121+ clip: true
122+ delegate: AbstractButton {
123+ height: root.itemHeight
124+ width: root.width
125+ y: ((department.parentId != "" ? 2 : 0) + index) * root.itemHeight
126+
127+ onClicked: root.enterDepartment(departmentId, hasChildren)
128+
129+ Label {
130+ anchors.verticalCenter: parent.verticalCenter
131+ anchors.left: parent.left
132+ anchors.leftMargin: units.gu(2)
133+ text: label
134+ }
135+
136+ Image {
137+ anchors.verticalCenter: parent.verticalCenter
138+ anchors.right: parent.right
139+ anchors.rightMargin: units.gu(2)
140+ source: "image://theme/chevron"
141+ height: parent.height - units.gu(2)
142+ fillMode: Image.PreserveAspectFit
143+ visible: hasChildren
144+ }
145+ Text {
146+ // TODO Be an image
147+ text: "✓"
148+ visible: !hasChildren && isActive
149+ anchors.right: parent.right
150+ anchors.rightMargin: units.gu(2)
151+ anchors.verticalCenter: parent.verticalCenter
152+ }
153+
154+ Rectangle {
155+ anchors.bottom: parent.bottom
156+ anchors.left: parent.left
157+ anchors.right: parent.right
158+ anchors.leftMargin: units.gu(2)
159+ anchors.rightMargin: units.gu(2)
160+ width: parent.width
161+ color: "gray"
162+ opacity: 0.1
163+ height: units.dp(1)
164+ visible: index != department.count - 1
165+ }
166+ }
167+ }
168+ }
169+}
tests/mocks/Unity/CMakeLists.txt+1
@@ -22,6 +22,7 @@ set(UnityQML_SOURCES
2222 fake_scope.cpp
2323 fake_scopes.cpp
2424 fake_categories.cpp
25+ fake_department.cpp
2526 fake_resultsmodel.cpp
2627 fake_previewmodel.cpp
2728 fake_previewstack.cpp
tests/mocks/Unity/fake_department.cppnew file+123
@@ -0,0 +1,123 @@
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+#include "fake_department.h"
18+
19+#include "fake_scope.h"
20+
21+#include <QTimer>
22+
23+Department::Department(const QString& departmentId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope)
24+ : QAbstractListModel()
25+ , m_departmentId(departmentId)
26+ , m_label(label)
27+ , m_allLabel(allLabel)
28+ , m_parentId(parentId)
29+ , m_parentLabel(parentLabel)
30+ , m_loaded(false)
31+ , m_scope(scope)
32+{
33+ QTimer::singleShot(1500, this, SLOT(slotLoaded()));
34+ connect(scope, SIGNAL(currentDepartmentChanged(QString)), this, SLOT(slotCurrentDepartmentChanged()));
35+}
36+
37+QString Department::departmentId() const
38+{
39+ return m_departmentId;
40+}
41+
42+QString Department::label() const
43+{
44+ return m_label;
45+}
46+
47+QString Department::allLabel() const
48+{
49+ return m_allLabel;
50+}
51+
52+QString Department::parentId() const
53+{
54+ return m_parentId;
55+}
56+
57+QString Department::parentLabel() const
58+{
59+ return m_parentLabel;
60+}
61+
62+void Department::slotLoaded()
63+{
64+ m_loaded = true;
65+ Q_EMIT loadedChanged(m_loaded);
66+}
67+
68+bool Department::loaded() const
69+{
70+ return m_loaded;
71+}
72+
73+int Department::count() const
74+{
75+ return rowCount();
76+}
77+
78+int Department::rowCount(const QModelIndex & /*parent*/) const
79+{
80+ if (m_departmentId.startsWith("child"))
81+ return 0;
82+ else
83+ return 8;
84+}
85+
86+QHash<int, QByteArray> Department::roleNames() const
87+{
88+ QHash<int, QByteArray> res;
89+ res[RoleDepartmentId] = "departmentId";
90+ res[RoleLabel] = "label";
91+ res[RoleHasChildren] = "hasChildren";
92+ res[RoleIsActive] = "isActive";
93+ return res;
94+}
95+
96+QVariant Department::data(const QModelIndex &index, int role) const
97+{
98+ switch (role) {
99+ case RoleDepartmentId:
100+ if (m_departmentId == "root")
101+ return QString("middle%1").arg(index.row());
102+ else if (m_departmentId.startsWith("middle"))
103+ return QString("child%1%2").arg(m_departmentId).arg(index.row());
104+ break;
105+ case RoleLabel:
106+ return QString("%1Child%2").arg(m_departmentId).arg(index.row());
107+ break;
108+ case RoleHasChildren:
109+ return m_departmentId == "root";
110+ break;
111+ case RoleIsActive:
112+ return m_scope->currentDepartment() == data(index, RoleDepartmentId);
113+ break;
114+ }
115+ return QVariant();
116+}
117+
118+void Department::slotCurrentDepartmentChanged()
119+{
120+ // This is wasteful, should only emit it if really something changed in this
121+ // deparment, but this is a mock, so no need to optimize
122+ Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector<int>() << RoleIsActive);
123+}
tests/mocks/Unity/fake_department.hnew file+85
@@ -0,0 +1,85 @@
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+#ifndef FAKE_DEPARTMENT_H
18+#define FAKE_DEPARTMENT_H
19+
20+#include <QAbstractListModel>
21+
22+class Scope;
23+
24+class Department : public QAbstractListModel
25+{
26+ Q_OBJECT
27+
28+ Q_PROPERTY(QString departmentId READ departmentId NOTIFY departmentIdChanged)
29+ Q_PROPERTY(QString label READ label NOTIFY labelChanged)
30+ Q_PROPERTY(QString allLabel READ allLabel NOTIFY allLabelChanged)
31+ Q_PROPERTY(QString parentId READ parentId NOTIFY parentIdChanged)
32+ Q_PROPERTY(QString parentLabel READ parentLabel NOTIFY parentLabelChanged)
33+ Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged)
34+ Q_PROPERTY(int count READ count NOTIFY countChanged)
35+ // roles: string departmentId, string label, bool hasChildren, bool isActive
36+
37+public:
38+ Department(const QString& departmentId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope);
39+
40+ enum Roles {
41+ RoleDepartmentId,
42+ RoleLabel,
43+ RoleHasChildren,
44+ RoleIsActive
45+ };
46+
47+ QString departmentId() const;
48+ QString label() const;
49+ QString allLabel() const;
50+ QString parentId() const;
51+ QString parentLabel() const;
52+ bool loaded() const;
53+ int count() const;
54+
55+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
56+ QHash<int, QByteArray> roleNames() const;
57+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
58+
59+public Q_SLOTS:
60+ void slotCurrentDepartmentChanged();
61+
62+Q_SIGNALS:
63+ void departmentIdChanged(const QString&);
64+ void labelChanged(const QString&);
65+ void allLabelChanged(const QString&);
66+ void parentIdChanged(const QString&);
67+ void parentLabelChanged(const QString&);
68+ void loadedChanged(bool);
69+ void countChanged(int);
70+
71+private Q_SLOTS:
72+ void slotLoaded();
73+
74+private:
75+ QString m_departmentId;
76+ QString m_label;
77+ QString m_allLabel;
78+ QString m_parentId;
79+ QString m_parentLabel;
80+ bool m_loaded;
81+ QString m_currentDepartment;
82+ Scope *m_scope;
83+};
84+
85+#endif // FAKE_DEPARTMENT_H
tests/mocks/Unity/fake_scope.cpp+35
@@ -15,6 +15,7 @@
1515 */
1616
1717#include "fake_scope.h"
18+#include "fake_department.h"
1819#include "fake_resultsmodel.h"
1920
2021Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
@@ -28,6 +29,7 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
2829 , m_visible(visible)
2930 , m_searching(false)
3031 , m_isActive(false)
32+ , m_currentDeparment("root")
3133 , m_previewRendererName("preview-generic")
3234 , m_categories(new Categories(20, this))
3335{
@@ -153,3 +155,36 @@ void Scope::closeScope(Scope* /*scope*/)
153155{
154156 qFatal("Scope::closeScope is not implemented");
155157}
158+
159+QString Scope::currentDepartment() const
160+{
161+ return m_currentDeparment;
162+}
163+
164+bool Scope::hasDepartments() const
165+{
166+ return true;
167+}
168+
169+Department *Scope::getDepartment(const QString& id)
170+{
171+ if (id.isEmpty())
172+ return nullptr;
173+
174+ QString parentId;
175+ QString parentLabel;
176+ if (id.startsWith("middle")) {
177+ parentId = "root";
178+ parentLabel = "root";
179+ } else if (id.startsWith("child")) {
180+ parentId = "middle";
181+ parentLabel = "middle";
182+ }
183+ return new Department(id, id, "all"+id, parentId, parentLabel, this);
184+}
185+
186+void Scope::loadDepartment(const QString& id)
187+{
188+ m_currentDeparment = id;
189+ currentDepartmentChanged(m_currentDeparment);
190+}
tests/mocks/Unity/fake_scope.h+18
@@ -23,6 +23,7 @@
2323#include <QObject>
2424#include <QTimer>
2525
26+class Department;
2627class Preview;
2728
2829class Scope : public QObject
@@ -45,6 +46,11 @@ class Scope : public QObject
4546 Q_PROPERTY(QString formFactor READ formFactor WRITE setFormFactor NOTIFY formFactorChanged)
4647 Q_PROPERTY(bool isActive READ isActive WRITE setActive NOTIFY isActiveChanged)
4748
49+// TODO
50+ Q_PROPERTY(QString currentDepartment READ currentDepartment NOTIFY currentDepartmentChanged)
51+ Q_PROPERTY(bool hasDepartments READ hasDepartments NOTIFY hasDepartmentsChanged)
52+// END TODO
53+
4854public:
4955 Scope(QObject* parent = 0);
5056 Scope(QString const& id, QString const& name, bool visible, QObject* parent = 0);
@@ -78,7 +84,18 @@ public:
7884 Q_INVOKABLE void cancelActivation();
7985 Q_INVOKABLE void closeScope(Scope* scope);
8086
87+ // TODO
88+ QString currentDepartment() const;
89+ bool hasDepartments() const;
90+ Q_INVOKABLE Department *getDepartment(const QString& id); // returns a Department with lazy-loaded children
91+ Q_INVOKABLE void loadDepartment(const QString& id); // loads results for the department
92+ // END TODO
93+
8194Q_SIGNALS:
95+ // TODO
96+ void currentDepartmentChanged(const QString&);
97+ void hasDepartmentsChanged(bool);
98+ // END TODO
8299 void idChanged();
83100 void nameChanged(const QString&);
84101 void iconHintChanged(const QString&);
@@ -117,6 +134,7 @@ protected:
117134 bool m_visible;
118135 bool m_searching;
119136 bool m_isActive;
137+ QString m_currentDeparment;
120138
121139 QString m_previewRendererName;
122140
tests/mocks/Unity/fake_unity_plugin.cpp+2
@@ -22,6 +22,7 @@
2222// local
2323#include "fake_scopes.h"
2424#include "fake_categories.h"
25+#include "fake_department.h"
2526#include "fake_previewmodel.h"
2627#include "fake_previewwidgetmodel.h"
2728#include "fake_resultsmodel.h"
@@ -41,6 +42,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
4142
4243 qmlRegisterType<Scopes>(uri, 0, 2, "Scopes");
4344 qmlRegisterType<Scope>(uri, 0, 2, "Scope");
45+ qmlRegisterUncreatableType<Department>(uri, 0, 2, "Department", "Can't create Department object in QML.");
4446 qmlRegisterUncreatableType<Categories>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
4547 qmlRegisterUncreatableType<ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create ResultsModel object in QML.");
4648 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");