Merge lp:~unity-team/unity8/fix_ticket_1105_tests into lp:unity8

Proposed by Albert Astals Cid on 2016-03-15
Status: Superseded
Proposed branch: lp:~unity-team/unity8/fix_ticket_1105_tests
Merge into: lp:unity8
Prerequisite: lp:~ci-train-bot/unity8/unity8-ubuntu-xenial-landing-041
Diff against target: 4679 lines (+2961/-880)
43 files modified
debian/control (+2/-2)
plugins/Dash/ScopeStyle.qml (+1/-1)
qml/Dash/DashContent.qml (+2/-2)
qml/Dash/DashNavigation.qml (+170/-89)
qml/Dash/DashNavigationButton.qml (+0/-233)
qml/Dash/DashNavigationHeader.qml (+73/-0)
qml/Dash/DashNavigationList.qml (+35/-87)
qml/Dash/DashPageHeader.qml (+149/-140)
qml/Dash/Filters/FilterOptionSelector.qml (+91/-0)
qml/Dash/Filters/FilterRangeInput.qml (+136/-0)
qml/Dash/Filters/FilterValueSlider.qml (+87/-0)
qml/Dash/Filters/FilterWidget.qml (+29/-0)
qml/Dash/Filters/FilterWidgetFactory.qml (+80/-0)
qml/Dash/FiltersPopover.qml (+105/-0)
qml/Dash/GenericScopeView.qml (+77/-47)
qml/Dash/PageHeaderExtraPanel.qml (+176/-0)
tests/mocks/Unity/CMakeLists.txt (+14/-1)
tests/mocks/Unity/fake_filters.cpp (+125/-0)
tests/mocks/Unity/fake_filters.h (+46/-0)
tests/mocks/Unity/fake_navigation.cpp (+9/-10)
tests/mocks/Unity/fake_optionselectorfilter.cpp (+65/-0)
tests/mocks/Unity/fake_optionselectorfilter.h (+52/-0)
tests/mocks/Unity/fake_optionselectoroptions.cpp (+77/-0)
tests/mocks/Unity/fake_optionselectoroptions.h (+47/-0)
tests/mocks/Unity/fake_rangeinputfilter.cpp (+167/-0)
tests/mocks/Unity/fake_rangeinputfilter.h (+79/-0)
tests/mocks/Unity/fake_scope.cpp (+61/-36)
tests/mocks/Unity/fake_scope.h (+20/-7)
tests/mocks/Unity/fake_unity_plugin.cpp (+7/-0)
tests/mocks/Unity/fake_valuesliderfilter.cpp (+84/-0)
tests/mocks/Unity/fake_valuesliderfilter.h (+55/-0)
tests/mocks/Unity/fake_valueslidervalues.cpp (+51/-0)
tests/mocks/Unity/fake_valueslidervalues.h (+36/-0)
tests/plugins/Dash/tst_ScopeStyle.qml (+1/-1)
tests/qmltests/CMakeLists.txt (+4/-0)
tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml (+96/-0)
tests/qmltests/Dash/Filters/tst_FilterRangeInput.qml (+247/-0)
tests/qmltests/Dash/Filters/tst_FilterValueSlider.qml (+101/-0)
tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml (+58/-0)
tests/qmltests/Dash/tst_DashContent.qml (+218/-205)
tests/qmltests/Dash/tst_DashPageHeader.qml (+24/-15)
tests/qmltests/Dash/tst_GenericScopeView.qml (+3/-3)
tests/qmltests/Dash/tst_PreviewView.qml (+1/-1)
To merge this branch: bzr merge lp:~unity-team/unity8/fix_ticket_1105_tests
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Needs Fixing on 2016-03-15
Unity Team 2016-03-15 Pending
Review via email: mp+289080@code.launchpad.net

This proposal has been superseded by a proposal from 2016-03-15.

Commit Message

Fix various tests

test_navigationShowFilterButton is gone since it was testing a feature that no longer exists

Description of the Change

 * Are there any related MPs required for this MP to build/function as expected
Prereq

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * Did you make sure that your branch does not contain spurious tags?
Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

 * If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
2279. By Lukáš Tinkl on 2016-03-15

merge lp:~aacid/unity8/listitemworkaround

2280. By Albert Astals Cid on 2016-03-15

Fix some ticket 1105 test failures

2281. By Albert Astals Cid on 2016-03-16

Another small test fix

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-03-10 22:42:34 +0000
3+++ debian/control 2016-03-15 19:17:30 +0000
4@@ -29,7 +29,7 @@
5 libqt5xmlpatterns5-dev,
6 libsystemsettings-dev,
7 libudev-dev,
8- libunity-api-dev (>= 7.107),
9+ libunity-api-dev (>= 7.108),
10 libusermetricsoutput1-dev,
11 # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
12 libx11-dev[!armhf],
13@@ -133,7 +133,7 @@
14 unity-application-impl-13,
15 unity-notifications-impl-3,
16 unity-plugin-scopes | unity-scopes-impl,
17- unity-scopes-impl-10,
18+ unity-scopes-impl-11,
19 unity8-fake-env | unity-application-impl,
20 ${misc:Depends},
21 Breaks: unity8 (<< 7.86),
22
23=== modified file 'plugins/Dash/ScopeStyle.qml'
24--- plugins/Dash/ScopeStyle.qml 2015-07-15 15:07:19 +0000
25+++ plugins/Dash/ScopeStyle.qml 2016-03-15 19:17:30 +0000
26@@ -58,7 +58,7 @@
27 readonly property url headerLogo: "logo" in d.headerStyle ? d.headerStyle["logo"] : ""
28
29 /// Background style for the header
30- readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : "color:///#f5f5f5"
31+ readonly property url headerBackground: "background" in d.headerStyle ? d.headerStyle["background"] : "color:///#ffffff"
32
33 /// Foreground color for the header
34 readonly property color headerForeground: "foreground-color" in d.headerStyle ? d.headerStyle["foreground-color"] : foreground
35
36=== modified file 'qml/Dash/DashContent.qml'
37--- qml/Dash/DashContent.qml 2015-08-03 13:48:14 +0000
38+++ qml/Dash/DashContent.qml 2016-03-15 19:17:30 +0000
39@@ -108,7 +108,7 @@
40 objectName: "dashContentList"
41
42 interactive: !dashContent.forceNonInteractive && dashContent.scopes.loaded && currentItem
43- && !currentItem.moving && !currentItem.navigationDisableParentInteractive && !currentItem.subPageShown
44+ && !currentItem.moving && !currentItem.subPageShown && !currentItem.extraPanelShown
45 anchors.fill: parent
46 orientation: ListView.Horizontal
47 boundsBehavior: Flickable.DragAndOvershootBounds
48@@ -170,7 +170,7 @@
49 objectName: "scopeLoader" + index
50
51 readonly property bool moving: item ? item.moving : false
52- readonly property bool navigationDisableParentInteractive: item ? item.navigationDisableParentInteractive : false
53+ readonly property bool extraPanelShown: item ? item.extraPanelShown : false
54 readonly property bool subPageShown: item ? item.subPageShown : false
55 readonly property var categoryView: item ? item.categoryView : null
56 readonly property var theScope: scope
57
58=== modified file 'qml/Dash/DashNavigation.qml'
59--- qml/Dash/DashNavigation.qml 2015-07-15 15:07:19 +0000
60+++ qml/Dash/DashNavigation.qml 2016-03-15 19:17:30 +0000
61@@ -17,101 +17,182 @@
62 import QtQuick 2.4
63 import Ubuntu.Components 1.3
64 import Dash 0.1
65-import "../Components"
66
67 Item {
68 id: root
69 objectName: "dashNavigation"
70
71+ // set by parent
72 property var scope: null
73- property var scopeStyle: null
74-
75- property alias windowWidth: blackRect.width
76- property alias windowHeight: blackRect.height
77- readonly property var openList: {
78- if (navigationButton.showList) return navigationButton.listView;
79- if (altNavigationButton.showList) return altNavigationButton.listView;
80- return null;
81- }
82- readonly property bool disableParentInteractive: {
83- return navigationButton.showList || altNavigationButton.showList ||
84- navigationButton.inverseMousePressed || altNavigationButton.inverseMousePressed;
85- }
86-
87- // FIXME this is only here for highlight purposes (see Background.qml, too)
88- readonly property var background: backgroundItem
89+ property real availableHeight
90+
91+ signal leafClicked()
92+
93+ // internal
94+ readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
95+ // Are we drilling down the tree or up?
96+ property bool isEnteringChildren: false
97
98 visible: height != 0
99- height: navigationButton.currentNavigation || altNavigationButton.currentNavigation ? units.gu(5) : 0
100-
101- QtObject {
102- id: d
103- readonly property color foregroundColor: root.scopeStyle
104- ? root.scopeStyle.getTextColor(backgroundItem.luminance)
105- : theme.palette.normal.baseText
106- readonly property bool bothVisible: altNavigationButton.visible && navigationButton.visible
107- readonly property real navigationWidth: root.width >= units.gu(60) ? units.gu(40) : root.width
108- readonly property real buttonWidth: navigationWidth / (bothVisible ? 2 : 1)
109- }
110-
111- Rectangle {
112- id: blackRect
113- objectName: "blackRect"
114- color: "black"
115- opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
116- Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
117- anchors { left: parent.left; right: parent.right }
118- visible: opacity > 0
119- }
120-
121- Background {
122- id: backgroundItem
123- anchors.fill: parent
124- style: scopeStyle ? scopeStyle.navigationBackground : "color:///#f5f5f5"
125- }
126-
127- Image {
128- fillMode: Image.Stretch
129- source: scopeStyle.backgroundLuminance > 0.2 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
130- anchors { top: parent.bottom; left: parent.left; right: parent.right }
131- }
132-
133- DashNavigationButton {
134- id: altNavigationButton
135- objectName: "altNavigationButton"
136- height: root.height
137- width: d.buttonWidth
138- scope: root.scope
139- scopeStyle: root.scopeStyle
140- foregroundColor: d.foregroundColor
141- listView.width: d.navigationWidth
142- isAltNavigation: true
143- showDivider: navigationButton.visible || root.width > d.navigationWidth
144- // needed so that InverseMouseArea is above navigationButton
145- z: listView.height > 0 ? 1 : 0
146- }
147-
148- DashNavigationButton {
149- id: navigationButton
150- objectName: "navigationButton"
151- height: root.height
152- width: altNavigationButton.visible ? d.buttonWidth : d.navigationWidth
153- x: altNavigationButton.visible ? d.buttonWidth : 0
154- scope: root.scope
155- scopeStyle: root.scopeStyle
156- foregroundColor: d.foregroundColor
157- listView.width: d.navigationWidth
158- listView.x: -x
159- showDivider: root.width > d.navigationWidth
160- }
161-
162- Image {
163- fillMode: Image.Stretch
164- source: backgroundItem.luminance > 0.7 ? "graphics/navigation_shadow.png" : "graphics/navigation_shadow_light.png"
165- x: navigationButton.listView.height > 0 ? altNavigationButton.x : navigationButton.x
166- width: d.buttonWidth
167- rotation: 180
168- anchors.bottom: parent.bottom
169- visible: d.bothVisible && (navigationButton.listView.height > 0 || altNavigationButton.listView.height > 0)
170- }
171+ implicitHeight: scope && scope.hasNavigation ? navigationListView.y + navigationListView.height : 0
172+
173+ function resetNavigation() {
174+ if (navigationModel.count > 1) {
175+ clear();
176+ }
177+ }
178+
179+ Column {
180+ id: headersColumn
181+ anchors {
182+ top: parent.top
183+ left: parent.left
184+ right: parent.right
185+ }
186+
187+ function pop(popsNeeded) {
188+ if (popsNeeded == 0)
189+ return;
190+ isEnteringChildren = false;
191+ navigationListView.currentIndex = navigationListView.currentIndex - popsNeeded;
192+ navigationModel.setProperty(navigationListView.currentIndex, "nullifyNavigation", false);
193+ navigationModel.remove(navigationModel.count - popsNeeded, popsNeeded);
194+
195+ popsNeeded = Math.min(headersModel.count, popsNeeded);
196+ // This is effectively deleting ourselves, so needs to be the last thing of the function
197+ headersModel.remove(headersModel.count - popsNeeded, popsNeeded);
198+ }
199+
200+ Repeater {
201+ model: ListModel {
202+ id: headersModel
203+ // Roles
204+ // headerText: the text to show
205+ // navigationId: the navigation Id that represents
206+ // parentNavigationId: the parent navigation Id
207+ }
208+ delegate: DashNavigationHeader {
209+ objectName: "dashNavigationHeader" + index
210+ height: index == 0 && headersModel.count > 1 ? 0 : units.gu(5)
211+ width: headersColumn.width
212+
213+ backVisible: index != 0
214+ text: headerText
215+
216+ onBackClicked: {
217+ scope.setNavigationState(parentNavigationId);
218+
219+ var popsNeeded = headersModel.count - index;
220+ headersColumn.pop(popsNeeded);
221+ }
222+
223+ onTextClicked: {
224+ scope.setNavigationState(navigationId);
225+
226+ var popsNeeded = headersModel.count - index - 1;
227+ headersColumn.pop(popsNeeded);
228+
229+ root.leafClicked();
230+ }
231+ }
232+ }
233+ }
234+
235+ ListView {
236+ id: navigationListView
237+ objectName: "navigationListView"
238+ orientation: ListView.Horizontal
239+ interactive: false
240+ model: ListModel {
241+ id: navigationModel
242+ // We have two roles
243+ // navigationId: the navigation id of the navigation the list represents
244+ // nullifyNavigation: overrides navigationId to be null
245+ // This is used to "clear" the delegate when going "right" on the tree
246+ }
247+ anchors {
248+ top: headersColumn.bottom
249+ left: parent.left
250+ right: parent.right
251+ }
252+ readonly property int maxHeight: root.availableHeight - navigationListView.y
253+ property int prevHeight: maxHeight
254+ height: currentItem ? currentItem.height : maxHeight
255+
256+ onHeightChanged: {
257+ if (currentItem) {
258+ prevHeight = currentItem.desiredHeight;
259+ }
260+ }
261+ highlightMoveDuration: UbuntuAnimation.FastDuration
262+ delegate: DashNavigationList {
263+ objectName: "navigation" + index
264+ visible: height > 0
265+ width: navigationListView.width
266+ property real desiredHeight: {
267+ if (navigation && navigation.loaded && x == navigationListView.contentX)
268+ {
269+ return Math.min(implicitHeight, navigationListView.maxHeight);
270+ } else {
271+ return navigationListView.prevHeight;
272+ }
273+ }
274+ height: desiredHeight
275+ navigation: (nullifyNavigation || !scope) ? null : scope.getNavigation(navigationId)
276+ currentNavigation: root.currentNavigation
277+ onEnterNavigation: { // var newNavigationId, string newNavigationLabel, bool hasChildren
278+ scope.setNavigationState(newNavigationId);
279+ // We only need to add a new item to the model
280+ // if we have children, otherwise just load it
281+ if (hasChildren) {
282+ isEnteringChildren = true;
283+ navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
284+ headersModel.append({"headerText": newNavigationLabel,
285+ "navigationId": newNavigationId,
286+ "parentNavigationId": navigationId
287+ });
288+ navigationListView.currentIndex++;
289+ } else {
290+ root.leafClicked();
291+ }
292+ }
293+ }
294+ onContentXChanged: {
295+ if (navigationListView.highlightMoveDuration == 0)
296+ return;
297+
298+ if (contentX == width * navigationListView.currentIndex) {
299+ if (isEnteringChildren) {
300+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
301+ }
302+ }
303+ }
304+ }
305+
306+ property bool isFirstLoad: false
307+ onScopeChanged: clear();
308+ function clear() {
309+ navigationModel.clear();
310+ headersModel.clear();
311+ isFirstLoad = true;
312+ }
313+ function setNewNavigation() {
314+ if (isFirstLoad && currentNavigation && currentNavigation.loaded) {
315+ isFirstLoad = false;
316+ if (currentNavigation.count > 0) {
317+ navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
318+ } else {
319+ navigationModel.append({"navigationId": currentNavigation.parentNavigationId, "nullifyNavigation": false});
320+ }
321+ headersModel.append({"headerText": currentNavigation.allLabel != "" ? currentNavigation.allLabel : currentNavigation.label,
322+ "navigationId": currentNavigation.navigationId,
323+ "parentNavigationId": currentNavigation.parentNavigationId
324+ });
325+ }
326+ }
327+ Connections {
328+ target: currentNavigation
329+ onLoadedChanged: setNewNavigation();
330+ }
331+ onCurrentNavigationChanged: setNewNavigation();
332 }
333
334=== removed file 'qml/Dash/DashNavigationButton.qml'
335--- qml/Dash/DashNavigationButton.qml 2015-07-15 15:07:19 +0000
336+++ qml/Dash/DashNavigationButton.qml 1970-01-01 00:00:00 +0000
337@@ -1,233 +0,0 @@
338-/*
339- * Copyright (C) 2014,2015 Canonical, Ltd.
340- *
341- * This program is free software; you can redistribute it and/or modify
342- * it under the terms of the GNU General Public License as published by
343- * the Free Software Foundation; version 3.
344- *
345- * This program is distributed in the hope that it will be useful,
346- * but WITHOUT ANY WARRANTY; without even the implied warranty of
347- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
348- * GNU General Public License for more details.
349- *
350- * You should have received a copy of the GNU General Public License
351- * along with this program. If not, see <http://www.gnu.org/licenses/>.
352- */
353-
354-import QtQuick 2.4
355-import Ubuntu.Components 1.3
356-
357-AbstractButton {
358- id: root
359- objectName: "dashNavigation"
360-
361- // Set by parent
362- property var scope: null
363- property var scopeStyle: null
364- property color foregroundColor: theme.palette.normal.baseText
365- property bool isAltNavigation: false
366- property bool showDivider: false
367-
368- // Used by parent
369- readonly property var currentNavigation: scope && scope[hasNavigation] ? getNavigation(scope[currentNavigationId]) : null
370- readonly property alias listView: navigationListView
371- readonly property bool inverseMousePressed: inverseMouseArea.pressed
372- property bool showList: false
373-
374- // Internal
375- // Are we drilling down the tree or up?
376- property bool isGoingBack: false
377- readonly property string hasNavigation: isAltNavigation ? "hasAltNavigation" : "hasNavigation"
378- readonly property string currentNavigationId: isAltNavigation ? "currentAltNavigationId" : "currentNavigationId"
379- function getNavigation(navId) {
380- if (isAltNavigation) {
381- return scope.getAltNavigation(navId);
382- } else {
383- return scope.getNavigation(navId);
384- }
385- }
386-
387- visible: root.currentNavigation != null
388-
389- onClicked: {
390- navigationListView.updateMaxHeight();
391- root.showList = !root.showList;
392- }
393-
394- Label {
395- anchors.fill: parent
396- anchors.margins: units.gu(2)
397- anchors.rightMargin: units.gu(5)
398- verticalAlignment: Text.AlignVCenter
399- text: root.currentNavigation ? root.currentNavigation.label : ""
400- color: root.foregroundColor
401- elide: Text.ElideRight
402- maximumLineCount: 1
403- }
404-
405- Icon {
406- anchors.verticalCenter: parent.verticalCenter
407- anchors.right: parent.right
408- anchors.rightMargin: units.gu(2)
409- name: showList ? "up" : "down"
410- height: units.gu(2)
411- width: height
412- color: root.foregroundColor
413- }
414-
415- // navigationListView is outside root
416- ListView {
417- id: navigationListView
418- objectName: "navigationListView"
419- visible: height > 0
420- orientation: ListView.Horizontal
421- interactive: false
422- clip: root.width != windowWidth
423- model: ListModel {
424- id: navigationModel
425- // We have two roles
426- // navigationId: the navigation id of the navigation the list represents
427- // nullifyNavigation: overrides navigationId to be null
428- // This is used to "clear" the delegate when going "right" on the tree
429- }
430- anchors.top: root.bottom
431- property int maxHeight: -1
432- Component.onCompleted: updateMaxHeight();
433- function updateMaxHeight()
434- {
435- maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
436- }
437- property int prevHeight: maxHeight
438- height: currentItem ? currentItem.height : maxHeight
439- onHeightChanged: {
440- if (root.showList) {
441- prevHeight = currentItem.desiredHeight;
442- }
443- }
444- highlightMoveDuration: UbuntuAnimation.FastDuration
445- delegate: DashNavigationList {
446- objectName: "navigation" + index
447- visible: height > 0
448- width: navigationListView.width
449- scopeStyle: root.scopeStyle
450- foregroundColor: root.foregroundColor
451- property real desiredHeight: {
452- if (root.showList) {
453- if (navigation && navigation.loaded && x == navigationListView.contentX)
454- {
455- navigationListView.updateMaxHeight();
456- return Math.min(implicitHeight, navigationListView.maxHeight);
457- } else {
458- return navigationListView.prevHeight;
459- }
460- } else {
461- return 0;
462- }
463- }
464- height: desiredHeight
465- navigation: (nullifyNavigation || !scope) ? null : getNavigation(navigationId)
466- currentNavigation: root.currentNavigation
467- onEnterNavigation: {
468- scope.setNavigationState(newNavigationId, isAltNavigation);
469- // We only need to add a new item to the model
470- // if we have children, otherwise just load it
471- if (hasChildren) {
472- isGoingBack = false;
473- navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
474- navigationListView.currentIndex++;
475- } else {
476- showList = false;
477- }
478- }
479- onGoBackToParentClicked: {
480- if (navigationListView.currentIndex == 0) {
481- // This can happen if we jumped to the non root of a deep tree and the user
482- // is now going back, create space in the list for the list to move "left"
483- var aux = navigationListView.highlightMoveDuration;
484- navigationListView.highlightMoveDuration = 0;
485- navigationModel.insert(0, {"navigationId": navigation.parentNavigationId, "nullifyNavigation": false});
486- navigationListView.currentIndex = navigationListView.currentIndex + 1;
487- navigationListView.contentX = width * navigationListView.currentIndex;
488- navigationListView.highlightMoveDuration = aux;
489- }
490-
491- scope.setNavigationState(navigation.parentNavigationId, isAltNavigation);
492- isGoingBack = true;
493- navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
494- navigationListView.currentIndex--;
495- }
496- onAllNavigationClicked: {
497- showList = false;
498- if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
499- // For leaves we have to go to the parent too
500- scope.setNavigationState(root.currentNavigation.parentNavigationId, isAltNavigation);
501- }
502- }
503- }
504- onContentXChanged: {
505- if (navigationListView.highlightMoveDuration == 0)
506- return;
507-
508- if (contentX == width * navigationListView.currentIndex) {
509- if (isGoingBack) {
510- navigationModel.remove(navigationListView.currentIndex + 1);
511- } else {
512- navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
513- }
514- }
515- }
516- }
517-
518- Image {
519- anchors {
520- top: navigationListView.bottom
521- left: navigationListView.left
522- right: navigationListView.right
523- }
524- fillMode: Image.Stretch
525- source: "graphics/navigation_shadow.png"
526- visible: root.showList
527- }
528-
529- property bool isFirstLoad: false
530- onScopeChanged: {
531- navigationModel.clear();
532- isFirstLoad = true;
533- }
534- function setNewNavigation() {
535- if (isFirstLoad && currentNavigation && currentNavigation.loaded) {
536- isFirstLoad = false;
537- if (currentNavigation.count > 0) {
538- navigationModel.append({"navigationId": scope[currentNavigationId], "nullifyNavigation": false});
539- } else {
540- navigationModel.append({"navigationId": currentNavigation.parentNavigationId, "nullifyNavigation": false});
541- }
542- }
543- }
544- Connections {
545- target: currentNavigation
546- onLoadedChanged: setNewNavigation();
547- }
548- onCurrentNavigationChanged: setNewNavigation();
549-
550- InverseMouseArea {
551- id: inverseMouseArea
552- anchors.fill: navigationListView
553- enabled: root.showList
554- onPressed: root.showList = false
555- }
556-
557- Rectangle {
558- visible: root.showDivider
559- anchors {
560- top: parent.top
561- topMargin: units.dp(1)
562- bottom: parent.bottom
563- left: parent.right
564- leftMargin: -units.dp(0.5)
565- }
566- width: units.dp(1)
567- color: root.foregroundColor
568- opacity: 0.2
569- }
570-}
571
572=== added file 'qml/Dash/DashNavigationHeader.qml'
573--- qml/Dash/DashNavigationHeader.qml 1970-01-01 00:00:00 +0000
574+++ qml/Dash/DashNavigationHeader.qml 2016-03-15 19:17:30 +0000
575@@ -0,0 +1,73 @@
576+/*
577+ * Copyright (C) 2014 Canonical, Ltd.
578+ *
579+ * This program is free software; you can redistribute it and/or modify
580+ * it under the terms of the GNU General Public License as published by
581+ * the Free Software Foundation; version 3.
582+ *
583+ * This program is distributed in the hope that it will be useful,
584+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
585+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
586+ * GNU General Public License for more details.
587+ *
588+ * You should have received a copy of the GNU General Public License
589+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
590+ */
591+
592+import QtQuick 2.4
593+import Ubuntu.Components 1.3
594+import Ubuntu.Components.ListItems 1.3 as ListItem
595+
596+Row {
597+ id: root
598+
599+ property alias backVisible: backImageItem.visible
600+ property alias textEnabled: textItem.enabled
601+ property alias text: textLabel.text
602+
603+ readonly property alias backButtonWidth: backImageItem.width
604+
605+ signal backClicked()
606+ signal textClicked()
607+
608+ ListItem.Standard {
609+ id: backImageItem
610+ objectName: "backButton"
611+ height: parent.height
612+ width: height
613+ showDivider: false
614+
615+ Icon {
616+ id: backImage
617+ anchors.centerIn: parent
618+ name: "back"
619+ height: units.gu(2)
620+ width: height
621+ }
622+
623+ onClicked: root.backClicked();
624+ }
625+
626+ ListItem.Standard {
627+ id: textItem
628+ height: parent.height
629+ showDivider: false
630+
631+ Label {
632+ id: textLabel
633+ anchors {
634+ verticalCenter: parent.verticalCenter
635+ left: parent.left
636+ right: parent.right
637+ leftMargin: backImageItem.visible ? 0 : units.gu(2)
638+ rightMargin: units.gu(2)
639+ }
640+ color: "#5D5D5D"
641+ wrapMode: Text.Wrap
642+ maximumLineCount: 2
643+ elide: Text.ElideMiddle
644+ }
645+
646+ onClicked: root.textClicked();
647+ }
648+}
649
650=== modified file 'qml/Dash/DashNavigationList.qml'
651--- qml/Dash/DashNavigationList.qml 2015-11-19 16:55:31 +0000
652+++ qml/Dash/DashNavigationList.qml 2016-03-15 19:17:30 +0000
653@@ -16,27 +16,18 @@
654
655 import QtQuick 2.4
656 import Ubuntu.Components 1.3
657-import Ubuntu.Components.ListItems 1.3 as ListItem
658+import Ubuntu.Components.ListItems 1.3 as ListItems
659 import "../Components"
660
661 Item {
662 id: root
663 property var navigation: null
664 property var currentNavigation: null
665- property var scopeStyle: null
666- property color foregroundColor: theme.palette.normal.baseText
667- signal enterNavigation(var newNavigationId, bool hasChildren)
668- signal goBackToParentClicked()
669- signal allNavigationClicked()
670+ signal enterNavigation(var newNavigationId, string newNavigationLabel, bool hasChildren)
671
672 readonly property int itemHeight: units.gu(5)
673 implicitHeight: flickable.contentHeight
674
675- Background {
676- style: root.scopeStyle ? root.scopeStyle.navigationBackground : "color:///#f5f5f5"
677- anchors.fill: parent
678- }
679-
680 clip: true
681
682 Behavior on height {
683@@ -59,89 +50,46 @@
684 id: column
685 width: parent.width
686
687- ListItem.Standard {
688- id: backButton
689- objectName: "backButton"
690- visible: navigation && !navigation.isRoot || false
691- height: itemHeight
692-
693- onClicked: root.goBackToParentClicked();
694-
695- Icon {
696- id: backImage
697- anchors {
698- verticalCenter: parent.verticalCenter
699- left: parent.left
700- leftMargin: units.gu(2)
701- }
702- name: "back"
703- height: units.gu(2)
704- width: height
705- color: root.foregroundColor
706- }
707-
708- Label {
709- anchors {
710- verticalCenter: parent.verticalCenter
711- left: backImage.right
712- right: parent.right
713- leftMargin: units.gu(0.5)
714- rightMargin: units.gu(2)
715- }
716- text: navigation ? navigation.parentLabel : ""
717- color: root.foregroundColor
718- wrapMode: Text.Wrap
719- maximumLineCount: 2
720- elide: Text.ElideMiddle
721- }
722- }
723-
724- ListItem.Standard {
725- id: allButton
726- objectName: "allButton"
727- visible: navigation && (!navigation.isRoot || (!navigation.hidden && root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
728- height: itemHeight
729-
730- Label {
731- anchors {
732- verticalCenter: parent.verticalCenter
733- left: parent.left
734- right: parent.right
735- leftMargin: units.gu(2)
736- rightMargin: units.gu(2)
737- }
738- text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
739- font.bold: true
740- color: root.foregroundColor
741- wrapMode: Text.Wrap
742- maximumLineCount: 2
743- elide: Text.ElideMiddle
744- }
745-
746- onClicked: root.allNavigationClicked();
747- }
748-
749 Repeater {
750 model: navigation && navigation.loaded ? navigation : null
751 clip: true
752- delegate: ListItem.Standard {
753+ // FIXME Move to ListItem (and remove import) once 1556971 is fixed
754+ delegate: ListItems.Empty {
755 objectName: root.objectName + "child" + index
756 height: root.itemHeight
757- showDivider: index != navigation.count - 1
758- selected: isActive
759-
760- onClicked: root.enterNavigation(navigationId, hasChildren)
761+ width: column.width
762+ anchors {
763+ left: column.left
764+ right: column.right
765+ leftMargin: units.gu(2)
766+ rightMargin: units.gu(2)
767+ }
768+
769+ onClicked: root.enterNavigation(navigationId, allLabel != "" ? allLabel : label, hasChildren)
770+
771+ Icon {
772+ id: leftIcon
773+ anchors {
774+ verticalCenter: parent.verticalCenter
775+ left: parent.left
776+ }
777+ height: units.gu(2)
778+ width: height
779+ name: "tick"
780+ color: "#3EB34F"
781+ visible: isActive
782+ }
783
784 Label {
785 anchors {
786 verticalCenter: parent.verticalCenter
787- left: parent.left
788- leftMargin: units.gu(2)
789- right: rightIcon.visible ? rightIcon.left : parent.right
790- rightMargin: rightIcon.visible ? units.gu(0.5) : units.gu(2)
791+ left: leftIcon.right
792+ leftMargin: units.gu(1)
793+ right: rightIcon.left
794+ rightMargin: units.gu(2)
795 }
796 text: label
797- color: root.foregroundColor
798+ color: isActive ? "#333333" : "#888888"
799 wrapMode: Text.Wrap
800 maximumLineCount: 2
801 elide: Text.ElideMiddle
802@@ -152,14 +100,14 @@
803 anchors {
804 verticalCenter: parent.verticalCenter
805 right: parent.right
806- rightMargin: units.gu(2)
807 }
808 height: units.gu(2)
809 width: height
810- name: hasChildren ? "go-next" : "tick"
811- color: root.foregroundColor
812- visible: hasChildren || isActive
813+ name: "go-next"
814+ visible: hasChildren
815 }
816+
817+ divider.visible: index != navigation.count - 1
818 }
819 }
820 }
821
822=== modified file 'qml/Dash/DashPageHeader.qml'
823--- qml/Dash/DashPageHeader.qml 2016-01-25 10:48:16 +0000
824+++ qml/Dash/DashPageHeader.qml 2016-03-15 19:17:30 +0000
825@@ -17,41 +17,45 @@
826 import QtQuick 2.4
827 import Ubuntu.Components 1.3
828 import Ubuntu.Components.Themes.Ambiance 1.3
829-import Ubuntu.Components.Popups 1.3
830 import Ubuntu.Components.ListItems 1.3
831 import "../Components"
832-import "../Components/SearchHistoryModel"
833
834 Item {
835 id: root
836 objectName: "pageHeader"
837- implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
838+ implicitHeight: headerContainer.height + signatureLineHeight
839+ readonly property real signatureLineHeight: showSignatureLine ? units.gu(2) : 0
840
841+ property int activeFiltersCount: 0
842+ property bool scopeHasFilters: false
843 property bool showBackButton: false
844 property bool backIsClose: false
845 property string title
846+ property var extraPanel
847+ property string navigationTag
848
849 property bool storeEntryEnabled: false
850 property bool searchEntryEnabled: false
851 property bool settingsEnabled: false
852 property bool favoriteEnabled: false
853 property bool favorite: false
854- property ListModel searchHistory: SearchHistoryModel
855+ property ListModel searchHistory
856 property alias searchQuery: searchTextField.text
857 property alias searchHint: searchTextField.placeholderText
858 property bool showSignatureLine: true
859
860- property alias bottomItem: bottomContainer.children
861 property int paginationCount: 0
862 property int paginationIndex: -1
863
864 property var scopeStyle: null
865
866+ signal clearSearch(bool keepPanelOpen)
867 signal backClicked()
868 signal storeClicked()
869 signal settingsClicked()
870 signal favoriteClicked()
871 signal searchTextFieldFocused()
872+ signal showFiltersPopup(var item)
873
874 onScopeStyleChanged: refreshLogo()
875 onSearchQueryChanged: {
876@@ -60,6 +64,12 @@
877 headerContainer.showSearch = true;
878 }
879 }
880+ onNavigationTagChanged: {
881+ // Make sure we are at the search page if the navigation tag changes behind our feet
882+ if (navigationTag) {
883+ headerContainer.showSearch = true;
884+ }
885+ }
886
887 function triggerSearch() {
888 if (searchEntryEnabled) {
889@@ -69,12 +79,14 @@
890 }
891
892 function closePopup(keepFocus) {
893- if (headerContainer.popover != null) {
894- headerContainer.popover.unfocusOnDestruction = !keepFocus;
895- PopupUtils.close(headerContainer.popover);
896+ if (extraPanel.visible) {
897+ extraPanel.visible = false;
898 } else if (!keepFocus) {
899 unfocus();
900 }
901+ if (!searchTextField.text && !root.navigationTag && searchHistory.count == 0) {
902+ headerContainer.showSearch = false;
903+ }
904 }
905
906 function resetSearch(keepFocus) {
907@@ -87,22 +99,17 @@
908
909 function unfocus() {
910 searchTextField.focus = false;
911- if (!searchTextField.text) {
912+ if (!searchTextField.text && !root.navigationTag) {
913 headerContainer.showSearch = false;
914 }
915 }
916
917- function openSearchHistory() {
918+ function openPopup() {
919 if (openSearchAnimation.running) {
920- openSearchAnimation.openSearchHistory = true;
921- } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
922- headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
923- {
924- "contentWidth": searchTextField.width,
925- "edgeMargins": units.gu(1)
926- }
927- );
928- searchTextField.forceActiveFocus();
929+ openSearchAnimation.openPopup = true;
930+ } else if (extraPanel.hasContents) {
931+ // Show extraPanel
932+ extraPanel.visible = true;
933 }
934 }
935
936@@ -121,13 +128,11 @@
937 }
938
939 InverseMouseArea {
940- anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
941+ anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + (extraPanel ? extraPanel.height : 0) }
942 visible: headerContainer.showSearch
943 onPressed: {
944+ extraPanel.visible = false;
945 closePopup(/* keepFocus */false);
946- if (!searchTextField.text) {
947- headerContainer.showSearch = false;
948- }
949 mouse.accepted = false;
950 }
951 }
952@@ -143,7 +148,6 @@
953 contentY: showSearch ? 0 : height
954
955 property bool showSearch: false
956- property var popover: null
957
958 Background {
959 id: background
960@@ -154,12 +158,12 @@
961 Behavior on contentY {
962 UbuntuNumberAnimation {
963 id: openSearchAnimation
964- property bool openSearchHistory: false
965+ property bool openPopup: false
966
967 onRunningChanged: {
968- if (!running && openSearchAnimation.openSearchHistory) {
969- openSearchAnimation.openSearchHistory = false;
970- root.openSearchHistory();
971+ if (!running && openSearchAnimation.openPopup) {
972+ openSearchAnimation.openPopup = false;
973+ root.openPopup();
974 }
975 }
976 }
977@@ -183,61 +187,124 @@
978 backgroundColor: "transparent"
979 config: PageHeadConfiguration {
980 foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : theme.palette.normal.baseText
981- backAction: Action {
982- iconName: "back"
983- onTriggered: {
984- root.resetSearch();
985+ }
986+ property var contents: Item {
987+ anchors.fill: parent
988+
989+ TextField {
990+ id: searchTextField
991+ objectName: "searchTextField"
992+ inputMethodHints: Qt.ImhNoPredictiveText
993+ hasClearButton: false
994+ anchors {
995+ top: parent.top
996+ topMargin: units.gu(1)
997+ left: parent.left
998+ bottom: parent.bottom
999+ bottomMargin: units.gu(1)
1000+ right: settingsButton.left
1001+ rightMargin: settingsButton.visible ? 0 : units.gu(2)
1002+ }
1003+
1004+ primaryItem: Rectangle {
1005+ color: "#F5F4F5"
1006+ width: root.navigationTag != "" ? tagLabel.width + units.gu(2) : 0
1007+ height: root.navigationTag != "" ? tagLabel.height + units.gu(1) : 0
1008+ radius: units.gu(0.5)
1009+ Label {
1010+ id: tagLabel
1011+ text: root.navigationTag
1012+ anchors.centerIn: parent
1013+ color: "#333333"
1014+ }
1015+ }
1016+
1017+ secondaryItem: AbstractButton {
1018+ id: clearButton
1019+ height: searchTextField.height
1020+ width: height
1021+ enabled: searchTextField.text.length > 0 || root.navigationTag != ""
1022+
1023+ Image {
1024+ objectName: "clearIcon"
1025+ anchors.fill: parent
1026+ anchors.margins: units.gu(1)
1027+ source: "image://theme/clear"
1028+ opacity: parent.enabled
1029+ visible: opacity > 0
1030+ Behavior on opacity {
1031+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
1032+ }
1033+ }
1034+
1035+ onClicked: {
1036+ root.clearSearch(true);
1037+ }
1038+ }
1039+
1040+ onActiveFocusChanged: {
1041+ if (activeFocus) {
1042+ root.searchTextFieldFocused();
1043+ root.openPopup();
1044+ }
1045+ }
1046+
1047+ onTextChanged: {
1048+ if (text != "") {
1049+ closePopup(/* keepFocus */true);
1050+ }
1051+ }
1052+ }
1053+
1054+ AbstractButton {
1055+ id: settingsButton
1056+ objectName: "settingsButton"
1057+
1058+ width: root.scopeHasFilters ? height : 0
1059+ visible: width > 0
1060+ anchors {
1061+ top: parent.top
1062+ right: cancelButton.left
1063+ bottom: parent.bottom
1064+ rightMargin: units.gu(-1)
1065+ }
1066+
1067+ Icon {
1068+ anchors.fill: parent
1069+ anchors.margins: units.gu(2)
1070+ name: "filters"
1071+ color: root.activeFiltersCount > 0 ? UbuntuColors.orange : Qt.rgba(0.0, 0.0, 0.0, 0.0)
1072+ }
1073+
1074+ onClicked: {
1075+ root.showFiltersPopup(settingsButton);
1076+ }
1077+ }
1078+
1079+ AbstractButton {
1080+ id: cancelButton
1081+ width: cancelLabel.width + cancelLabel.anchors.rightMargin + cancelLabel.anchors.leftMargin
1082+ anchors {
1083+ top: parent.top
1084+ right: parent.right
1085+ bottom: parent.bottom
1086+ }
1087+ onClicked: {
1088+ root.clearSearch(false);
1089 headerContainer.showSearch = false;
1090 }
1091- }
1092- }
1093- property var contents: TextField {
1094- id: searchTextField
1095- objectName: "searchTextField"
1096- inputMethodHints: Qt.ImhNoPredictiveText
1097- hasClearButton: false
1098- anchors {
1099- fill: parent
1100- leftMargin: units.gu(1)
1101- topMargin: units.gu(1)
1102- bottomMargin: units.gu(1)
1103- rightMargin: root.width > units.gu(60) ? root.width - units.gu(40) : units.gu(1)
1104- }
1105-
1106- secondaryItem: AbstractButton {
1107- height: searchTextField.height
1108- width: height
1109- enabled: searchTextField.text.length > 0
1110-
1111- Image {
1112- objectName: "clearIcon"
1113- anchors.fill: parent
1114- anchors.margins: units.gu(.75)
1115- source: "image://theme/clear"
1116- opacity: searchTextField.text.length > 0
1117- visible: opacity > 0
1118- Behavior on opacity {
1119- UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
1120+ Label {
1121+ id: cancelLabel
1122+ text: i18n.tr("Cancel")
1123+ color: header.panelForegroundColor
1124+ verticalAlignment: Text.AlignVCenter
1125+ anchors {
1126+ verticalCenter: parent.verticalCenter
1127+ right: parent.right
1128+ rightMargin: units.gu(2)
1129+ leftMargin: units.gu(1)
1130 }
1131 }
1132-
1133- onClicked: {
1134- root.resetSearch(true);
1135- root.openSearchHistory();
1136- }
1137- }
1138-
1139- onActiveFocusChanged: {
1140- if (activeFocus) {
1141- root.searchTextFieldFocused();
1142- root.openSearchHistory();
1143- }
1144- }
1145-
1146- onTextChanged: {
1147- if (text != "") {
1148- closePopup(/* keepFocus */true);
1149- }
1150 }
1151 }
1152 }
1153@@ -322,47 +389,6 @@
1154 }
1155 }
1156
1157- Component {
1158- id: popoverComponent
1159- Popover {
1160- id: popover
1161- autoClose: false
1162-
1163- property bool unfocusOnDestruction: false
1164-
1165- Component.onDestruction: {
1166- headerContainer.popover = null;
1167- if (unfocusOnDestruction) {
1168- root.unfocus();
1169- }
1170- }
1171-
1172- Column {
1173- anchors {
1174- top: parent.top
1175- left: parent.left
1176- right: parent.right
1177- }
1178-
1179- Repeater {
1180- id: recentSearches
1181- objectName: "recentSearches"
1182- model: searchHistory
1183-
1184- delegate: Standard {
1185- showDivider: index < recentSearches.count - 1
1186- text: query
1187- onClicked: {
1188- searchHistory.addQuery(text);
1189- searchTextField.text = text;
1190- closePopup(/* keepFocus */false);
1191- }
1192- }
1193- }
1194- }
1195- }
1196- }
1197-
1198 Rectangle {
1199 id: bottomBorder
1200 visible: showSignatureLine
1201@@ -370,7 +396,7 @@
1202 top: headerContainer.bottom
1203 left: parent.left
1204 right: parent.right
1205- bottom: bottomContainer.top
1206+ bottom: parent.bottom
1207 }
1208
1209 color: root.scopeStyle ? root.scopeStyle.headerDividerColor : "#e0e0e0"
1210@@ -411,7 +437,7 @@
1211 id: bottomHighlight
1212 visible: bottomBorder.visible
1213 anchors {
1214- top: bottomContainer.top
1215+ top: parent.bottom
1216 left: parent.left
1217 right: parent.right
1218 }
1219@@ -419,30 +445,13 @@
1220 height: units.dp(1)
1221 opacity: 0.6
1222
1223- // FIXME this should be a shader when bottomItem exists
1224- // to support image backgrounds
1225 Rectangle {
1226 anchors.fill: parent
1227- color: if (bottomItem && bottomItem.background) {
1228- Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
1229- bottomItem.background.topColor.g,
1230- bottomItem.background.topColor.b, 1.0), 1.2);
1231- } else if (!bottomItem && root.scopeStyle) {
1232+ color: if (root.scopeStyle) {
1233 Qt.lighter(Qt.rgba(root.scopeStyle.background.r,
1234 root.scopeStyle.background.g,
1235 root.scopeStyle.background.b, 1.0), 1.2);
1236 } else "#CCFFFFFF"
1237 }
1238 }
1239-
1240- Item {
1241- id: bottomContainer
1242-
1243- anchors {
1244- left: parent.left
1245- right: parent.right
1246- bottom: parent.bottom
1247- }
1248- height: childrenRect.height
1249- }
1250 }
1251
1252=== added directory 'qml/Dash/Filters'
1253=== added file 'qml/Dash/Filters/FilterOptionSelector.qml'
1254--- qml/Dash/Filters/FilterOptionSelector.qml 1970-01-01 00:00:00 +0000
1255+++ qml/Dash/Filters/FilterOptionSelector.qml 2016-03-15 19:17:30 +0000
1256@@ -0,0 +1,91 @@
1257+/*
1258+ * Copyright (C) 2015 Canonical, Ltd.
1259+ *
1260+ * This program is free software; you can redistribute it and/or modify
1261+ * it under the terms of the GNU General Public License as published by
1262+ * the Free Software Foundation; version 3.
1263+ *
1264+ * This program is distributed in the hope that it will be useful,
1265+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1266+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1267+ * GNU General Public License for more details.
1268+ *
1269+ * You should have received a copy of the GNU General Public License
1270+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1271+ */
1272+
1273+import QtQuick 2.4
1274+import Ubuntu.Components 1.3
1275+import Ubuntu.Components.ListItems 1.3 as ListItems
1276+
1277+/*! Option Selector Filter Widget. */
1278+
1279+FilterWidget {
1280+ id: root
1281+
1282+ implicitHeight: expandingItem.height
1283+
1284+ ListItems.Expandable {
1285+ id: expandingItem
1286+ objectName: "expandingItem"
1287+
1288+ expandedHeight: collapsedHeight + column.height
1289+ width: parent.width
1290+ showDivider: false
1291+
1292+ onClicked: {
1293+ expanded = !expanded;
1294+ forceActiveFocus();
1295+ }
1296+
1297+ Item {
1298+ id: holder
1299+ anchors.top: parent.top
1300+ height: expandingItem.collapsedHeight
1301+ width: parent.width
1302+
1303+ Label {
1304+ anchors.left: parent.left
1305+ anchors.right: dropDown.left
1306+ anchors.verticalCenter: parent.verticalCenter
1307+ text: widgetData.label || ""
1308+ }
1309+
1310+ Image {
1311+ id: dropDown
1312+ height: units.gu(3)
1313+ fillMode: Image.PreserveAspectFit
1314+ anchors.right: parent.right
1315+ anchors.verticalCenter: parent.verticalCenter
1316+ source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
1317+ }
1318+ }
1319+
1320+ Column {
1321+ id: column
1322+ anchors.top: holder.bottom
1323+ width: parent.width
1324+ Repeater {
1325+ model: widgetData.options
1326+
1327+ ListItems.Standard {
1328+ text: label
1329+ objectName: root.objectName + "label" + index;
1330+
1331+ Image {
1332+ height: units.gu(3)
1333+ fillMode: Image.PreserveAspectFit
1334+ anchors.right: parent.right
1335+ anchors.verticalCenter: parent.verticalCenter
1336+ source: "image://theme/tick"
1337+ visible: checked
1338+ }
1339+
1340+ onClicked: {
1341+ widgetData.options.setChecked(index, !checked);
1342+ }
1343+ }
1344+ }
1345+ }
1346+ }
1347+}
1348
1349=== added file 'qml/Dash/Filters/FilterRangeInput.qml'
1350--- qml/Dash/Filters/FilterRangeInput.qml 1970-01-01 00:00:00 +0000
1351+++ qml/Dash/Filters/FilterRangeInput.qml 2016-03-15 19:17:30 +0000
1352@@ -0,0 +1,136 @@
1353+/*
1354+ * Copyright (C) 2015 Canonical, Ltd.
1355+ *
1356+ * This program is free software; you can redistribute it and/or modify
1357+ * it under the terms of the GNU General Public License as published by
1358+ * the Free Software Foundation; version 3.
1359+ *
1360+ * This program is distributed in the hope that it will be useful,
1361+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1362+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1363+ * GNU General Public License for more details.
1364+ *
1365+ * You should have received a copy of the GNU General Public License
1366+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1367+ */
1368+
1369+import QtQuick 2.4
1370+import QtQuick.Layouts 1.1
1371+import Ubuntu.Components 1.3
1372+
1373+/*! Range Input Filter Widget. */
1374+
1375+FilterWidget {
1376+ id: root
1377+
1378+ implicitHeight: field1.height + units.gu(2)
1379+
1380+ function setFieldValue(field, hasValue, value) {
1381+ if (hasValue) {
1382+ // Need this othewise if we are on 4.5 and backspace instead of
1383+ // having 4. in the text field we end up with 4 which is confusing
1384+ if (field.text != value) {
1385+ field.text = value;
1386+ }
1387+ } else {
1388+ field.text = "";
1389+ }
1390+ }
1391+
1392+ Connections {
1393+ target: widgetData
1394+ onStartValueChanged: root.setFieldValue(field1, widgetData.hasStartValue, widgetData.startValue);
1395+ onHasStartValueChanged: root.setFieldValue(field1, widgetData.hasStartValue, widgetData.startValue);
1396+ onEndValueChanged: root.setFieldValue(field2, widgetData.hasEndValue, widgetData.endValue);
1397+ onHasEndValueChanged: root.setFieldValue(field2, widgetData.hasEndValue, widgetData.endValue);
1398+ }
1399+
1400+ onWidgetDataChanged: {
1401+ if (widgetData) {
1402+ root.setFieldValue(field1, widgetData.hasStartValue, widgetData.startValue);
1403+ root.setFieldValue(field2, widgetData.hasEndValue, widgetData.endValue);
1404+ } else {
1405+ root.setFieldValue(field1, false, -1);
1406+ root.setFieldValue(field2, false, -1);
1407+ }
1408+ }
1409+
1410+ RowLayout {
1411+ anchors {
1412+ fill: parent
1413+ topMargin: units.gu(1)
1414+ bottomMargin: units.gu(1)
1415+ }
1416+
1417+ Item {
1418+ Layout.fillWidth: true
1419+ }
1420+
1421+ Label {
1422+ text: widgetData.startPrefixLabel
1423+ verticalAlignment: Text.AlignVCenter
1424+ }
1425+
1426+ TextField {
1427+ id: field1
1428+ objectName: "startValueField"
1429+ implicitWidth: units.gu(9)
1430+ verticalAlignment: Text.AlignVCenter
1431+ inputMethodHints: Qt.ImhFormattedNumbersOnly
1432+ validator: DoubleValidator {
1433+ notation: DoubleValidator.StandardNotation
1434+ }
1435+ onTextChanged: {
1436+ if (text === "") widgetData.eraseStartValue();
1437+ else widgetData.startValue = text;
1438+ }
1439+ }
1440+
1441+ Label {
1442+ text: widgetData.startPostfixLabel
1443+ verticalAlignment: Text.AlignVCenter
1444+ }
1445+
1446+ Item {
1447+ Layout.fillWidth: true
1448+ }
1449+
1450+ Label {
1451+ text: widgetData.centralLabel
1452+ verticalAlignment: Text.AlignVCenter
1453+ }
1454+
1455+ Item {
1456+ Layout.fillWidth: true
1457+ }
1458+
1459+ Label {
1460+ text: widgetData.endPrefixLabel
1461+ verticalAlignment: Text.AlignVCenter
1462+ }
1463+
1464+ TextField {
1465+ id: field2
1466+ objectName: "endValueField"
1467+ implicitWidth: units.gu(9)
1468+ verticalAlignment: Text.AlignVCenter
1469+ inputMethodHints: Qt.ImhFormattedNumbersOnly
1470+ validator: DoubleValidator {
1471+ notation: DoubleValidator.StandardNotation
1472+ }
1473+ onTextChanged: {
1474+ if (text === "") widgetData.eraseEndValue();
1475+ else widgetData.endValue = text;
1476+ }
1477+ }
1478+
1479+ Label {
1480+ text: widgetData.endPostfixLabel
1481+ verticalAlignment: Text.AlignVCenter
1482+ }
1483+
1484+ Item {
1485+ Layout.fillWidth: true
1486+ }
1487+ }
1488+}
1489
1490=== added file 'qml/Dash/Filters/FilterValueSlider.qml'
1491--- qml/Dash/Filters/FilterValueSlider.qml 1970-01-01 00:00:00 +0000
1492+++ qml/Dash/Filters/FilterValueSlider.qml 2016-03-15 19:17:30 +0000
1493@@ -0,0 +1,87 @@
1494+/*
1495+ * Copyright (C) 2015 Canonical, Ltd.
1496+ *
1497+ * This program is free software; you can redistribute it and/or modify
1498+ * it under the terms of the GNU General Public License as published by
1499+ * the Free Software Foundation; version 3.
1500+ *
1501+ * This program is distributed in the hope that it will be useful,
1502+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1503+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1504+ * GNU General Public License for more details.
1505+ *
1506+ * You should have received a copy of the GNU General Public License
1507+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1508+ */
1509+
1510+import QtQuick 2.4
1511+import QtQuick.Layouts 1.1
1512+import Ubuntu.Components 1.3
1513+
1514+/*! Value Slider Filter Widget. */
1515+
1516+FilterWidget {
1517+ id: root
1518+
1519+ implicitHeight: childrenRect.height + units.gu(2)
1520+
1521+ Connections {
1522+ target: widgetData
1523+ // One would think that this is not needed since
1524+ // we have value: widgetData.value on the slider
1525+ // but it is, otherwise reset doesn't seem to work
1526+ onValueChanged: {
1527+ if (widgetData.value !== slider.value) {
1528+ slider.value = widgetData.value;
1529+ }
1530+ }
1531+ }
1532+
1533+ Slider {
1534+ id: slider
1535+ objectName: "slider"
1536+
1537+ anchors {
1538+ top: parent.top
1539+ topMargin: units.gu(1)
1540+ left: parent.left
1541+ leftMargin: units.gu(2)
1542+ right: parent.right
1543+ rightMargin: units.gu(2)
1544+ }
1545+
1546+ minimumValue: widgetData.minValue
1547+ maximumValue: widgetData.maxValue
1548+ value: widgetData.value
1549+ onValueChanged: {
1550+ widgetData.value = value;
1551+ }
1552+ onPressedChanged: {
1553+ if (pressed) forceActiveFocus();
1554+ }
1555+
1556+ readonly property Item thumb: __internals.thumb
1557+ readonly property real barMinusThumb: __internals.barMinusThumb
1558+ }
1559+
1560+ Repeater {
1561+ objectName: "repeater"
1562+ model: widgetData.values
1563+ delegate: Label {
1564+ anchors {
1565+ top: slider.bottom
1566+ // The slider is too tall, so move a bit up
1567+ topMargin: -units.gu(1)
1568+ }
1569+ text: label
1570+ visible: value <= widgetData.maxValue && value >= widgetData.minValue
1571+ x: {
1572+ var halfThumbDifference = (width - slider.thumb.width) / 2;
1573+ var result = (value - widgetData.minValue) * slider.barMinusThumb / (widgetData.maxValue - widgetData.minValue) - halfThumbDifference;
1574+ result = Math.max(result, 0);
1575+ result = Math.min(result, slider.width - width);
1576+ return units.gu(2) + result;
1577+ }
1578+ }
1579+ }
1580+}
1581
1582=== added file 'qml/Dash/Filters/FilterWidget.qml'
1583--- qml/Dash/Filters/FilterWidget.qml 1970-01-01 00:00:00 +0000
1584+++ qml/Dash/Filters/FilterWidget.qml 2016-03-15 19:17:30 +0000
1585@@ -0,0 +1,29 @@
1586+/*
1587+ * Copyright (C) 2015 Canonical, Ltd.
1588+ *
1589+ * This program is free software; you can redistribute it and/or modify
1590+ * it under the terms of the GNU General Public License as published by
1591+ * the Free Software Foundation; version 3.
1592+ *
1593+ * This program is distributed in the hope that it will be useful,
1594+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1595+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1596+ * GNU General Public License for more details.
1597+ *
1598+ * You should have received a copy of the GNU General Public License
1599+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1600+ */
1601+
1602+import QtQuick 2.4
1603+
1604+/*! Interface for filter widgets. */
1605+
1606+Item {
1607+ //! The widget identifier
1608+ property string widgetId
1609+
1610+ //! Variable used to contain widget's data
1611+ property var widgetData: null
1612+
1613+ objectName: widgetId
1614+}
1615
1616=== added file 'qml/Dash/Filters/FilterWidgetFactory.qml'
1617--- qml/Dash/Filters/FilterWidgetFactory.qml 1970-01-01 00:00:00 +0000
1618+++ qml/Dash/Filters/FilterWidgetFactory.qml 2016-03-15 19:17:30 +0000
1619@@ -0,0 +1,80 @@
1620+/*
1621+ * Copyright (C) 2015 Canonical, Ltd.
1622+ *
1623+ * This program is free software; you can redistribute it and/or modify
1624+ * it under the terms of the GNU General Public License as published by
1625+ * the Free Software Foundation; version 3.
1626+ *
1627+ * This program is distributed in the hope that it will be useful,
1628+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1629+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1630+ * GNU General Public License for more details.
1631+ *
1632+ * You should have received a copy of the GNU General Public License
1633+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1634+ */
1635+
1636+import QtQuick 2.4
1637+import Ubuntu.Components 1.3
1638+import Unity 0.2
1639+
1640+//! \brief This component loads the widgets based on widgetType.
1641+
1642+Item {
1643+ id: root
1644+ //! Identifier of the widget.
1645+ property string widgetId: ""
1646+
1647+ //! Type of the widget to display.
1648+ property int widgetType
1649+
1650+ //! Widget data, forwarded to the widget as is.
1651+ property var widgetData: null
1652+
1653+ implicitHeight: title.height + title.anchors.topMargin + loader.height
1654+
1655+ Label {
1656+ id: title
1657+ text: widgetData ? widgetData.title : ""
1658+ height: text != "" ? implicitHeight : 0
1659+
1660+ anchors {
1661+ top: parent.top
1662+ left: parent.left
1663+ right: parent.right
1664+
1665+ topMargin: height > 0 ? units.gu(1) : 0
1666+ leftMargin: units.gu(2)
1667+ rightMargin: anchors.leftMargin
1668+ }
1669+ }
1670+
1671+ property alias active: loader.active
1672+
1673+ Loader {
1674+ id: loader
1675+ objectName: "loader"
1676+
1677+ anchors {
1678+ top: title.bottom
1679+ left: parent.left
1680+ right: parent.right
1681+ }
1682+
1683+ source: widgetSource
1684+
1685+ readonly property url widgetSource: {
1686+ switch (widgetType) {
1687+ case Filters.OptionSelectorFilter: return "FilterOptionSelector.qml";
1688+ case Filters.RangeInputFilter: return "FilterRangeInput.qml";
1689+ case Filters.ValueSliderFilter: return "FilterValueSlider.qml";
1690+ default: return "";
1691+ }
1692+ }
1693+
1694+ onLoaded: {
1695+ item.widgetId = Qt.binding(function() { return root.widgetId } )
1696+ item.widgetData = Qt.binding(function() { return root.widgetData } )
1697+ }
1698+ }
1699+}
1700
1701=== added file 'qml/Dash/FiltersPopover.qml'
1702--- qml/Dash/FiltersPopover.qml 1970-01-01 00:00:00 +0000
1703+++ qml/Dash/FiltersPopover.qml 2016-03-15 19:17:30 +0000
1704@@ -0,0 +1,105 @@
1705+/*
1706+ * Copyright (C) 2013-2015 Canonical, Ltd.
1707+ *
1708+ * This program is free software; you can redistribute it and/or modify
1709+ * it under the terms of the GNU General Public License as published by
1710+ * the Free Software Foundation; version 3.
1711+ *
1712+ * This program is distributed in the hope that it will be useful,
1713+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1714+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1715+ * GNU General Public License for more details.
1716+ *
1717+ * You should have received a copy of the GNU General Public License
1718+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1719+ */
1720+
1721+import QtQuick 2.4
1722+import Ubuntu.Components 1.3
1723+import Ubuntu.Components.Popups 1.3
1724+import Ubuntu.Components.ListItems 1.3 as ListItems
1725+import "Filters" as Filters
1726+
1727+Popover {
1728+ id: root
1729+ objectName: "filtersPopover"
1730+
1731+ Flickable {
1732+ id: flickable
1733+ anchors {
1734+ top: parent.top
1735+ left: parent.left
1736+ right: parent.right
1737+ }
1738+ height: {
1739+ // Popover doesn't like being 75% or bigger than the screen (counting the "empty" part on top)
1740+ var posToRootParent = flickable.mapToItem(null, 0, 0).y;
1741+ var threeQuartersParent = root.parent.height * 3 / 4 - posToRootParent - 1;
1742+ var parentAndKeyboard = root.parent.height - posToRootParent - (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height + units.gu(3) : 0)
1743+ return Math.min(parentAndKeyboard, Math.min(threeQuartersParent, column.height));
1744+ }
1745+ clip: true
1746+ contentHeight: column.height
1747+ contentWidth: width
1748+
1749+ Column {
1750+ id: column
1751+ width: parent.width
1752+
1753+ Item {
1754+ width: parent.width
1755+ height: resetLabel.height + units.gu(3)
1756+
1757+ Label {
1758+ anchors {
1759+ left: parent.left
1760+ right: resetLabel.left
1761+ margins: units.gu(2)
1762+ verticalCenter: parent.verticalCenter
1763+ }
1764+ text: i18n.tr("Refine your results")
1765+ }
1766+ Label {
1767+ id: resetLabel
1768+ anchors {
1769+ right: parent.right
1770+ rightMargin: units.gu(2)
1771+ verticalCenter: parent.verticalCenter
1772+ }
1773+ text: i18n.tr("Reset")
1774+
1775+ AbstractButton {
1776+ anchors {
1777+ fill: parent
1778+ rightMargin: units.gu(-2)
1779+ leftMargin: units.gu(-2)
1780+ topMargin: units.gu(-1)
1781+ bottomMargin: units.gu(-1)
1782+ }
1783+ onClicked: {
1784+ scopeView.scope.resetFilters();
1785+ }
1786+ }
1787+ }
1788+ }
1789+
1790+ Repeater {
1791+ id: repeater
1792+ model: scopeView.scope.filters
1793+
1794+ delegate: Filters.FilterWidgetFactory {
1795+ width: parent.width
1796+
1797+ widgetId: id
1798+ widgetType: type
1799+ widgetData: filter
1800+
1801+ ListItems.ThinDivider {
1802+ anchors.bottom: parent.bottom
1803+ visible: index != repeater.count - 1
1804+ }
1805+ }
1806+ }
1807+ }
1808+ }
1809+}
1810
1811=== modified file 'qml/Dash/GenericScopeView.qml'
1812--- qml/Dash/GenericScopeView.qml 2016-03-10 22:39:17 +0000
1813+++ qml/Dash/GenericScopeView.qml 2016-03-15 19:17:30 +0000
1814@@ -16,6 +16,8 @@
1815
1816 import QtQuick 2.4
1817 import Ubuntu.Components 1.3
1818+import Ubuntu.Components.Popups 1.3
1819+import "../Components/SearchHistoryModel"
1820 import Utils 0.1
1821 import Unity 0.2
1822 import Dash 0.1
1823@@ -25,7 +27,6 @@
1824 FocusScope {
1825 id: scopeView
1826
1827- readonly property bool navigationDisableParentInteractive: pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].disableParentInteractive : false
1828 property bool forceNonInteractive: false
1829 property var scope: null
1830 property UnitySortFilterProxyModel categories: categoryFilter
1831@@ -34,14 +35,15 @@
1832 property bool hasBackAction: false
1833 property bool enableHeightBehaviorOnNextCreation: false
1834 property var categoryView: categoryView
1835- property bool showPageHeader: true
1836 readonly property alias subPageShown: subPageLoader.subPageShown
1837+ readonly property alias extraPanelShown: peExtraPanel.visible
1838 property int paginationCount: 0
1839 property int paginationIndex: 0
1840 property bool visibleToParent: false
1841 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
1842 property var holdingList: null
1843 property bool wasCurrentOnMoveStart: false
1844+ property var filtersPopover: null
1845
1846 property var scopeStyle: ScopeStyle {
1847 style: scope ? scope.customizations : {}
1848@@ -68,8 +70,7 @@
1849 }
1850
1851 function resetSearch() {
1852- if(pageHeaderLoader.item && showPageHeader)
1853- pageHeaderLoader.item.resetSearch()
1854+ categoryView.pageHeader.resetSearch()
1855 }
1856
1857 property var maybePreviewResult;
1858@@ -120,24 +121,26 @@
1859 if (!holdingList || !holdingList.moving) {
1860 wasCurrentOnMoveStart = scopeView.isCurrent;
1861 }
1862- if (pageHeaderLoader.item && showPageHeader) {
1863- pageHeaderLoader.item.resetSearch();
1864- }
1865+ categoryView.pageHeader.resetSearch();
1866 subPageLoader.closeSubPage();
1867+ if (filtersPopover) {
1868+ PopupUtils.close(filtersPopover)
1869+ scopeView.filtersPopover = null;
1870+ }
1871 }
1872
1873 Binding {
1874 target: scopeView.scope
1875 property: "searchQuery"
1876- value: pageHeaderLoader.item ? pageHeaderLoader.item.searchQuery : ""
1877- when: isCurrent && showPageHeader
1878+ value: categoryView.pageHeader.searchQuery
1879+ when: isCurrent
1880 }
1881
1882 Binding {
1883- target: pageHeaderLoader.item
1884+ target: categoryView.pageHeader
1885 property: "searchQuery"
1886 value: scopeView.scope ? scopeView.scope.searchQuery : ""
1887- when: isCurrent && showPageHeader
1888+ when: isCurrent
1889 }
1890
1891 Connections {
1892@@ -189,8 +192,8 @@
1893 property string expandedCategoryId: ""
1894 property int runMaximizeAfterSizeChanges: 0
1895
1896- readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
1897- ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
1898+ readonly property bool pageHeaderTotallyVisible:
1899+ ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == categoryView.pageHeader.height))
1900
1901 onExpandedCategoryIdChanged: {
1902 var firstCreated = firstCreatedIndex();
1903@@ -615,41 +618,68 @@
1904 }
1905 }
1906
1907- pageHeader: scopeView.showPageHeader ? pageHeaderLoader : null
1908- Loader {
1909- id: pageHeaderLoader
1910+ pageHeader: DashPageHeader {
1911+ objectName: "scopePageHeader"
1912 width: parent.width
1913- sourceComponent: scopeView.showPageHeader ? pageHeaderComponent : undefined
1914- Component {
1915- id: pageHeaderComponent
1916- DashPageHeader {
1917- objectName: "scopePageHeader"
1918- width: parent.width
1919- title: scopeView.scope ? scopeView.scope.name : ""
1920- searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.ctr("Label: Hint for dash search line edit", "Search")
1921- showBackButton: scopeView.hasBackAction
1922- searchEntryEnabled: true
1923- settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
1924- favoriteEnabled: scopeView.scope && scopeView.scope.id !== "clickscope"
1925- favorite: scopeView.scope && scopeView.scope.favorite
1926- scopeStyle: scopeView.scopeStyle
1927- paginationCount: scopeView.paginationCount
1928- paginationIndex: scopeView.paginationIndex
1929-
1930- bottomItem: DashNavigation {
1931- scope: scopeView.scope
1932- anchors { left: parent.left; right: parent.right }
1933- windowHeight: scopeView.height
1934- windowWidth: scopeView.width
1935- scopeStyle: scopeView.scopeStyle
1936- }
1937-
1938- onBackClicked: scopeView.backClicked()
1939- onSettingsClicked: subPageLoader.openSubPage("settings")
1940- onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
1941- onSearchTextFieldFocused: scopeView.showHeader()
1942+ title: scopeView.scope ? scopeView.scope.name : ""
1943+ extraPanel: peExtraPanel
1944+ searchHistory: SearchHistoryModel
1945+ searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.ctr("Label: Hint for dash search line edit", "Search")
1946+ scopeHasFilters: scopeView.scope.filters != null
1947+ activeFiltersCount: scopeView.scope.activeFiltersCount
1948+ showBackButton: scopeView.hasBackAction
1949+ searchEntryEnabled: true
1950+ settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
1951+ favoriteEnabled: scopeView.scope && scopeView.scope.id !== "clickscope"
1952+ favorite: scopeView.scope && scopeView.scope.favorite
1953+ navigationTag: scopeView.scope ? scopeView.scope.primaryNavigationTag : ""
1954+ scopeStyle: scopeView.scopeStyle
1955+ paginationCount: scopeView.paginationCount
1956+ paginationIndex: scopeView.paginationIndex
1957+
1958+ onBackClicked: scopeView.backClicked()
1959+ onSettingsClicked: subPageLoader.openSubPage("settings")
1960+ onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
1961+ onSearchTextFieldFocused: scopeView.showHeader()
1962+ onClearSearch: { // keepPanelOpen
1963+ var panelOpen = peExtraPanel.visible;
1964+ resetSearch(keepPanelOpen);
1965+ scopeView.scope.resetPrimaryNavigationTag();
1966+ peExtraPanel.resetNavigation();
1967+ if ((panelOpen || searchHistory.count > 0) && keepPanelOpen) {
1968+ openPopup();
1969 }
1970 }
1971+ onShowFiltersPopup: { // item
1972+ extraPanel.visible = false;
1973+ scopeView.filtersPopover = PopupUtils.open(Qt.resolvedUrl("FiltersPopover.qml"), item, { "contentWidth": scopeView.width - units.gu(2) } );
1974+ }
1975+ }
1976+
1977+ PageHeaderExtraPanel {
1978+ id: peExtraPanel
1979+ objectName: "peExtraPanel"
1980+ width: parent.width >= units.gu(60) ? units.gu(40) : parent.width
1981+ anchors {
1982+ top: categoryView.pageHeader.bottom
1983+ topMargin: -categoryView.pageHeader.signatureLineHeight
1984+ }
1985+ z: 1
1986+ visible: false
1987+
1988+ searchHistory: SearchHistoryModel
1989+ scope: scopeView.scope
1990+ windowHeight: scopeView.height
1991+
1992+ onHistoryItemClicked: {
1993+ SearchHistoryModel.addQuery(text);
1994+ categoryView.pageHeader.searchQuery = text;
1995+ }
1996+
1997+ onDashNavigationLeafClicked: {
1998+ categoryView.pageHeader.closePopup();
1999+ categoryView.pageHeader.unfocus();
2000+ }
2001 }
2002 }
2003
2004@@ -658,7 +688,7 @@
2005 anchors.left: parent.left
2006 anchors.right: parent.right
2007 anchors.bottom: parent.bottom
2008- height: parent.height - pullToRefresh.contentY + (pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].height - pageHeaderLoader.item.height : 0)
2009+ height: parent.height - pullToRefresh.contentY - categoryView.pageHeader.height
2010 clip: true
2011
2012 PullToRefresh {
2013@@ -793,7 +823,7 @@
2014 open = true;
2015 }
2016
2017- onOpenChanged: pageHeaderLoader.item.unfocus()
2018+ onOpenChanged: categoryView.pageHeader.unfocus()
2019
2020 onVisibleChanged: if (!visible) subPage = ""
2021
2022
2023=== added file 'qml/Dash/PageHeaderExtraPanel.qml'
2024--- qml/Dash/PageHeaderExtraPanel.qml 1970-01-01 00:00:00 +0000
2025+++ qml/Dash/PageHeaderExtraPanel.qml 2016-03-15 19:17:30 +0000
2026@@ -0,0 +1,176 @@
2027+/*
2028+ * Copyright (C) 2013-2015 Canonical, Ltd.
2029+ *
2030+ * This program is free software; you can redistribute it and/or modify
2031+ * it under the terms of the GNU General Public License as published by
2032+ * the Free Software Foundation; version 3.
2033+ *
2034+ * This program is distributed in the hope that it will be useful,
2035+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2036+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2037+ * GNU General Public License for more details.
2038+ *
2039+ * You should have received a copy of the GNU General Public License
2040+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2041+ */
2042+
2043+import QtQuick 2.4
2044+import Ubuntu.Components 1.3
2045+import Ubuntu.Components.ListItems 1.3 as ListItems
2046+import "Filters" as Filters
2047+
2048+Item {
2049+ id: root
2050+
2051+ readonly property real searchesHeight: recentSearchesRepeater.count > 0 ? searchColumn.height + recentSearchesLabels.height + recentSearchesLabels.anchors.topMargin : 0
2052+
2053+ implicitHeight: searchesHeight + dashNavigation.implicitHeight + dashNavigation.anchors.topMargin + primaryFilter.height + primaryFilter.anchors.topMargin
2054+
2055+ // Set by parent
2056+ property ListModel searchHistory
2057+ property var scope: null
2058+ property real windowHeight
2059+
2060+ // Used by PageHeader
2061+ readonly property bool hasContents: searchHistory.count > 0 || scope && scope.hasNavigation || scope && scope.primaryNavigationFilter
2062+
2063+ signal historyItemClicked(string text)
2064+ signal dashNavigationLeafClicked()
2065+
2066+ function resetNavigation() {
2067+ dashNavigation.resetNavigation();
2068+ }
2069+
2070+ Rectangle {
2071+ color: "white"
2072+ anchors.fill: parent
2073+ }
2074+
2075+ ListItems.ThinDivider {
2076+ anchors.top: parent.top
2077+ }
2078+
2079+ Label {
2080+ id: recentSearchesLabels
2081+ text: i18n.tr("Recent Searches")
2082+ visible: recentSearchesRepeater.count > 0
2083+ anchors {
2084+ top: parent.top
2085+ left: parent.left
2086+ margins: units.gu(2)
2087+ topMargin: units.gu(3)
2088+ }
2089+ }
2090+
2091+ Label {
2092+ text: i18n.tr("Clear All")
2093+ fontSize: "small"
2094+ visible: recentSearchesRepeater.count > 0
2095+ anchors {
2096+ top: parent.top
2097+ right: parent.right
2098+ margins: units.gu(2)
2099+ topMargin: units.gu(3)
2100+ }
2101+
2102+ AbstractButton {
2103+ anchors.fill: parent
2104+ onClicked: searchHistory.clear();
2105+ }
2106+ }
2107+
2108+ Column {
2109+ id: searchColumn
2110+ anchors {
2111+ top: recentSearchesLabels.bottom
2112+ left: parent.left
2113+ right: parent.right
2114+ }
2115+
2116+ Repeater {
2117+ id: recentSearchesRepeater
2118+ objectName: "recentSearchesRepeater"
2119+ model: searchHistory
2120+
2121+ // FIXME Move to ListItem once 1556971 is fixed
2122+ delegate: ListItems.Empty {
2123+ anchors {
2124+ left: parent.left
2125+ right: parent.right
2126+ leftMargin: units.gu(2)
2127+ rightMargin: units.gu(2)
2128+ }
2129+ height: units.gu(5)
2130+
2131+ Icon {
2132+ id: searchIcon
2133+ anchors {
2134+ verticalCenter: parent.verticalCenter
2135+ left: parent.left
2136+ }
2137+ height: units.gu(1.5)
2138+ width: height
2139+ name: "search"
2140+ }
2141+
2142+ Label {
2143+ anchors {
2144+ verticalCenter: parent.verticalCenter
2145+ left: searchIcon.right
2146+ leftMargin: units.gu(1)
2147+ right: parent.right
2148+ }
2149+ text: query
2150+ color: "#888888"
2151+ }
2152+
2153+ divider.visible: index != repeater.count - 1 || (scope && scope.hasNavigation) || primaryFilter.active
2154+
2155+ onClicked: root.historyItemClicked(query)
2156+ }
2157+ }
2158+ }
2159+
2160+ DashNavigation {
2161+ id: dashNavigation
2162+ scope: root.scope
2163+ anchors {
2164+ top: recentSearchesRepeater.count > 0 ? searchColumn.bottom : parent.top
2165+ topMargin: implicitHeight && recentSearchesRepeater.count > 0 ? units.gu(2) : 0
2166+ left: parent.left
2167+ right: parent.right
2168+ }
2169+ availableHeight: windowHeight * 4 / 6 - searchesHeight
2170+
2171+ onLeafClicked: root.dashNavigationLeafClicked();
2172+ }
2173+
2174+ Filters.FilterWidgetFactory {
2175+ id: primaryFilter
2176+ active: scope && !scope.hasNavigation
2177+
2178+ property var filter: active ? scope.primaryNavigationFilter : null
2179+
2180+ anchors {
2181+ top: recentSearchesRepeater.count > 0 ? searchColumn.bottom : parent.top
2182+ topMargin: active && recentSearchesRepeater.count > 0 ? units.gu(2) : 0
2183+ left: parent.left
2184+ right: parent.right
2185+ }
2186+
2187+ widgetId: filter ? filter.filterId : ""
2188+ widgetType: filter ? filter.filterType : -1
2189+ widgetData: filter
2190+ }
2191+
2192+ // This is outside the item
2193+ Image {
2194+ anchors {
2195+ top: parent.bottom
2196+ left: parent.left
2197+ right: parent.right
2198+ }
2199+ fillMode: Image.Stretch
2200+ source: "graphics/navigation_shadow.png"
2201+ }
2202+}
2203
2204=== modified file 'tests/mocks/Unity/CMakeLists.txt'
2205--- tests/mocks/Unity/CMakeLists.txt 2016-02-19 12:42:44 +0000
2206+++ tests/mocks/Unity/CMakeLists.txt 2016-03-15 19:17:30 +0000
2207@@ -8,7 +8,7 @@
2208 add_subdirectory(Screens)
2209
2210 pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
2211-pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=10)
2212+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=11)
2213
2214 include_directories(
2215 ${CMAKE_CURRENT_BINARY_DIR}
2216@@ -29,15 +29,28 @@
2217 fake_resultsmodel.cpp
2218 fake_previewmodel.cpp
2219 fake_previewwidgetmodel.cpp
2220+ fake_filters.cpp
2221+ fake_optionselectorfilter.cpp
2222+ fake_optionselectoroptions.cpp
2223+ fake_rangeinputfilter.cpp
2224+ fake_valuesliderfilter.cpp
2225+ fake_valueslidervalues.cpp
2226 fake_unity_plugin.cpp
2227 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
2228+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/FilterBaseInterface.h
2229+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/FiltersInterface.h
2230 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/NavigationInterface.h
2231+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorFilterInterface.h
2232+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/OptionSelectorOptionsInterface.h
2233 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h
2234 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewWidgetModelInterface.h
2235+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/RangeInputFilterInterface.h
2236 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ResultsModelInterface.h
2237 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopeInterface.h
2238 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ScopesInterface.h
2239 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
2240+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderFilterInterface.h
2241+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderValuesInterface.h
2242 )
2243
2244 # Workaround for gcc failure LP: #1417664
2245
2246=== added file 'tests/mocks/Unity/fake_filters.cpp'
2247--- tests/mocks/Unity/fake_filters.cpp 1970-01-01 00:00:00 +0000
2248+++ tests/mocks/Unity/fake_filters.cpp 2016-03-15 19:17:30 +0000
2249@@ -0,0 +1,125 @@
2250+/*
2251+ * Copyright (C) 2015 Canonical, Ltd.
2252+ *
2253+ * This program is free software; you can redistribute it and/or modify
2254+ * it under the terms of the GNU General Public License as published by
2255+ * the Free Software Foundation; version 3.
2256+ *
2257+ * This program is distributed in the hope that it will be useful,
2258+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2259+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2260+ * GNU General Public License for more details.
2261+ *
2262+ * You should have received a copy of the GNU General Public License
2263+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2264+ */
2265+
2266+#include "fake_filters.h"
2267+
2268+#include "fake_optionselectorfilter.h"
2269+#include "fake_rangeinputfilter.h"
2270+#include "fake_valuesliderfilter.h"
2271+#include "fake_scope.h"
2272+
2273+Filters::Filters(Scope* parent)
2274+ : unity::shell::scopes::FiltersInterface(parent)
2275+{
2276+ addFilter(new FakeOptionSelectorFilter("OSF1", "Tag1", "Which Cake you like More", false, QStringList() << "cheese" << "carrot" << "chocolate", this));
2277+
2278+ FakeRangeInputFilter *rif = new FakeRangeInputFilter("RIF1", "Tag3", this);
2279+ rif->setTitle("How much do you want to walk?");
2280+ rif->setCentralLabel("to");
2281+ rif->setStartPostfixLabel("m");
2282+ rif->setEndPostfixLabel("m");
2283+ addFilter(rif);
2284+
2285+ QMap<double, QString> labels;
2286+ labels[15] = "Default";
2287+ labels[50] = "50";
2288+ labels[100] = "100";
2289+ FakeValueSliderFilter *vsf = new FakeValueSliderFilter("VS1", "Tag4", 15, 10, 150, labels, this);
2290+ addFilter(vsf);
2291+
2292+ addFilter(new FakeOptionSelectorFilter("OSF2", "Tag2", "Which Countries have you been to?", true, QStringList() << "Germany" << "UK" << "New Zealand", this));
2293+}
2294+
2295+void Filters::addFilter(unity::shell::scopes::FilterBaseInterface *f)
2296+{
2297+ switch (f->filterType()) {
2298+ case FiltersInterface::OptionSelectorFilter: {
2299+ FakeOptionSelectorFilter *osf = static_cast<FakeOptionSelectorFilter *>(f);
2300+ connect(osf, &FakeOptionSelectorFilter::isActiveChanged, this, &Filters::activeFiltersCountChanged);
2301+ }
2302+ break;
2303+
2304+ case FiltersInterface::RangeInputFilter: {
2305+ FakeRangeInputFilter *rif = static_cast<FakeRangeInputFilter *>(f);
2306+ connect(rif, &FakeRangeInputFilter::isActiveChanged, this, &Filters::activeFiltersCountChanged);
2307+ }
2308+ break;
2309+
2310+ case FiltersInterface::ValueSliderFilter: {
2311+ // Not counting value slider as active in the mock
2312+ }
2313+
2314+ case Invalid:
2315+ break;
2316+ }
2317+ m_filters << f;
2318+}
2319+
2320+int Filters::rowCount(const QModelIndex &parent) const
2321+{
2322+ if (parent.isValid())
2323+ return 0;
2324+
2325+ return m_filters.count();
2326+}
2327+
2328+QVariant Filters::data(const QModelIndex &index, int role) const
2329+{
2330+ const int row = index.row();
2331+ if (row < 0 || row >= m_filters.count())
2332+ return QVariant();
2333+
2334+ unity::shell::scopes::FilterBaseInterface *filter = m_filters[row];
2335+
2336+ switch (role) {
2337+ case RoleFilterId:
2338+ return filter->filterId();
2339+ case RoleFilterType:
2340+ return filter->filterType();
2341+ case RoleFilter:
2342+ return QVariant::fromValue<unity::shell::scopes::FilterBaseInterface *>(filter);
2343+ default:
2344+ return QVariant();
2345+ }
2346+}
2347+
2348+int Filters::activeFiltersCount() const
2349+{
2350+ int active = 0;
2351+ Q_FOREACH(unity::shell::scopes::FilterBaseInterface *f, m_filters) {
2352+ switch (f->filterType()) {
2353+ case FiltersInterface::OptionSelectorFilter: {
2354+ FakeOptionSelectorFilter *osf = static_cast<FakeOptionSelectorFilter *>(f);
2355+ if (osf->isActive()) ++active;
2356+ }
2357+ break;
2358+
2359+ case FiltersInterface::RangeInputFilter: {
2360+ FakeRangeInputFilter *rif = static_cast<FakeRangeInputFilter *>(f);
2361+ if (rif->isActive()) ++active;
2362+ }
2363+ break;
2364+
2365+ case FiltersInterface::ValueSliderFilter: {
2366+ // Not counting value slider as active in the mock
2367+ }
2368+
2369+ case Invalid:
2370+ break;
2371+ }
2372+ }
2373+ return active;
2374+}
2375
2376=== added file 'tests/mocks/Unity/fake_filters.h'
2377--- tests/mocks/Unity/fake_filters.h 1970-01-01 00:00:00 +0000
2378+++ tests/mocks/Unity/fake_filters.h 2016-03-15 19:17:30 +0000
2379@@ -0,0 +1,46 @@
2380+/*
2381+ * Copyright (C) 2015 Canonical, Ltd.
2382+ *
2383+ * This program is free software; you can redistribute it and/or modify
2384+ * it under the terms of the GNU General Public License as published by
2385+ * the Free Software Foundation; version 3.
2386+ *
2387+ * This program is distributed in the hope that it will be useful,
2388+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2389+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2390+ * GNU General Public License for more details.
2391+ *
2392+ * You should have received a copy of the GNU General Public License
2393+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2394+ */
2395+
2396+#ifndef FAKE_FILTERS_H
2397+#define FAKE_FILTERS_H
2398+
2399+#include <unity/shell/scopes/FiltersInterface.h>
2400+#include <unity/shell/scopes/FilterBaseInterface.h>
2401+
2402+class Scope;
2403+
2404+class Filters : public unity::shell::scopes::FiltersInterface
2405+{
2406+ Q_OBJECT
2407+
2408+public:
2409+ Filters(Scope* parent);
2410+
2411+ int rowCount(const QModelIndex &parent) const override;
2412+ QVariant data(const QModelIndex &index, int role) const override;
2413+
2414+ int activeFiltersCount() const;
2415+
2416+Q_SIGNALS:
2417+ void activeFiltersCountChanged();
2418+
2419+private:
2420+ void addFilter(unity::shell::scopes::FilterBaseInterface* f);
2421+
2422+ QVector<unity::shell::scopes::FilterBaseInterface*> m_filters;
2423+};
2424+
2425+#endif
2426
2427=== modified file 'tests/mocks/Unity/fake_navigation.cpp'
2428--- tests/mocks/Unity/fake_navigation.cpp 2015-08-19 13:56:21 +0000
2429+++ tests/mocks/Unity/fake_navigation.cpp 2016-03-15 19:17:30 +0000
2430@@ -32,7 +32,6 @@
2431 {
2432 QTimer::singleShot(1500, this, &Navigation::slotLoaded);
2433 connect(scope, &Scope::currentNavigationIdChanged, this, &Navigation::slotCurrentNavigationChanged);
2434- connect(scope, &Scope::currentAltNavigationIdChanged, this, &Navigation::slotCurrentNavigationChanged);
2435 }
2436
2437 QString Navigation::navigationId() const
2438@@ -78,17 +77,17 @@
2439
2440 bool Navigation::isRoot() const
2441 {
2442- return m_navigationId == "root" || m_navigationId == "altroot";
2443+ return m_navigationId == "root";
2444 }
2445
2446 bool Navigation::hidden() const
2447 {
2448- return m_navigationId == "altroot";
2449+ return false;
2450 }
2451
2452 int Navigation::rowCount(const QModelIndex & /*parent*/) const
2453 {
2454- if (!m_loaded || m_navigationId.startsWith("child") || m_navigationId.startsWith("altrootChild") || m_navigationId == "middle3")
2455+ if (!m_loaded ||(m_navigationId.startsWith("child") && !m_navigationId.startsWith("childmiddle4")) || m_navigationId == "middle3")
2456 return 0;
2457 else
2458 return 8;
2459@@ -102,16 +101,16 @@
2460 return QString("middle%1").arg(index.row());
2461 else if (m_navigationId.startsWith("middle"))
2462 return QString("child%1%2").arg(m_navigationId).arg(index.row());
2463+ else if (m_navigationId.startsWith("childmiddle"))
2464+ return QString("grandchild%1%2").arg(m_navigationId).arg(index.row());
2465 case RoleLabel:
2466 return QString("%1Child%2").arg(m_navigationId).arg(index.row());
2467+ case RoleAllLabel:
2468+ return QString("all%1Child%2").arg(m_navigationId).arg(index.row());
2469 case RoleHasChildren:
2470- return m_navigationId == "root" && index.row() != 3;
2471+ return (m_navigationId == "root" && index.row() != 3) || (m_navigationId == "middle4");
2472 case RoleIsActive:
2473- if (m_navigationId.startsWith("alt")) {
2474- return m_scope->currentAltNavigationId() == data(index, RoleNavigationId);
2475- } else {
2476- return m_scope->currentNavigationId() == data(index, RoleNavigationId);
2477- }
2478+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
2479 }
2480 return QVariant();
2481 }
2482
2483=== added file 'tests/mocks/Unity/fake_optionselectorfilter.cpp'
2484--- tests/mocks/Unity/fake_optionselectorfilter.cpp 1970-01-01 00:00:00 +0000
2485+++ tests/mocks/Unity/fake_optionselectorfilter.cpp 2016-03-15 19:17:30 +0000
2486@@ -0,0 +1,65 @@
2487+/*
2488+ * Copyright (C) 2015 Canonical, Ltd.
2489+ *
2490+ * This program is free software; you can redistribute it and/or modify
2491+ * it under the terms of the GNU General Public License as published by
2492+ * the Free Software Foundation; version 3.
2493+ *
2494+ * This program is distributed in the hope that it will be useful,
2495+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2496+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2497+ * GNU General Public License for more details.
2498+ *
2499+ * You should have received a copy of the GNU General Public License
2500+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2501+ */
2502+
2503+#include "fake_optionselectorfilter.h"
2504+
2505+#include "fake_optionselectoroptions.h"
2506+
2507+FakeOptionSelectorFilter::FakeOptionSelectorFilter(const QString &id, const QString &tag, const QString &label, bool multiselect, const QStringList &optionLabels, QObject* parent)
2508+ : unity::shell::scopes::OptionSelectorFilterInterface(parent),
2509+ m_filterId(id),
2510+ m_filterTag(tag),
2511+ m_label(label),
2512+ m_multiSelect(multiselect)
2513+{
2514+ m_options = new FakeOptionSelectorOptions(optionLabels, this);
2515+ connect(m_options, &FakeOptionSelectorOptions::anyCheckedChanged, this, &FakeOptionSelectorFilter::isActiveChanged);
2516+}
2517+
2518+QString FakeOptionSelectorFilter::filterId() const
2519+{
2520+ return m_filterId;
2521+}
2522+
2523+QString FakeOptionSelectorFilter::filterTag() const
2524+{
2525+ return m_filterTag;
2526+}
2527+
2528+QString FakeOptionSelectorFilter::title() const
2529+{
2530+ return m_title;
2531+}
2532+
2533+QString FakeOptionSelectorFilter::label() const
2534+{
2535+ return m_label;
2536+}
2537+
2538+bool FakeOptionSelectorFilter::multiSelect() const
2539+{
2540+ return m_multiSelect;
2541+}
2542+
2543+unity::shell::scopes::OptionSelectorOptionsInterface* FakeOptionSelectorFilter::options() const
2544+{
2545+ return m_options;
2546+}
2547+
2548+bool FakeOptionSelectorFilter::isActive() const
2549+{
2550+ return m_options->anyChecked();
2551+}
2552
2553=== added file 'tests/mocks/Unity/fake_optionselectorfilter.h'
2554--- tests/mocks/Unity/fake_optionselectorfilter.h 1970-01-01 00:00:00 +0000
2555+++ tests/mocks/Unity/fake_optionselectorfilter.h 2016-03-15 19:17:30 +0000
2556@@ -0,0 +1,52 @@
2557+/*
2558+ * Copyright (C) 2015 Canonical, Ltd.
2559+ *
2560+ * This program is free software; you can redistribute it and/or modify
2561+ * it under the terms of the GNU General Public License as published by
2562+ * the Free Software Foundation; version 3.
2563+ *
2564+ * This program is distributed in the hope that it will be useful,
2565+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2566+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2567+ * GNU General Public License for more details.
2568+ *
2569+ * You should have received a copy of the GNU General Public License
2570+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2571+ */
2572+
2573+#ifndef FAKE_OPTIONSELECTORFILTER_H
2574+#define FAKE_OPTIONSELECTORFILTER_H
2575+
2576+#include <unity/shell/scopes/OptionSelectorFilterInterface.h>
2577+
2578+class FakeOptionSelectorOptions;
2579+
2580+class FakeOptionSelectorFilter : public unity::shell::scopes::OptionSelectorFilterInterface
2581+{
2582+ Q_OBJECT
2583+
2584+public:
2585+ FakeOptionSelectorFilter(const QString &id, const QString &tag, const QString &label, bool multiselect, const QStringList &optionLabels, QObject* parent);
2586+
2587+ QString filterId() const override;
2588+ QString filterTag() const override;
2589+ QString title() const override;
2590+ QString label() const override;
2591+ bool multiSelect() const override;
2592+ unity::shell::scopes::OptionSelectorOptionsInterface* options() const override;
2593+
2594+ bool isActive() const;
2595+
2596+Q_SIGNALS:
2597+ void isActiveChanged();
2598+
2599+private:
2600+ QString m_filterId;
2601+ QString m_filterTag;
2602+ QString m_title;
2603+ QString m_label;
2604+ bool m_multiSelect;
2605+ FakeOptionSelectorOptions* m_options;
2606+};
2607+
2608+#endif
2609
2610=== added file 'tests/mocks/Unity/fake_optionselectoroptions.cpp'
2611--- tests/mocks/Unity/fake_optionselectoroptions.cpp 1970-01-01 00:00:00 +0000
2612+++ tests/mocks/Unity/fake_optionselectoroptions.cpp 2016-03-15 19:17:30 +0000
2613@@ -0,0 +1,77 @@
2614+/*
2615+ * Copyright (C) 2015 Canonical, Ltd.
2616+ *
2617+ * This program is free software; you can redistribute it and/or modify
2618+ * it under the terms of the GNU General Public License as published by
2619+ * the Free Software Foundation; version 3.
2620+ *
2621+ * This program is distributed in the hope that it will be useful,
2622+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2623+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2624+ * GNU General Public License for more details.
2625+ *
2626+ * You should have received a copy of the GNU General Public License
2627+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2628+ */
2629+
2630+#include "fake_optionselectoroptions.h"
2631+
2632+#include "fake_optionselectorfilter.h"
2633+
2634+FakeOptionSelectorOptions::FakeOptionSelectorOptions(const QStringList &optionLabels, FakeOptionSelectorFilter* parent)
2635+ : unity::shell::scopes::OptionSelectorOptionsInterface(parent),
2636+ m_optionLabels(optionLabels)
2637+{
2638+}
2639+
2640+int FakeOptionSelectorOptions::rowCount(const QModelIndex &parent) const
2641+{
2642+ if (parent.isValid())
2643+ return 0;
2644+
2645+ return m_optionLabels.count();
2646+}
2647+
2648+QVariant FakeOptionSelectorOptions::data(const QModelIndex &index, int role) const
2649+{
2650+ const int row = index.row();
2651+ if (row < 0 || row >= m_optionLabels.count())
2652+ return QVariant();
2653+
2654+ switch (role) {
2655+ case RoleOptionId: {
2656+ const QString id = static_cast<FakeOptionSelectorFilter*>(parent())->filterId() + QString::number(row);
2657+ return id;
2658+ }
2659+ case RoleOptionLabel:
2660+ return m_optionLabels[row];
2661+ case RoleOptionChecked:
2662+ return m_checkedIndexes.contains(row);
2663+ default:
2664+ return QVariant();
2665+ }
2666+
2667+}
2668+
2669+void FakeOptionSelectorOptions::setChecked(int row, bool checked)
2670+{
2671+ if (checked) {
2672+ if (!static_cast<FakeOptionSelectorFilter*>(parent())->multiSelect()) {
2673+ if (!m_checkedIndexes.isEmpty()) {
2674+ setChecked(*m_checkedIndexes.begin(), false);
2675+ }
2676+ Q_ASSERT(m_checkedIndexes.isEmpty());
2677+ }
2678+ m_checkedIndexes << row;
2679+ } else {
2680+ m_checkedIndexes.remove(row);
2681+ }
2682+ const QModelIndex idx = index(row, 0);
2683+ Q_EMIT dataChanged(idx, idx, QVector<int>() << RoleOptionChecked);
2684+ Q_EMIT anyCheckedChanged();
2685+}
2686+
2687+bool FakeOptionSelectorOptions::anyChecked() const
2688+{
2689+ return !m_checkedIndexes.isEmpty();
2690+}
2691
2692=== added file 'tests/mocks/Unity/fake_optionselectoroptions.h'
2693--- tests/mocks/Unity/fake_optionselectoroptions.h 1970-01-01 00:00:00 +0000
2694+++ tests/mocks/Unity/fake_optionselectoroptions.h 2016-03-15 19:17:30 +0000
2695@@ -0,0 +1,47 @@
2696+/*
2697+ * Copyright (C) 2015 Canonical, Ltd.
2698+ *
2699+ * This program is free software; you can redistribute it and/or modify
2700+ * it under the terms of the GNU General Public License as published by
2701+ * the Free Software Foundation; version 3.
2702+ *
2703+ * This program is distributed in the hope that it will be useful,
2704+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2705+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2706+ * GNU General Public License for more details.
2707+ *
2708+ * You should have received a copy of the GNU General Public License
2709+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2710+ */
2711+
2712+#ifndef FAKE_OPTIONSELECTOROPTIONS_H
2713+#define FAKE_OPTIONSELECTOROPTIONS_H
2714+
2715+#include <unity/shell/scopes/OptionSelectorFilterInterface.h>
2716+
2717+#include <QSet>
2718+
2719+class FakeOptionSelectorFilter;
2720+
2721+class FakeOptionSelectorOptions : public unity::shell::scopes::OptionSelectorOptionsInterface
2722+{
2723+ Q_OBJECT
2724+
2725+public:
2726+ FakeOptionSelectorOptions(const QStringList &optionLabels, FakeOptionSelectorFilter* parent);
2727+
2728+ int rowCount(const QModelIndex &parent) const override;
2729+ QVariant data(const QModelIndex &index, int role) const override;
2730+ Q_INVOKABLE void setChecked(int index, bool checked) override;
2731+
2732+ bool anyChecked() const;
2733+
2734+Q_SIGNALS:
2735+ void anyCheckedChanged();
2736+
2737+private:
2738+ QStringList m_optionLabels;
2739+ QSet<int> m_checkedIndexes;
2740+};
2741+
2742+#endif
2743
2744=== added file 'tests/mocks/Unity/fake_rangeinputfilter.cpp'
2745--- tests/mocks/Unity/fake_rangeinputfilter.cpp 1970-01-01 00:00:00 +0000
2746+++ tests/mocks/Unity/fake_rangeinputfilter.cpp 2016-03-15 19:17:30 +0000
2747@@ -0,0 +1,167 @@
2748+/*
2749+ * Copyright (C) 2015 Canonical, Ltd.
2750+ *
2751+ * This program is free software; you can redistribute it and/or modify
2752+ * it under the terms of the GNU General Public License as published by
2753+ * the Free Software Foundation; version 3.
2754+ *
2755+ * This program is distributed in the hope that it will be useful,
2756+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2757+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2758+ * GNU General Public License for more details.
2759+ *
2760+ * You should have received a copy of the GNU General Public License
2761+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2762+ */
2763+
2764+#include "fake_rangeinputfilter.h"
2765+
2766+FakeRangeInputFilter::FakeRangeInputFilter(const QString &id, const QString &tag, QObject* parent)
2767+ : unity::shell::scopes::RangeInputFilterInterface(parent),
2768+ m_filterId(id),
2769+ m_filterTag(tag),
2770+ m_hasStartValue(false),
2771+ m_hasEndValue(false),
2772+ m_startValue(-1),
2773+ m_endValue(-1)
2774+{
2775+ connect(this, &FakeRangeInputFilter::hasStartValueChanged, this, &FakeRangeInputFilter::isActiveChanged);
2776+ connect(this, &FakeRangeInputFilter::hasEndValueChanged, this, &FakeRangeInputFilter::isActiveChanged);
2777+}
2778+
2779+QString FakeRangeInputFilter::filterId() const
2780+{
2781+ return m_filterId;
2782+}
2783+
2784+QString FakeRangeInputFilter::filterTag() const
2785+{
2786+ return m_filterTag;
2787+}
2788+
2789+QString FakeRangeInputFilter::title() const
2790+{
2791+ return m_title;
2792+}
2793+
2794+double FakeRangeInputFilter::startValue() const
2795+{
2796+ return m_startValue;
2797+}
2798+
2799+double FakeRangeInputFilter::endValue() const
2800+{
2801+ return m_endValue;
2802+}
2803+
2804+void FakeRangeInputFilter::setStartValue(double value)
2805+{
2806+ if (m_startValue != value) {
2807+ m_startValue = value;
2808+ Q_EMIT startValueChanged();
2809+ }
2810+ if (!m_hasStartValue) {
2811+ m_hasStartValue = true;
2812+ Q_EMIT hasStartValueChanged();
2813+ }
2814+}
2815+
2816+void FakeRangeInputFilter::setEndValue(double value)
2817+{
2818+ if (m_endValue != value) {
2819+ m_endValue = value;
2820+ Q_EMIT endValueChanged();
2821+ }
2822+ if (!m_hasEndValue) {
2823+ m_hasEndValue = true;
2824+ Q_EMIT hasEndValueChanged();
2825+ }
2826+}
2827+
2828+QString FakeRangeInputFilter::startPrefixLabel() const
2829+{
2830+ return m_startPrefixLabel;
2831+}
2832+
2833+QString FakeRangeInputFilter::startPostfixLabel() const
2834+{
2835+ return m_startPostfixLabel;
2836+}
2837+
2838+QString FakeRangeInputFilter::centralLabel() const
2839+{
2840+ return m_centralLabel;
2841+}
2842+
2843+QString FakeRangeInputFilter::endPrefixLabel() const
2844+{
2845+ return m_endPrefixLabel;
2846+}
2847+
2848+QString FakeRangeInputFilter::endPostfixLabel() const
2849+{
2850+ return m_endPostfixLabel;
2851+}
2852+
2853+bool FakeRangeInputFilter::hasStartValue() const
2854+{
2855+ return m_hasStartValue;
2856+}
2857+
2858+bool FakeRangeInputFilter::hasEndValue() const
2859+{
2860+ return m_hasEndValue;
2861+}
2862+
2863+void FakeRangeInputFilter::eraseStartValue()
2864+{
2865+ m_hasStartValue = false;
2866+ Q_EMIT hasStartValueChanged();
2867+}
2868+
2869+void FakeRangeInputFilter::eraseEndValue()
2870+{
2871+ m_hasEndValue = false;
2872+ Q_EMIT hasEndValueChanged();
2873+}
2874+
2875+bool FakeRangeInputFilter::isActive() const
2876+{
2877+ return hasStartValue() && hasEndValue();
2878+}
2879+
2880+void FakeRangeInputFilter::setTitle(const QString &title)
2881+{
2882+ m_title = title;
2883+ Q_EMIT titleChanged();
2884+}
2885+
2886+void FakeRangeInputFilter::setStartPrefixLabel(const QString &startPrefixLabel)
2887+{
2888+ m_startPrefixLabel = startPrefixLabel;
2889+ Q_EMIT startPrefixLabelChanged();
2890+}
2891+
2892+void FakeRangeInputFilter::setStartPostfixLabel(const QString &startPostfixLabel)
2893+{
2894+ m_startPostfixLabel = startPostfixLabel;
2895+ Q_EMIT startPostfixLabelChanged();
2896+}
2897+
2898+void FakeRangeInputFilter::setCentralLabel(const QString &centralLabel)
2899+{
2900+ m_centralLabel = centralLabel;
2901+ Q_EMIT centralLabelChanged();
2902+}
2903+
2904+void FakeRangeInputFilter::setEndPrefixLabel(const QString &endPrefixLabel)
2905+{
2906+ m_endPrefixLabel = endPrefixLabel;
2907+ Q_EMIT endPrefixLabelChanged();
2908+}
2909+
2910+void FakeRangeInputFilter::setEndPostfixLabel(const QString &endPostfixLabel)
2911+{
2912+ m_endPostfixLabel = endPostfixLabel;
2913+ Q_EMIT endPostfixLabelChanged();
2914+}
2915
2916=== added file 'tests/mocks/Unity/fake_rangeinputfilter.h'
2917--- tests/mocks/Unity/fake_rangeinputfilter.h 1970-01-01 00:00:00 +0000
2918+++ tests/mocks/Unity/fake_rangeinputfilter.h 2016-03-15 19:17:30 +0000
2919@@ -0,0 +1,79 @@
2920+/*
2921+ * Copyright (C) 2015 Canonical, Ltd.
2922+ *
2923+ * This program is free software; you can redistribute it and/or modify
2924+ * it under the terms of the GNU General Public License as published by
2925+ * the Free Software Foundation; version 3.
2926+ *
2927+ * This program is distributed in the hope that it will be useful,
2928+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2929+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2930+ * GNU General Public License for more details.
2931+ *
2932+ * You should have received a copy of the GNU General Public License
2933+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2934+ */
2935+
2936+#ifndef FAKE_RANGEINPUTFILTER_H
2937+#define FAKE_RANGEINPUTFILTER_H
2938+
2939+#include <unity/shell/scopes/RangeInputFilterInterface.h>
2940+
2941+class FakeRangeInputFilter : public unity::shell::scopes::RangeInputFilterInterface
2942+{
2943+ Q_OBJECT
2944+
2945+public:
2946+ FakeRangeInputFilter(const QString &id, const QString &tag, QObject* parent);
2947+
2948+ QString filterId() const override;
2949+ QString filterTag() const override;
2950+ QString title() const override;
2951+
2952+ double startValue() const override;
2953+ double endValue() const override;
2954+ void setStartValue(double value) override;
2955+ void setEndValue(double value) override;
2956+ QString startPrefixLabel() const override;
2957+ QString startPostfixLabel() const override;
2958+ QString centralLabel() const override;
2959+ QString endPrefixLabel() const override;
2960+ QString endPostfixLabel() const override;
2961+ bool hasStartValue() const override;
2962+ bool hasEndValue() const override;
2963+
2964+ void eraseStartValue() override;
2965+ void eraseEndValue() override;
2966+
2967+ // Not part of the iface, for mock/testing purposes
2968+ bool isActive() const;
2969+
2970+ void setTitle(const QString &title);
2971+ void setStartPrefixLabel(const QString &startPrefixLabel);
2972+ void setStartPostfixLabel(const QString &startPostfixLabel);
2973+ void setCentralLabel(const QString &centralLabel);
2974+ void setEndPrefixLabel(const QString &endPrefixLabel);
2975+ void setEndPostfixLabel(const QString &endPostfixLabel);
2976+
2977+Q_SIGNALS:
2978+ void isActiveChanged();
2979+
2980+private:
2981+ QString m_filterId;
2982+ QString m_filterTag;
2983+ QString m_title;
2984+
2985+ bool m_hasStartValue;
2986+ bool m_hasEndValue;
2987+
2988+ double m_startValue;
2989+ double m_endValue;
2990+
2991+ QString m_startPrefixLabel;
2992+ QString m_startPostfixLabel;
2993+ QString m_centralLabel;
2994+ QString m_endPrefixLabel;
2995+ QString m_endPostfixLabel;
2996+};
2997+
2998+#endif
2999
3000=== modified file 'tests/mocks/Unity/fake_scope.cpp'
3001--- tests/mocks/Unity/fake_scope.cpp 2016-02-22 12:59:40 +0000
3002+++ tests/mocks/Unity/fake_scope.cpp 2016-03-15 19:17:30 +0000
3003@@ -1,5 +1,5 @@
3004 /*
3005- * Copyright (C) 2013, 2014 Canonical, Ltd.
3006+ * Copyright (C) 2013, 2014, 2015 Canonical, Ltd.
3007 *
3008 * This program is free software; you can redistribute it and/or modify
3009 * it under the terms of the GNU General Public License as published by
3010@@ -19,7 +19,9 @@
3011
3012 #include "fake_scope.h"
3013
3014+#include "fake_filters.h"
3015 #include "fake_navigation.h"
3016+#include "fake_optionselectorfilter.h"
3017 #include "fake_resultsmodel.h"
3018 #include "fake_scopes.h"
3019 #include "fake_settingsmodel.h"
3020@@ -37,14 +39,18 @@
3021 , m_searching(false)
3022 , m_favorite(favorite)
3023 , m_isActive(false)
3024+ , m_hasNavigation(true)
3025+ , m_hasPrimaryFilter(true)
3026 , m_currentNavigationId("root")
3027- , m_currentAltNavigationId("altrootChild1")
3028 , m_previewRendererName("preview-generic")
3029 , m_categories(new Categories(categories, this))
3030 , m_openScope(nullptr)
3031 , m_settings(new SettingsModel(this))
3032+ , m_filters(new Filters(this))
3033 , m_returnNullPreview(returnNullPreview)
3034 {
3035+ m_primaryNavigationFilter = new FakeOptionSelectorFilter("OSF3", "PFTag", "Which food you like More", false, QStringList() << "meat" << "vegetables", this);
3036+ connect(m_filters, &Filters::activeFiltersCountChanged, this, &Scope::activeFiltersCountChanged);
3037 }
3038
3039 QString Scope::id() const
3040@@ -229,17 +235,13 @@
3041
3042 bool Scope::hasNavigation() const
3043 {
3044- return true;
3045-}
3046-
3047-QString Scope::currentAltNavigationId() const
3048-{
3049- return m_currentAltNavigationId;
3050-}
3051-
3052-bool Scope::hasAltNavigation() const
3053-{
3054- return true;
3055+ return m_hasNavigation;
3056+}
3057+
3058+void Scope::setHasNavigation(bool hasNavigation)
3059+{
3060+ m_hasNavigation = hasNavigation;
3061+ Q_EMIT hasNavigationChanged();
3062 }
3063
3064 Scope::Status Scope::status() const
3065@@ -276,6 +278,11 @@
3066 Q_EMIT refreshed();
3067 }
3068
3069+void Scope::resetFilters()
3070+{
3071+ qWarning() << "Scope::resetFilters is unimplemented";
3072+}
3073+
3074 void Scope::activateAction(QVariant const& /*result*/, QString const& /*categoryId*/, QString const& /*actionId*/)
3075 {
3076 qFatal("Using Scope::activateAction");
3077@@ -298,29 +305,47 @@
3078 return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
3079 }
3080
3081-unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const& id)
3082-{
3083- if (id.isEmpty())
3084- return nullptr;
3085-
3086- QString parentId;
3087- QString parentLabel;
3088- if (id != "altroot") {
3089- parentId = "altroot";
3090- parentLabel = "altroot";
3091- }
3092- return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
3093-}
3094-
3095-void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
3096-{
3097- if (isAltNavigation) {
3098- m_currentAltNavigationId = navigationId;
3099- Q_EMIT currentAltNavigationIdChanged();
3100- } else {
3101- m_currentNavigationId = navigationId;
3102- Q_EMIT currentNavigationIdChanged();
3103- }
3104+void Scope::setNavigationState(const QString &navigationId)
3105+{
3106+ m_currentNavigationId = navigationId;
3107+ Q_EMIT currentNavigationIdChanged();
3108+ Q_EMIT primaryNavigationTagChanged();
3109+}
3110+
3111+unity::shell::scopes::FilterBaseInterface* Scope::primaryNavigationFilter() const
3112+{
3113+ return m_hasPrimaryFilter ? m_primaryNavigationFilter : nullptr;
3114+}
3115+
3116+unity::shell::scopes::FiltersInterface* Scope::filters() const
3117+{
3118+ return m_filters;
3119+}
3120+
3121+QString Scope::primaryNavigationTag() const
3122+{
3123+ if (m_currentNavigationId == "root")
3124+ return QString();
3125+ else
3126+ return const_cast<Scope*>(this)->getNavigation(m_currentNavigationId)->label();
3127+}
3128+
3129+int Scope::activeFiltersCount() const
3130+{
3131+ return m_filters->activeFiltersCount();
3132+}
3133+
3134+void Scope::resetPrimaryNavigationTag()
3135+{
3136+ if (m_currentNavigationId != "root") {
3137+ setNavigationState("root");
3138+ }
3139+}
3140+
3141+void Scope::setHasPrimaryFilter(bool hasPrimaryFilter)
3142+{
3143+ m_hasPrimaryFilter = hasPrimaryFilter;
3144+ Q_EMIT primaryNavigationFilterChanged();
3145 }
3146
3147 void Scope::performQuery(const QString& query)
3148
3149=== modified file 'tests/mocks/Unity/fake_scope.h'
3150--- tests/mocks/Unity/fake_scope.h 2016-02-19 11:42:42 +0000
3151+++ tests/mocks/Unity/fake_scope.h 2016-03-15 19:17:30 +0000
3152@@ -1,5 +1,5 @@
3153 /*
3154- * Copyright (C) 2013, 2014 Canonical, Ltd.
3155+ * Copyright (C) 2013, 2014, 2015 Canonical, Ltd.
3156 *
3157 * This program is free software; you can redistribute it and/or modify
3158 * it under the terms of the GNU General Public License as published by
3159@@ -24,7 +24,9 @@
3160
3161 #include <QTimer>
3162
3163+class Filters;
3164 class Scopes;
3165+class FakeOptionSelectorFilter;
3166
3167 class Scope : public unity::shell::scopes::ScopeInterface
3168 {
3169@@ -68,11 +70,17 @@
3170
3171 QString currentNavigationId() const override;
3172 bool hasNavigation() const override;
3173- QString currentAltNavigationId() const override;
3174- bool hasAltNavigation() const override;
3175 Q_INVOKABLE unity::shell::scopes::NavigationInterface* getNavigation(QString const& navigationId) override;
3176- Q_INVOKABLE unity::shell::scopes::NavigationInterface* getAltNavigation(QString const& altNavigationId) override;
3177- Q_INVOKABLE void setNavigationState(const QString &navigationId, bool isAltNavigation) override;
3178+ Q_INVOKABLE void setNavigationState(const QString &navigationId) override;
3179+ Q_INVOKABLE void setHasNavigation(bool hasNavigation); // This is not invokable in the Interface, here for testing benefits
3180+
3181+ unity::shell::scopes::FilterBaseInterface* primaryNavigationFilter() const override;
3182+ unity::shell::scopes::FiltersInterface* filters() const override;
3183+ QString primaryNavigationTag() const override;
3184+ int activeFiltersCount() const override;
3185+ Q_INVOKABLE void resetPrimaryNavigationTag() override;
3186+ Q_INVOKABLE void setHasPrimaryFilter(bool hasPrimaryFilter); // This is not invokable in the Interface, here for testing benefits
3187+
3188 void performQuery(const QString& query) override;
3189
3190 Status status() const override;
3191@@ -80,7 +88,9 @@
3192
3193 Q_INVOKABLE void refresh() override;
3194
3195- Q_INVOKABLE virtual void activateAction(QVariant const& result, QString const& categoryId, QString const& actionId) override;
3196+ Q_INVOKABLE void resetFilters() override;
3197+
3198+ Q_INVOKABLE void activateAction(QVariant const& result, QString const& categoryId, QString const& actionId) override;
3199
3200 Q_SIGNALS:
3201 // These are not in the Interface, here for testing benefits
3202@@ -99,14 +109,17 @@
3203 bool m_searching;
3204 bool m_favorite;
3205 bool m_isActive;
3206+ bool m_hasNavigation;
3207+ bool m_hasPrimaryFilter;
3208 QString m_currentNavigationId;
3209- QString m_currentAltNavigationId;
3210
3211 QString m_previewRendererName;
3212
3213 unity::shell::scopes::CategoriesInterface* m_categories;
3214 unity::shell::scopes::ScopeInterface* m_openScope;
3215 unity::shell::scopes::SettingsModelInterface* m_settings;
3216+ Filters* m_filters;
3217+ FakeOptionSelectorFilter* m_primaryNavigationFilter;
3218
3219 bool m_returnNullPreview;
3220 };
3221
3222=== modified file 'tests/mocks/Unity/fake_unity_plugin.cpp'
3223--- tests/mocks/Unity/fake_unity_plugin.cpp 2016-02-19 11:42:42 +0000
3224+++ tests/mocks/Unity/fake_unity_plugin.cpp 2016-03-15 19:17:30 +0000
3225@@ -22,11 +22,14 @@
3226 // local
3227 #include "fake_scopes.h"
3228 #include "fake_categories.h"
3229+#include "fake_filters.h"
3230 #include "fake_navigation.h"
3231+#include "fake_optionselectoroptions.h"
3232 #include "fake_previewmodel.h"
3233 #include "fake_previewwidgetmodel.h"
3234 #include "fake_resultsmodel.h"
3235 #include "fake_settingsmodel.h"
3236+#include "fake_valueslidervalues.h"
3237
3238 // External
3239 #include <glib-object.h>
3240@@ -52,4 +55,8 @@
3241 qmlRegisterType<ResultsModel>(uri, 0, 2, "FakeResultsModel");
3242 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");
3243 qmlRegisterUncreatableType<PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
3244+ qmlRegisterUncreatableType<unity::shell::scopes::FiltersInterface>(uri, 0, 2, "Filters", "Can't create Filters object in QML. Get them from Scope instance.");
3245+ qmlRegisterUncreatableType<unity::shell::scopes::FilterBaseInterface>(uri, 0, 2, "Filter", "Can't create Filter object in QML. Get them from Scope instance.");
3246+ qmlRegisterUncreatableType<unity::shell::scopes::OptionSelectorOptionsInterface>(uri, 0, 2, "OptionSelectorOptions", "Can't create OptionSelectorOptions object in QML");
3247+ qmlRegisterUncreatableType<unity::shell::scopes::ValueSliderValuesInterface>(uri, 0, 2, "ValueSliderValues", "Can't create ValueSliderValuesInterface object in QML");
3248 }
3249
3250=== added file 'tests/mocks/Unity/fake_valuesliderfilter.cpp'
3251--- tests/mocks/Unity/fake_valuesliderfilter.cpp 1970-01-01 00:00:00 +0000
3252+++ tests/mocks/Unity/fake_valuesliderfilter.cpp 2016-03-15 19:17:30 +0000
3253@@ -0,0 +1,84 @@
3254+/*
3255+ * Copyright (C) 2015 Canonical, Ltd.
3256+ *
3257+ * This program is free software; you can redistribute it and/or modify
3258+ * it under the terms of the GNU General Public License as published by
3259+ * the Free Software Foundation; version 3.
3260+ *
3261+ * This program is distributed in the hope that it will be useful,
3262+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3263+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3264+ * GNU General Public License for more details.
3265+ *
3266+ * You should have received a copy of the GNU General Public License
3267+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3268+ */
3269+
3270+#include "fake_valuesliderfilter.h"
3271+#include "fake_valueslidervalues.h"
3272+
3273+FakeValueSliderFilter::FakeValueSliderFilter(const QString &id, const QString &tag, double value, double minValue, double maxValue, const QMap<double, QString> &labels, QObject* parent)
3274+ : unity::shell::scopes::ValueSliderFilterInterface(parent),
3275+ m_filterId(id),
3276+ m_filterTag(tag),
3277+ m_value(value),
3278+ m_minValue(minValue),
3279+ m_maxValue(maxValue),
3280+ m_values(new FakeValueSliderValues(labels, this))
3281+{
3282+}
3283+
3284+QString FakeValueSliderFilter::filterId() const
3285+{
3286+ return m_filterId;
3287+}
3288+
3289+QString FakeValueSliderFilter::filterTag() const
3290+{
3291+ return m_filterTag;
3292+}
3293+
3294+QString FakeValueSliderFilter::title() const
3295+{
3296+ return m_title;
3297+}
3298+
3299+double FakeValueSliderFilter::value() const
3300+{
3301+ return m_value;
3302+}
3303+
3304+void FakeValueSliderFilter::setValue(double value)
3305+{
3306+ if (value != m_value) {
3307+ m_value = value;
3308+ Q_EMIT valueChanged();
3309+ }
3310+}
3311+
3312+double FakeValueSliderFilter::minValue() const
3313+{
3314+ return m_minValue;
3315+}
3316+
3317+double FakeValueSliderFilter::maxValue() const
3318+{
3319+ return m_maxValue;
3320+}
3321+
3322+unity::shell::scopes::ValueSliderValuesInterface* FakeValueSliderFilter::values() const
3323+{
3324+ return m_values;
3325+}
3326+
3327+bool FakeValueSliderFilter::isActive() const
3328+{
3329+ // Doesn't really matter
3330+ return false;
3331+}
3332+
3333+void FakeValueSliderFilter::setTitle(const QString &title)
3334+{
3335+ m_title = title;
3336+ Q_EMIT titleChanged();
3337+}
3338
3339=== added file 'tests/mocks/Unity/fake_valuesliderfilter.h'
3340--- tests/mocks/Unity/fake_valuesliderfilter.h 1970-01-01 00:00:00 +0000
3341+++ tests/mocks/Unity/fake_valuesliderfilter.h 2016-03-15 19:17:30 +0000
3342@@ -0,0 +1,55 @@
3343+/*
3344+ * Copyright (C) 2015 Canonical, Ltd.
3345+ *
3346+ * This program is free software; you can redistribute it and/or modify
3347+ * it under the terms of the GNU General Public License as published by
3348+ * the Free Software Foundation; version 3.
3349+ *
3350+ * This program is distributed in the hope that it will be useful,
3351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3353+ * GNU General Public License for more details.
3354+ *
3355+ * You should have received a copy of the GNU General Public License
3356+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3357+ */
3358+
3359+#ifndef FAKE_VALUESLIDERFILTER_H
3360+#define FAKE_VALUESLIDERFILTER_H
3361+
3362+#include <unity/shell/scopes/ValueSliderFilterInterface.h>
3363+
3364+class FakeValueSliderFilter : public unity::shell::scopes::ValueSliderFilterInterface
3365+{
3366+ Q_OBJECT
3367+
3368+public:
3369+ FakeValueSliderFilter(const QString &id, const QString &tag, double value, double minValue, double maxValue, const QMap<double, QString> &labels, QObject* parent);
3370+
3371+ QString filterId() const override;
3372+ QString filterTag() const override;
3373+ QString title() const override;
3374+
3375+ double value() const override;
3376+ void setValue(double value) override;
3377+ double minValue() const override;
3378+ double maxValue() const override;
3379+ unity::shell::scopes::ValueSliderValuesInterface* values() const override;
3380+
3381+ // Not part of the iface, for mock/testing purposes
3382+ bool isActive() const;
3383+
3384+ void setTitle(const QString &title);
3385+
3386+private:
3387+ QString m_filterId;
3388+ QString m_filterTag;
3389+ QString m_title;
3390+
3391+ double m_value;
3392+ double m_minValue;
3393+ double m_maxValue;
3394+ unity::shell::scopes::ValueSliderValuesInterface *m_values;
3395+};
3396+
3397+#endif
3398
3399=== added file 'tests/mocks/Unity/fake_valueslidervalues.cpp'
3400--- tests/mocks/Unity/fake_valueslidervalues.cpp 1970-01-01 00:00:00 +0000
3401+++ tests/mocks/Unity/fake_valueslidervalues.cpp 2016-03-15 19:17:30 +0000
3402@@ -0,0 +1,51 @@
3403+/*
3404+ * Copyright (C) 2015 Canonical, Ltd.
3405+ *
3406+ * This program is free software; you can redistribute it and/or modify
3407+ * it under the terms of the GNU General Public License as published by
3408+ * the Free Software Foundation; version 3.
3409+ *
3410+ * This program is distributed in the hope that it will be useful,
3411+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3412+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3413+ * GNU General Public License for more details.
3414+ *
3415+ * You should have received a copy of the GNU General Public License
3416+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3417+ */
3418+
3419+#include "fake_valueslidervalues.h"
3420+
3421+FakeValueSliderValues::FakeValueSliderValues(const QMap<double, QString> &labels, QObject* parent)
3422+ : unity::shell::scopes::ValueSliderValuesInterface(parent)
3423+ , m_labels(labels)
3424+{
3425+}
3426+
3427+int FakeValueSliderValues::rowCount(const QModelIndex &parent) const
3428+{
3429+ if (parent.isValid())
3430+ return 0;
3431+
3432+ return m_labels.count();
3433+}
3434+
3435+QVariant FakeValueSliderValues::data(const QModelIndex &index, int role) const
3436+{
3437+ if (!index.isValid())
3438+ return QVariant();
3439+
3440+ const int row = index.row();
3441+ auto it = m_labels.begin();
3442+ it += row;
3443+
3444+ switch (role) {
3445+ case RoleValue:
3446+ return it.key();
3447+
3448+ case RoleLabel:
3449+ return it.value();
3450+ }
3451+
3452+ return QVariant();
3453+}
3454
3455=== added file 'tests/mocks/Unity/fake_valueslidervalues.h'
3456--- tests/mocks/Unity/fake_valueslidervalues.h 1970-01-01 00:00:00 +0000
3457+++ tests/mocks/Unity/fake_valueslidervalues.h 2016-03-15 19:17:30 +0000
3458@@ -0,0 +1,36 @@
3459+/*
3460+ * Copyright (C) 2015 Canonical, Ltd.
3461+ *
3462+ * This program is free software; you can redistribute it and/or modify
3463+ * it under the terms of the GNU General Public License as published by
3464+ * the Free Software Foundation; version 3.
3465+ *
3466+ * This program is distributed in the hope that it will be useful,
3467+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3468+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3469+ * GNU General Public License for more details.
3470+ *
3471+ * You should have received a copy of the GNU General Public License
3472+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3473+ */
3474+
3475+#ifndef FAKE_VALUESLIDERVALUES_H
3476+#define FAKE_VALUESLIDERVALUES_H
3477+
3478+#include <unity/shell/scopes/ValueSliderValuesInterface.h>
3479+
3480+class FakeValueSliderValues : public unity::shell::scopes::ValueSliderValuesInterface
3481+{
3482+ Q_OBJECT
3483+
3484+public:
3485+ FakeValueSliderValues(const QMap<double, QString> &labels, QObject* parent);
3486+
3487+ int rowCount(const QModelIndex &parent) const override;
3488+ QVariant data(const QModelIndex &index, int role) const override;
3489+
3490+private:
3491+ const QMap<double, QString> m_labels;
3492+};
3493+
3494+#endif
3495
3496=== modified file 'tests/plugins/Dash/tst_ScopeStyle.qml'
3497--- tests/plugins/Dash/tst_ScopeStyle.qml 2015-07-15 15:07:19 +0000
3498+++ tests/plugins/Dash/tst_ScopeStyle.qml 2016-03-15 19:17:30 +0000
3499@@ -137,7 +137,7 @@
3500
3501 function test_headerBackground_data() {
3502 return [
3503- { tag: "default", index: 0, headerBackground: "color:///#f5f5f5" },
3504+ { tag: "default", index: 0, headerBackground: "color:///#ffffff" },
3505 { tag: "black", index: 3, headerBackground: "gradient:///white/blue" },
3506 ];
3507 }
3508
3509=== modified file 'tests/qmltests/CMakeLists.txt'
3510--- tests/qmltests/CMakeLists.txt 2016-03-10 22:41:38 +0000
3511+++ tests/qmltests/CMakeLists.txt 2016-03-15 19:17:30 +0000
3512@@ -53,6 +53,10 @@
3513 add_unity8_qmltest(Dash/Previews PreviewVideoPlayback)
3514 add_unity8_qmltest(Dash/Previews PreviewWidgetFactory)
3515 add_unity8_qmltest(Dash/Previews PreviewZoomableImage)
3516+add_unity8_qmltest(Dash/Filters FilterOptionSelector)
3517+add_unity8_qmltest(Dash/Filters FilterRangeInput)
3518+add_unity8_qmltest(Dash/Filters FilterValueSlider)
3519+add_unity8_qmltest(Dash/Filters FilterWidgetFactory)
3520 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingBoolean)
3521 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingList)
3522 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingNumber)
3523
3524=== added directory 'tests/qmltests/Dash/Filters'
3525=== added file 'tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml'
3526--- tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml 1970-01-01 00:00:00 +0000
3527+++ tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml 2016-03-15 19:17:30 +0000
3528@@ -0,0 +1,96 @@
3529+/*
3530+ * Copyright 2015 Canonical Ltd.
3531+ *
3532+ * This program is free software; you can redistribute it and/or modify
3533+ * it under the terms of the GNU General Public License as published by
3534+ * the Free Software Foundation; version 3.
3535+ *
3536+ * This program is distributed in the hope that it will be useful,
3537+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3538+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3539+ * GNU General Public License for more details.
3540+ *
3541+ * You should have received a copy of the GNU General Public License
3542+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3543+ */
3544+
3545+import QtQuick 2.4
3546+import QtTest 1.0
3547+import "../../../../qml/Dash/Filters"
3548+import Unity.Test 0.1 as UT
3549+import Unity 0.2
3550+import Ubuntu.Components 1.3
3551+
3552+Rectangle {
3553+ id: root
3554+ width: units.gu(60)
3555+ height: units.gu(80)
3556+ color: theme.palette.selected.background
3557+
3558+ property var singleSelectionWidgetData
3559+
3560+ ListModel {
3561+ id: optionsSingleSelect
3562+ ListElement {
3563+ label: "A"
3564+ checked: false
3565+ }
3566+ ListElement {
3567+ label: "B"
3568+ checked: false
3569+ }
3570+ ListElement {
3571+ label: "C"
3572+ checked: false
3573+ }
3574+
3575+ function setChecked(index, checked) {
3576+ for (var i = 0; i < optionsSingleSelect.count; ++i)
3577+ optionsSingleSelect.setProperty(i, "checked", false);
3578+ optionsSingleSelect.setProperty(index, "checked", checked);
3579+ }
3580+ }
3581+
3582+ Component.onCompleted: {
3583+ singleSelectionWidgetData = { label: "Hola", options: optionsSingleSelect }
3584+ }
3585+
3586+ FilterWidgetFactory {
3587+ id: factory
3588+ widgetId: "testOptionSelectorFilter"
3589+ widgetType: Filters.OptionSelectorFilter
3590+ widgetData: singleSelectionWidgetData
3591+ anchors {
3592+ left: parent.left
3593+ right: parent.right
3594+ }
3595+ }
3596+
3597+ UT.UnityTestCase {
3598+ name: "FilterOptionSelector"
3599+ when: windowShown
3600+
3601+ function test_optionSelector() {
3602+ var expandingItem = findChild(factory, "expandingItem");
3603+ // Open the selector
3604+ mouseClick(factory);
3605+ // wait for it to stop growing
3606+ tryCompare(factory, "implicitHeight", expandingItem.expandedHeight);
3607+
3608+ // Check the first option
3609+ var option0 = findChild(factory, "testOptionSelectorFilterlabel0");
3610+ mouseClick(option0);
3611+ verify(optionsSingleSelect.get(0).checked);
3612+
3613+ // Check the second option
3614+ var option1 = findChild(factory, "testOptionSelectorFilterlabel1");
3615+ mouseClick(option1);
3616+ verify(!optionsSingleSelect.get(0).checked);
3617+ verify(optionsSingleSelect.get(1).checked);
3618+
3619+ // Uncheck the second option
3620+ mouseClick(option1);
3621+ verify(!optionsSingleSelect.get(1).checked);
3622+ }
3623+ }
3624+}
3625
3626=== added file 'tests/qmltests/Dash/Filters/tst_FilterRangeInput.qml'
3627--- tests/qmltests/Dash/Filters/tst_FilterRangeInput.qml 1970-01-01 00:00:00 +0000
3628+++ tests/qmltests/Dash/Filters/tst_FilterRangeInput.qml 2016-03-15 19:17:30 +0000
3629@@ -0,0 +1,247 @@
3630+/*
3631+ * Copyright 2015 Canonical Ltd.
3632+ *
3633+ * This program is free software; you can redistribute it and/or modify
3634+ * it under the terms of the GNU General Public License as published by
3635+ * the Free Software Foundation; version 3.
3636+ *
3637+ * This program is distributed in the hope that it will be useful,
3638+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3639+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3640+ * GNU General Public License for more details.
3641+ *
3642+ * You should have received a copy of the GNU General Public License
3643+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3644+ */
3645+
3646+import QtQuick 2.4
3647+import QtTest 1.0
3648+import Ubuntu.Components 1.3
3649+import "../../../../qml/Dash/Filters"
3650+import Unity.Test 0.1 as UT
3651+import Unity 0.2
3652+
3653+Rectangle {
3654+ id: root
3655+ width: units.gu(60)
3656+ height: units.gu(80)
3657+ color: theme.palette.selected.background
3658+
3659+ QtObject {
3660+ id: widgetData1
3661+
3662+ property string title
3663+ property real startValue
3664+ property real endValue
3665+ property bool hasStartValue
3666+ property bool hasEndValue
3667+ property string startPrefixLabel
3668+ property string startPostfixLabel
3669+ property string centralLabel
3670+ property string endPrefixLabel
3671+ property string endPostfixLabel
3672+
3673+ onStartValueChanged: {
3674+ hasStartValue = true;
3675+ }
3676+
3677+ onEndValueChanged: {
3678+ hasEndValue = true;
3679+ }
3680+
3681+ function eraseStartValue() {
3682+ hasStartValue = false;
3683+ }
3684+
3685+ function eraseEndValue() {
3686+ hasEndValue = false;
3687+ }
3688+ }
3689+
3690+ Component.onCompleted: {
3691+ generateData();
3692+ }
3693+
3694+ function generateData() {
3695+ widgetData1.title = title.text
3696+ widgetData1.startValue = startValue.text;
3697+ widgetData1.endValue = endValue.text;
3698+ widgetData1.hasStartValue = startValueHasValue.checked;
3699+ widgetData1.hasEndValue = endValueHasValue.checked;
3700+ widgetData1.startPrefixLabel = startValuePrefixLabel.text;
3701+ widgetData1.startPostfixLabel = startValuePostfixLabel.text;
3702+ widgetData1.centralLabel = centralLabel.text;
3703+ widgetData1.endPrefixLabel = endValuePrefixLabel.text;
3704+ widgetData1.endPostfixLabel = endValuePostfixLabel.text;
3705+
3706+ factory.widgetData = widgetData1;
3707+ }
3708+
3709+ FilterWidgetFactory {
3710+ id: factory
3711+ widgetId: "testRangeInputFilter"
3712+ widgetType: Filters.RangeInputFilter
3713+ anchors {
3714+ left: parent.left
3715+ right: parent.right
3716+ }
3717+ clip: true
3718+ }
3719+
3720+ Column {
3721+ anchors.top: factory.bottom
3722+ width: parent.width
3723+ spacing: units.gu(1)
3724+
3725+ Text {
3726+ text: "Values"
3727+ }
3728+
3729+ Row {
3730+ spacing: units.gu(1)
3731+ Text {
3732+ text: "Title"
3733+ verticalAlignment: Text.AlignVCenter
3734+ height: parent.height
3735+ }
3736+ TextField {
3737+ id: title
3738+ text: ""
3739+ }
3740+ }
3741+
3742+ Row {
3743+ spacing: units.gu(1)
3744+ Text {
3745+ text: "Start Value Prefix Label"
3746+ verticalAlignment: Text.AlignVCenter
3747+ height: parent.height
3748+ }
3749+ TextField {
3750+ id: startValuePrefixLabel
3751+ text: "Pre1"
3752+ }
3753+ }
3754+
3755+ Row {
3756+ spacing: units.gu(1)
3757+ Text {
3758+ text: "Start Value Postfix Label"
3759+ verticalAlignment: Text.AlignVCenter
3760+ height: parent.height
3761+ }
3762+ TextField {
3763+ id: startValuePostfixLabel
3764+ text: "Post1"
3765+ }
3766+ }
3767+
3768+ Row {
3769+ spacing: units.gu(1)
3770+ Text {
3771+ text: "Central Label"
3772+ verticalAlignment: Text.AlignVCenter
3773+ height: parent.height
3774+ }
3775+ TextField {
3776+ id: centralLabel
3777+ text: "to"
3778+ }
3779+ }
3780+
3781+ Row {
3782+ spacing: units.gu(1)
3783+ Text {
3784+ text: "End Value Prefix Label"
3785+ verticalAlignment: Text.AlignVCenter
3786+ height: parent.height
3787+ }
3788+ TextField {
3789+ id: endValuePrefixLabel
3790+ text: "Pre2"
3791+ }
3792+ }
3793+
3794+ Row {
3795+ spacing: units.gu(1)
3796+ Text {
3797+ text: "End Value Postfix Label"
3798+ verticalAlignment: Text.AlignVCenter
3799+ height: parent.height
3800+ }
3801+ TextField {
3802+ id: endValuePostfixLabel
3803+ text: "Post2"
3804+ }
3805+ }
3806+
3807+ Row {
3808+ spacing: units.gu(1)
3809+ Text {
3810+ text: "Start Value"
3811+ verticalAlignment: Text.AlignVCenter
3812+ height: parent.height
3813+ }
3814+ TextField {
3815+ id: startValue
3816+ text: "-1"
3817+ }
3818+ CheckBox {
3819+ id: startValueHasValue
3820+ }
3821+ }
3822+
3823+
3824+ Row {
3825+ spacing: units.gu(1)
3826+ Text {
3827+ text: "End Value"
3828+ verticalAlignment: Text.AlignVCenter
3829+ height: parent.height
3830+ }
3831+ TextField {
3832+ id: endValue
3833+ text: "5"
3834+ }
3835+ CheckBox {
3836+ id: endValueHasValue
3837+ checked: true
3838+ }
3839+ }
3840+
3841+ Button {
3842+ text: "Set Values"
3843+ onClicked: root.generateData();
3844+ }
3845+ }
3846+
3847+ UT.UnityTestCase {
3848+ name: "FilterRangeInput"
3849+ when: windowShown
3850+
3851+ function init() {
3852+ root.generateData();
3853+ }
3854+
3855+ function test_initialStatus() {
3856+ var startValueField = findChild(factory, "startValueField");
3857+ compare(startValueField.text, "");
3858+
3859+ var endValueField = findChild(factory, "endValueField");
3860+ compare(endValueField.text, "5");
3861+ }
3862+
3863+ function test_dotStays() {
3864+ var startValueField = findChild(factory, "startValueField");
3865+ compare(startValueField.text, "");
3866+
3867+ startValueField.text = "4.5";
3868+ compare(startValueField.text, "4.5");
3869+ compare(widgetData1.startValue, 4.5);
3870+
3871+ startValueField.text = "4.";
3872+ compare(startValueField.text, "4.");
3873+ compare(widgetData1.startValue, 4);
3874+ }
3875+ }
3876+}
3877
3878=== added file 'tests/qmltests/Dash/Filters/tst_FilterValueSlider.qml'
3879--- tests/qmltests/Dash/Filters/tst_FilterValueSlider.qml 1970-01-01 00:00:00 +0000
3880+++ tests/qmltests/Dash/Filters/tst_FilterValueSlider.qml 2016-03-15 19:17:30 +0000
3881@@ -0,0 +1,101 @@
3882+/*
3883+ * Copyright 2015 Canonical Ltd.
3884+ *
3885+ * This program is free software; you can redistribute it and/or modify
3886+ * it under the terms of the GNU General Public License as published by
3887+ * the Free Software Foundation; version 3.
3888+ *
3889+ * This program is distributed in the hope that it will be useful,
3890+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3891+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3892+ * GNU General Public License for more details.
3893+ *
3894+ * You should have received a copy of the GNU General Public License
3895+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3896+ */
3897+
3898+import QtQuick 2.4
3899+import QtTest 1.0
3900+import Ubuntu.Components 1.3
3901+import "../../../../qml/Dash/Filters"
3902+import Unity.Test 0.1 as UT
3903+import Unity 0.2
3904+
3905+Rectangle {
3906+ id: root
3907+ width: units.gu(60)
3908+ height: units.gu(80)
3909+ color: theme.palette.selected.background
3910+
3911+ QtObject {
3912+ id: widgetData1
3913+
3914+ property string title
3915+ property int value: 30
3916+ property int minValue: 10
3917+ property int maxValue: 150
3918+
3919+ property ListModel values: ListModel {
3920+ ListElement {
3921+ value: 10
3922+ label: "10"
3923+ }
3924+ ListElement {
3925+ value: 30
3926+ label: "30"
3927+ }
3928+ ListElement {
3929+ value: 100
3930+ label: "100"
3931+ }
3932+ ListElement {
3933+ value: 150
3934+ label: "150"
3935+ }
3936+ }
3937+ }
3938+
3939+ FilterWidgetFactory {
3940+ id: factory
3941+ widgetId: "testRangeValueSlider"
3942+ widgetType: Filters.ValueSliderFilter
3943+ widgetData: widgetData1;
3944+ y: 100
3945+ anchors {
3946+ left: parent.left
3947+ right: parent.right
3948+ }
3949+ }
3950+
3951+ UT.UnityTestCase {
3952+ name: "FilterValueSlider"
3953+ when: windowShown
3954+
3955+ function test_valueChanges() {
3956+ var slider = findChild(factory, "slider");
3957+ compare(slider.value, 30);
3958+
3959+ slider.value = 50
3960+ compare(widgetData1.value, 50);
3961+ }
3962+
3963+ function test_labelPositions() {
3964+ var slider = findChild(factory, "slider");
3965+ var repeater = findChild(factory, "repeater");
3966+
3967+ // It's very hard to check the position of the
3968+ // labels is right, but at least we can check
3969+ // they all have different X positions and the
3970+ // same Y one
3971+ compare(repeater.count, 4);
3972+ for (var i = 0; i < repeater.count; ++i) {
3973+ var itemI = repeater.itemAt(i);
3974+ for (var j = i + 1; j < repeater.count; ++j) {
3975+ var itemJ = repeater.itemAt(j);
3976+ verify(itemI.x != itemJ.x);
3977+ verify(itemI.y == itemJ.y);
3978+ }
3979+ }
3980+ }
3981+ }
3982+}
3983
3984=== added file 'tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml'
3985--- tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml 1970-01-01 00:00:00 +0000
3986+++ tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml 2016-03-15 19:17:30 +0000
3987@@ -0,0 +1,58 @@
3988+/*
3989+ * Copyright 2015 Canonical Ltd.
3990+ *
3991+ * This program is free software; you can redistribute it and/or modify
3992+ * it under the terms of the GNU General Public License as published by
3993+ * the Free Software Foundation; version 3.
3994+ *
3995+ * This program is distributed in the hope that it will be useful,
3996+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3997+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3998+ * GNU General Public License for more details.
3999+ *
4000+ * You should have received a copy of the GNU General Public License
4001+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4002+ */
4003+
4004+import QtQuick 2.4
4005+import QtTest 1.0
4006+import "../../../../qml/Dash/Filters"
4007+import Unity.Test 0.1 as UT
4008+import Unity 0.2
4009+import Ubuntu.Components 1.3
4010+
4011+Rectangle {
4012+ id: root
4013+ width: units.gu(60)
4014+ height: units.gu(80)
4015+ color: theme.palette.selected.background
4016+
4017+ FilterWidgetFactory {
4018+ id: factory
4019+ anchors {
4020+ left: parent.left
4021+ right: parent.right
4022+ }
4023+ }
4024+
4025+ UT.UnityTestCase {
4026+ name: "FilterWidgetFactory"
4027+ when: windowShown
4028+
4029+ function test_mapping_data() {
4030+ return [
4031+ { tag: "OptionSelector", type: Filters.OptionSelectorFilter, source: "FilterOptionSelector.qml" },
4032+ { tag: "RangeInputFilter", type: Filters.RangeInputFilter, source: "FilterRangeInput.qml" },
4033+ { tag: "ValueSliderFilter", type: Filters.ValueSliderFilter, source: "FilterValueSlider.qml" }
4034+ ];
4035+ }
4036+
4037+ function test_mapping(data) {
4038+ factory.widgetData = {};
4039+ factory.widgetType = data.type;
4040+
4041+ var loader = findChild(factory, "loader");
4042+ verify((String(loader.source)).indexOf(data.source) != -1);
4043+ }
4044+ }
4045+}
4046
4047=== modified file 'tests/qmltests/Dash/tst_DashContent.qml'
4048--- tests/qmltests/Dash/tst_DashContent.qml 2015-12-16 14:35:39 +0000
4049+++ tests/qmltests/Dash/tst_DashContent.qml 2016-03-15 19:17:30 +0000
4050@@ -284,14 +284,15 @@
4051 var dashContentList = findChild(dashContent, "dashContentList");
4052 tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4053 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4054- tryCompare(dashNavigation, "visible", true);
4055- var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
4056- compare(dashNavigationButton.showList, false);
4057- waitForRendering(dashNavigationButton);
4058- mouseClick(dashNavigationButton);
4059- compare(dashNavigationButton.showList, true);
4060+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4061+ var searchButton = findChild(dashContentList.currentItem, "search_button");
4062+ var searchTextField = findChild(dashContentList.currentItem, "searchTextField");
4063+ compare(peExtraPanel.visible, false);
4064+ mouseClick(searchButton);
4065+ tryCompare(peExtraPanel, "visible", true);
4066+ peExtraPanel.searchHistory.clear();
4067
4068- var navigationListView = findChild(dashNavigationButton, "navigationListView");
4069+ var navigationListView = findChild(dashNavigation, "navigationListView");
4070 tryCompareFunction(function() {
4071 return navigationListView.currentItem &&
4072 navigationListView.currentItem.navigation &&
4073@@ -300,235 +301,247 @@
4074 waitForRendering(navigationListView);
4075 waitForRendering(navigationListView.currentItem);
4076
4077- var allButton = findChild(dashNavigationButton, "allButton");
4078- compare(allButton.visible, false);
4079-
4080- var navigation = findChild(dashNavigationButton, "navigation0child3");
4081- mouseClick(navigation);
4082- compare(dashNavigationButton.showList, false);
4083- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle3");
4084- tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4085-
4086- mouseClick(dashNavigationButton);
4087- compare(dashNavigationButton.showList, true);
4088- waitForRendering(navigationListView);
4089- waitForRendering(navigationListView.currentItem);
4090- compare(allButton.visible, true);
4091-
4092- mouseClick(allButton);
4093- compare(dashNavigationButton.showList, false);
4094- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
4095- tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4096-
4097- mouseClick(dashNavigationButton);
4098- compare(dashNavigationButton.showList, true);
4099- waitForRendering(navigationListView);
4100- waitForRendering(navigationListView.currentItem);
4101- compare(allButton.visible, false);
4102-
4103- navigation = findChild(dashNavigationButton, "navigation0child2");
4104- mouseClick(navigation);
4105- compare(dashNavigationButton.showList, true);
4106- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
4107+ var navigation = findChild(dashNavigation, "navigation0child3");
4108+ mouseClick(navigation);
4109+ compare(peExtraPanel.visible, false);
4110+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
4111+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4112+
4113+ mouseClick(searchTextField);
4114+ tryCompare(peExtraPanel, "visible", true);
4115+ waitForRendering(navigationListView);
4116+ waitForRendering(navigationListView.currentItem);
4117+
4118+ var header0 = findChild(dashNavigation, "dashNavigationHeader0");
4119+ compare(header0.backVisible, false);
4120+ mouseClick(header0);
4121+ compare(peExtraPanel.visible, false);
4122+ tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
4123+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4124+
4125+ var headerContainer = findChild(dashContentList.currentItem, "headerContainer");
4126+ tryCompare(headerContainer, "contentY", headerContainer.height);
4127+ mouseClick(searchButton);
4128+ tryCompare(peExtraPanel, "visible", true);
4129+ waitForRendering(navigationListView);
4130+ waitForRendering(navigationListView.currentItem);
4131+
4132+ navigation = findChild(dashNavigation, "navigation0child2");
4133+ mouseClick(navigation);
4134+ compare(peExtraPanel.visible, true);
4135+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
4136 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
4137
4138- var navigationList1 = findChild(dashNavigationButton, "navigation1");
4139- allButton = findChild(navigationList1, "allButton");
4140- var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
4141- compare(allButton.visible, true);
4142- compare(backButton.visible, true);
4143+ var navigationList1 = findChild(dashNavigation, "navigation1");
4144+ var header1 = findChild(dashNavigation, "dashNavigationHeader1");
4145+ compare(header0.height, 0);
4146+ compare(header1.backVisible, true);
4147
4148 tryCompare(navigationListView, "contentX", navigationList1.x);
4149 waitForRendering(navigationListView);
4150- mouseClick(allButton);
4151- compare(dashNavigationButton.showList, false);
4152- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
4153+ mouseClick(header1);
4154+ compare(peExtraPanel.visible, false);
4155+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
4156 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
4157
4158- mouseClick(dashNavigationButton);
4159- compare(dashNavigationButton.showList, true);
4160+ mouseClick(searchTextField);
4161+ tryCompare(peExtraPanel, "visible", true);
4162 waitForRendering(navigationListView);
4163 waitForRendering(navigationListView.currentItem);
4164- compare(allButton.visible, true);
4165- compare(backButton.visible, true);
4166+ compare(header0.height, 0);
4167+ compare(header1.backVisible, true);
4168
4169 tryCompare(navigationList1.navigation, "loaded", true);
4170- tryCompare(navigationList1, "implicitHeight", navigationList1.itemHeight * 10);
4171+ tryCompare(navigationList1, "implicitHeight", navigationList1.itemHeight * 8);
4172 tryCompare(navigationList1, "height", navigationList1.implicitHeight);
4173- navigation = findChild(dashNavigationButton, "navigation1child2");
4174+ navigation = findChild(dashNavigation, "navigation1child2");
4175 mouseClick(navigation);
4176- compare(dashNavigationButton.showList, false);
4177- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle22");
4178+ compare(peExtraPanel.visible, false);
4179+ tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
4180 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
4181
4182- mouseClick(dashNavigationButton);
4183- compare(dashNavigationButton.showList, true);
4184+ mouseClick(searchTextField);
4185+ tryCompare(peExtraPanel, "visible", true);
4186 waitForRendering(navigationListView);
4187 waitForRendering(navigationListView.currentItem);
4188
4189 tryCompare(navigationList1.navigation, "loaded", true);
4190- navigation = findChild(dashNavigationButton, "navigation1child3");
4191+ navigation = findChild(dashNavigation, "navigation1child3");
4192 mouseClick(navigation);
4193- compare(dashNavigationButton.showList, false);
4194- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "childmiddle23");
4195+ compare(peExtraPanel.visible, false);
4196+ tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
4197 tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
4198
4199- mouseClick(dashNavigationButton);
4200- compare(dashNavigationButton.showList, true);
4201+ mouseClick(searchTextField);
4202+ tryCompare(peExtraPanel, "visible", true);
4203 waitForRendering(navigationListView);
4204 waitForRendering(navigationListView.currentItem);
4205- mouseClick(backButton);
4206+ mouseClick(findChild(header1, "backButton"));
4207
4208- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "root");
4209+ tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
4210 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4211- compare(dashNavigationButton.showList, true);
4212- mouseClick(dashNavigationButton);
4213- compare(dashNavigationButton.showList, false);
4214+ compare(peExtraPanel.visible, true);
4215+ mouseClick(header0);
4216+ compare(peExtraPanel.visible, false);
4217
4218- mouseClick(dashNavigationButton);
4219- compare(dashNavigationButton.showList, true);
4220+ tryCompare(headerContainer, "contentY", headerContainer.height);
4221+ mouseClick(searchButton);
4222+ tryCompare(peExtraPanel, "visible", true);
4223 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
4224- var navigationList0 = findChild(dashNavigationButton, "navigation0");
4225+ var navigationList0 = findChild(dashNavigation, "navigation0");
4226 tryCompare(navigationList0, "implicitHeight", navigationList0.itemHeight * 8);
4227 tryCompare(navigationList0, "height", navigationList0.implicitHeight);
4228- navigation = findChild(dashNavigationButton, "navigation0child2");
4229+ navigation = findChild(dashNavigation, "navigation0child2");
4230 mouseClick(navigation);
4231- compare(dashNavigationButton.showList, true);
4232- navigationList1 = findChild(dashNavigationButton, "navigation1");
4233+ compare(peExtraPanel.visible, true);
4234+ navigationList1 = findChild(dashNavigation, "navigation1");
4235 compare(navigationList1.navigation.loaded, false);
4236- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
4237- allButton = findChild(navigationList1, "allButton");
4238- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
4239- mouseClick(allButton);
4240- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "middle2");
4241- }
4242-
4243- function test_altNavigation() {
4244- var dashContentList = findChild(dashContent, "dashContentList");
4245- tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4246- var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4247- tryCompare(dashNavigation, "visible", true);
4248- var dashNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
4249- verify(dashNavigationButton, "Can't find navigation button");
4250-
4251- compare(dashNavigationButton.showList, false);
4252- waitForRendering(dashNavigationButton);
4253- mouseClick(dashNavigationButton);
4254- compare(dashNavigationButton.showList, true);
4255-
4256- var navigationListView = findChild(dashNavigationButton, "navigationListView");
4257- tryCompareFunction(function() {
4258- return navigationListView.currentItem &&
4259- navigationListView.currentItem.navigation &&
4260- navigationListView.currentItem.navigation.loaded; }, true);
4261-
4262- waitForRendering(navigationListView);
4263- waitForRendering(navigationListView.currentItem);
4264-
4265- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild1");
4266- tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
4267-
4268- var allButton = findChild(dashNavigationButton, "allButton");
4269- compare(allButton.visible, false);
4270- var backButton = findChild(navigationListView, "backButton");
4271- compare(backButton.visible, false);
4272-
4273- var navigation = findChild(dashNavigationButton, "navigation0child3");
4274- mouseClick(navigation);
4275- compare(dashNavigationButton.showList, false);
4276- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild3");
4277- tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
4278-
4279- mouseClick(dashNavigationButton);
4280- compare(dashNavigationButton.showList, true);
4281- waitForRendering(navigationListView);
4282- waitForRendering(navigationListView.currentItem);
4283- compare(allButton.visible, false);
4284-
4285- navigation = findChild(dashNavigationButton, "navigation0child2");
4286- mouseClick(navigation);
4287- compare(dashNavigationButton.showList, false);
4288- tryCompare(dashNavigationButton.currentNavigation, "navigationId", "altrootChild2");
4289- tryCompare(navigationListView.currentItem.navigation, "navigationId", "altroot");
4290-
4291- mouseClick(dashNavigationButton);
4292- compare(dashNavigationButton.showList, true);
4293- waitForRendering(navigationListView);
4294- waitForRendering(navigationListView.currentItem);
4295- verify(!backButton.visible, "Back button should not be visible");
4296-
4297- compare(navigationListView.count, 1, "There should be no second-level navigation");
4298-
4299- mouseClick(dashNavigationButton);
4300- compare(dashNavigationButton.showList, false);
4301- }
4302-
4303- function test_navigationsClosing() {
4304- var dashContentList = findChild(dashContent, "dashContentList");
4305- tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4306- var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4307- tryCompare(dashNavigation, "visible", true);
4308- var dashNavigationButton = findChild(dashContentList.currentItem, "navigationButton");
4309- verify(dashNavigationButton, "Can't find navigation button");
4310- var dashAltNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
4311- verify(dashAltNavigationButton, "Can't find alternative navigation button");
4312-
4313- waitForRendering(dashNavigationButton);
4314- waitForRendering(dashAltNavigationButton);
4315-
4316- compare(dashNavigationButton.showList, false);
4317- compare(dashAltNavigationButton.showList, false);
4318-
4319- mouseClick(dashAltNavigationButton);
4320- compare(dashNavigationButton.showList, false);
4321- compare(dashAltNavigationButton.showList, true);
4322-
4323- var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
4324- tryCompareFunction(function() {
4325- return navigationListView.currentItem &&
4326- navigationListView.currentItem.navigation &&
4327- navigationListView.currentItem.navigation.loaded; }, true);
4328-
4329- var blackRect = findChild(dashNavigation, "blackRect");
4330- tryCompare(blackRect, "opacity", 0.5);
4331-
4332- mouseClick(dashNavigationButton);
4333- compare(dashNavigationButton.showList, false);
4334- compare(dashAltNavigationButton.showList, false);
4335-
4336- tryCompare(navigationListView, "visible", false);
4337-
4338- mouseClick(dashNavigationButton);
4339- compare(dashNavigationButton.showList, true);
4340- compare(dashAltNavigationButton.showList, false);
4341-
4342- navigationListView = findChild(dashNavigationButton, "navigationListView");
4343- tryCompare(navigationListView.currentItem.navigation, "loaded", true);
4344-
4345- mouseClick(dashAltNavigationButton);
4346- compare(dashNavigationButton.showList, false);
4347- compare(dashAltNavigationButton.showList, false);
4348- }
4349-
4350- function test_navigationsSwipeToNextScope() {
4351- var dashContentList = findChild(dashContent, "dashContentList");
4352- tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4353- var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4354- tryCompare(dashNavigation, "visible", true);
4355- var dashNavigationButton = findChild(dashContentList.currentItem, "altNavigationButton");
4356- verify(dashNavigationButton, "Can't find navigation button");
4357-
4358- compare(dashNavigationButton.showList, false);
4359- waitForRendering(dashNavigationButton);
4360- mouseClick(dashNavigationButton, 0, 0);
4361- compare(dashNavigationButton.showList, true);
4362-
4363- mouseFlick(dashNavigationButton, dashNavigationButton.width - units.gu(1), units.gu(1), units.gu(1), units.gu(1));
4364- compare(dashNavigationButton.showList, false);
4365-
4366- var dashContentList = findChild(dashContent, "dashContentList");
4367- expectFail("", "should not change the parent list.");
4368- tryCompare(dashContentList, "currentIndex", 1);
4369+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
4370+ mouseClick(header0);
4371+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
4372+ }
4373+
4374+ function goToSecondLevel() {
4375+ var dashContentList = findChild(dashContent, "dashContentList");
4376+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4377+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4378+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4379+ var searchButton = findChild(dashContentList.currentItem, "search_button");
4380+ var searchTextField = findChild(dashContentList.currentItem, "searchTextField");
4381+ compare(peExtraPanel.visible, false);
4382+ mouseClick(searchButton);
4383+ tryCompare(peExtraPanel, "visible", true);
4384+
4385+ var navigationListView = findChild(dashNavigation, "navigationListView");
4386+ tryCompareFunction(function() {
4387+ return navigationListView.currentItem &&
4388+ navigationListView.currentItem.navigation &&
4389+ navigationListView.currentItem.navigation.loaded; }, true);
4390+ waitForRendering(navigationListView);
4391+ waitForRendering(navigationListView.currentItem);
4392+
4393+ var navigation4 = findChild(dashNavigation, "navigation0child4");
4394+ mouseClick(navigation4);
4395+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle4");
4396+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle4");
4397+ tryCompare(dashNavigation.currentNavigation, "loaded", true);
4398+
4399+ waitForRendering(navigationListView);
4400+ waitForRendering(navigationListView.currentItem);
4401+
4402+ var navigation44 = findChild(dashNavigation, "navigation1child4");
4403+ mouseClick(navigation44);
4404+ tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle44");
4405+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "childmiddle44");
4406+ tryCompare(dashNavigation.currentNavigation, "loaded", true);
4407+
4408+ waitForRendering(navigationListView);
4409+ waitForRendering(navigationListView.currentItem);
4410+
4411+ // header 0 is hidden
4412+ var header0 = findChild(dashNavigation, "dashNavigationHeader0");
4413+ compare(header0.height, 0);
4414+ }
4415+
4416+ function test_navigationSecondLevelToRoot() {
4417+ goToSecondLevel();
4418+
4419+ var dashContentList = findChild(dashContent, "dashContentList");
4420+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4421+ var navigationListView = findChild(dashNavigation, "navigationListView");
4422+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4423+
4424+ // Go directly to the root pressing the back button of header1
4425+ var header1 = findChild(dashNavigation, "dashNavigationHeader1");
4426+ compare(header1.backVisible, true);
4427+ mouseClick(findChild(header1, "backButton"));
4428+
4429+ tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
4430+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
4431+ compare(peExtraPanel.visible, true);
4432+ }
4433+
4434+ function test_navigationSecondLevelToFirstName() {
4435+ goToSecondLevel();
4436+
4437+ var dashContentList = findChild(dashContent, "dashContentList");
4438+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4439+ var navigationListView = findChild(dashNavigation, "navigationListView");
4440+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4441+
4442+ // Go directly to the first pressing the header1
4443+ var header1 = findChild(dashNavigation, "dashNavigationHeader1");
4444+ mouseClick(header1);
4445+
4446+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle4");
4447+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle4");
4448+ compare(peExtraPanel.visible, false);
4449+ }
4450+
4451+ function test_navigationSecondLevelToFirstBack() {
4452+ goToSecondLevel();
4453+
4454+ var dashContentList = findChild(dashContent, "dashContentList");
4455+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
4456+ var navigationListView = findChild(dashNavigation, "navigationListView");
4457+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4458+
4459+ // Go back to the first level pressing the back button of header2
4460+ var header2 = findChild(dashNavigation, "dashNavigationHeader2");
4461+ compare(header2.backVisible, true);
4462+ mouseClick(findChild(header2, "backButton"));
4463+
4464+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle4");
4465+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle4");
4466+ compare(peExtraPanel.visible, true);
4467+ }
4468+
4469+ function test_clearSearchWithNavigationClosed() {
4470+ var dashContentList = findChild(dashContent, "dashContentList");
4471+ var searchButton = findChild(dashContentList.currentItem, "search_button");
4472+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4473+ var searchTextField = findChild(dashContentList.currentItem, "searchTextField");
4474+
4475+ compare(peExtraPanel.visible, false);
4476+ mouseClick(searchButton);
4477+ tryCompare(peExtraPanel, "visible", true);
4478+
4479+ typeString("A");
4480+ compare(peExtraPanel.visible, false);
4481+
4482+ var clearIcon = findChild(searchTextField, "clearIcon");
4483+ mouseClick(clearIcon);
4484+ tryCompare(peExtraPanel, "visible", true);
4485+ }
4486+
4487+ function test_navigationShowFilterPopup() {
4488+ goToSecondLevel();
4489+
4490+ var dashContentList = findChild(dashContent, "dashContentList");
4491+ var settingsButton = findChild(dashContentList.currentItem, "settingsButton");
4492+
4493+ var filtersPopover = findChild(shell, "filtersPopover")
4494+ verify(!filtersPopover);
4495+
4496+ mouseClick(settingsButton);
4497+
4498+ filtersPopover = findChild(shell, "filtersPopover")
4499+
4500+ verify(filtersPopover);
4501+ }
4502+
4503+ function test_primaryFilter() {
4504+ var dashContentList = findChild(dashContent, "dashContentList");
4505+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
4506+ dashContentList.currentItem.item.scope.setHasNavigation(false);
4507+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
4508+ var searchButton = findChild(dashContentList.currentItem, "search_button");
4509+
4510+ compare(peExtraPanel.visible, false);
4511+ mouseClick(searchButton);
4512+ tryCompare(peExtraPanel, "visible", true);
4513+
4514+ tryCompareFunction(function() { return findChild(peExtraPanel, "OSF3") != null; }, true);
4515 }
4516
4517 function test_searchHint() {
4518@@ -540,7 +553,7 @@
4519 var categoryListView = findChild(scope, "categoryListView");
4520 waitForRendering(categoryListView);
4521
4522- compare(categoryListView.pageHeader.item.searchHint, "Search People");
4523+ compare(categoryListView.pageHeader.searchHint, "Search People");
4524 }
4525
4526 function compareArrays(a, b) {
4527
4528=== modified file 'tests/qmltests/Dash/tst_DashPageHeader.qml'
4529--- tests/qmltests/Dash/tst_DashPageHeader.qml 2015-11-19 16:55:31 +0000
4530+++ tests/qmltests/Dash/tst_DashPageHeader.qml 2016-03-15 19:17:30 +0000
4531@@ -18,6 +18,7 @@
4532 import QtTest 1.0
4533 import ".."
4534 import "../../../qml/Dash"
4535+import "../../../qml/Components/SearchHistoryModel"
4536 import Ubuntu.Components 1.3
4537 import Unity 0.2
4538 import Unity.Test 0.1 as UT
4539@@ -25,7 +26,7 @@
4540 Item {
4541 id: root
4542 width: units.gu(110)
4543- height: units.gu(30)
4544+ height: units.gu(50)
4545
4546 UT.UnityTestCase {
4547 name: "PageHeaderLabelTest"
4548@@ -54,7 +55,7 @@
4549 pageHeader.searchHistory.clear();
4550
4551 // Check initial state
4552- tryCompareFunction(function() { return headerContainer.popover === null; }, true);
4553+ tryCompare(pageHeader.extraPanel, "visible", false);
4554 compare(pageHeader.searchHistory.count, 0);
4555 }
4556
4557@@ -146,7 +147,7 @@
4558 doResetSearch();
4559 }
4560
4561- function test_popover() {
4562+ function test_extraPanel() {
4563 searchEnabled = true;
4564 pageHeader.searchHistory.clear();
4565
4566@@ -155,22 +156,18 @@
4567
4568 pageHeader.triggerSearch();
4569
4570- var headerContainer = findChild(pageHeader, "headerContainer");
4571- verify(headerContainer !== null, "headerContainer != null");
4572- tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
4573-
4574- tryCompare(headerContainer.popover, "visible", true);
4575+ tryCompare(pageHeader.extraPanel, "visible", true);
4576
4577 var searchTextField = findChild(pageHeader, "searchTextField");
4578 compare(searchTextField.focus, true);
4579
4580- var recentSearches = findChild(headerContainer.popover, "recentSearches");
4581- verify(recentSearches, "Could not find recent searches in the popover");
4582+ var recentSearches = findChild(pageHeader.extraPanel, "recentSearchesRepeater");
4583+ verify(recentSearches, "Could not find recent searches");
4584 waitForRendering(recentSearches);
4585 mouseClick(recentSearches.itemAt(0));
4586
4587 compare(pageHeader.searchQuery, "Search2");
4588- tryCompareFunction(function() { return headerContainer.popover === null; }, true);
4589+ tryCompare(pageHeader.extraPanel, "visible", false);
4590 compare(searchTextField.focus, false);
4591 }
4592
4593@@ -212,9 +209,7 @@
4594 pageHeader.triggerSearch();
4595
4596 var headerContainer = findChild(pageHeader, "headerContainer");
4597- verify(headerContainer !== null, "headerContainer != null");
4598- tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
4599- compare(headerContainer.popover.visible, true);
4600+ tryCompare(pageHeader.extraPanel, "visible", true);
4601
4602 pageHeader.searchQuery = data.searchText;
4603
4604@@ -225,7 +220,7 @@
4605 }
4606
4607 tryCompare(headerContainer, "showSearch", !data.hideSearch);
4608- tryCompareFunction(function() { return headerContainer.popover === null; }, true);
4609+ tryCompare(pageHeader.extraPanel, "visible", false);
4610
4611 doResetSearch();
4612 }
4613@@ -251,8 +246,10 @@
4614 right: parent.right
4615 }
4616
4617+ searchHistory: SearchHistoryModel
4618 searchEntryEnabled: true
4619 title: "%^$%^%^&%^&%^$%GHR%"
4620+ extraPanel: peExtraPanel
4621 scopeStyle: QtObject {
4622 readonly property color foreground: theme.palette.normal.baseText
4623 readonly property url headerLogo: showImageCheckBox.checked ? pageHeader.titleImageSource : ""
4624@@ -264,6 +261,18 @@
4625 onBackClicked: lastBackClicked = new Date()
4626 }
4627
4628+ PageHeaderExtraPanel {
4629+ id: peExtraPanel
4630+ width: parent.width
4631+ z: 1
4632+ visible: false
4633+ searchHistory: SearchHistoryModel
4634+ onHistoryItemClicked: {
4635+ SearchHistoryModel.addQuery(text);
4636+ pageHeader.searchQuery = text;
4637+ }
4638+ }
4639+
4640 Row {
4641 spacing: units.gu(1)
4642 anchors {
4643
4644=== modified file 'tests/qmltests/Dash/tst_GenericScopeView.qml'
4645--- tests/qmltests/Dash/tst_GenericScopeView.qml 2016-02-22 17:07:15 +0000
4646+++ tests/qmltests/Dash/tst_GenericScopeView.qml 2016-03-15 19:17:30 +0000
4647@@ -428,8 +428,8 @@
4648
4649 function test_header_style_data() {
4650 return [
4651- { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#f5f5f5", logo: "" },
4652- { tag: "Foreground", index: 1, foreground: "yellow", background: "color:///#f5f5f5", logo: "" },
4653+ { tag: "Default", index: 0, foreground: UbuntuColors.darkGrey, background: "color:///#ffffff", logo: "" },
4654+ { tag: "Foreground", index: 1, foreground: "yellow", background: "color:///#ffffff", logo: "" },
4655 { tag: "Logo+Background", index: 2, foreground: UbuntuColors.darkGrey, background: "gradient:///lightgrey/grey",
4656 logo: Qt.resolvedUrl("../Dash/tst_PageHeader/logo-ubuntu-orange.svg") },
4657 ];
4658@@ -478,7 +478,7 @@
4659 mouseClick(seeAll0);
4660 verify(category0.expanded);
4661 tryCompare(category0, "height", category0.item.expandedHeight + seeAll0.height);
4662- tryCompare(genericScopeView.categoryView, "contentY", units.gu(13));
4663+ tryCompare(genericScopeView.categoryView, "contentY", units.gu(8));
4664
4665 scrollToEnd();
4666
4667
4668=== modified file 'tests/qmltests/Dash/tst_PreviewView.qml'
4669--- tests/qmltests/Dash/tst_PreviewView.qml 2016-02-23 16:30:23 +0000
4670+++ tests/qmltests/Dash/tst_PreviewView.qml 2016-03-15 19:17:30 +0000
4671@@ -98,7 +98,7 @@
4672 var header = findChild(view, "innerPageHeader");
4673 verify(header, "Could not find the preview header");
4674
4675- compare(header.config.title, "Mock Scope");
4676+ compare(header.config.contents.text, "Mock Scope");
4677 }
4678
4679 function test_header_style() {

Subscribers

People subscribed via source and target branches