improved desktop UI
[mardrone] / mardrone / imports / com / nokia / meego / ScrollDecorator.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Components project.
8 **
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 import QtQuick 1.1
42 import "." 1.0
43 import "UIConstants.js" as UI
44
45 /**
46   * Class: ScrollDecorator
47   * A ScrollDecorator shows the current position in a scrollable area.
48   */
49 Item {
50     id: root
51
52     /**
53       * Property: flickableItem
54       * [Flickable] The Item that should show the ScrollDecorator
55       */
56     property Flickable flickableItem
57
58     property int __topPageMargin: 0
59     property int __bottomPageMargin: 0
60     property int __leftPageMargin: 0
61     property int __rightPageMargin: 0
62     property bool __hasPageWidth : false
63     property bool __hasPageHeight: false
64
65     // These can also be modified (but probably shouldn't)
66     property int __minIndicatorSize: 20
67     property int __hideTimeout: 500
68
69     property bool __alwaysShowIndicator: true
70
71     property Style platformStyle: ScrollDecoratorStyle{}
72
73     //Deprecated, TODO Remove this on w13
74     property alias style: root.platformStyle
75
76     // This function ensures that we allways anchor the decorator correctly according
77     // to the page margins.
78     function __updatePageMargin() {
79         if (!flickableItem)
80             return
81         var p = flickableItem.parent
82         while (p) {
83             if (p.hasOwnProperty("__isPage")) {
84                 __hasPageHeight = function() { return p.height == flickableItem.height }
85                 __hasPageWidth = function() { return p.width == flickableItem.width }
86                 __topPageMargin = function() { return p.anchors.topMargin }
87                 __bottomPageMargin = function() { return p.anchors.bottomMargin }
88                 __leftPageMargin = function() { return p.anchors.leftMargin }
89                 __rightPageMargin = function() { return p.anchors.rightMargin }
90                 return;
91             } else {
92                 p = p.parent;
93             }
94         }
95     }
96
97     onFlickableItemChanged: { __updatePageMargin() }
98
99     QtObject {
100         id: privateApi
101         function canFlick(direction) {
102            return flickableItem.flickableDirection === direction
103                   || flickableItem.flickableDirection === Flickable.HorizontalAndVerticalFlick
104                   || flickableItem.flickableDirection === Flickable.AutoFlickDirection;
105         }
106     }
107
108     // Private stuff
109     anchors.fill: flickableItem
110
111     Timer {
112         // Hack to have the indicators flash when the view is shown the first time.
113         // Ideally we would wait until the Flickable is complete, but it doesn't look
114         // possible given the current limitations of QML.
115         interval: 100
116         running: true
117         repeat: false
118         onTriggered: {
119             if (verticalIndicator.shouldShow) {
120                 verticalIndicator.state = "visible";
121                 verticalIndicator.state = "";
122             }
123             if (horizontalIndicator.shouldShow) {
124                 horizontalIndicator.state = "visible";
125                 horizontalIndicator.state = "";
126             }
127         }
128     }
129
130     Component {
131        id: verticalSizerWrapper
132         ScrollDecoratorSizerCPP {
133             id: verticalSizer
134             positionRatio: flickableItem ? flickableItem.visibleArea.yPosition : 0
135             sizeRatio: flickableItem ? flickableItem.visibleArea.heightRatio : 0
136             maxPosition: flickableItem ? flickableItem.height : 0
137             minSize: __minIndicatorSize
138         }
139    }
140
141     Component {
142         id: horizontalSizerWrapper
143         ScrollDecoratorSizerCPP {
144             id: horizontalSizer
145             positionRatio: flickableItem ? flickableItem.visibleArea.xPosition : 0
146             sizeRatio: flickableItem ? flickableItem.visibleArea.widthRatio : 0
147             maxPosition: flickableItem ? flickableItem.width : 0
148             minSize: __minIndicatorSize
149         }
150     }
151
152     Loader {id: verticalSizerLoader}
153     Loader {id: horizontalSizerLoader}
154
155     Item {
156         id: verticalIndicator
157         property bool shouldShow: flickableItem != null && ((__alwaysShowIndicator && privateApi.canFlick(Flickable.VerticalFlick)) && (flickableItem.height > 0 && flickableItem.contentHeight > flickableItem.height))
158         opacity: 0
159         anchors.right: parent.right
160         anchors.rightMargin: UI.SCROLLDECORATOR_LONG_MARGIN - (__hasPageWidth ? __rightPageMargin : 0)
161         anchors.top: parent.top
162         anchors.topMargin: UI.SCROLLDECORATOR_SHORT_MARGIN - (__hasPageWidth ? __topPageMargin : 0)
163         anchors.bottom: parent.bottom
164         anchors.bottomMargin: UI.SCROLLDECORATOR_SHORT_MARGIN - (__hasPageWidth ? __bottomPageMargin : 0)
165
166         onShouldShowChanged: {
167             if (shouldShow)
168                 verticalSizerLoader.sourceComponent = verticalSizerWrapper;
169             else
170                 verticalSizerLoader.sourceComponent = undefined;
171         }
172
173         Image {
174             source: platformStyle.background
175             height: parent.height
176             anchors.right: parent.right
177         }
178         BorderImage {
179             source: platformStyle.indicator
180             border { left: 2; top: 4; right: 2; bottom: 4 }
181             anchors.right: parent.right
182             y:      verticalIndicator.shouldShow && verticalSizerLoader.status == Loader.Ready ? verticalSizerLoader.item.position : 0
183             height: verticalIndicator.shouldShow && verticalSizerLoader.status == Loader.Ready ?
184                     verticalSizerLoader.item.size - parent.anchors.topMargin - parent.anchors.bottomMargin : 0
185         }
186
187         states: State {
188             name: "visible"
189             when: verticalIndicator.shouldShow && flickableItem.moving
190             PropertyChanges {
191                 target: verticalIndicator
192                 opacity: 1
193             }
194         }
195
196         transitions: Transition {
197             from: "visible"; to: ""
198             NumberAnimation {
199                 properties: "opacity"
200                 duration: root.__hideTimeout
201             }
202         }
203     }
204
205     Item {
206         id: horizontalIndicator
207         property bool shouldShow: flickableItem != null && ((__alwaysShowIndicator && privateApi.canFlick(Flickable.HorizontalFlick)) && (flickableItem.width > 0 && flickableItem.contentWidth > flickableItem.width))
208         opacity: 0
209         anchors.bottom: parent.bottom
210         anchors.bottomMargin: UI.SCROLLDECORATOR_LONG_MARGIN - (__hasPageHeight  ? __bottomPageMargin : 0)
211         anchors.right: parent.right
212         anchors.rightMargin: UI.SCROLLDECORATOR_SHORT_MARGIN - (__hasPageHeight  ? __rightPageMargin : 0)
213         anchors.left: parent.left
214         anchors.leftMargin: UI.SCROLLDECORATOR_SHORT_MARGIN - (__hasPageHeight  ? __leftPageMargin : 0)
215
216         onShouldShowChanged: {
217             if (shouldShow)
218                 horizontalSizerLoader.sourceComponent = horizontalSizerWrapper;
219             else
220                 horizontalSizerLoader.sourceComponent = undefined;
221         }
222
223         Image {
224             source: platformStyle.backgroundHorizontal
225             width: parent.width
226             anchors.left: parent.left
227             anchors.bottom: parent.bottom
228         }
229         BorderImage {
230             source: platformStyle.indicatorHorizontal
231             border { left: 4; top: 2; right: 4; bottom: 2 }
232             anchors.bottom: parent.bottom
233             x:     horizontalIndicator.shouldShow && horizontalSizerLoader.status == Loader.Ready ? horizontalSizerLoader.item.position : 0
234             width: horizontalIndicator.shouldShow && horizontalSizerLoader.status == Loader.Ready ?
235                     horizontalSizerLoader.item.size - parent.anchors.leftMargin - parent.anchors.rightMargin : 0
236         }
237
238         states: State {
239             name: "visible"
240             when: horizontalIndicator.shouldShow && flickableItem.moving
241             PropertyChanges {
242                 target: horizontalIndicator
243                 opacity: 1
244             }
245         }
246
247         transitions: Transition {
248             from: "visible"; to: ""
249             NumberAnimation {
250                 properties: "opacity"
251                 duration: root.__hideTimeout
252             }
253         }
254     }
255 }
256