~unity-team/+junk/dashboard-playground

« back to all changes in this revision

Viewing changes to Components/ListViewWithPageHeader.qml

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

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
}