Merge lp:~aacid/unity8/optionselector_filter into lp:unity8

Proposed by Albert Astals Cid
Status: Merged
Approved by: Andrea Cimitan
Approved revision: 2019
Merged at revision: 2296
Proposed branch: lp:~aacid/unity8/optionselector_filter
Merge into: lp:unity8
Prerequisite: lp:~aacid/unity8/new_dash_navigation
Diff against target: 1035 lines (+621/-30)
21 files modified
qml/Dash/DashNavigation.qml (+1/-1)
qml/Dash/DashPageHeader.qml (+36/-16)
qml/Dash/Filters/FilterOptionSelector.qml (+89/-0)
qml/Dash/Filters/FilterWidget.qml (+29/-0)
qml/Dash/Filters/FilterWidgetFactory.qml (+49/-0)
qml/Dash/FiltersPopover.qml (+97/-0)
qml/Dash/GenericScopeView.qml (+11/-0)
qml/Dash/PageHeaderExtraPanel.qml (+20/-2)
tests/mocks/Unity/fake_filters.cpp (+17/-3)
tests/mocks/Unity/fake_filters.h (+9/-2)
tests/mocks/Unity/fake_optionselectorfilter.cpp (+6/-0)
tests/mocks/Unity/fake_optionselectorfilter.h (+8/-1)
tests/mocks/Unity/fake_optionselectoroptions.cpp (+12/-0)
tests/mocks/Unity/fake_optionselectoroptions.h (+5/-0)
tests/mocks/Unity/fake_scope.cpp (+20/-4)
tests/mocks/Unity/fake_scope.h (+8/-1)
tests/mocks/Unity/fake_unity_plugin.cpp (+5/-0)
tests/qmltests/CMakeLists.txt (+2/-0)
tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml (+96/-0)
tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml (+55/-0)
tests/qmltests/Dash/tst_DashContent.qml (+46/-0)
To merge this branch: bzr merge lp:~aacid/unity8/optionselector_filter
Reviewer Review Type Date Requested Status
Andrea Cimitan (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+273194@code.launchpad.net

Commit message

Filter implementation with Option Selector filter as the only filter

Description of the change

* Are there any related MPs required for this MP to build/function as expected?
Branches from https://requests.ci-train.ubuntu.com/#/ticket/506

 * 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?
Not yet

To post a comment you must log in.
1994. By Albert Astals Cid

Implement Scope::activeFiltersCount in the mock plugin

1995. By Albert Astals Cid

Number with the active filters

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1996. By Albert Astals Cid

Fix whitespace

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1997. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1998. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1999. By Albert Astals Cid

Wrap the filters in a flickable

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2000. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2001. By Albert Astals Cid

Merge

2002. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2003. By Albert Astals Cid

mege

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2004. By Albert Astals Cid

Merge

2005. By Albert Astals Cid

Merge

2006. By Albert Astals Cid

Merge

2007. By Albert Astals Cid

Proper name

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2008. By Albert Astals Cid

Fix import versions

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2009. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2010. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2011. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2012. By Albert Astals Cid

Add "Refine your results" + "Reset" to the filter popover

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) wrote :

couple of comments for now

review: Needs Fixing
2013. By Albert Astals Cid

drop unneeded id

2014. By Albert Astals Cid

Better wait for it to check for growth to have stopped

Revision history for this message
Albert Astals Cid (aacid) wrote :

Both fixed

2015. By Albert Astals Cid

Merge

Revision history for this message
Andrea Cimitan (cimi) wrote :

Thanks! A couple more

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2016. By Albert Astals Cid

More reviews fixes

Revision history for this message
Albert Astals Cid (aacid) wrote :

> Thanks! A couple more

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2017. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2018. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2019. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
yes, silo 54
 * Did CI run pass? If not, please explain why.
unrelated
 * Did you make sure that the branch does not contain spurious tags?
y

review: Approve
2020. By Albert Astals Cid

Merge

2021. By Albert Astals Cid

Merge

2022. By Albert Astals Cid

Hide popover when scope stops being current

2023. By Albert Astals Cid

Remove clicking on the scope title to trigger search

Was a bug in the prototype i ended up copying ^_^

2024. By Albert Astals Cid

Take into account OSK height for limiting the popover height

2025. By Albert Astals Cid

Give some space between the popover and the OSK

3gu decided by Patty

2026. By Albert Astals Cid

Merge

2027. By Albert Astals Cid

Merge

2028. By Albert Astals Cid

Merge

2029. By Albert Astals Cid

Merge

2030. By Albert Astals Cid

Merge

2031. By Albert Astals Cid

Merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'qml/Dash/DashNavigation.qml'
2--- qml/Dash/DashNavigation.qml 2016-03-14 08:53:26 +0000
3+++ qml/Dash/DashNavigation.qml 2016-03-14 08:53:26 +0000
4@@ -35,7 +35,7 @@
5 property bool isEnteringChildren: false
6
7 visible: height != 0
8- implicitHeight: navigationListView.y + navigationListView.height
9+ implicitHeight: scope && scope.hasNavigation ? navigationListView.y + navigationListView.height : 0
10
11 function resetNavigation() {
12 if (navigationModel.count > 1) {
13
14=== modified file 'qml/Dash/DashPageHeader.qml'
15--- qml/Dash/DashPageHeader.qml 2016-03-14 08:53:26 +0000
16+++ qml/Dash/DashPageHeader.qml 2016-03-14 08:53:26 +0000
17@@ -17,7 +17,6 @@
18 import QtQuick 2.4
19 import Ubuntu.Components 1.3
20 import Ubuntu.Components.Themes.Ambiance 1.3
21-import Ubuntu.Components.Popups 1.3
22 import Ubuntu.Components.ListItems 1.3
23 import "../Components"
24
25@@ -27,6 +26,8 @@
26 implicitHeight: headerContainer.height + signatureLineHeight
27 readonly property real signatureLineHeight: showSignatureLine ? units.gu(2) : 0
28
29+ property int activeFiltersCount: 0
30+ property bool scopeHasFilters: false
31 property bool showBackButton: false
32 property bool backIsClose: false
33 property string title
34@@ -54,6 +55,7 @@
35 signal settingsClicked()
36 signal favoriteClicked()
37 signal searchTextFieldFocused()
38+ signal showFiltersPopup(var item)
39
40 onScopeStyleChanged: refreshLogo()
41 onSearchQueryChanged: {
42@@ -204,29 +206,47 @@
43 rightMargin: units.gu(1)
44 }
45
46+ readonly property bool clearIsSettings: !searchTextField.focus && root.scopeHasFilters
47+
48 primaryItem: Label {
49 text: root.navigationTag
50 }
51
52- secondaryItem: AbstractButton {
53+ secondaryItem: Row {
54 height: searchTextField.height
55- width: height
56- enabled: searchTextField.text.length > 0 || root.navigationTag != ""
57-
58- Image {
59- objectName: "clearIcon"
60- anchors.fill: parent
61- anchors.margins: units.gu(.75)
62- source: "image://theme/clear"
63- opacity: parent.enabled
64- visible: opacity > 0
65- Behavior on opacity {
66- UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
67+
68+ AbstractButton {
69+ id: clearOrSettingsButton
70+ height: parent.height
71+ width: height
72+ enabled: searchTextField.text.length > 0 || root.navigationTag != ""
73+
74+ Image {
75+ objectName: "clearIcon"
76+ anchors.fill: parent
77+ anchors.margins: units.gu(.75)
78+ source: searchTextField.clearIsSettings ? "image://theme/settings" : "image://theme/clear"
79+ opacity: parent.enabled
80+ visible: opacity > 0
81+ Behavior on opacity {
82+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
83+ }
84+ }
85+
86+ onClicked: {
87+ if (searchTextField.clearIsSettings) {
88+ root.showFiltersPopup(clearOrSettingsButton);
89+ } else {
90+ root.clearSearch(true);
91+ }
92 }
93 }
94
95- onClicked: {
96- root.clearSearch(true);
97+ Label {
98+ visible: searchTextField.clearIsSettings && root.activeFiltersCount > 0
99+ height: parent.height
100+ text: root.activeFiltersCount
101+ verticalAlignment: Text.AlignVCenter
102 }
103 }
104
105
106=== added directory 'qml/Dash/Filters'
107=== added file 'qml/Dash/Filters/FilterOptionSelector.qml'
108--- qml/Dash/Filters/FilterOptionSelector.qml 1970-01-01 00:00:00 +0000
109+++ qml/Dash/Filters/FilterOptionSelector.qml 2016-03-14 08:53:26 +0000
110@@ -0,0 +1,89 @@
111+/*
112+ * Copyright (C) 2015 Canonical, Ltd.
113+ *
114+ * This program is free software; you can redistribute it and/or modify
115+ * it under the terms of the GNU General Public License as published by
116+ * the Free Software Foundation; version 3.
117+ *
118+ * This program is distributed in the hope that it will be useful,
119+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
120+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121+ * GNU General Public License for more details.
122+ *
123+ * You should have received a copy of the GNU General Public License
124+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
125+ */
126+
127+import QtQuick 2.4
128+import Ubuntu.Components 1.3
129+import Ubuntu.Components.ListItems 1.3 as ListItems
130+
131+/*! Option Selector Filter Widget. */
132+
133+FilterWidget {
134+ id: root
135+
136+ implicitHeight: expandingItem.height
137+
138+ ListItems.Expandable {
139+ id: expandingItem
140+ objectName: "expandingItem"
141+
142+ expandedHeight: collapsedHeight + column.height
143+ width: parent.width
144+
145+ onClicked: {
146+ expanded = !expanded;
147+ }
148+
149+ Item {
150+ id: holder
151+ anchors.top: parent.top
152+ height: expandingItem.collapsedHeight
153+ width: parent.width
154+
155+ Label {
156+ anchors.left: parent.left
157+ anchors.right: dropDown.left
158+ anchors.verticalCenter: parent.verticalCenter
159+ text: widgetData.label || ""
160+ }
161+
162+ Image {
163+ id: dropDown
164+ height: units.gu(3)
165+ fillMode: Image.PreserveAspectFit
166+ anchors.right: parent.right
167+ anchors.verticalCenter: parent.verticalCenter
168+ source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
169+ }
170+ }
171+
172+ Column {
173+ id: column
174+ anchors.top: holder.bottom
175+ width: parent.width
176+ Repeater {
177+ model: widgetData.options
178+
179+ ListItems.Standard {
180+ text: label
181+ objectName: root.objectName + "label" + index;
182+
183+ Image {
184+ height: units.gu(3)
185+ fillMode: Image.PreserveAspectFit
186+ anchors.right: parent.right
187+ anchors.verticalCenter: parent.verticalCenter
188+ source: "image://theme/tick"
189+ visible: checked
190+ }
191+
192+ onClicked: {
193+ widgetData.options.setChecked(index, !checked);
194+ }
195+ }
196+ }
197+ }
198+ }
199+}
200
201=== added file 'qml/Dash/Filters/FilterWidget.qml'
202--- qml/Dash/Filters/FilterWidget.qml 1970-01-01 00:00:00 +0000
203+++ qml/Dash/Filters/FilterWidget.qml 2016-03-14 08:53:26 +0000
204@@ -0,0 +1,29 @@
205+/*
206+ * Copyright (C) 2015 Canonical, Ltd.
207+ *
208+ * This program is free software; you can redistribute it and/or modify
209+ * it under the terms of the GNU General Public License as published by
210+ * the Free Software Foundation; version 3.
211+ *
212+ * This program is distributed in the hope that it will be useful,
213+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
214+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
215+ * GNU General Public License for more details.
216+ *
217+ * You should have received a copy of the GNU General Public License
218+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
219+ */
220+
221+import QtQuick 2.4
222+
223+/*! Interface for filter widgets. */
224+
225+Item {
226+ //! The widget identifier
227+ property string widgetId
228+
229+ //! Variable used to contain widget's data
230+ property var widgetData
231+
232+ objectName: widgetId
233+}
234
235=== added file 'qml/Dash/Filters/FilterWidgetFactory.qml'
236--- qml/Dash/Filters/FilterWidgetFactory.qml 1970-01-01 00:00:00 +0000
237+++ qml/Dash/Filters/FilterWidgetFactory.qml 2016-03-14 08:53:26 +0000
238@@ -0,0 +1,49 @@
239+/*
240+ * Copyright (C) 2015 Canonical, Ltd.
241+ *
242+ * This program is free software; you can redistribute it and/or modify
243+ * it under the terms of the GNU General Public License as published by
244+ * the Free Software Foundation; version 3.
245+ *
246+ * This program is distributed in the hope that it will be useful,
247+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
248+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
249+ * GNU General Public License for more details.
250+ *
251+ * You should have received a copy of the GNU General Public License
252+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
253+ */
254+
255+import QtQuick 2.4
256+import Unity 0.2
257+
258+//! \brief This component loads the widgets based on widgetType.
259+
260+Loader {
261+ id: root
262+
263+ //! Identifier of the widget.
264+ property string widgetId: ""
265+
266+ //! Type of the widget to display.
267+ property int widgetType
268+
269+ //! Widget data, forwarded to the widget as is.
270+ property var widgetData: null
271+
272+ source: widgetSource
273+
274+ //! \cond private
275+ readonly property url widgetSource: {
276+ switch (widgetType) {
277+ case Filters.OptionSelectorFilter: return "FilterOptionSelector.qml";
278+ default: return "";
279+ }
280+ }
281+ //! \endcond
282+
283+ onLoaded: {
284+ item.widgetId = Qt.binding(function() { return root.widgetId } )
285+ item.widgetData = Qt.binding(function() { return root.widgetData } )
286+ }
287+}
288
289=== added file 'qml/Dash/FiltersPopover.qml'
290--- qml/Dash/FiltersPopover.qml 1970-01-01 00:00:00 +0000
291+++ qml/Dash/FiltersPopover.qml 2016-03-14 08:53:26 +0000
292@@ -0,0 +1,97 @@
293+/*
294+ * Copyright (C) 2013-2015 Canonical, Ltd.
295+ *
296+ * This program is free software; you can redistribute it and/or modify
297+ * it under the terms of the GNU General Public License as published by
298+ * the Free Software Foundation; version 3.
299+ *
300+ * This program is distributed in the hope that it will be useful,
301+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
302+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303+ * GNU General Public License for more details.
304+ *
305+ * You should have received a copy of the GNU General Public License
306+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
307+ */
308+
309+import QtQuick 2.4
310+import Ubuntu.Components 1.3
311+import Ubuntu.Components.Popups 1.3
312+import "Filters" as Filters
313+
314+Popover {
315+ id: root
316+ objectName: "filtersPopover"
317+
318+ Flickable {
319+ id: flickable
320+ anchors {
321+ top: parent.top
322+ left: parent.left
323+ right: parent.right
324+ }
325+ height: {
326+ // Popover doesn't like being 75% or bigger than the screen (counting the "empty" part on top)
327+ var posToRootParent = flickable.mapToItem(null, 0, 0).y;
328+ var threeQuartersParent = root.parent.height * 3 / 4 - posToRootParent - 1;
329+ var parentAndKeyboard = root.parent.height - posToRootParent - (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height + units.gu(3) : 0)
330+ return Math.min(parentAndKeyboard, Math.min(threeQuartersParent, column.height));
331+ }
332+ contentHeight: column.height
333+ contentWidth: width
334+
335+ Column {
336+ id: column
337+ width: parent.width
338+
339+ Item {
340+ width: parent.width
341+ height: resetLabel.height + units.gu(3)
342+
343+ Label {
344+ anchors {
345+ left: parent.left
346+ right: resetLabel.left
347+ margins: units.gu(2)
348+ verticalCenter: parent.verticalCenter
349+ }
350+ text: i18n.tr("Refine your results")
351+ }
352+ Label {
353+ id: resetLabel
354+ anchors {
355+ right: parent.right
356+ rightMargin: units.gu(2)
357+ verticalCenter: parent.verticalCenter
358+ }
359+ text: i18n.tr("Reset")
360+
361+ AbstractButton {
362+ anchors {
363+ fill: parent
364+ rightMargin: units.gu(-2)
365+ leftMargin: units.gu(-2)
366+ topMargin: units.gu(-1)
367+ bottomMargin: units.gu(-1)
368+ }
369+ onClicked: {
370+ scopeView.scope.resetFilters();
371+ }
372+ }
373+ }
374+ }
375+
376+ Repeater {
377+ model: scopeView.scope.filters
378+
379+ delegate: Filters.FilterWidgetFactory {
380+ width: parent.width
381+
382+ widgetId: id
383+ widgetType: type
384+ widgetData: filter
385+ }
386+ }
387+ }
388+ }
389+}
390
391=== modified file 'qml/Dash/GenericScopeView.qml'
392--- qml/Dash/GenericScopeView.qml 2016-03-14 08:53:26 +0000
393+++ qml/Dash/GenericScopeView.qml 2016-03-14 08:53:26 +0000
394@@ -16,6 +16,7 @@
395
396 import QtQuick 2.4
397 import Ubuntu.Components 1.3
398+import Ubuntu.Components.Popups 1.3
399 import "../Components/SearchHistoryModel"
400 import Utils 0.1
401 import Unity 0.2
402@@ -42,6 +43,7 @@
403 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
404 property var holdingList: null
405 property bool wasCurrentOnMoveStart: false
406+ property var filtersPopover: null
407
408 property var scopeStyle: ScopeStyle {
409 style: scope ? scope.customizations : {}
410@@ -121,6 +123,10 @@
411 }
412 categoryView.pageHeader.resetSearch();
413 subPageLoader.closeSubPage();
414+ if (filtersPopover) {
415+ PopupUtils.close(filtersPopover)
416+ scopeView.filtersPopover = null;
417+ }
418 }
419
420 Binding {
421@@ -619,6 +625,8 @@
422 extraPanel: peExtraPanel
423 searchHistory: SearchHistoryModel
424 searchHint: scopeView.scope && scopeView.scope.searchHint || i18n.ctr("Label: Hint for dash search line edit", "Search")
425+ scopeHasFilters: scopeView.scope.filters != null
426+ activeFiltersCount: scopeView.scope.activeFiltersCount
427 showBackButton: scopeView.hasBackAction
428 searchEntryEnabled: true
429 settingsEnabled: scopeView.scope && scopeView.scope.settings && scopeView.scope.settings.count > 0 || false
430@@ -642,6 +650,9 @@
431 openPopup();
432 }
433 }
434+ onShowFiltersPopup: { // item
435+ scopeView.filtersPopover = PopupUtils.open(Qt.resolvedUrl("FiltersPopover.qml"), item, { "contentWidth": scopeView.width - units.gu(2) } );
436+ }
437 }
438
439 PageHeaderExtraPanel {
440
441=== modified file 'qml/Dash/PageHeaderExtraPanel.qml'
442--- qml/Dash/PageHeaderExtraPanel.qml 2016-03-14 08:53:26 +0000
443+++ qml/Dash/PageHeaderExtraPanel.qml 2016-03-14 08:53:26 +0000
444@@ -17,13 +17,14 @@
445 import QtQuick 2.4
446 import Ubuntu.Components 1.3
447 import Ubuntu.Components.ListItems 1.3
448+import "Filters" as Filters
449
450 Item {
451 id: root
452
453 readonly property real searchesHeight: recentSearchesRepeater.count > 0 ? searchColumn.height + recentSearchesLabels.height + recentSearchesLabels.anchors.margins : 0
454
455- implicitHeight: searchesHeight + dashNavigation.implicitHeight
456+ implicitHeight: searchesHeight + dashNavigation.implicitHeight + primaryFilter.height
457
458 // Set by parent
459 property ListModel searchHistory
460@@ -32,7 +33,7 @@
461 property real windowHeight
462
463 // Used by PageHeader
464- readonly property bool hasContents: searchHistory.count > 0 || scope && scope.hasNavigation
465+ readonly property bool hasContents: searchHistory.count > 0 || scope && scope.hasNavigation || scope && scope.primaryNavigationFilter
466
467 signal historyItemClicked(string text)
468 signal dashNavigationLeafClicked()
469@@ -111,6 +112,23 @@
470 onLeafClicked: root.dashNavigationLeafClicked();
471 }
472
473+ Filters.FilterWidgetFactory {
474+ id: primaryFilter
475+ active: scope && !scope.hasNavigation
476+
477+ property var filter: active ? scope.primaryNavigationFilter : null
478+
479+ anchors {
480+ top: recentSearchesRepeater.count > 0 ? searchColumn.bottom : parent.top
481+ left: parent.left
482+ right: parent.right
483+ }
484+
485+ widgetId: filter ? filter.filterId : ""
486+ widgetType: filter ? filter.filterType : -1
487+ widgetData: filter
488+ }
489+
490 // This is outside the item
491 Image {
492 anchors {
493
494=== modified file 'tests/mocks/Unity/fake_filters.cpp'
495--- tests/mocks/Unity/fake_filters.cpp 2016-03-14 08:53:26 +0000
496+++ tests/mocks/Unity/fake_filters.cpp 2016-03-14 08:53:26 +0000
497@@ -22,9 +22,14 @@
498 Filters::Filters(Scope* parent)
499 : unity::shell::scopes::FiltersInterface(parent)
500 {
501- auto optionFilter1 = new FakeOptionSelectorFilter("OSF1", "Tag1", "Which Cake you like More", false, QStringList() << "cheese" << "carrot" << "chocolate", this);
502- auto optionFilter2 = new FakeOptionSelectorFilter("OSF2", "Tag2", "Which Countries have you been to?", true, QStringList() << "Germany" << "UK" << "New Zealand", this);
503- m_filters << optionFilter1 << optionFilter2;
504+ addFilter(new FakeOptionSelectorFilter("OSF1", "Tag1", "Which Cake you like More", false, QStringList() << "cheese" << "carrot" << "chocolate", this));
505+ addFilter(new FakeOptionSelectorFilter("OSF2", "Tag2", "Which Countries have you been to?", true, QStringList() << "Germany" << "UK" << "New Zealand", this));
506+}
507+
508+void Filters::addFilter(FakeOptionSelectorFilter *f)
509+{
510+ connect(f, &FakeOptionSelectorFilter::isActiveChanged, this, &Filters::activeFiltersCountChanged);
511+ m_filters << f;
512 }
513
514 int Filters::rowCount(const QModelIndex &parent) const
515@@ -54,3 +59,12 @@
516 return QVariant();
517 }
518 }
519+
520+int Filters::activeFiltersCount() const
521+{
522+ int active = 0;
523+ Q_FOREACH(FakeOptionSelectorFilter *f, m_filters) {
524+ if (f->isActive()) ++active;
525+ }
526+ return active;
527+}
528
529=== modified file 'tests/mocks/Unity/fake_filters.h'
530--- tests/mocks/Unity/fake_filters.h 2016-03-14 08:53:26 +0000
531+++ tests/mocks/Unity/fake_filters.h 2016-03-14 08:53:26 +0000
532@@ -18,9 +18,9 @@
533 #define FAKE_FILTERS_H
534
535 #include <unity/shell/scopes/FiltersInterface.h>
536-#include <unity/shell/scopes/FilterBaseInterface.h>
537
538 class Scope;
539+class FakeOptionSelectorFilter;
540
541 class Filters : public unity::shell::scopes::FiltersInterface
542 {
543@@ -32,8 +32,15 @@
544 int rowCount(const QModelIndex &parent) const override;
545 QVariant data(const QModelIndex &index, int role) const override;
546
547+ int activeFiltersCount() const;
548+
549+Q_SIGNALS:
550+ void activeFiltersCountChanged();
551+
552 private:
553- QVector<unity::shell::scopes::FilterBaseInterface*> m_filters;
554+ void addFilter(FakeOptionSelectorFilter* f);
555+
556+ QVector<FakeOptionSelectorFilter*> m_filters;
557 };
558
559 #endif
560
561=== modified file 'tests/mocks/Unity/fake_optionselectorfilter.cpp'
562--- tests/mocks/Unity/fake_optionselectorfilter.cpp 2016-03-14 08:53:26 +0000
563+++ tests/mocks/Unity/fake_optionselectorfilter.cpp 2016-03-14 08:53:26 +0000
564@@ -26,6 +26,7 @@
565 m_multiSelect(multiselect)
566 {
567 m_options = new FakeOptionSelectorOptions(optionLabels, this);
568+ connect(m_options, &FakeOptionSelectorOptions::anyCheckedChanged, this, &FakeOptionSelectorFilter::isActiveChanged);
569 }
570
571 QString FakeOptionSelectorFilter::filterId() const
572@@ -57,3 +58,8 @@
573 {
574 return m_options;
575 }
576+
577+bool FakeOptionSelectorFilter::isActive() const
578+{
579+ return m_options->anyChecked();
580+}
581
582=== modified file 'tests/mocks/Unity/fake_optionselectorfilter.h'
583--- tests/mocks/Unity/fake_optionselectorfilter.h 2016-03-14 08:53:26 +0000
584+++ tests/mocks/Unity/fake_optionselectorfilter.h 2016-03-14 08:53:26 +0000
585@@ -19,6 +19,8 @@
586
587 #include <unity/shell/scopes/OptionSelectorFilterInterface.h>
588
589+class FakeOptionSelectorOptions;
590+
591 class FakeOptionSelectorFilter : public unity::shell::scopes::OptionSelectorFilterInterface
592 {
593 Q_OBJECT
594@@ -33,13 +35,18 @@
595 bool multiSelect() const override;
596 unity::shell::scopes::OptionSelectorOptionsInterface* options() const override;
597
598+ bool isActive() const;
599+
600+Q_SIGNALS:
601+ void isActiveChanged();
602+
603 private:
604 QString m_filterId;
605 QString m_filterTag;
606 QString m_title;
607 QString m_label;
608 bool m_multiSelect;
609- unity::shell::scopes::OptionSelectorOptionsInterface* m_options;
610+ FakeOptionSelectorOptions* m_options;
611 };
612
613 #endif
614
615=== modified file 'tests/mocks/Unity/fake_optionselectoroptions.cpp'
616--- tests/mocks/Unity/fake_optionselectoroptions.cpp 2016-03-14 08:53:26 +0000
617+++ tests/mocks/Unity/fake_optionselectoroptions.cpp 2016-03-14 08:53:26 +0000
618@@ -56,10 +56,22 @@
619 void FakeOptionSelectorOptions::setChecked(int row, bool checked)
620 {
621 if (checked) {
622+ if (!static_cast<FakeOptionSelectorFilter*>(parent())->multiSelect()) {
623+ if (!m_checkedIndexes.isEmpty()) {
624+ setChecked(*m_checkedIndexes.begin(), false);
625+ }
626+ Q_ASSERT(m_checkedIndexes.isEmpty());
627+ }
628 m_checkedIndexes << row;
629 } else {
630 m_checkedIndexes.remove(row);
631 }
632 const QModelIndex idx = index(row, 0);
633 Q_EMIT dataChanged(idx, idx, QVector<int>() << RoleOptionChecked);
634+ Q_EMIT anyCheckedChanged();
635+}
636+
637+bool FakeOptionSelectorOptions::anyChecked() const
638+{
639+ return !m_checkedIndexes.isEmpty();
640 }
641
642=== modified file 'tests/mocks/Unity/fake_optionselectoroptions.h'
643--- tests/mocks/Unity/fake_optionselectoroptions.h 2016-03-14 08:53:26 +0000
644+++ tests/mocks/Unity/fake_optionselectoroptions.h 2016-03-14 08:53:26 +0000
645@@ -34,6 +34,11 @@
646 QVariant data(const QModelIndex &index, int role) const override;
647 Q_INVOKABLE void setChecked(int index, bool checked) override;
648
649+ bool anyChecked() const;
650+
651+Q_SIGNALS:
652+ void anyCheckedChanged();
653+
654 private:
655 QStringList m_optionLabels;
656 QSet<int> m_checkedIndexes;
657
658=== modified file 'tests/mocks/Unity/fake_scope.cpp'
659--- tests/mocks/Unity/fake_scope.cpp 2016-03-14 08:53:26 +0000
660+++ tests/mocks/Unity/fake_scope.cpp 2016-03-14 08:53:26 +0000
661@@ -21,6 +21,7 @@
662
663 #include "fake_filters.h"
664 #include "fake_navigation.h"
665+#include "fake_optionselectorfilter.h"
666 #include "fake_resultsmodel.h"
667 #include "fake_scopes.h"
668 #include "fake_settingsmodel.h"
669@@ -38,6 +39,8 @@
670 , m_searching(false)
671 , m_favorite(favorite)
672 , m_isActive(false)
673+ , m_hasNavigation(true)
674+ , m_hasPrimaryFilter(true)
675 , m_currentNavigationId("root")
676 , m_previewRendererName("preview-generic")
677 , m_categories(new Categories(categories, this))
678@@ -46,6 +49,8 @@
679 , m_filters(new Filters(this))
680 , m_returnNullPreview(returnNullPreview)
681 {
682+ m_primaryNavigationFilter = new FakeOptionSelectorFilter("OSF3", "PFTag", "Which food you like More", false, QStringList() << "meat" << "vegetables", this);
683+ connect(m_filters, &Filters::activeFiltersCountChanged, this, &Scope::activeFiltersCountChanged);
684 }
685
686 QString Scope::id() const
687@@ -230,7 +235,13 @@
688
689 bool Scope::hasNavigation() const
690 {
691- return true;
692+ return m_hasNavigation;
693+}
694+
695+void Scope::setHasNavigation(bool hasNavigation)
696+{
697+ m_hasNavigation = hasNavigation;
698+ Q_EMIT hasNavigationChanged();
699 }
700
701 Scope::Status Scope::status() const
702@@ -303,7 +314,7 @@
703
704 unity::shell::scopes::FilterBaseInterface* Scope::primaryNavigationFilter() const
705 {
706- return nullptr;
707+ return m_hasPrimaryFilter ? m_primaryNavigationFilter : nullptr;
708 }
709
710 unity::shell::scopes::FiltersInterface* Scope::filters() const
711@@ -321,8 +332,7 @@
712
713 int Scope::activeFiltersCount() const
714 {
715- // TODO
716- return 0;
717+ return m_filters->activeFiltersCount();
718 }
719
720 void Scope::resetPrimaryNavigationTag()
721@@ -332,6 +342,12 @@
722 }
723 }
724
725+void Scope::setHasPrimaryFilter(bool hasPrimaryFilter)
726+{
727+ m_hasPrimaryFilter = hasPrimaryFilter;
728+ Q_EMIT primaryNavigationFilterChanged();
729+}
730+
731 void Scope::performQuery(const QString& query)
732 {
733 Q_EMIT queryPerformed(query);
734
735=== modified file 'tests/mocks/Unity/fake_scope.h'
736--- tests/mocks/Unity/fake_scope.h 2016-03-14 08:53:26 +0000
737+++ tests/mocks/Unity/fake_scope.h 2016-03-14 08:53:26 +0000
738@@ -24,7 +24,9 @@
739
740 #include <QTimer>
741
742+class Filters;
743 class Scopes;
744+class FakeOptionSelectorFilter;
745
746 class Scope : public unity::shell::scopes::ScopeInterface
747 {
748@@ -70,12 +72,14 @@
749 bool hasNavigation() const override;
750 Q_INVOKABLE unity::shell::scopes::NavigationInterface* getNavigation(QString const& navigationId) override;
751 Q_INVOKABLE void setNavigationState(const QString &navigationId) override;
752+ Q_INVOKABLE void setHasNavigation(bool hasNavigation); // This is not invokable in the Interface, here for testing benefits
753
754 unity::shell::scopes::FilterBaseInterface* primaryNavigationFilter() const override;
755 unity::shell::scopes::FiltersInterface* filters() const override;
756 QString primaryNavigationTag() const override;
757 int activeFiltersCount() const override;
758 Q_INVOKABLE void resetPrimaryNavigationTag() override;
759+ Q_INVOKABLE void setHasPrimaryFilter(bool hasPrimaryFilter); // This is not invokable in the Interface, here for testing benefits
760
761 void performQuery(const QString& query) override;
762
763@@ -105,6 +109,8 @@
764 bool m_searching;
765 bool m_favorite;
766 bool m_isActive;
767+ bool m_hasNavigation;
768+ bool m_hasPrimaryFilter;
769 QString m_currentNavigationId;
770
771 QString m_previewRendererName;
772@@ -112,7 +118,8 @@
773 unity::shell::scopes::CategoriesInterface* m_categories;
774 unity::shell::scopes::ScopeInterface* m_openScope;
775 unity::shell::scopes::SettingsModelInterface* m_settings;
776- unity::shell::scopes::FiltersInterface* m_filters;
777+ Filters* m_filters;
778+ FakeOptionSelectorFilter* m_primaryNavigationFilter;
779
780 bool m_returnNullPreview;
781 };
782
783=== modified file 'tests/mocks/Unity/fake_unity_plugin.cpp'
784--- tests/mocks/Unity/fake_unity_plugin.cpp 2016-02-19 11:42:42 +0000
785+++ tests/mocks/Unity/fake_unity_plugin.cpp 2016-03-14 08:53:26 +0000
786@@ -22,7 +22,9 @@
787 // local
788 #include "fake_scopes.h"
789 #include "fake_categories.h"
790+#include "fake_filters.h"
791 #include "fake_navigation.h"
792+#include "fake_optionselectoroptions.h"
793 #include "fake_previewmodel.h"
794 #include "fake_previewwidgetmodel.h"
795 #include "fake_resultsmodel.h"
796@@ -52,4 +54,7 @@
797 qmlRegisterType<ResultsModel>(uri, 0, 2, "FakeResultsModel");
798 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");
799 qmlRegisterUncreatableType<PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
800+ qmlRegisterUncreatableType<unity::shell::scopes::FiltersInterface>(uri, 0, 2, "Filters", "Can't create Filters object in QML. Get them from Scope instance.");
801+ qmlRegisterUncreatableType<unity::shell::scopes::FilterBaseInterface>(uri, 0, 2, "Filter", "Can't create Filter object in QML. Get them from Scope instance.");
802+ qmlRegisterUncreatableType<unity::shell::scopes::OptionSelectorOptionsInterface>(uri, 0, 2, "OptionSelectorOptions", "Can't create Filters object in QML. Get them from OptionSelector instance.");
803 }
804
805=== modified file 'tests/qmltests/CMakeLists.txt'
806--- tests/qmltests/CMakeLists.txt 2016-03-10 22:41:38 +0000
807+++ tests/qmltests/CMakeLists.txt 2016-03-14 08:53:26 +0000
808@@ -53,6 +53,8 @@
809 add_unity8_qmltest(Dash/Previews PreviewVideoPlayback)
810 add_unity8_qmltest(Dash/Previews PreviewWidgetFactory)
811 add_unity8_qmltest(Dash/Previews PreviewZoomableImage)
812+add_unity8_qmltest(Dash/Filters FilterOptionSelector)
813+add_unity8_qmltest(Dash/Filters FilterWidgetFactory)
814 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingBoolean)
815 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingList)
816 add_unity8_qmltest(Dash/ScopeSettings ScopeSettingNumber)
817
818=== added directory 'tests/qmltests/Dash/Filters'
819=== added file 'tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml'
820--- tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml 1970-01-01 00:00:00 +0000
821+++ tests/qmltests/Dash/Filters/tst_FilterOptionSelector.qml 2016-03-14 08:53:26 +0000
822@@ -0,0 +1,96 @@
823+/*
824+ * Copyright 2015 Canonical Ltd.
825+ *
826+ * This program is free software; you can redistribute it and/or modify
827+ * it under the terms of the GNU General Public License as published by
828+ * the Free Software Foundation; version 3.
829+ *
830+ * This program is distributed in the hope that it will be useful,
831+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
832+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
833+ * GNU General Public License for more details.
834+ *
835+ * You should have received a copy of the GNU General Public License
836+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
837+ */
838+
839+import QtQuick 2.4
840+import QtTest 1.0
841+import "../../../../qml/Dash/Filters"
842+import Unity.Test 0.1 as UT
843+import Unity 0.2
844+import Ubuntu.Components 1.3
845+
846+Rectangle {
847+ id: root
848+ width: units.gu(60)
849+ height: units.gu(80)
850+ color: theme.palette.selected.background
851+
852+ property var singleSelectionWidgetData
853+
854+ ListModel {
855+ id: optionsSingleSelect
856+ ListElement {
857+ label: "A"
858+ checked: false
859+ }
860+ ListElement {
861+ label: "B"
862+ checked: false
863+ }
864+ ListElement {
865+ label: "C"
866+ checked: false
867+ }
868+
869+ function setChecked(index, checked) {
870+ for (var i = 0; i < optionsSingleSelect.count; ++i)
871+ optionsSingleSelect.setProperty(i, "checked", false);
872+ optionsSingleSelect.setProperty(index, "checked", checked);
873+ }
874+ }
875+
876+ Component.onCompleted: {
877+ singleSelectionWidgetData = { label: "Hola", options: optionsSingleSelect }
878+ }
879+
880+ FilterWidgetFactory {
881+ id: factory
882+ widgetId: "testOptionSelectorFilter"
883+ widgetType: Filters.OptionSelectorFilter
884+ widgetData: singleSelectionWidgetData
885+ anchors {
886+ left: parent.left
887+ right: parent.right
888+ }
889+ }
890+
891+ UT.UnityTestCase {
892+ name: "FilterOptionSelector"
893+ when: windowShown
894+
895+ function test_optionSelector() {
896+ var expandingItem = findChild(factory, "expandingItem");
897+ // Open the selector
898+ mouseClick(factory);
899+ // wait for it to stop growing
900+ tryCompare(factory, "implicitHeight", expandingItem.expandedHeight);
901+
902+ // Check the first option
903+ var option0 = findChild(factory, "testOptionSelectorFilterlabel0");
904+ mouseClick(option0);
905+ verify(optionsSingleSelect.get(0).checked);
906+
907+ // Check the second option
908+ var option1 = findChild(factory, "testOptionSelectorFilterlabel1");
909+ mouseClick(option1);
910+ verify(!optionsSingleSelect.get(0).checked);
911+ verify(optionsSingleSelect.get(1).checked);
912+
913+ // Uncheck the second option
914+ mouseClick(option1);
915+ verify(!optionsSingleSelect.get(1).checked);
916+ }
917+ }
918+}
919
920=== added file 'tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml'
921--- tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml 1970-01-01 00:00:00 +0000
922+++ tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml 2016-03-14 08:53:26 +0000
923@@ -0,0 +1,55 @@
924+/*
925+ * Copyright 2015 Canonical Ltd.
926+ *
927+ * This program is free software; you can redistribute it and/or modify
928+ * it under the terms of the GNU General Public License as published by
929+ * the Free Software Foundation; version 3.
930+ *
931+ * This program is distributed in the hope that it will be useful,
932+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
933+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
934+ * GNU General Public License for more details.
935+ *
936+ * You should have received a copy of the GNU General Public License
937+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
938+ */
939+
940+import QtQuick 2.4
941+import QtTest 1.0
942+import "../../../../qml/Dash/Filters"
943+import Unity.Test 0.1 as UT
944+import Unity 0.2
945+import Ubuntu.Components 1.3
946+
947+Rectangle {
948+ id: root
949+ width: units.gu(60)
950+ height: units.gu(80)
951+ color: theme.palette.selected.background
952+
953+ FilterWidgetFactory {
954+ id: factory
955+ anchors {
956+ left: parent.left
957+ right: parent.right
958+ }
959+ }
960+
961+ UT.UnityTestCase {
962+ name: "FilterWidgetFactory"
963+ when: windowShown
964+
965+ function test_mapping_data() {
966+ return [
967+ { tag: "OptionSelector", type: Filters.OptionSelectorFilter, source: "FilterOptionSelector.qml" }
968+ ];
969+ }
970+
971+ function test_mapping(data) {
972+ factory.widgetData = {};
973+ factory.widgetType = data.type;
974+
975+ verify((String(factory.source)).indexOf(data.source) != -1);
976+ }
977+ }
978+}
979
980=== modified file 'tests/qmltests/Dash/tst_DashContent.qml'
981--- tests/qmltests/Dash/tst_DashContent.qml 2016-03-14 08:53:26 +0000
982+++ tests/qmltests/Dash/tst_DashContent.qml 2016-03-14 08:53:26 +0000
983@@ -513,6 +513,52 @@
984 tryCompare(peExtraPanel, "visible", true);
985 }
986
987+ function test_navigationShowFilterButton() {
988+ goToSecondLevel();
989+
990+ var dashContentList = findChild(dashContent, "dashContentList");
991+ var searchTextField = findChild(dashContentList.currentItem, "searchTextField");
992+
993+ verify(!searchTextField.focus)
994+ verify(searchTextField.clearIsSettings)
995+
996+ mouseClick(searchTextField)
997+
998+ verify(searchTextField.focus)
999+ verify(!searchTextField.clearIsSettings)
1000+ }
1001+
1002+ function test_navigationShowFilterPopup() {
1003+ goToSecondLevel();
1004+
1005+ var dashContentList = findChild(dashContent, "dashContentList");
1006+ var searchTextField = findChild(dashContentList.currentItem, "searchTextField");
1007+ var clearIcon = findChild(searchTextField, "clearIcon");
1008+
1009+ var filtersPopover = findChild(shell, "filtersPopover")
1010+ verify(!filtersPopover);
1011+
1012+ mouseClick(clearIcon);
1013+
1014+ filtersPopover = findChild(shell, "filtersPopover")
1015+
1016+ verify(filtersPopover);
1017+ }
1018+
1019+ function test_primaryFilter() {
1020+ var dashContentList = findChild(dashContent, "dashContentList");
1021+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
1022+ dashContentList.currentItem.item.scope.setHasNavigation(false);
1023+ var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
1024+ var searchButton = findChild(dashContentList.currentItem, "search_action_button");
1025+
1026+ compare(peExtraPanel.visible, false);
1027+ mouseClick(searchButton);
1028+ tryCompare(peExtraPanel, "visible", true);
1029+
1030+ tryCompareFunction(function() { return findChild(peExtraPanel, "OSF3") != null; }, true);
1031+ }
1032+
1033 function test_searchHint() {
1034 var dashContentList = findChild(dashContent, "dashContentList");
1035 verify(dashContentList !== null);

Subscribers

People subscribed via source and target branches