on xenial help sign in
Fix binding loop in FilterGrid height
a14b4bbAlbert Astals
qml/Components/FilterGrid.qml+37−24
@@ -28,11 +28,6 @@ import "../Components"
28Item {
28Item {
29 id: root
29 id: root
30
30
31− /* If true, the number of elements displayed will be limited by collapsedRowCount.
32− If false, all elements will be displayed, effectively looking the same as a regular
33− ResponsiveGridView. */
34− property bool filter: true
35−
36 /* Whether, when collapsed, a button should be displayed enabling the user to expand
31 /* Whether, when collapsed, a button should be displayed enabling the user to expand
37 the grid to its full size. */
32 the grid to its full size. */
38 readonly property bool expandable: model.count > rowsWhenCollapsed * iconTileGrid.columns
33 readonly property bool expandable: model.count > rowsWhenCollapsed * iconTileGrid.columns
@@ -65,30 +60,48 @@ Item {
65 readonly property alias pressDelay: iconTileGrid.pressDelay
60 readonly property alias pressDelay: iconTileGrid.pressDelay
66 property alias highlightIndex: iconTileGrid.highlightIndex
61 property alias highlightIndex: iconTileGrid.highlightIndex
67 readonly property alias currentItem: iconTileGrid.currentItem
62 readonly property alias currentItem: iconTileGrid.currentItem
63+ readonly property alias filtered: d.filter
64+
65+ QtObject {
66+ id: d
67+ // We do have filter and collapsed properties because we need to decouple
68+ // the real filtering with the animation since the closing animation
69+ // i.e. setFilter(false. true) we still need to not be filtering until
70+ // the animation finishes otherwise we hide the items when the animation
71+ // is still running
72+ property bool filter: true
73+ property bool collapsed: true
74+ }
68
75
69− height: !filterAnimation.running ? childrenRect.height : height
76+ height: d.collapsed ? root.collapsedHeight : root.uncollapsedHeight
70 clip: filterAnimation.running
77 clip: filterAnimation.running
71
78
79+ Behavior on height {
80+ id: heightBehaviour
81+ enabled: false
72− NumberAnimation {
82+ NumberAnimation {
73− property bool filterEndValue
74− id: filterAnimation
83+ id: filterAnimation
75− target: root
76− property: "height"
77− to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
78− // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
84+ // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
79− // otherwise since both animations can run at the same time you'll get
85+ // otherwise since both animations can run at the same time you'll get
80− // some visual weirdness.
86+ // some visual weirdness.
81− duration: 200
87+ duration: 200
82− easing.type: Easing.InOutQuad
88+ easing.type: Easing.InOutQuad
83− onStopped: {
89+ onRunningChanged: {
84− root.filter = filterEndValue;
90+ if (!running) {
91+ d.filter = d.collapsed;
92+ }
93+ heightBehaviour.enabled = false;
94+ }
85 }
95 }
86 }
96 }
87
97
88− function startFilterAnimation(filter) {
98+ function setFilter(filter, animate) {
89− filterAnimation.filterEndValue = filter
99+ heightBehaviour.enabled = animate;
90− filterAnimation.start();
100+ d.collapsed = filter;
101+ if (!animate || !filter) {
102+ d.filter = filter;
103+ }
91− }
104+ }
92
105
93 ResponsiveGridView {
106 ResponsiveGridView {
94 id: iconTileGrid
107 id: iconTileGrid
@@ -105,7 +118,7 @@ Item {
105
118
106 model: LimitProxyModel {
119 model: LimitProxyModel {
107 model: root.model
120 model: root.model
108− limit: (filter && !filterAnimation.running) ? rowsWhenCollapsed * iconTileGrid.columns : -1
121+ limit: d.filter ? rowsWhenCollapsed * iconTileGrid.columns : -1
109 }
122 }
110 }
123 }
111}
124}
qml/Dash/CardFilterGrid.qml+3−8
@@ -28,9 +28,10 @@ DashRenderer {
28 verticalSpacing: units.gu(1)
28 verticalSpacing: units.gu(1)
29 currentItem: filterGrid.currentItem
29 currentItem: filterGrid.currentItem
30 height: filterGrid.height
30 height: filterGrid.height
31+ filtered: filterGrid.filtered
31
32
32− function startFilterAnimation(filter) {
33+ function setFilter(filter, animate) {
33− filterGrid.startFilterAnimation(filter)
34+ filterGrid.setFilter(filter, animate)
34 }
35 }
35
36
36 FilterGrid {
37 FilterGrid {
@@ -41,7 +42,6 @@ DashRenderer {
41 delegateHeight: cardTool.cardHeight
42 delegateHeight: cardTool.cardHeight
42 verticalSpacing: genericFilterGrid.verticalSpacing
43 verticalSpacing: genericFilterGrid.verticalSpacing
43 model: genericFilterGrid.model
44 model: genericFilterGrid.model
44− filter: genericFilterGrid.filter
45 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
45 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
46 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
46 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
47 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
47 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
@@ -66,10 +66,5 @@ DashRenderer {
66 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
66 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
67 }
67 }
68 }
68 }
69−
70− onFilterChanged: {
71− genericFilterGrid.filter = filter
72− filter = Qt.binding(function() { return genericFilterGrid.filter })
73− }
74 }
69 }
75}
70}
qml/Dash/DashRenderer.qml+3−3
@@ -20,8 +20,8 @@ Item {
20 // Can the item be expanded?
20 // Can the item be expanded?
21 property bool expandable: false
21 property bool expandable: false
22
22
23− // In case it can be expanded, should we filter it
23+ // In case it can be expanded, is it filtered
24− property bool filter: true
24+ property bool filtered: true
25
25
26 property int collapsedHeight: height
26 property int collapsedHeight: height
27
27
@@ -58,6 +58,6 @@ Item {
58 /// @param itemY is y of the held delegate
58 /// @param itemY is y of the held delegate
59 signal pressAndHold(int index, real itemY)
59 signal pressAndHold(int index, real itemY)
60
60
61− function startFilterAnimation(filter) {
61+ function setFilter(filter, animate) {
62 }
62 }
63}
63}
qml/Dash/GenericScopeView.qml+4−9
@@ -122,7 +122,7 @@ FocusScope {
122 showDivider: false
122 showDivider: false
123
123
124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
125− readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
125+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
126 readonly property string category: categoryId
126 readonly property string category: categoryId
127 readonly property var item: rendererLoader.item
127 readonly property var item: rendererLoader.item
128
128
@@ -168,9 +168,7 @@ FocusScope {
168 item.objectName = Qt.binding(function() { return categoryId })
168 item.objectName = Qt.binding(function() { return categoryId })
169 if (item.expandable) {
169 if (item.expandable) {
170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
171− if (shouldFilter != item.filter) {
171+ item.setFilter(shouldFilter, false /*animate*/);
172− item.filter = shouldFilter;
173− }
174 }
172 }
175 updateDelegateCreationRange();
173 updateDelegateCreationRange();
176 item.cardTool = cardTool;
174 item.cardTool = cardTool;
@@ -219,11 +217,8 @@ FocusScope {
219 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
217 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
220 var growingVisible = !shouldFilter && y + height < categoryView.height;
218 var growingVisible = !shouldFilter && y + height < categoryView.height;
221 if (!previewListView.open || !shouldFilter) {
219 if (!previewListView.open || !shouldFilter) {
222− if (shrinkingVisible || growingVisible) {
220+ var animate = shrinkingVisible || growingVisible;
223− item.startFilterAnimation(shouldFilter)
221+ item.setFilter(shouldFilter, animate)
224− } else {
225− item.filter = shouldFilter;
226− }
227 if (!shouldFilter && !previewListView.open) {
222 if (!shouldFilter && !previewListView.open) {
228 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
223 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
229 }
224 }
tests/autopilot/unity8/shell/tests/test_emulators.py+1−1
@@ -196,7 +196,7 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
196 category)
196 category)
197 grid = category_element.select_single('CardFilterGrid')
197 grid = category_element.select_single('CardFilterGrid')
198 filtergrid = grid.select_single('FilterGrid')
198 filtergrid = grid.select_single('FilterGrid')
199− if (grid.filter):
199+ if (grid.filtered):
200 return filtergrid.collapsedRowCount * filtergrid.columns
200 return filtergrid.collapsedRowCount * filtergrid.columns
201 else:
201 else:
202 return filtergrid.uncollapsedRowCount * filtergrid.columns
202 return filtergrid.uncollapsedRowCount * filtergrid.columns
tests/qmltests/Components/tst_FilterGrid.qml+1−1
@@ -50,6 +50,7 @@ Rectangle {
50 CheckBox {
50 CheckBox {
51 id: filterCheckBox
51 id: filterCheckBox
52 checked: true
52 checked: true
53+ onCheckedChanged: filterGrid.setFilter(checked, false /*animate*/)
53 }
54 }
54 }
55 }
55 }
56 }
@@ -99,7 +100,6 @@ Rectangle {
99 maximumNumberOfColumns: 3
100 maximumNumberOfColumns: 3
100 collapsedRowCount:
101 collapsedRowCount:
101 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
102 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
102− filter: filterCheckBox.checked
103 minimumHorizontalSpacing: units.gu(1)
103 minimumHorizontalSpacing: units.gu(1)
104 delegateWidth: units.gu(6)
104 delegateWidth: units.gu(6)
105 delegateHeight: units.gu(6)
105 delegateHeight: units.gu(6)
tests/qmltests/Dash/tst_GenericScopeView.qml+3−3
@@ -140,7 +140,7 @@ Item {
140 mouseClick(header, header.width / 2, header.height / 2);
140 mouseClick(header, header.width / 2, header.height / 2);
141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
142 tryCompare(category, "filtered", false);
142 tryCompare(category, "filtered", false);
143− verify(category.height > middleHeight);
143+ tryCompareFunction(function() { return category.height > middleHeight; }, true);
144
144
145 mouseClick(header, header.width / 2, header.height / 2);
145 mouseClick(header, header.width / 2, header.height / 2);
146 verify(category.expandable);
146 verify(category.expandable);
@@ -165,7 +165,7 @@ Item {
165
165
166 mouseClick(header2, header2.width / 2, header2.height / 2);
166 mouseClick(header2, header2.width / 2, header2.height / 2);
167 tryCompare(category2, "filtered", false);
167 tryCompare(category2, "filtered", false);
168− tryCompare(category2FilterGrid, "filter", false);
168+ tryCompare(category2FilterGrid, "filtered", false);
169
169
170 categoryListView.positionAtBeginning();
170 categoryListView.positionAtBeginning();
171
171
@@ -182,7 +182,7 @@ Item {
182 mouseClick(header0, header0.width / 2, header0.height / 2);
182 mouseClick(header0, header0.width / 2, header0.height / 2);
183 tryCompare(category0, "filtered", false);
183 tryCompare(category0, "filtered", false);
184 tryCompare(category2, "filtered", true);
184 tryCompare(category2, "filtered", true);
185− tryCompare(category2FilterGrid, "filter", true);
185+ tryCompare(category2FilterGrid, "filtered", true);
186 mouseClick(header0, header0.width / 2, header0.height / 2);
186 mouseClick(header0, header0.width / 2, header0.height / 2);
187 tryCompare(category0, "filtered", true);
187 tryCompare(category0, "filtered", true);
188 tryCompare(category2, "filtered", true);
188 tryCompare(category2, "filtered", true);