~faenil/ubuntu-ui-toolkit/flickableBasedScrollView

« back to all changes in this revision

Viewing changes to src/Ubuntu/Components/1.3/ScrollView.qml

  • Committer: Andrea Bernabei
  • Date: 2015-12-08 16:14:43 UTC
  • Revision ID: andrea.bernabei@canonical.com-20151208161443-t6wjxyn87hxnpjdu
[qml scrollview]: use Flickable as base type of ScrollView. ABANDONED (see commit msg)

This doesn't work, because:            
- it requires        
  Scrollbar { parent: scrollview.parent } 
  to make the scrollbar interactive even when Flickable is flicking. 
  After talking to SDK team, we decided we don't want that, scrollview.parent could be anything.
  We can't even grab mouse from within scrollbar, because Flickable doesn't let us receive
  events while it's flicking. (tried with preventStealing, onPressed is not fired).

- In the ListView usecase, we would have Flickable { ListView {} }... no good.

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
 
54
54
    NOTE: the items that are wrapped in the ScrollView are reparanted to \l viewport.
55
55
*/
56
 
StyledItem {
 
56
Flickable {
57
57
    id: root
58
58
 
59
59
    implicitWidth: 240
60
60
    implicitHeight: 150
61
 
    activeFocusOnPress: true
 
61
    clip: true
 
62
    //TODO: do we want to have a default value for this?
 
63
    //This would need overriding when the size of the children depends on the size
 
64
    //of their parent, otherwise we have a binding loop
 
65
    //(child.width->contentItem.width->childrenRect.width->child.width)
 
66
    contentWidth: contentItem.childrenRect.width
 
67
    contentHeight: contentItem.childrenRect.height
62
68
 
 
69
    focus: true
63
70
    //TODO: add horizontalScrollbarPolicy
64
71
    //TODO: add verticalScrollbarPolicy
65
72
 
66
73
    //Currently we want to push the platform towards overlay scrollbars
67
74
    //readonly property bool alwaysOnScrollbars: false
68
75
 
69
 
    /*!
70
 
        \qmlproperty Item ScrollView::viewport
71
 
        This property holds the viewport Item. The children of the ScrollView element are
72
 
        reparented to this item to make sure the scrollbars are correctly positioned and
73
 
        the items are clipped at their boundaries.
74
 
    */
75
 
    readonly property alias viewport: viewportItem
76
 
 
77
 
    /*!
78
 
        \qmlproperty Item ScrollView::flickableItem
79
 
        The flickableItem of the ScrollView. If the contentItem provided
80
 
        to the ScrollView is a Flickable, that will be the \l flickableItem.
81
 
        Otherwise ScrollView will create a Flickable which will hold the
82
 
        items provided as children.
83
 
    */
84
 
    readonly property alias flickableItem: internal.flickableItem
85
 
 
86
 
    /*!
87
 
        The contentItem of the ScrollView. This is set by the user.
88
 
        Note that the definition of contentItem is somewhat different to that
89
 
        of a Flickable, where the contentItem is implicitly created.
90
 
    */
91
 
    default property Item contentItem
92
 
 
93
76
    /*! \internal */
94
77
    //property alias __horizontalScrollBar: scroller.horizontalScrollBar
95
78
    /*! \internal */
96
79
    //property alias __verticalScrollBar: scroller.verticalScrollBar
97
80
 
98
 
    onContentItemChanged: {
99
 
        // Check if the item provided is a Flickable
100
 
        if (contentItem.hasOwnProperty("contentWidth") &&
101
 
                contentItem.hasOwnProperty("flickableDirection")) {
102
 
            internal.flickableItem = contentItem
103
 
            internal.flickableItem.parent = viewportItem
104
 
        } else {
105
 
            //Create a dummy Flickable if the app dev didn't provide any
106
 
            internal.flickableItem = flickableComponent.createObject(viewportItem)
107
 
            contentItem.parent = internal.flickableItem.contentItem
108
 
        }
109
 
        internal.flickableItem.anchors.fill = viewportItem
110
 
    }
111
 
 
112
 
    children: Item {
113
 
        id: internal
114
 
 
115
 
        property Flickable flickableItem
116
 
        property real nonOverlayScrollbarMargin: verticalScrollbar.__styleInstance ? verticalScrollbar.__styleInstance.nonOverlayScrollbarMargin : 0
117
 
 
118
 
        //if the flickable is not coming from the user but from our internal Component...
119
 
        Binding {
120
 
            target: flickableItem
121
 
            when: contentItem !== flickableItem
122
 
            property: "contentHeight"
123
 
            value: contentItem ? contentItem.height : 0
124
 
        }
125
 
        Binding {
126
 
            target: flickableItem
127
 
            when: contentItem !== flickableItem
128
 
            property: "contentWidth"
129
 
            value: contentItem ? contentItem.width : 0
130
 
        }
131
 
 
 
81
    //shortScrollingRation is used for arrow keys, longScrollingRatio is used for pgUp/pgDown
 
82
    //0.1 means we will scroll 10% of the *visible* flickable area
 
83
    property real shortScrollingRatio: __styleInstance ? __styleInstance.shortScrollingRatio : 0.1
 
84
    property real longScrollingRatio: __styleInstance ? __styleInstance.longScrollingRatio : 0.9
 
85
    property real nonOverlayScrollbarMargin: verticalScrollbar.__styleInstance ? verticalScrollbar.__styleInstance.nonOverlayScrollbarMargin : 0
 
86
 
 
87
    Keys.enabled: true
 
88
    Keys.onLeftPressed: {
 
89
        console.log("Left pressed")
 
90
        if (horizontalScrollbar.__styleInstance !== null) {
 
91
            horizontalScrollbar.__styleInstance.scroll(-root.width*shortScrollingRatio)
 
92
        }
 
93
    }
 
94
    Keys.onRightPressed: {
 
95
        console.log("Right pressed")
 
96
        if (horizontalScrollbar.__styleInstance !== null) {
 
97
            horizontalScrollbar.__styleInstance.scroll(root.width*shortScrollingRatio)
 
98
        }
 
99
    }
 
100
    Keys.onDownPressed: {
 
101
        console.log("Down pressed")
 
102
        if (verticalScrollbar.__styleInstance !== null) {
 
103
            verticalScrollbar.__styleInstance.scroll(root.height*shortScrollingRatio)
 
104
        }
 
105
    }
 
106
    Keys.onUpPressed: {
 
107
        console.log("Up pressed")
 
108
        if (verticalScrollbar.__styleInstance !== null) {
 
109
            verticalScrollbar.__styleInstance.scroll(-root.height*shortScrollingRatio)
 
110
        }
 
111
    }
 
112
    Keys.onPressed:  {
 
113
        console.log("Pressed")
 
114
        if (event.key == Qt.Key_Escape) {
 
115
            var scrollbarWithActiveDrag = (horizontalScrollbar.__styleInstance && horizontalScrollbar.__styleInstance.draggingThumb)
 
116
                    || (verticalScrollbar.__styleInstance && verticalScrollbar.__styleInstance.draggingThumb)
 
117
                    || null
 
118
            if (scrollbarWithActiveDrag !== null) {
 
119
                scrollbarWithActiveDrag.__styleInstance.resetScrollingToPreDrag()
 
120
            }
 
121
            event.accepted = true
 
122
        } else if (verticalScrollbar.__styleInstance !== null) {
 
123
            if (event.key == Qt.Key_PageDown) {
 
124
                verticalScrollbar.__styleInstance.scroll(root.height*longScrollingRatio)
 
125
            } else if (event.key === Qt.Key_PageUp) {
 
126
                verticalScrollbar.__styleInstance.scroll(-root.height*longScrollingRatio)
 
127
            } else if (event.key === Qt.Key_Home) {
 
128
                verticalScrollbar.__styleInstance.scrollToBeginning()
 
129
            } else if (event.key === Qt.Key_End) {
 
130
                verticalScrollbar.__styleInstance.scrollToEnd()
 
131
            }
 
132
            event.accepted = true
 
133
        }
 
134
    }
 
135
 
 
136
 
 
137
    //replacement for StyledItem's activeFocusOnPress
 
138
    //this has to come before Scrollbars, because its parent property is initialized after Scrollbar's
 
139
    //(due to the order in which QML instantiates items), so it will end up on top of the scrollbars.
 
140
    //And that's what we want, to ensure tapping on the scrollbar also focuses the corresponding scrollview.
 
141
    //The alternative is to set "z", or force focus from inside Scrollbar (but why make scrollbar more complex?)
 
142
    MouseArea {
132
143
        anchors.fill: parent
133
 
 
134
 
        Component {
135
 
            id: flickableComponent
136
 
            Flickable {}
137
 
        }
138
 
 
139
 
        Item {
140
 
            id: viewportItem
141
 
            anchors.fill: parent
142
 
            anchors.topMargin: (horizontalScrollbar.align === Qt.AlignTop && horizontalScrollbar.__alwaysOnScrollbars)
143
 
                               ? internal.nonOverlayScrollbarMargin : 0
144
 
            anchors.leftMargin: (verticalScrollbar.align === Qt.AlignLeading && horizontalScrollbar.__alwaysOnScrollbars)
145
 
                                ? internal.nonOverlayScrollbarMargin : 0
146
 
            anchors.rightMargin: (verticalScrollbar.align === Qt.AlignTrailing && horizontalScrollbar.__alwaysOnScrollbars)
147
 
                                 ? internal.nonOverlayScrollbarMargin : 0
148
 
            anchors.bottomMargin: (horizontalScrollbar.align === Qt.AlignBottom && horizontalScrollbar.__alwaysOnScrollbars)
149
 
                                  ? internal.nonOverlayScrollbarMargin : 0
150
 
 
151
 
            //shortScrollingRation is used for arrow keys, longScrollingRatio is used for pgUp/pgDown
152
 
            //0.1 means we will scroll 10% of the *visible* flickable area
153
 
            property real shortScrollingRatio: __styleInstance ? __styleInstance.shortScrollingRatio : 0.1
154
 
            property real longScrollingRatio: __styleInstance ? __styleInstance.longScrollingRatio : 0.9
155
 
 
156
 
            clip: true
157
 
            focus: true
158
 
            Keys.enabled: true
159
 
            Keys.onLeftPressed: {
160
 
                console.log("Left pressed")
161
 
                if (horizontalScrollbar.__styleInstance !== null) {
162
 
                    horizontalScrollbar.__styleInstance.scroll(-flickableItem.width*shortScrollingRatio)
163
 
                }
164
 
            }
165
 
            Keys.onRightPressed: {
166
 
                console.log("Right pressed")
167
 
                if (horizontalScrollbar.__styleInstance !== null) {
168
 
                    horizontalScrollbar.__styleInstance.scroll(flickableItem.width*shortScrollingRatio)
169
 
                }
170
 
            }
171
 
            Keys.onDownPressed: {
172
 
                console.log("Down pressed")
173
 
                if (verticalScrollbar.__styleInstance !== null) {
174
 
                    verticalScrollbar.__styleInstance.scroll(flickableItem.height*shortScrollingRatio)
175
 
                }
176
 
            }
177
 
            Keys.onUpPressed: {
178
 
                console.log("Up pressed")
179
 
                if (verticalScrollbar.__styleInstance !== null) {
180
 
                    verticalScrollbar.__styleInstance.scroll(-flickableItem.height*shortScrollingRatio)
181
 
                }
182
 
            }
183
 
            Keys.onPressed:  {
184
 
                console.log("Pressed")
185
 
                if (event.key == Qt.Key_Escape) {
186
 
                    var scrollbarWithActiveDrag = (horizontalScrollbar.__styleInstance && horizontalScrollbar.__styleInstance.draggingThumb)
187
 
                            || (verticalScrollbar.__styleInstance && verticalScrollbar.__styleInstance.draggingThumb)
188
 
                            || null
189
 
                    if (scrollbarWithActiveDrag !== null) {
190
 
                        scrollbarWithActiveDrag.__styleInstance.resetScrollingToPreDrag()
191
 
                    }
192
 
                    event.accepted = true
193
 
                } else if (verticalScrollbar.__styleInstance !== null) {
194
 
                    if (event.key == Qt.Key_PageDown) {
195
 
                        verticalScrollbar.__styleInstance.scroll(flickableItem.height*longScrollingRatio)
196
 
                    } else if (event.key == Qt.Key_PageUp) {
197
 
                        verticalScrollbar.__styleInstance.scroll(-flickableItem.height*longScrollingRatio)
198
 
                    } else if (event.key == Qt.Key_Home) {
199
 
                        verticalScrollbar.__styleInstance.scrollToBeginning()
200
 
                    } else if (event.key == Qt.Key_End) {
201
 
                        verticalScrollbar.__styleInstance.scrollToEnd()
202
 
                    }
203
 
                    event.accepted = true
204
 
                }
205
 
            }
206
 
        }
207
 
 
208
 
        //When you click outside of a child of the scrollview, this restores the focus to the ScrollView
209
 
        //Why is this needed?
210
 
        //  Suppose the viewport has a child ScrollView (or another Item which handles some or all of the hw keys we handle).
211
 
        //  Now, if that child is coded to set its focus to true when you tap on it (which is, for instance, what
212
 
        //  StyledItem's activeFocusOnPress does), tapping on that child will set viewportItem.focus = false, because
213
 
        //  that child is just another Item inside "root" and "root" is a FocusScope, and that's how FocusScope works.
214
 
        //  At this point, we need something to restore the focus to the viewportItem once you tap on viewportItem, outside
215
 
        //  any other child. And that's what the MouseArea below does.
216
 
        //
217
 
        //The alternative could be making the viewport a StyledItem and using activeFocusOnPress:true, but that wouldn't work
218
 
        //because it would make viewportItem a focus scope, and we don't want that, because at that point,
219
 
        //being in a similar situation as described above, we wouldn't be able to transfer the focus to viewportItem, because
220
 
        //being a focus scope means viewportItem would forward the focus to the one between its children who asked it most
221
 
        //recently
222
 
        MouseArea {
223
 
            anchors.fill: parent
224
 
            enabled: true
225
 
            onPressed: {
226
 
                console.log("REQUESTING FOCUS ON VIEWPORT OF", root)
227
 
                viewportItem.focus = true
228
 
                mouse.accepted = false
229
 
            }
230
 
        }
231
 
        Scrollbar {
232
 
            id: horizontalScrollbar
233
 
            flickableItem: internal.flickableItem
234
 
            __viewport: viewportItem
235
 
            align: Qt.AlignBottom
236
 
            __buddyScrollbar: verticalScrollbar
237
 
            __alwaysOnScrollbars: false//alwaysOnScrollbars
238
 
            focus: false
239
 
        }
240
 
 
241
 
        Scrollbar {
242
 
            id: verticalScrollbar
243
 
            flickableItem: internal.flickableItem
244
 
            __viewport: viewportItem
245
 
            align: Qt.AlignTrailing
246
 
            __buddyScrollbar: horizontalScrollbar
247
 
            __alwaysOnScrollbars: false//alwaysOnScrollbars
248
 
            focus: false
249
 
        }
250
 
 
251
 
        Column {
252
 
            anchors.left: viewportItem.left
253
 
            anchors.right: viewportItem.right
254
 
            Text { color: root.activeFocus ? "red" : "black"; text:"ROOT focus " + root.focus + " activeFocus " + root.activeFocus; }
255
 
            Text { color: viewportItem.activeFocus ? "red" : "black"; text:"VIEWPORT focus " + viewportItem.focus + " activeFocus " + viewportItem.activeFocus; }
256
 
            Text { color: internal.activeFocus ? "red" : "black"; text:"INTERNAL focus " + internal.focus + " activeFocus " + internal.activeFocus; }
257
 
        }
258
 
 
 
144
        parent: root
 
145
        onPressed: {
 
146
            console.log("FORCING FOCUS ON SCROLLVIEW", root);
 
147
            root.forceActiveFocus();
 
148
            mouse.accepted = false
 
149
        }
 
150
    }
 
151
 
 
152
    Scrollbar {
 
153
        id: horizontalScrollbar
 
154
        //we don't want to be flickable's child, because that means we can't interact
 
155
        //with the scrollbar when flickable grabs the mouse (i.e. while flicking)
 
156
        parent: root
 
157
        flickableItem: root
 
158
        align: Qt.AlignBottom
 
159
        __buddyScrollbar: verticalScrollbar
 
160
        __alwaysOnScrollbars: false//alwaysOnScrollbars
 
161
        focus: false
 
162
    }
 
163
 
 
164
    Scrollbar {
 
165
        id: verticalScrollbar
 
166
        parent: root
 
167
        flickableItem: root
 
168
        align: Qt.AlignTrailing
 
169
        __buddyScrollbar: horizontalScrollbar
 
170
        __alwaysOnScrollbars: false//alwaysOnScrollbars
 
171
        focus: false
 
172
    }
 
173
 
 
174
    //DEBUG
 
175
    Text {
 
176
        anchors.left: parent.left
 
177
        z: 100
 
178
        color: root.activeFocus ? "red" : "black";
 
179
        text:"ROOT focus " + root.focus + " activeFocus " + root.activeFocus;
259
180
    }
260
181
}