Merge lp:~tpeeters/ubuntu-ui-toolkit/invisible-header-topmargin into lp:ubuntu-ui-toolkit/staging

Proposed by Tim Peeters on 2016-03-31
Status: Merged
Approved by: Tim Peeters on 2016-04-11
Approved revision: 1934
Merged at revision: 1931
Proposed branch: lp:~tpeeters/ubuntu-ui-toolkit/invisible-header-topmargin
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 410 lines (+112/-58)
6 files modified
src/Ubuntu/Components/1.3/Page.qml (+1/-4)
src/Ubuntu/Components/plugin/ucheader.cpp (+30/-22)
src/Ubuntu/Components/plugin/ucheader.h (+2/-2)
tests/unit_x11/tst_components/tst_header.qml (+63/-4)
tests/unit_x11/tst_components/tst_page_with_header.qml (+11/-13)
tests/unit_x11/tst_components/tst_scrollbar_header.qml (+5/-13)
To merge this branch: bzr merge lp:~tpeeters/ubuntu-ui-toolkit/invisible-header-topmargin
Reviewer Review Type Date Requested Status
ubuntu-sdk-build-bot continuous-integration Approve on 2016-04-11
Zsombor Egri (community) 2016-03-31 Approve on 2016-04-08
Review via email: mp+290659@code.launchpad.net

Commit Message

Prevent invisible header from setting the flickable topMargin.

To post a comment you must log in.
Andrea Bernabei (faenil) wrote :

see comments inline :)

Tim Peeters (tpeeters) wrote :

> see comments inline :)

addressed in r1929..r1932. See replies to inline comments for the specific commit for each comment.

Tim Peeters (tpeeters) wrote :

> > see comments inline :)
>
> addressed in r1929..r1932. See replies to inline comments for the specific
> commit for each comment.

Something went wrong with my inline comments, but I think the commits speak for themselves.

About the "visible : parent && parent.visible": It is kind of specific to the code here (an Item outside of the Page where the Header is defined, and I want that Header to be hidden initially), but there may be cases where the app developer has to do the same. The same would be needed for a regular Item that you want to be hidden initially, so any different behavior would be confusing.

Normally, if you define Page.header: Header { ... }, or you use a Loader to load different headers, this is not needed.

Zsombor Egri (zsombi) wrote :

Good, nice stuff!!!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Ubuntu/Components/1.3/Page.qml'
2--- src/Ubuntu/Components/1.3/Page.qml 2016-02-15 14:17:32 +0000
3+++ src/Ubuntu/Components/1.3/Page.qml 2016-04-11 16:41:31 +0000
4@@ -162,19 +162,16 @@
5 }
6
7 property Item previousHeader: null
8- property Item previousHeaderParent: null
9 function updateHeader() {
10 internal.showDeprecationWarning = false;
11 if (internal.previousHeader) {
12- internal.previousHeader.parent = internal.previousHeaderParent;
13+ internal.previousHeader.parent = null;
14 }
15 if (page.header) {
16- internal.previousHeaderParent = page.header.parent;
17 internal.previousHeader = page.header;
18 page.header.parent = page;
19 } else {
20 internal.previousHeader = null;
21- internal.previousHeaderParent = null;
22 }
23 }
24
25
26=== modified file 'src/Ubuntu/Components/plugin/ucheader.cpp'
27--- src/Ubuntu/Components/plugin/ucheader.cpp 2016-03-31 11:09:23 +0000
28+++ src/Ubuntu/Components/plugin/ucheader.cpp 2016-04-11 16:41:31 +0000
29@@ -21,8 +21,6 @@
30 #include <QtQuick/private/qquickanimation_p.h>
31 #include "ucubuntuanimation.h"
32 #include "ucunits.h"
33-#include "propertychange_p.h"
34-
35
36 /*!
37 \qmltype Header
38@@ -79,8 +77,8 @@
39 : UCStyledItemBase(parent)
40 , m_flickable(Q_NULLPTR)
41 , m_showHideAnimation(new QQuickNumberAnimation)
42- , m_flickableTopMarginBackup(Q_NULLPTR)
43 , m_previous_contentY(0)
44+ , m_previous_header_height(0)
45 , m_exposed(true)
46 , m_moving(false)
47 {
48@@ -97,8 +95,7 @@
49
50 UCHeader::~UCHeader() {
51 if (m_flickable != Q_NULLPTR) {
52- Q_ASSERT(m_flickableTopMarginBackup != Q_NULLPTR);
53- delete m_flickableTopMarginBackup;
54+ m_flickable->setTopMargin(m_flickable->topMargin() - m_previous_header_height);
55 }
56 }
57
58@@ -116,6 +113,13 @@
59 }
60 }
61
62+void UCHeader::itemChange(ItemChange change, const ItemChangeData &value) {
63+ if (change == ItemVisibleHasChanged || change == ItemParentHasChanged) {
64+ updateFlickableMargins();
65+ }
66+ UCStyledItemBase::itemChange(change, value);
67+}
68+
69 /*!
70 * \qmlproperty Flickable Header::flickable
71 *
72@@ -163,7 +167,9 @@
73 *
74 * The topMargin of the flickable will automatically be updated to always match
75 * the height of the header. When changing the flickable, the topMargin of the previous
76- * flickable is set to 0.
77+ * flickable is set to 0. When the header is invisible because its visible property is
78+ * false, or the header has no parent, the flickable topMargin does not reflect the
79+ * header height.
80 *
81 * It is permitted to use a ListView as the value of flickable, but this works
82 * well only if the ListView.header property is not set. Alternatively,
83@@ -179,8 +185,6 @@
84 return;
85 }
86 if (!m_flickable.isNull()) {
87- Q_ASSERT(m_flickableTopMarginBackup != Q_NULLPTR);
88-
89 // Finish the current header movement in case the current
90 // flickable is disconnected while scrolling.
91 if (m_exposed) {
92@@ -190,23 +194,24 @@
93 }
94 m_flickable->disconnect(this);
95
96- qreal oldMargin = m_flickable->topMargin();
97- // store contentY to compensate for Flickable updating the position due to margin change.
98- qreal oldContentY = m_flickable->contentY();
99- delete m_flickableTopMarginBackup; // Restores previous value/binding for topMargin.
100- m_flickableTopMarginBackup = Q_NULLPTR;
101+ // store the current sum of the topMargin and contentY so that we
102+ // can add the change in topMargin+contentY to the new contentY after
103+ // updating the topMargin so that the user will still see the same
104+ // flickable contents at the top of the view after the header height changed.
105+ qreal delta = m_flickable->topMargin() + m_flickable->contentY();
106+ m_flickable->setTopMargin(m_flickable->topMargin() - m_previous_header_height);
107+ m_previous_header_height = 0;
108+ delta -= m_flickable->topMargin() + m_flickable->contentY();
109
110- qreal delta = m_flickable->topMargin() - oldMargin + m_flickable->contentY() - oldContentY;
111 // revert the flickable content Y.
112- m_flickable->setContentY(m_flickable->contentY() - delta);
113+ m_flickable->setContentY(m_flickable->contentY() + delta);
114 }
115
116 m_flickable = flickable;
117 Q_EMIT flickableChanged();
118
119- Q_ASSERT(m_flickableTopMarginBackup == Q_NULLPTR);
120+ Q_ASSERT(m_previous_header_height == 0);
121 if (!m_flickable.isNull()) {
122- m_flickableTopMarginBackup = new PropertyChange(m_flickable, "topMargin");
123 updateFlickableMargins();
124 connect(m_flickable, SIGNAL(contentYChanged()),
125 this, SLOT(_q_scrolledContents()));
126@@ -225,14 +230,17 @@
127 if (m_flickable.isNull()) {
128 return;
129 }
130- qreal headerHeight = height();
131- qreal previousHeaderHeight = m_flickable->topMargin();
132- if (headerHeight != previousHeaderHeight) {
133+ qreal headerHeight = 0.0;
134+ if (isVisible() && parentItem()) {
135+ headerHeight = height();
136+ } // else: header is not visible, so do not add to the topMargin.
137+ if (headerHeight != m_previous_header_height) {
138 qreal previousContentY = m_flickable->contentY();
139- PropertyChange::setValue(m_flickableTopMarginBackup, headerHeight);
140+ m_flickable->setTopMargin(m_flickable->topMargin() + headerHeight - m_previous_header_height);
141 // Push down contents when header grows,
142 // pull up contents when header shrinks.
143- m_flickable->setContentY(previousContentY - headerHeight + previousHeaderHeight);
144+ m_flickable->setContentY(previousContentY - headerHeight + m_previous_header_height);
145+ m_previous_header_height = headerHeight;
146 }
147 }
148
149
150=== modified file 'src/Ubuntu/Components/plugin/ucheader.h'
151--- src/Ubuntu/Components/plugin/ucheader.h 2016-02-02 12:17:58 +0000
152+++ src/Ubuntu/Components/plugin/ucheader.h 2016-04-11 16:41:31 +0000
153@@ -23,7 +23,6 @@
154 class QQuickFlickable;
155 class QQuickNumberAnimation;
156 class UCUbuntuAnimation;
157-class PropertyChange;
158
159 class UCHeader : public UCStyledItemBase
160 {
161@@ -49,6 +48,7 @@
162 protected:
163 virtual void show(bool animate);
164 virtual void hide(bool animate);
165+ virtual void itemChange(ItemChange change, const ItemChangeData &value);
166
167 private Q_SLOTS:
168 void _q_scrolledContents();
169@@ -61,9 +61,9 @@
170 private:
171 QPointer<QQuickFlickable> m_flickable;
172 QQuickNumberAnimation* m_showHideAnimation;
173- PropertyChange* m_flickableTopMarginBackup;
174
175 qreal m_previous_contentY;
176+ qreal m_previous_header_height;
177 bool m_exposed:1;
178 bool m_moving:1;
179
180
181=== modified file 'tests/unit_x11/tst_components/tst_header.qml'
182--- tests/unit_x11/tst_components/tst_header.qml 2016-02-01 21:50:28 +0000
183+++ tests/unit_x11/tst_components/tst_header.qml 2016-04-11 16:41:31 +0000
184@@ -28,8 +28,8 @@
185 Header {
186 id: header
187 flickable: flickable
188- z:1
189- width: parent.width
190+ z: 1
191+ width: parent ? parent.width : 234
192 height: root.initialHeaderHeight
193
194 Rectangle {
195@@ -196,6 +196,7 @@
196 }
197
198 function init() {
199+ header.flickable = flickable;
200 flickable.contentHeight = 2*flickable.height;
201 flickable.interactive = true;
202 flickable.contentY = -header.height;
203@@ -343,14 +344,72 @@
204
205 compare(otherFlickable.topMargin, otherFlickable.initialTopMargin, "Flickable top margin is not initialized properly.");
206 header.flickable = otherFlickable;
207- compare(otherFlickable.topMargin, header.height, "Setting flickable does not update flickable top margin.");
208- compare(flickable.topMargin, 0, "Changing the flickable does not reset the previous flickable top margin to 0.");
209+ compare(otherFlickable.topMargin, header.height + otherFlickable.initialTopMargin,
210+ "Setting flickable does not update flickable top margin.");
211+ compare(flickable.topMargin, 0,
212+ "Changing the flickable does not reset the previous flickable top margin to 0.");
213
214 header.flickable = flickable;
215 compare(otherFlickable.topMargin, otherFlickable.initialTopMargin, "Reverting flickable does not restore the other flickable top margin.");
216 compare(flickable.topMargin, header.height, "Reverting flickable breaks flickable top margin.");
217 }
218
219+ function test_flickable_margins_invisible_header_bug1560458_bug1560419_data() {
220+ return [
221+ { tag: "Inititial topMargin = 0",
222+ flickable: flickable,
223+ initialTopMargin: 0
224+ },
225+ { tag: "Initial topMargin > 0",
226+ flickable: otherFlickable,
227+ initialTopMargin: otherFlickable.initialTopMargin
228+ }
229+ ];
230+ }
231+
232+ function test_flickable_margins_invisible_header_bug1560458_bug1560419(data) {
233+ var h = header.height;
234+ var fl = data.flickable;
235+ // the original margin before the flickable is connected to the header
236+ var fmargin = data.initialTopMargin;
237+ if (header.flickable !== fl) {
238+ compare(fl.topMargin, fmargin,
239+ "Incorrect initial topMargin for flickable.");
240+ header.flickable = fl;
241+ }
242+
243+ compare(fl.topMargin, h + fmargin,
244+ "Flickable top margin does not match header height.");
245+ header.visible = false;
246+ compare(fl.topMargin, fmargin,
247+ "Invisible header sets flickable.topMargin.");
248+ header.visible = true;
249+ compare(fl.topMargin, h + fmargin,
250+ "Making flickable visible does not set flickable.topMargin.");
251+ header.height = 0;
252+ compare(fl.topMargin, fmargin,
253+ "Header with height = 0 sets flickable.topMargin.");
254+ header.height = h;
255+ compare(fl.topMargin, h + fmargin,
256+ "Setting header height does not set flickable.topMargin.");
257+
258+ // Setting opacity to 0 should not change flickable.topMargin.
259+ // This allows opacity animations.
260+ header.opacity = 0.0;
261+ compare(fl.topMargin, h + fmargin,
262+ "Setting header opacity to 0 changes flickable.topMargin.");
263+ header.opacity = 1.0;
264+ compare(fl.topMargin, h + fmargin,
265+ "Setting header opacity to 1 changes flickable.topMargin.");
266+
267+ header.parent = null;
268+ compare(fl.topMargin, fmargin,
269+ "Header with no parent sets flickable.topMargin.");
270+ header.parent = root;
271+ compare(fl.topMargin, h + fmargin,
272+ "Header with parent does not set flickable.topMargin.");
273+ }
274+
275 function test_flickable_contentHeight_bug1156573() {
276 var old_height = flickable.contentHeight;
277 header.exposed = false;
278
279=== modified file 'tests/unit_x11/tst_components/tst_page_with_header.qml'
280--- tests/unit_x11/tst_components/tst_page_with_header.qml 2015-10-14 12:07:15 +0000
281+++ tests/unit_x11/tst_components/tst_page_with_header.qml 2016-04-11 16:41:31 +0000
282@@ -1,5 +1,5 @@
283 /*
284- * Copyright (C) 2015 Canonical Ltd.
285+ * Copyright (C) 2016 Canonical Ltd.
286 *
287 * This program is free software; you can redistribute it and/or modify
288 * it under the terms of the GNU Lesser General Public License as published by
289@@ -31,10 +31,10 @@
290 objectName: "my_rectangle"
291 id: myRectangle
292 anchors {
293- left: parent.left
294- right: parent.right
295+ left: parent ? parent.left : undefined
296+ right: parent ? parent.right : undefined
297 }
298- height: units.gu(6)
299+ height: myPageHeader.height
300 color: UbuntuColors.red
301 }
302
303@@ -64,7 +64,7 @@
304 anchors {
305 left: parent.left
306 right: parent.right
307- top: parent.top
308+ top: page.header ? page.header.bottom : parent.top
309 topMargin: units.gu(10)
310 leftMargin: units.gu(10)
311 rightMargin: units.gu(10)
312@@ -126,26 +126,24 @@
313
314 function test_page_with_no_header() {
315 page.header = null;
316- compare(myPageHeader.parent, invisible,
317- "Header parent is not correctly reverted when unsetting Page.header.");
318- compare(myPageHeader.visible, false,
319- "My PageHeader is still visible after re-parenting it to an invisible Item.");
320+ compare(myPageHeader.parent, null,
321+ "Header parent is not wet to null when unsetting Page.header.");
322 compare(appHeader.visible, true,
323 "AppHeader does not become visible when Page.header is null.");
324 }
325
326 function test_page_with_alternative_header() {
327 page.header = myRectangle;
328- compare(myPageHeader.parent, invisible,
329- "Header parent not correctly reverted when setting a different Page.header.");
330+ compare(myPageHeader.parent, null,
331+ "Header parent not null after setting a different Page.header.");
332 compare(myRectangle.parent, page,
333 "Rectangle parent is not correctly set to page after setting it as Page.header.");
334 compare(appHeader.visible, false,
335 "Setting a different Page.header Item shows the AppHeader.");
336
337 page.header = myPageHeader;
338- compare(myRectangle.parent, invisible,
339- "Rectangle parent is not correctly reverted after unsetting it as Page.header.");
340+ compare(myRectangle.parent, null,
341+ "Rectangle parent is not set to null after unsetting it as Page.header.");
342 // myPageHeader parent is checked in cleanup().
343 }
344 }
345
346=== modified file 'tests/unit_x11/tst_components/tst_scrollbar_header.qml'
347--- tests/unit_x11/tst_components/tst_scrollbar_header.qml 2016-03-30 15:14:32 +0000
348+++ tests/unit_x11/tst_components/tst_scrollbar_header.qml 2016-04-11 16:41:31 +0000
349@@ -124,7 +124,8 @@
350
351 PageHeader {
352 id: standardHeaderItem
353- visible: pageItem.header === standardHeaderItem
354+ // hide the header until it is used by the page
355+ parent: null
356 title: "Default title"
357 flickable: flickable_movingHeaderTest
358 trailingActionBar.actions: [
359@@ -137,7 +138,8 @@
360 }
361 PageHeader {
362 id: editHeaderItem
363- visible: pageItem.header === editHeaderItem
364+ // hide the header until it is used by the page
365+ parent: null
366 flickable: flickable_movingHeaderTest
367 property Component delegate: Component {
368 AbstractButton {
369@@ -241,14 +243,7 @@
370 function test_handlingOfMovingHeader(data) {
371 var page = pageItem
372 var header = data.header
373-
374 page.header = header
375- console.log(page.header, header.flickable)
376-
377- //make sure the currently tested header is the one driving the flickable changes
378- //FIXME: this should not be needed after #1560458 is fixed
379- header.flickable = null
380- header.flickable = flickable_movingHeaderTest
381
382 compare(page.header, header, "Handling of moving header: wrong header.")
383 compare(scrollbar_movingHeaderTest.__styleInstance.isVertical, true, "Scrollbar is not vertical.")
384@@ -260,9 +255,8 @@
385 checkScrollbarPositionRelativeToPage(scrollbar_movingHeaderTest, page, page.head.contents.height, data.tag)
386 return
387 } else {
388- header.flickable = flickable_movingHeaderTest
389 compare(header.flickable, flickable_movingHeaderTest, "Wrong PageHeader flickable.")
390- checkScrollbarPositionRelativeToPage(scrollbar_movingHeaderTest, page, page.header.height, data.tag)
391+ checkScrollbarPositionRelativeToPage(scrollbar_movingHeaderTest, page, page.header.height, data.tag + ", at initialization.")
392 }
393
394 var tmpHeaderHeight = header.height
395@@ -279,7 +273,6 @@
396 //should cover the scrollbar)
397 header.flickable = null
398 compare(header.flickable, null, "Wrong PageHeader flickable.")
399- expectFailContinue("Standard header", "Waiting on Header bug #1560458")
400 checkScrollbarPositionRelativeToPage(scrollbar_movingHeaderTest, page, 0, data.tag)
401
402 //reassign the correct flickable and check again
403@@ -289,7 +282,6 @@
404
405 header.visible = false
406 compare(header.visible, false, "Header visibility did not change, should have been false.")
407- expectFailContinue("", "Waiting on Header bug #1560458")
408 checkScrollbarPositionRelativeToPage(scrollbar_movingHeaderTest, page, 0, data.tag + ", invisible header")
409
410 header.visible = true

Subscribers

People subscribed via source and target branches