~unity-team/unity/8.0

« back to all changes in this revision

Viewing changes to Components/ListViewWithPageHeader.qml

  • Committer: Tarmac
  • Author(s): Albert Astals
  • Date: 2013-07-01 17:27:33 UTC
  • mfrom: (3.3.97 lvwph)
  • Revision ID: tarmac-20130701172733-rfc412k2380yzae9
ListViewWithPageHeader implementation in C++. Fixes: https://bugs.launchpad.net/bugs/1171918.

Approved by PS Jenkins bot, Gerry Boland.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2013 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.0
18
 
import "Math.js" as MathLocal
19
 
 
20
 
Item {
21
 
    id: root
22
 
    property Item pageHeader: null
23
 
    property Component sectionDelegate: null
24
 
    property string sectionProperty: ""
25
 
    property alias model: listView.model
26
 
    property alias delegate: listView.delegate
27
 
    property ListView view: listView
28
 
    property alias moving: flicker.moving
29
 
    property alias atYEnd: flicker.atYEnd
30
 
    property bool clipListView: true
31
 
 
32
 
    readonly property real __headerHeight: (pageHeader) ? pageHeader.implicitHeight : 0
33
 
    property real __headerVisibleHeight: __headerHeight
34
 
    readonly property real __overshootHeight: (flicker.contentY < 0) ? -flicker.contentY : 0
35
 
 
36
 
 
37
 
    // TODO move to AnimationController
38
 
    ParallelAnimation {
39
 
        id: headerAnimation
40
 
        property real targetContentY
41
 
        NumberAnimation {
42
 
            target: root
43
 
            property: "__headerVisibleHeight"
44
 
            to: root.__headerHeight
45
 
            duration: 200
46
 
            easing.type: Easing.OutQuad
47
 
        }
48
 
        NumberAnimation {
49
 
            target: listView
50
 
            property: "contentY"
51
 
            to: headerAnimation.targetContentY
52
 
            duration: 200
53
 
            easing.type: Easing.OutQuad
54
 
        }
55
 
    }
56
 
 
57
 
    function positionAtBeginning() {
58
 
        __headerVisibleHeight = __headerHeight
59
 
        flicker.contentY = 0
60
 
    }
61
 
 
62
 
    function showHeader() {
63
 
        headerAnimation.targetContentY = listView.contentY + (__headerHeight - __headerVisibleHeight)
64
 
        headerAnimation.start()
65
 
    }
66
 
 
67
 
    function flick(xVelocity, yVelocity) {
68
 
        flicker.flick(xVelocity, yVelocity);
69
 
    }
70
 
 
71
 
    onPageHeaderChanged: {
72
 
        pageHeader.parent = pageHeaderContainer;
73
 
        pageHeader.anchors.fill = pageHeaderContainer;
74
 
    }
75
 
 
76
 
    Item {
77
 
        id: pageHeaderClipper
78
 
        parent: flicker // parent to Flickable so mouse click events passed through to the header component
79
 
        anchors {
80
 
            top: parent.top
81
 
            left: parent.left
82
 
            right: parent.right
83
 
        }
84
 
        height: __headerVisibleHeight + __overshootHeight
85
 
 
86
 
        Item {
87
 
            id: pageHeaderContainer
88
 
            anchors {
89
 
                left: parent.left
90
 
                right: parent.right
91
 
                bottom: parent.bottom
92
 
            }
93
 
            height: __headerHeight + __overshootHeight
94
 
        }
95
 
    }
96
 
 
97
 
    ListView {
98
 
        id: listView
99
 
        parent: flicker // parent to Flickable so mouse click events passed through to List delegates
100
 
        anchors {
101
 
            left: parent.left
102
 
            right: parent.right
103
 
            top: parent.top
104
 
            topMargin: __headerVisibleHeight
105
 
        }
106
 
        height: root.height
107
 
 
108
 
        // FIXME scrolling workaround, see below
109
 
        cacheBuffer: height*10
110
 
 
111
 
        section.property: sectionProperty
112
 
        section.criteria: ViewSection.FullString
113
 
        section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
114
 
        section.delegate: sectionDelegate
115
 
 
116
 
        interactive: false
117
 
        clip: root.clipListView
118
 
 
119
 
        property int __sectionDelegateHeight: __getHeight(section.delegate)
120
 
        function __getHeight(component) {
121
 
            // want height (minus allowed overlap) of the section delegate as is needed for clipping
122
 
            if (component === null) return 0;
123
 
            var object = component.createObject(); //FIXME: throws 'section' not defined error
124
 
            var value = object.height - object.bottomBorderAllowedOverlap;
125
 
            object.destroy();
126
 
            return value;
127
 
        }
128
 
 
129
 
        property real previousOriginY: 0
130
 
        onOriginYChanged: {
131
 
            var deltaOriginY = originY - previousOriginY;
132
 
            previousOriginY = originY;
133
 
            /* When originY changes, it causes the top of the flicker and the top of the list to fall
134
 
               out of sync - and thus the contentY positioning will be broken. To correct for this we
135
 
               manually change the flickable's contentY here */
136
 
            flicker.contentY -= deltaOriginY;
137
 
        }
138
 
 
139
 
        /* For case when list content greater than container height and list scrolled down so header
140
 
           hidden. If content shrinks to be smaller than the container height, we want the header to
141
 
           re-appear */
142
 
        property real __previousContentHeight: 0
143
 
        onContentHeightChanged: {
144
 
            var deltaContentHeight = contentHeight - __previousContentHeight;
145
 
            __previousContentHeight = contentHeight;
146
 
            if (contentHeight < height && deltaContentHeight < 0 && __headerVisibleHeight < height - contentHeight) {
147
 
                __headerVisibleHeight = Math.min(height - contentHeight, __headerHeight);
148
 
            }
149
 
        }
150
 
    }
151
 
 
152
 
    Flickable {
153
 
        id: flicker
154
 
        anchors.fill: parent
155
 
        contentHeight: listView.contentHeight + __headerHeight
156
 
        maximumFlickVelocity: height * 10
157
 
        flickDeceleration: height * 2
158
 
        onContentYChanged: {
159
 
            var deltaContentY = contentY - __previousContentY;
160
 
            __previousContentY = contentY;
161
 
 
162
 
            // first decide if movement will prompt the page header to change height
163
 
            if ((deltaContentY < 0 && __headerVisibleHeight >= 0) ||
164
 
                    (deltaContentY > 0 && __headerVisibleHeight <= __headerHeight)) {
165
 
 
166
 
                // calculate header height - but prevent bounce from changing it
167
 
                if (contentY > 0 && contentY < contentHeight - height) {
168
 
                    __headerVisibleHeight = MathLocal.clamp(__headerVisibleHeight - deltaContentY, 0, __headerHeight);
169
 
                }
170
 
            }
171
 
 
172
 
            // now we move list position, taking into account page header height
173
 
 
174
 
            // BUG: With section headers enabled, the values of originY and contentY appear not
175
 
            // correct at the exact point originY changes. originY changes when the ListView
176
 
            // deletes/creates hidden delegates which are above the visible delegates.
177
 
            // As a result of this bug, you experience jittering scrolling when rapidly moving
178
 
            // around in large lists. See https://bugreports.qt-project.org/browse/QTBUG-27997
179
 
            // A workaround is to use a large enough cacheBuffer to prevent deletions/creations
180
 
            // so effectively originY is always zero.
181
 
            var newContentY = flicker.contentY + listView.originY - __headerHeight + __headerVisibleHeight
182
 
            if (newContentY < listView.contentHeight) {
183
 
                listView.contentY = newContentY;
184
 
            }
185
 
        }
186
 
 
187
 
        property real __previousContentY: 0
188
 
    }
189
 
}