libandroidplugin added
[mardrone] / mardrone / imports / com / nokia / android.1.1 / ToolBarLayout.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 // ToolBarLayout is a container for items on a toolbar that automatically
42 // implements an appropriate layout for its children.
43
44 import QtQuick 1.1
45 import "." 1.1
46
47 Item {
48     id: root
49
50     property bool backButton: true // deprecated
51     onBackButtonChanged: { console.log("warning: ToolBarLayout.backButton is deprecated.") }
52
53     implicitWidth: screen.width
54     implicitHeight: internal.defaultHeightToolBar
55
56     visible: false
57
58     Connections {
59         target: privateStyle
60         onLayoutParametersChanged: internal.layoutChildren()
61     }
62     Connections {
63         target: screen
64         onCurrentOrientationChanged: internal.layoutChildren()
65     }
66
67     QtObject {
68         id: internal
69         objectName: "internal"
70
71         property bool portrait: screen.width < screen.height
72
73         // These are the dynamic layout parameters used by the toolbar layout.
74         property real defaultHeightToolBar: portrait ?
75             privateStyle.toolBarHeightPortrait : privateStyle.toolBarHeightLandscape
76         property real defaultHeightToolButton: privateStyle.toolBarHeightLandscape
77         property real outerMarginHorizontal: portrait ?
78             0 : (2 * platformStyle.paddingLarge)
79         property real outerMarginButtonRowLong: portrait ?
80             platformStyle.paddingLarge : (3 * platformStyle.paddingLarge)
81         property real innerSpacingTextButtonSingle: portrait ?
82             platformStyle.paddingMedium + (3 * platformStyle.paddingLarge) : (3 * platformStyle.paddingLarge)
83         property real innerSpacingTextButtonDouble: portrait ?
84             platformStyle.paddingSmall : (3 * platformStyle.paddingLarge)
85         property real innerSpacingButtonRowTwoChildren: portrait ?
86             platformStyle.paddingMedium : (3 * platformStyle.paddingLarge)
87         property real innerSpacingButtonRowLong: portrait ?
88             platformStyle.paddingMedium : platformStyle.paddingLarge
89         property real centerSpacingTextButtonDouble: platformStyle.paddingLarge
90
91         function isIconButton(item) {
92             return item.hasOwnProperty("iconSource")
93                 && item.hasOwnProperty("text")
94                 && item.text == ""
95         }
96
97         function isTextToolButton(item) {
98             // ToolButton has both iconSource and flat property,
99             // Button only has iconSource
100             return (item.hasOwnProperty("iconSource")
101                     && item.iconSource == ""
102                     && item.hasOwnProperty("flat"))
103         }
104
105         function isButtonRow(item) {
106             return item.hasOwnProperty("checkedButton")
107         }
108
109         function buttonWidth(child) {
110             if ((isTextToolButton(child)) || !(child.hasOwnProperty("implicitWidth"))) {
111                 // ImplicitWidth for the ToolButton returns wrong value right after
112                 // orientation change, and also we want to override its own
113                 // layout width calculation, so use the actual width
114                 return child.width
115             }
116             return child.implicitWidth
117         }
118
119         function centerOffset(outerLength, innerLength) {
120             // calculate the offset of the leading edge of a centered child item
121             return Math.floor((outerLength - innerLength) / 2.0)
122         }
123
124         function widthTextButtonSingle(leftMargin, innerSpacing) {
125             // calculate the remaining width for a centered item
126             var outerContents = leftMargin + innerSpacing
127             return root.width - (outerContents * 2)
128         }
129
130         function widthTextButtonDouble(leftMargin, innerSpacing, centerSpacing) {
131             // calculate the space available when there are two items with a center
132             // margin, and share it between the two
133             var outerContents = leftMargin + innerSpacing
134             return Math.round((root.width - (outerContents * 2) - centerSpacing) / 2.0)
135         }
136
137         function widthButtonRowLong(leftButton, rightButton, itemMargin, innerSpacing, outerMargin) {
138             // calculate the width of a long button row, which is used in the case that there are more
139             // than three icons. If either left or right button is present, allocate the itemMargin to
140             // ensure that there is sufficient space; otherwise we can use the special
141             // outerMargin value
142             var leftContents = leftButton ? itemMargin + innerSpacing : outerMargin
143             var rightContents = rightButton ? itemMargin + innerSpacing : outerMargin
144             return root.width - leftContents - rightContents
145         }
146
147         function layoutChildren() {
148             var numChildren = children.length
149             if (parent == null || root.width == 0 || numChildren == 0)
150                 return
151
152             for (var i = 0; i < numChildren; ++i) {
153                 // make sure all the children have correct parent, height, and y
154                 children[i].parent = root
155                 if (isButtonRow(children[i])) {
156                     var buttonRow = children[i]
157                     // ButtonRow frame height is always tool bar's height in
158                     // landscape, regardless of the current orientation, so we need
159                     // to override the heights of the buttons within (because it's a
160                     // Row, so its height is based on its children)
161                     for (var j = 0; j < buttonRow.children.length; ++j) {
162                         buttonRow.children[j].implicitHeight = defaultHeightToolButton
163                     }
164                 }
165                 // child's vertical center always goes to middle of the toolbar
166                 var childHeight = children[i].hasOwnProperty("implicitHeight") ?
167                         children[i].implicitHeight : children[i].height
168                 children[i].y = root.y + centerOffset(root.implicitHeight, childHeight)
169             }
170
171             // detect whether we have left and or right items. we need to lay out
172             // the remaining children (that are not left or right items) whether they
173             // are tool buttons, text buttons or a button row
174             var leftItem = isIconButton(children[0]) ?
175                     children[0] : undefined
176             var rightItem = isIconButton(children[numChildren-1]) ?
177                     children[numChildren-1] : undefined
178             var childrenRemaining = numChildren - (leftItem != undefined ? 1 : 0) - (rightItem != undefined ? 1 : 0)
179             var firstRemainingIndex = leftItem != undefined ? 1 : 0
180             var lastRemainingIndex = rightItem != undefined ? (numChildren - 2) : (numChildren - 1)
181
182             // precalculate the margins for the left and right items, we will work
183             // out child sizes assuming they are present
184             var leftMargin = outerMarginHorizontal + defaultHeightToolBar
185             var rightMargin = root.width - leftMargin
186
187             // In the case of a lone remaining chlld, or in the case of 2 text
188             // buttons, we need to override the width
189             var overrideChildWidth = 0
190             for (var p = firstRemainingIndex; p <= lastRemainingIndex; p++) {
191                 var child = children[p]
192                 overrideChildWidth = buttonWidth(child)
193
194                 // If necessary, we calculate and override the width first before we
195                 // can calculate the x positions
196                 if ((isTextToolButton(child) && childrenRemaining == 1)
197                         || (isButtonRow(child) && child.children.length == 1)) {
198                     // we treat a button row with a single item like a single tool button,
199                     // but in landscape, calculate size as if there were two buttons
200                     overrideChildWidth = portrait ?
201                         widthTextButtonSingle(leftMargin, innerSpacingTextButtonSingle) :
202                         widthTextButtonDouble(leftMargin, innerSpacingTextButtonDouble, innerSpacingTextButtonDouble)
203
204                 } else if (isTextToolButton(child) && childrenRemaining == 2) {
205                     // special case of margins for two text buttons
206                     overrideChildWidth = widthTextButtonDouble(
207                                 leftMargin, innerSpacingTextButtonDouble, centerSpacingTextButtonDouble)
208
209                 } else if (isButtonRow(child) && ((child.children.length == 2)
210                               || (child.children.length > 2 && leftItem != undefined && rightItem != undefined))) {
211                     // there are special margins if the button row has two children,
212                     // or if it has more than two children and there is a left and
213                     // a right item
214                     overrideChildWidth = widthTextButtonSingle(
215                                 leftMargin, innerSpacingButtonRowTwoChildren)
216
217                 } else if (isButtonRow(child) && child.children.length > 2) {
218                     // the long button row has special margins, which are used on
219                     // either side if the side icon button is missing on that side. If the item is present,
220                     // the leftMargin can be used on either side to leave space for either icon button
221                     overrideChildWidth = widthButtonRowLong(
222                                 leftItem != undefined,
223                                 rightItem != undefined,
224                                 leftMargin,
225                                 innerSpacingButtonRowLong,
226                                 outerMarginButtonRowLong)
227                 }
228
229                 child.width = overrideChildWidth
230             }
231
232             if (numChildren == 1) {
233                 var loneChild = children[0]
234                 var loneChildWidth = buttonWidth(loneChild)
235                 if (isButtonRow(loneChild)) {
236                     loneChildWidth = overrideChildWidth
237                 }
238                 if (isIconButton(loneChild))
239                     loneChild.x = outerMarginHorizontal
240                 else
241                     loneChild.x = centerOffset(root.width, loneChildWidth)
242                 return
243             }
244
245             // we can easily calculate the positions of the left and right items,
246             // but if they are missing then correct the margins
247             if (leftItem != undefined){
248                 leftItem.x = outerMarginHorizontal
249             } else {
250                 leftMargin = 0
251             }
252             if (rightItem != undefined){
253                 rightItem.x = root.width - defaultHeightToolBar - outerMarginHorizontal
254             } else {
255                 rightMargin = root.width
256             }
257
258             if (!childrenRemaining)
259                 return;
260
261             if (childrenRemaining == 1) {
262                 var loneChild = children[firstRemainingIndex]
263                 var loneChildWidth = buttonWidth(loneChild)
264                 if (isButtonRow(loneChild)) {
265                     // ButtonRow should have the override width (but it won't have
266                     // been updated yet)
267                     loneChildWidth = overrideChildWidth
268                 }
269                 // lone child is always centered, unless it's a long button row on
270                 // one side only
271                 if (isButtonRow(loneChild) && loneChild.children.length >= 3
272                         && ((leftItem == undefined) != (rightItem == undefined))) {
273                     loneChild.x = (leftItem != undefined) ?
274                                 (leftMargin + innerSpacingButtonRowLong) : outerMarginButtonRowLong
275                 } else {
276                     loneChild.x = centerOffset(root.width, loneChildWidth)
277                 }
278             } else if (childrenRemaining == 2 && isTextToolButton(children[firstRemainingIndex])) {
279                 // text buttons are distributed around the center with a center spacing
280                 var midPoint = Math.floor(root.width / 2.0)
281                 var halfSpacing = Math.round(platformStyle.paddingLarge / 2.0)
282                 children[firstRemainingIndex].x = midPoint - halfSpacing - buttonWidth(children[firstRemainingIndex])
283                 children[firstRemainingIndex + 1].x = midPoint + halfSpacing
284             } else {
285                 // icon buttons are deployed evenly in the remaining space,
286                 // but we need to ensure that the spacings are integer values,
287                 // and share the rounding error to ensure that they are centered
288                 var remainingSpace = rightMargin - leftMargin
289                 var spacingNotRounded = remainingSpace
290                 for (var p = 0; p < childrenRemaining; p++) {
291                     var nextChild = children[leftItem != undefined ? p + 1 : p]
292                     spacingNotRounded -= buttonWidth(nextChild)
293                 }
294                 spacingNotRounded /= (childrenRemaining + 1)
295                 var spacing = Math.floor(spacingNotRounded)
296                 var totalRoundingError = (spacingNotRounded - spacing) * (childrenRemaining + 1)
297                 var curPos = leftMargin + Math.floor(totalRoundingError / 2.0)
298
299                 for (var p = 0; p < childrenRemaining; p++) {
300                     var nextChild = children[leftItem != undefined ? p + 1 : p]
301                     curPos += spacing
302                     nextChild.x = curPos
303                     curPos += buttonWidth(nextChild)
304                 }
305             }
306         }
307     }
308
309     Component.onCompleted: internal.layoutChildren()
310     onParentChanged: internal.layoutChildren()
311     onChildrenChanged: internal.layoutChildren()
312     onImplicitWidthChanged: internal.layoutChildren()
313     onImplicitHeightChanged: internal.layoutChildren()
314 }