on xenial help sign in
Expandable Filter Widget
f61e91cAlbert Astals Cid
debian/control+2−2
@@ -30,7 +30,7 @@ Build-Depends: cmake,
30 libqt5xmlpatterns5-dev,
30 libqt5xmlpatterns5-dev,
31 libsystemsettings-dev,
31 libsystemsettings-dev,
32 libudev-dev,
32 libudev-dev,
33− libunity-api-dev (>= 7.110),
33+ libunity-api-dev (>= 7.111),
34 libusermetricsoutput1-dev,
34 libusermetricsoutput1-dev,
35# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
35# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
36 libx11-dev[!armhf],
36 libx11-dev[!armhf],
@@ -136,7 +136,7 @@ Depends: qml-module-qtquick-layouts,
136 unity-application-impl-15,
136 unity-application-impl-15,
137 unity-notifications-impl-3,
137 unity-notifications-impl-3,
138 unity-plugin-scopes | unity-scopes-impl,
138 unity-plugin-scopes | unity-scopes-impl,
139− unity-scopes-impl-11,
139+ unity-scopes-impl-12,
140 unity8-fake-env | unity-application-impl,
140 unity8-fake-env | unity-application-impl,
141 ${misc:Depends},
141 ${misc:Depends},
142Breaks: unity8 (<< 7.86),
142Breaks: unity8 (<< 7.86),
qml/Dash/Filters/FilterExpandableWidget.qmlnew file+96
@@ -0,0 +1,96 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Ubuntu.Components.ListItems 1.3 as ListItems
20+
21+/*! Expandable Filter Widget. */
22+
23+FilterWidget {
24+ id: root
25+
26+ showsTitleOnItsOwn: true
27+
28+ implicitHeight: expandingItem.height
29+
30+ ListItems.Expandable {
31+ id: expandingItem
32+ objectName: "expandingItem"
33+
34+ expandedHeight: collapsedHeight + column.height
35+ anchors.left: parent.left
36+ anchors.right: parent.right
37+ showDivider: false
38+
39+ onClicked: {
40+ expanded = !expanded;
41+ forceActiveFocus();
42+ }
43+ __contentsMargins: 0
44+
45+ Item {
46+ id: titleHolder
47+ anchors.top: parent.top
48+ height: expandingItem.collapsedHeight
49+ anchors.left: parent.left
50+ anchors.right: parent.right
51+
52+ Label {
53+ anchors.left: parent.left
54+ anchors.leftMargin: units.gu(2)
55+ anchors.right: dropDown.left
56+ anchors.verticalCenter: parent.verticalCenter
57+ text: widgetData.title || ""
58+ }
59+
60+ Image {
61+ id: dropDown
62+ height: units.gu(3)
63+ fillMode: Image.PreserveAspectFit
64+ anchors.right: parent.right
65+ anchors.rightMargin: units.gu(2)
66+ anchors.verticalCenter: parent.verticalCenter
67+ source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
68+ }
69+ }
70+
71+ Column {
72+ id: column
73+ anchors.left: parent.left
74+ anchors.right: parent.right
75+ anchors.top: titleHolder.bottom
76+
77+ Repeater {
78+ id: repeater
79+ model: widgetData.filters
80+
81+ delegate: FilterWidgetFactory {
82+ width: parent.width
83+
84+ widgetId: id
85+ widgetType: type
86+ widgetData: filter
87+
88+ ListItems.ThinDivider {
89+ anchors.bottom: parent.bottom
90+ visible: index !== repeater.count - 1
91+ }
92+ }
93+ }
94+ }
95+ }
96+}
qml/Dash/Filters/FilterWidget.qml+3
@@ -25,5 +25,8 @@ Item {
25 //! Variable used to contain widget's data
25 //! Variable used to contain widget's data
26 property var widgetData: null
26 property var widgetData: null
27
27
28+ //! Does the widget show the title inside?
29+ property bool showsTitleOnItsOwn: false
30+
28 objectName: widgetId
31 objectName: widgetId
29}
32}
qml/Dash/Filters/FilterWidgetFactory.qml+2−1
@@ -35,7 +35,7 @@ Item {
35
35
36 Label {
36 Label {
37 id: title
37 id: title
38− text: widgetData ? widgetData.title : ""
38+ text: widgetData && !(loader.item && loader.item.showsTitleOnItsOwn) ? widgetData.title : ""
39 height: text != "" ? implicitHeight : 0
39 height: text != "" ? implicitHeight : 0
40
40
41 anchors {
41 anchors {
@@ -68,6 +68,7 @@ Item {
68 case Filters.OptionSelectorFilter: return "FilterOptionSelector.qml";
68 case Filters.OptionSelectorFilter: return "FilterOptionSelector.qml";
69 case Filters.RangeInputFilter: return "FilterRangeInput.qml";
69 case Filters.RangeInputFilter: return "FilterRangeInput.qml";
70 case Filters.ValueSliderFilter: return "FilterValueSlider.qml";
70 case Filters.ValueSliderFilter: return "FilterValueSlider.qml";
71+ case Filters.ExpandableFilterWidget: return "FilterExpandableWidget.qml";
71 default: return "";
72 default: return "";
72 }
73 }
73 }
74 }
tests/mocks/Unity/CMakeLists.txt+3−1
@@ -8,7 +8,7 @@ add_subdirectory(DashCommunicator)
8add_subdirectory(Screens)
8add_subdirectory(Screens)
9
9
10pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
10pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
11−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=11)
11+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=12)
12
12
13include_directories(
13include_directories(
14 ${CMAKE_CURRENT_BINARY_DIR}
14 ${CMAKE_CURRENT_BINARY_DIR}
@@ -34,6 +34,7 @@ set(UnityQML_SOURCES
34 fake_optionselectoroptions.cpp
34 fake_optionselectoroptions.cpp
35 fake_rangeinputfilter.cpp
35 fake_rangeinputfilter.cpp
36 fake_valuesliderfilter.cpp
36 fake_valuesliderfilter.cpp
37+ fake_expandablefilterwidget.cpp
37 fake_valueslidervalues.cpp
38 fake_valueslidervalues.cpp
38 fake_unity_plugin.cpp
39 fake_unity_plugin.cpp
39 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
40 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
@@ -51,6 +52,7 @@ set(UnityQML_SOURCES
51 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
52 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
52 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderFilterInterface.h
53 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderFilterInterface.h
53 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderValuesInterface.h
54 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ValueSliderValuesInterface.h
55+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/ExpandableFilterWidgetInterface.h
54)
56)
55
57
56# Workaround for gcc failure LP: #1417664
58# Workaround for gcc failure LP: #1417664
tests/mocks/Unity/fake_expandablefilterwidget.cppnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_expandablefilterwidget.h"
18+
19+#include "fake_filters.h"
20+
21+FakeExpandbleFilterWidget::FakeExpandbleFilterWidget(const QString &id, const QString &tag, const QString &title, QObject* parent)
22+ : unity::shell::scopes::ExpandableFilterWidgetInterface(parent)
23+ , m_filterId(id)
24+ , m_filterTag(tag)
25+ , m_title(title)
26+{
27+ m_filters = new Filters(this);
28+}
29+
30+QString FakeExpandbleFilterWidget::filterId() const
31+{
32+ return m_filterId;
33+}
34+
35+QString FakeExpandbleFilterWidget::filterTag() const
36+{
37+ return m_filterTag;
38+}
39+
40+QString FakeExpandbleFilterWidget::title() const
41+{
42+ return m_title;
43+}
44+
45+unity::shell::scopes::FiltersInterface* FakeExpandbleFilterWidget::filters() const
46+{
47+ return m_filters;
48+}
49+
50+void FakeExpandbleFilterWidget::addFilter(unity::shell::scopes::FilterBaseInterface* f)
51+{
52+ m_filters->addFilter(f);
53+}
tests/mocks/Unity/fake_expandablefilterwidget.hnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_EXPANDABLEFILTERWIDGET_H
18+#define FAKE_EXPANDABLEFILTERWIDGET_H
19+
20+#include <unity/shell/scopes/ExpandableFilterWidgetInterface.h>
21+
22+class Filters;
23+
24+class FakeExpandbleFilterWidget : public unity::shell::scopes::ExpandableFilterWidgetInterface
25+{
26+ Q_OBJECT
27+
28+public:
29+ FakeExpandbleFilterWidget(const QString &id, const QString &tag, const QString &title, QObject* parent);
30+
31+ QString filterId() const override;
32+ QString filterTag() const override;
33+ QString title() const override;
34+
35+ unity::shell::scopes::FiltersInterface* filters() const override;
36+
37+ // mock API
38+ void addFilter(unity::shell::scopes::FilterBaseInterface* f);
39+
40+private:
41+ QString m_filterId;
42+ QString m_filterTag;
43+ QString m_title;
44+
45+ Filters *m_filters;
46+};
47+
48+#endif
tests/mocks/Unity/fake_filters.cpp+19−8
@@ -19,10 +19,14 @@
19#include "fake_optionselectorfilter.h"
19#include "fake_optionselectorfilter.h"
20#include "fake_rangeinputfilter.h"
20#include "fake_rangeinputfilter.h"
21#include "fake_valuesliderfilter.h"
21#include "fake_valuesliderfilter.h"
22−#include "fake_scope.h"
22+#include "fake_expandablefilterwidget.h"
23
23
24−Filters::Filters(Scope* parent)
24+Filters::Filters(QObject* parent)
25 : unity::shell::scopes::FiltersInterface(parent)
25 : unity::shell::scopes::FiltersInterface(parent)
26+{
27+}
28+
29+void Filters::addFakeFilters()
26{
30{
27 addFilter(new FakeOptionSelectorFilter("OSF1", "Tag1", "Which Cake you like More", false, QStringList() << "cheese" << "carrot" << "chocolate", this));
31 addFilter(new FakeOptionSelectorFilter("OSF1", "Tag1", "Which Cake you like More", false, QStringList() << "cheese" << "carrot" << "chocolate", this));
28
32
@@ -41,6 +45,11 @@ Filters::Filters(Scope* parent)
41 addFilter(vsf);
45 addFilter(vsf);
42
46
43 addFilter(new FakeOptionSelectorFilter("OSF2", "Tag2", "Which Countries have you been to?", true, QStringList() << "Germany" << "UK" << "New Zealand", this));
47 addFilter(new FakeOptionSelectorFilter("OSF2", "Tag2", "Which Countries have you been to?", true, QStringList() << "Germany" << "UK" << "New Zealand", this));
48+
49+ auto expandableFilter = new FakeExpandbleFilterWidget("EFW", "Tag5", "Advanced Settings", this);
50+ expandableFilter->addFilter(new FakeValueSliderFilter("VS2", "Tag6", 15, 10, 150, labels, this));
51+
52+ addFilter(expandableFilter);
44}
53}
45
54
46void Filters::addFilter(unity::shell::scopes::FilterBaseInterface *f)
55void Filters::addFilter(unity::shell::scopes::FilterBaseInterface *f)
@@ -58,9 +67,10 @@ void Filters::addFilter(unity::shell::scopes::FilterBaseInterface *f)
58 }
67 }
59 break;
68 break;
60
69
61− case FiltersInterface::ValueSliderFilter: {
70+ case FiltersInterface::ExpandableFilterWidget:
71+ // Can't be active
72+ case FiltersInterface::ValueSliderFilter:
62− // Not counting value slider as active in the mock
73+ // Not counting value as active in the mock
63− }
64
74
65 case Invalid:
75 case Invalid:
66 break;
76 break;
@@ -113,9 +123,10 @@ int Filters::activeFiltersCount() const
113 }
123 }
114 break;
124 break;
115
125
116− case FiltersInterface::ValueSliderFilter: {
126+ case FiltersInterface::ExpandableFilterWidget:
127+ // Can't be active
128+ case FiltersInterface::ValueSliderFilter:
117− // Not counting value slider as active in the mock
129+ // Not counting value as active in the mock
118− }
119
130
120 case Invalid:
131 case Invalid:
121 break;
132 break;
tests/mocks/Unity/fake_filters.h+4−4
@@ -20,25 +20,25 @@
20#include <unity/shell/scopes/FiltersInterface.h>
20#include <unity/shell/scopes/FiltersInterface.h>
21#include <unity/shell/scopes/FilterBaseInterface.h>
21#include <unity/shell/scopes/FilterBaseInterface.h>
22
22
23−class Scope;
24−
25class Filters : public unity::shell::scopes::FiltersInterface
23class Filters : public unity::shell::scopes::FiltersInterface
26{
24{
27 Q_OBJECT
25 Q_OBJECT
28
26
29public:
27public:
30− Filters(Scope* parent);
28+ Filters(QObject* parent);
31
29
32 int rowCount(const QModelIndex &parent) const override;
30 int rowCount(const QModelIndex &parent) const override;
33 QVariant data(const QModelIndex &index, int role) const override;
31 QVariant data(const QModelIndex &index, int role) const override;
34
32
33+ // Mock API
35 int activeFiltersCount() const;
34 int activeFiltersCount() const;
35+ void addFakeFilters();
36+ void addFilter(unity::shell::scopes::FilterBaseInterface* f);
36
37
37Q_SIGNALS:
38Q_SIGNALS:
38 void activeFiltersCountChanged();
39 void activeFiltersCountChanged();
39
40
40private:
41private:
41− void addFilter(unity::shell::scopes::FilterBaseInterface* f);
42
42
43 QVector<unity::shell::scopes::FilterBaseInterface*> m_filters;
43 QVector<unity::shell::scopes::FilterBaseInterface*> m_filters;
44};
44};
tests/mocks/Unity/fake_scope.cpp+2
@@ -49,6 +49,8 @@ Scope::Scope(QString const& id, QString const& name, bool favorite, Scopes* pare
49 , m_filters(new Filters(this))
49 , m_filters(new Filters(this))
50 , m_returnNullPreview(returnNullPreview)
50 , m_returnNullPreview(returnNullPreview)
51{
51{
52+ m_filters->addFakeFilters();
53+
52 m_primaryNavigationFilter = new FakeOptionSelectorFilter("OSF3", "PFTag", "Which food you like More", false, QStringList() << "meat" << "vegetables", this);
54 m_primaryNavigationFilter = new FakeOptionSelectorFilter("OSF3", "PFTag", "Which food you like More", false, QStringList() << "meat" << "vegetables", this);
53 connect(m_filters, &Filters::activeFiltersCountChanged, this, &Scope::activeFiltersCountChanged);
55 connect(m_filters, &Filters::activeFiltersCountChanged, this, &Scope::activeFiltersCountChanged);
54}
56}
tests/qmltests/CMakeLists.txt+1
@@ -57,6 +57,7 @@ add_unity8_qmltest(Dash/Filters FilterOptionSelector)
57add_unity8_qmltest(Dash/Filters FilterRangeInput)
57add_unity8_qmltest(Dash/Filters FilterRangeInput)
58add_unity8_qmltest(Dash/Filters FilterValueSlider)
58add_unity8_qmltest(Dash/Filters FilterValueSlider)
59add_unity8_qmltest(Dash/Filters FilterWidgetFactory)
59add_unity8_qmltest(Dash/Filters FilterWidgetFactory)
60+add_unity8_qmltest(Dash/Filters FilterExpandableWidget)
60add_unity8_qmltest(Dash/ScopeSettings ScopeSettingBoolean)
61add_unity8_qmltest(Dash/ScopeSettings ScopeSettingBoolean)
61add_unity8_qmltest(Dash/ScopeSettings ScopeSettingList)
62add_unity8_qmltest(Dash/ScopeSettings ScopeSettingList)
62add_unity8_qmltest(Dash/ScopeSettings ScopeSettingNumber)
63add_unity8_qmltest(Dash/ScopeSettings ScopeSettingNumber)
tests/qmltests/Dash/Filters/tst_FilterExpandableWidget.qmlnew file+99
@@ -0,0 +1,99 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtTest 1.0
19+import Ubuntu.Components 1.3
20+import "../../../../qml/Dash/Filters"
21+import Unity.Test 0.1 as UT
22+import Unity 0.2
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(60)
27+ height: units.gu(80)
28+ color: theme.palette.selected.background
29+
30+ QtObject {
31+ id: widgetData1
32+
33+ property string title: "Expand me!"
34+ property ListModel filters: ListModel {
35+ dynamicRoles: true
36+ }
37+ }
38+
39+ QtObject {
40+ id: sliderObject
41+
42+ property string title
43+ property int value: 30
44+ property int minValue: 10
45+ property int maxValue: 150
46+
47+ property ListModel values: ListModel {
48+ ListElement {
49+ value: 10
50+ label: "10"
51+ }
52+ ListElement {
53+ value: 30
54+ label: "30"
55+ }
56+ ListElement {
57+ value: 100
58+ label: "100"
59+ }
60+ ListElement {
61+ value: 150
62+ label: "150"
63+ }
64+ }
65+ }
66+
67+ Component.onCompleted: {
68+ widgetData1.filters.append({"type": Filters.ValueSliderFilter, "filter": sliderObject, id: "trololo"});
69+ }
70+
71+ FilterWidgetFactory {
72+ id: factory
73+ widgetId: "testExpandableWidget"
74+ widgetType: Filters.ExpandableFilterWidget
75+ widgetData: widgetData1
76+ anchors {
77+ left: parent.left
78+ right: parent.right
79+ }
80+ height: implicitHeight
81+ clip: true
82+ }
83+
84+ UT.UnityTestCase {
85+ name: "FilterExpandableWidget"
86+ when: windowShown
87+
88+ function test_expandedChanges() {
89+ var expandingItem = findChild(factory, "expandingItem");
90+ verify(!expandingItem.expanded)
91+ compare(expandingItem.height, expandingItem.collapsedHeight)
92+ mouseClick(expandingItem);
93+ verify(expandingItem.expanded)
94+ tryCompare(expandingItem, "height", expandingItem.expandedHeight)
95+ verify(expandingItem.collapsedHeight < expandingItem.expandedHeight / 2);
96+ }
97+
98+ }
99+}
tests/qmltests/Dash/Filters/tst_FilterWidgetFactory.qml+2−1
@@ -43,7 +43,8 @@ Rectangle {
43 return [
43 return [
44 { tag: "OptionSelector", type: Filters.OptionSelectorFilter, source: "FilterOptionSelector.qml" },
44 { tag: "OptionSelector", type: Filters.OptionSelectorFilter, source: "FilterOptionSelector.qml" },
45 { tag: "RangeInputFilter", type: Filters.RangeInputFilter, source: "FilterRangeInput.qml" },
45 { tag: "RangeInputFilter", type: Filters.RangeInputFilter, source: "FilterRangeInput.qml" },
46− { tag: "ValueSliderFilter", type: Filters.ValueSliderFilter, source: "FilterValueSlider.qml" }
46+ { tag: "ValueSliderFilter", type: Filters.ValueSliderFilter, source: "FilterValueSlider.qml" },
47+ { tag: "ExpandableFilter", type: Filters.ExpandableFilterWidget, source: "FilterExpandableWidget.qml" }
47 ];
48 ];
48 }
49 }
49
50