From: Kate Alhola Date: Mon, 3 Sep 2012 18:10:08 +0000 (+0300) Subject: added android components X-Git-Url: http://git.maemo.org/git/?p=mardrone;a=commitdiff_plain;h=423f5226eddc5c327ef47559c03b0e2775add19e;ds=sidebyside added android components --- diff --git a/mardrone/imports/com/nokia/android.1.1/AppManager.js b/mardrone/imports/com/nokia/android.1.1/AppManager.js new file mode 100644 index 0000000..daed77a --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/AppManager.js @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function rootObject() { + var next = parent + while (next && next.parent) + next = next.parent + return next +} + +function findParent(child, propertyName) { + if (!child) + return null + var next = child.parent + while (next && !next.hasOwnProperty(propertyName)) + next = next.parent + return next +} + +function sceneX(item) { + // Binding may cause that this function is evaluated even when item is undefined, + // but in that case the Binding isn't active however so we can safely return 0 + var x = 0 + if (item) { + x = item.x + var p = item.parent + while (p) { + x += p.x + p = p.parent + } + } + return x +} + +function sceneY(item) { + // Binding may cause that this function is evaluated even when item is undefined, + // but in that case the Binding isn't active however so we can safely return 0 + var y = 0 + if (item) { + y = item.y + var p = item.parent + while (p) { + y += p.y + p = p.parent + } + } + return y +} diff --git a/mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml b/mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml new file mode 100644 index 0000000..9edc9a5 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Window { + id: root + + property bool fullScreen: false + default property alias content: contentItem.data + property alias pageStack: stack + property variant initialPage + property bool softwareInputPanelEnabled: false + + onInitialPageChanged: { + if (initialPage && contentArea.initialized) { + if (stack.depth == 0) + stack.push(initialPage, null, true) + else if (stack.depth == 1) + stack.replace(initialPage, null, true) + else + console.log("Cannot update ApplicationWindow.initialPage") + } + } + + Component.onCompleted: { + console.log("warning: ApplicationWindow is an experimental component. Use Window instead.") + contentArea.initialized = true + if (initialPage && stack.depth == 0) + stack.push(initialPage, null, true) + } + + Item { + id: contentArea + + property bool initialized: false + + anchors.top: sbar.bottom + anchors.bottom: sip.top + anchors.left: parent.left + anchors.right: parent.right + Item { + id: contentItem + anchors.fill: parent + PageStack { + id: stack + anchors.fill: parent + toolBar: tbar + } + } + } + + StatusBar { + id: sbar + + width: parent.width + state: root.fullScreen ? "Hidden" : "Visible" + platformInverted: root.platformInverted + + states: [ + State { + name: "Visible" + PropertyChanges { target: sbar; y: 0; opacity: 1 } + }, + State { + name: "Hidden" + PropertyChanges { target: sbar; y: -height; opacity: 0 } + } + ] + + transitions: [ + Transition { + from: "Hidden"; to: "Visible" + ParallelAnimation { + NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad } + NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + }, + Transition { + from: "Visible"; to: "Hidden" + ParallelAnimation { + NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.Linear } + NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + } + ] + } + + Item { + id: sip + + anchors { bottom: parent.bottom; left: parent.left; right: parent.right } + + Behavior on height { PropertyAnimation { duration: 200 } } + + states: [ + State { + name: "Visible"; when: inputContext.visible && softwareInputPanelEnabled + PropertyChanges { target: sip; height: inputContext.height } + }, + + State { + name: "Hidden"; when: !root.fullScreen + PropertyChanges { target: sip; height: tbar.height } + }, + + State { + name: "HiddenInFullScreen"; when: root.fullScreen + PropertyChanges { target: sip; height: 0 } + } + ] + } + + ToolBar { + id: tbar + + width: parent.width + state: root.fullScreen ? "Hidden" : "Visible" + platformInverted: root.platformInverted + + states: [ + State { + name: "Visible" + PropertyChanges { target: tbar; y: parent.height - height; opacity: 1 } + }, + State { + name: "Hidden" + PropertyChanges { target: tbar; y: parent.height; opacity: 0 } + } + ] + + transitions: [ + Transition { + from: "Hidden"; to: "Visible" + ParallelAnimation { + NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad } + NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + }, + Transition { + from: "Visible"; to: "Hidden" + ParallelAnimation { + NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.Linear } + NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + } + ] + } + + // event preventer when page transition is active + MouseArea { + anchors.fill: parent + enabled: pageStack.busy + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml b/mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml new file mode 100644 index 0000000..cb75863 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import com.nokia.android 1.1 +import "." 1.1 + +Item { + id: root + + property bool running: false + + // Symbian specific API + property bool platformInverted: false + + implicitWidth: platformStyle.graphicSizeSmall + implicitHeight: platformStyle.graphicSizeSmall + + Image { + id: spinner + property int index: 1 + + // cannot use anchors.fill here because the size will be 0 during + // construction and that gives out nasty debug warnings + width: parent.width + height: parent.height + sourceSize.width: width + sourceSize.height: height + source: privateStyle.imagePath("qtg_anim_spinner_large_" + index, + root.platformInverted) + smooth: true + + NumberAnimation on index { + id: numAni + from: 1; to: 10 + duration: 1000 + running: root.visible + // QTBUG-19080 is preventing the following line from working + // We will have to use workaround for now + // http://bugreports.qt.nokia.com/browse/QTBUG-19080 + // paused: !root.running || !android.foreground + loops: Animation.Infinite + } + + // START workaround for QTBUG-19080 + Component { + id: bindingCom + Binding { + target: numAni + property: "paused" + value: numAni.running ? (!root.running || !android.foreground) : false + } + } + Component.onCompleted: bindingCom.createObject(numAni) + // END workaround + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Button.qml b/mardrone/imports/com/nokia/android.1.1/Button.qml new file mode 100644 index 0000000..de6d101 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Button.qml @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: button + + // Common Public API + property bool checked: false + property bool checkable: false + property bool pressed: (stateGroup.state == "Pressed" || stateGroup.state == "AutoRepeating") && mouseArea.containsMouse + property alias text: label.text + property alias iconSource: icon.source + property alias font: label.font + + signal clicked + + // Symbian specific signals and properties + signal platformReleased + signal platformPressAndHold + + // specific API + property bool platformInverted: false + property bool platformAutoRepeat: true + + implicitWidth: Math.max(container.contentWidth + 2 * internal.horizontalPadding, privateStyle.buttonSize) + implicitHeight: Math.max(container.contentHeight + 2 * internal.verticalPadding, privateStyle.buttonSize) + + QtObject { + id: internal + objectName: "internal" + + property int autoRepeatInterval: 60 + property int verticalPadding: (privateStyle.buttonSize - platformStyle.graphicSizeSmall) / 2 + property int horizontalPadding: label.text ? platformStyle.paddingLarge : verticalPadding + + // "pressed" is a transient state, see press() function + function modeName() { + if (belongsToButtonRow()) + return parent.privateModeName(button, 0) + else if (!button.enabled) + return "disabled" + else if (button.checked) + return "latched" + else + return "normal" + } + + function toggleChecked() { + if (checkable) + checked = !checked + } + + function press() { + if (!belongsToButtonGroup()) { + if (checkable && checked) + privateStyle.play(Android.SensitiveButton) + else + privateStyle.play(Android.BasicButton) + } else if (checkable && !checked) { + privateStyle.play(Android.BasicButton) + } + highlight.source = privateStyle.imagePath(internal.imageName() + "pressed", + button.platformInverted) + container.scale = 0.95 + highlight.opacity = 1 + } + + function release() { + container.scale = 1 + highlight.opacity = 0 + if (tapRepeatTimer.running) + tapRepeatTimer.stop() + button.platformReleased() + } + + function click() { + if ((checkable && checked && !belongsToButtonGroup()) || !checkable) + privateStyle.play(Android.BasicButton) + internal.toggleChecked() + clickedEffect.restart() + button.clicked() + } + + function repeat() { + if (!checkable) + privateStyle.play(Android.SensitiveButton) + button.clicked() + } + + // The function imageName() handles fetching correct graphics for the Button. + // If the parent of a Button is ButtonRow, segmented-style graphics are used to create a + // seamless row of buttons. Otherwise normal Button graphics are utilized. + function imageName() { + var mirror = button.LayoutMirroring.enabled // To create binding + if (belongsToButtonRow()) + return parent.privateGraphicsName(button, 0) + return "qtg_fr_pushbutton_" + } + + function belongsToButtonGroup() { + return button.parent + && button.parent.hasOwnProperty("checkedButton") + && button.parent.exclusive + } + + function belongsToButtonRow() { + return button.parent + && button.parent.hasOwnProperty("checkedButton") + && button.parent.hasOwnProperty("privateDirection") + && button.parent.privateDirection == Qt.Horizontal + && button.parent.children.length > 1 + } + } + + StateGroup { + id: stateGroup + + states: [ + State { name: "Pressed" }, + State { name: "AutoRepeating" }, + State { name: "Canceled" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: internal.press() } + }, + Transition { + from: "Pressed" + to: "AutoRepeating" + ScriptAction { script: tapRepeatTimer.start() } + }, + Transition { + from: "Pressed" + to: "" + ScriptAction { script: internal.release() } + ScriptAction { script: internal.click() } + }, + Transition { + from: "Pressed" + to: "Canceled" + ScriptAction { script: internal.release() } + }, + Transition { + from: "AutoRepeating" + ScriptAction { script: internal.release() } + } + ] + } + + BorderImage { + source: privateStyle.imagePath(internal.imageName() + internal.modeName(), + button.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + anchors.fill: parent + + BorderImage { + id: highlight + border { left: 20; top: 20; right: 20; bottom: 20 } + opacity: 0 + anchors.fill: parent + } + } + + Item { + id: container + + // Having both icon and text simultaneously is unspecified but supported by implementation + property int spacing: (icon.height && label.text) ? platformStyle.paddingSmall : 0 + property int contentWidth: Math.max(icon.width, label.textWidth) + property int contentHeight: icon.height + spacing + label.height + + width: Math.min(contentWidth, button.width - 2 * internal.horizontalPadding) + height: Math.min(contentHeight, button.height - 2 * internal.verticalPadding) + clip: true + anchors.centerIn: parent + + Image { + id: icon + sourceSize.width: platformStyle.graphicSizeSmall + sourceSize.height: platformStyle.graphicSizeSmall + smooth: true + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + } + Text { + id: label + elide: Text.ElideRight + property int textWidth: text ? privateStyle.textWidth(text, font) : 0 + anchors { + top: icon.bottom + topMargin: parent.spacing + left: parent.left + right: parent.right + } + height: text ? privateStyle.fontHeight(font) : 0 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge } + color: { + if (!button.enabled) + return button.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + else if (button.pressed) + return button.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + else if (button.checked) + return button.platformInverted ? platformStyle.colorLatchedInverted + : platformStyle.colorLatched + else + return button.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + } + } + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + + onPressed: stateGroup.state = "Pressed" + + onReleased: stateGroup.state = "" + + onCanceled: { + // Mark as canceled + stateGroup.state = "Canceled" + // Reset state. Can't expect a release since mouse was ungrabbed + stateGroup.state = "" + } + + onPressAndHold: { + if (stateGroup.state != "Canceled" && platformAutoRepeat) + stateGroup.state = "AutoRepeating" + button.platformPressAndHold() + } + + onExited: stateGroup.state = "Canceled" + } + + Timer { + id: tapRepeatTimer + + interval: internal.autoRepeatInterval; running: false; repeat: true + onTriggered: internal.repeat() + } + + ParallelAnimation { + id: clickedEffect + PropertyAnimation { + target: container + property: "scale" + from: 0.95 + to: 1.0 + easing.type: Easing.Linear + duration: 100 + } + PropertyAnimation { + target: highlight + property: "opacity" + from: 1 + to: 0 + easing.type: Easing.Linear + duration: 150 + } + } + + Keys.onPressed: { + if ((!event.isAutoRepeat || platformAutoRepeat) + && (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) { + stateGroup.state = "Pressed" + event.accepted = true + } + } + + Keys.onReleased: { + if ((!event.isAutoRepeat || platformAutoRepeat) + && (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) { + stateGroup.state = "" + event.accepted = true + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml b/mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml new file mode 100644 index 0000000..e545cee --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "ButtonGroup.js" as Engine + +Column { + id: root + + // Common public API + property bool exclusive: true + property Item checkedButton + + property int privateDirection: Qt.Vertical + + Component.onCompleted: Engine.create(root) + Component.onDestruction: Engine.destroy() +} diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonGroup.js b/mardrone/imports/com/nokia/android.1.1/ButtonGroup.js new file mode 100644 index 0000000..3843719 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ButtonGroup.js @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var self +var clickHandlers = [] +var visibleButtons = [] +var resizing = false +var checkingOverallExclusivity = false +var checkedBtn + +function create(that) { + destroy() + self = that + + // If the item is not visible at this stage, we store the value of the property + // checkedButton to ensure a proper initialization. The value is restored in + // initCheckedButton(). + if (!self.visible) + checkedBtn = self.checkedButton + + buildItems() + self.childrenChanged.connect(buildItems) + self.widthChanged.connect(resizeButtons) + self.exclusiveChanged.connect(checkOverallExclusivity) + self.checkedButtonChanged.connect(checkOverallExclusivity) + self.visibleChanged.connect(initCheckedButton) +} + +function destroy() { + if (self !== undefined) { + self.childrenChanged.disconnect(buildItems) + self.widthChanged.disconnect(resizeButtons) + self.exclusiveChanged.disconnect(checkOverallExclusivity) + self.checkedButtonChanged.disconnect(checkOverallExclusivity) + self.visibleChanged.disconnect(initCheckedButton) + releaseItemConnections() + self = undefined + } +} + +function initCheckedButton() { + // When the item becomes visible, restore the value of checkedButton property + // that was stored in the create function. + if (self.visible && checkedBtn !== null) { + self.checkedButton = checkedBtn + checkedBtn = null + } +} + +function buildItems() { + releaseItemConnections() + visibleButtons = [] + for (var i = 0; i < self.children.length; i++) { + var item = self.children[i] + + // set up item connections + clickHandlers[i] = checkExclusive(item) + item.clicked.connect(clickHandlers[i]) + item.visibleChanged.connect(buildItems) + + // update visibleButtons array + if (item.visible) + visibleButtons.push(item) + } + checkOverallExclusivity() + resizeButtons() +} + +function releaseItemConnections() { + for (var i = 0; i < self.children.length; i++) { + self.children[i].clicked.disconnect(clickHandlers[i]) + self.children[i].visibleChanged.disconnect(buildItems) + } + clickHandlers = [] +} + +function checkOverallExclusivity() { + if (!checkingOverallExclusivity && self.exclusive) { + // prevent re-entrant calls + checkingOverallExclusivity = true + if (visibleButtons.length > 0) { + if ((self.checkedButton === null || !self.checkedButton.visible)) + self.checkedButton = visibleButtons[0] + self.checkedButton.checked = true + } + else { + self.checkedButton = null + } + + for (var i = 0; i < self.children.length; i++) { + var item = self.children[i] + // e.g CheckBox can be added to ButtonGroup but doesn't have "checkable" property + if (self.exclusive && item.hasOwnProperty("checkable")) + item.checkable = true + if (item !== self.checkedButton) + item.checked = false + } + checkingOverallExclusivity = false + } +} + +function checkExclusive(item) { + var button = item + return function() { + if (self.exclusive) { + for (var i = 0, ref; (ref = visibleButtons[i]); i++) + ref.checked = button === ref + } + self.checkedButton = button + } +} + +function resizeButtons() { + if (!resizing && visibleButtons.length && self.privateDirection == Qt.Horizontal) { + // if ButtonRow has implicit size, a loop may occur where resizing individual + // Button affects ButtonRow size, which triggers again resizing... + // therefore we prevent re-entrant resizing attempts + resizing = true + var buttonWidth = self.width / visibleButtons.length + for (var i = 0; i < self.children.length; i++) { + self.children[i].width = self.children[i].visible ? buttonWidth : 0 + } + resizing = false + } +} + +// Binding would not work properly if visibleButtons would just be returned, +// it would miss visibility changes. In the long run ButtonGroup.js could be +// refactored. +function visibleItems(item) { + var visibleChildren = [] + for (var i = 0; i < item.children.length; i++) { + if (item.children[i].visible) + visibleChildren.push(item.children[i]) + } + return visibleChildren +} diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonRow.qml b/mardrone/imports/com/nokia/android.1.1/ButtonRow.qml new file mode 100644 index 0000000..df75132 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ButtonRow.qml @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "ButtonGroup.js" as Engine + +Row { + id: root + + // Common public API + property bool exclusive: true + property Item checkedButton + + property int privateDirection: Qt.Horizontal + + function privateGraphicsName(button, buttonType) { + var siblingButtons = Engine.visibleItems(root) + var imageName = "" + var rightToLeft = root.LayoutMirroring.enabled + ? (root.layoutDirection == Qt.LeftToRight) + : (root.layoutDirection == Qt.RightToLeft) + if (siblingButtons.length > 1) { + if (buttonType == 0) + imageName = "qtg_fr_pushbutton_segmented" + else if (buttonType == 1) + imageName = "qtg_fr_toolbutton_segmented" + + if (button === siblingButtons[0]) + imageName += rightToLeft ? "_r_" : "_l_" + else if (button === siblingButtons[siblingButtons.length - 1]) + imageName += rightToLeft ? "_l_" : "_r_" + else + imageName += "_c_" + } + else if (buttonType == 0) + imageName = "qtg_fr_pushbutton_" + else if (buttonType == 1) + imageName = "qtg_fr_toolbutton_text_" + + return imageName + } + + function privateModeName(button, buttonType) { + var siblingButtons = Engine.visibleItems(root) + if (!button.enabled) + return "disabled" + else if (button.checked && siblingButtons.length > 1) + return "latched" + else + return "normal" + } + + Component.onCompleted: Engine.create(root) + Component.onDestruction: Engine.destroy() +} diff --git a/mardrone/imports/com/nokia/android.1.1/CheckBox.qml b/mardrone/imports/com/nokia/android.1.1/CheckBox.qml new file mode 100644 index 0000000..e42e8b6 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/CheckBox.qml @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + // Common Public API + property bool checked: false + property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed" + + signal clicked + + property alias text: label.text + + // Symbian specific API + property bool platformInverted: false + + // Performance optimization: + // Use value assignment when property changes instead of binding to js function + onCheckedChanged: { contentIcon.source = internal.iconSource() } + onPressedChanged: { contentIcon.source = internal.iconSource() } + onEnabledChanged: { contentIcon.source = internal.iconSource() } + onPlatformInvertedChanged: { contentIcon.source = internal.iconSource() } + + QtObject { + id: internal + objectName: "internal" + property color disabledColor: root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + property color pressedColor: root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + property color normalColor: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + + function iconSource() { + var id + if (!root.enabled) { + if (root.checked) + id = "disabled_selected" + else + id = "disabled_unselected" + } else { + if (root.pressed) + id = "pressed" + else if (root.checked) + id = "normal_selected" + else + id = "normal_unselected" + } + return privateStyle.imagePath("qtg_graf_checkbox_" + id, root.platformInverted) + } + + function toggle() { + clickedEffect.restart() + root.checked = !root.checked + root.clicked() + } + } + + StateGroup { + id: stateGroup + + states: [ + State { name: "Pressed" }, + State { name: "KeyPressed" }, + State { name: "Canceled" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: privateStyle.play(Android.BasicItem) } + }, + Transition { + from: "Pressed" + to: "" + ScriptAction { script: privateStyle.play(Android.CheckBox) } + ScriptAction { script: internal.toggle() } + }, + Transition { + from: "KeyPressed" + to: "" + ScriptAction { script: internal.toggle() } + } + + ] + } + + implicitWidth: label.text ? privateStyle.buttonSize + platformStyle.paddingMedium + privateStyle.textWidth(label.text, label.font) + : privateStyle.buttonSize + implicitHeight: privateStyle.buttonSize + + Image { + id: contentIcon + source: privateStyle.imagePath("qtg_graf_checkbox_normal_unselected", root.platformInverted); + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + sourceSize.width: privateStyle.buttonSize + sourceSize.height: privateStyle.buttonSize + smooth: true + + MouseArea { + id: mouseArea + anchors.fill: parent + + onPressed: stateGroup.state = "Pressed" + onReleased: stateGroup.state = "" + onClicked: stateGroup.state = "" + onExited: stateGroup.state = "Canceled" + onCanceled: { + // Mark as canceled + stateGroup.state = "Canceled" + // Reset state. Can't expect a release since mouse was ungrabbed + stateGroup.state = "" + } + } + } + + Text { + id: label + anchors.left: contentIcon.right + anchors.leftMargin: text ? platformStyle.paddingMedium : 0 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + color: root.enabled ? (root.pressed ? internal.pressedColor : internal.normalColor) + : internal.disabledColor + } + + SequentialAnimation { + id: clickedEffect + PropertyAnimation { + target: contentIcon + property: "scale" + from: 1.0 + to: 0.8 + easing.type: Easing.Linear + duration: 50 + } + PropertyAnimation { + target: contentIcon + property: "scale" + from: 0.8 + to: 1.0 + easing.type: Easing.OutQuad + duration: 170 + } + } + + Keys.onPressed: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "KeyPressed" + event.accepted = true + } + } + + Keys.onReleased: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "" + event.accepted = true + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/CommonDialog.qml b/mardrone/imports/com/nokia/android.1.1/CommonDialog.qml new file mode 100644 index 0000000..045c74d --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/CommonDialog.qml @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Dialog { + id: root + + property alias titleText: titleAreaText.text + property url titleIcon + property variant buttonTexts: [] + property bool privateCloseIcon: false + + signal buttonClicked(int index) + + onButtonTextsChanged: { + for (var i = buttonRow.children.length; i > 0; --i) { + buttonRow.children[i - 1].destroy() + } + for (var j = 0; j < buttonTexts.length; ++j) { + var button = buttonComponent.createObject(buttonRow) + button.text = buttonTexts[j] + button.index = j + } + } + + Component { + id: buttonComponent + ToolButton { + property int index + + width: internal.buttonWidth() + height: privateStyle.toolBarHeightLandscape + platformInverted: root.platformInverted + + onClicked: { + if (root.status == DialogStatus.Open) { + root.buttonClicked(index) + root.close() + } + } + } + } + + QtObject { + id: internal + + function buttonWidth() { + switch (buttonTexts.length) { + case 0: return 0 + case 1: return Math.round((privateStyle.dialogMaxSize - 3 * platformStyle.paddingMedium) / 2) + default: return (buttonContainer.width - (buttonTexts.length + 1) * + platformStyle.paddingMedium) / buttonTexts.length + } + } + + function iconSource() { + if (privateCloseIcon) { + return privateStyle.imagePath((iconMouseArea.pressed && !iconMouseArea.pressCancelled + ? "qtg_graf_popup_close_pressed" + : "qtg_graf_popup_close_normal"), + root.platformInverted) + } else { + return root.titleIcon + } + } + } + + title: Item { + anchors.left: parent.left + anchors.right: parent.right + height: platformStyle.graphicSizeSmall + 2 * platformStyle.paddingLarge + + LayoutMirroring.enabled: privateCloseIcon ? false : undefined + LayoutMirroring.childrenInherit: true + + Item { + id: titleLayoutHelper // needed to make the text mirror correctly + + anchors.left: parent.left + anchors.right: titleAreaIcon.source == "" ? parent.right : titleAreaIcon.left + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: platformStyle.paddingLarge + + Text { + id: titleAreaText + + LayoutMirroring.enabled: root.LayoutMirroring.enabled + + anchors.fill: parent + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge } + color: root.platformInverted ? platformStyle.colorNormalLinkInverted + : platformStyle.colorNormalLink + elide: Text.ElideRight + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Image { + id: titleAreaIcon + + anchors.right: parent.right + anchors.rightMargin: platformStyle.paddingLarge + anchors.verticalCenter: parent.verticalCenter + source: internal.iconSource() + sourceSize.height: platformStyle.graphicSizeSmall + sourceSize.width: platformStyle.graphicSizeSmall + + MouseArea { + id: iconMouseArea + + property bool pressCancelled + + anchors.centerIn: parent + width: parent.width + 2 * platformStyle.paddingLarge + height: parent.height + 2 * platformStyle.paddingLarge + enabled: privateCloseIcon && root.status == DialogStatus.Open + + onPressed: { + pressCancelled = false + privateStyle.play(Android.BasicButton) + } + onClicked: { + if (!pressCancelled) + root.reject() + } + onReleased: { + if (!pressCancelled) + privateStyle.play(Android.PopupClose) + } + onExited: pressCancelled = true + } + } + } + + buttons: Item { + id: buttonContainer + + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + + width: parent.width + height: buttonTexts.length ? privateStyle.toolBarHeightLandscape + 2 * platformStyle.paddingSmall : 0 + + Row { + id: buttonRow + objectName: "buttonRow" + anchors.centerIn: parent + spacing: platformStyle.paddingMedium + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ContextMenu.qml b/mardrone/imports/com/nokia/android.1.1/ContextMenu.qml new file mode 100644 index 0000000..508ca5c --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ContextMenu.qml @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + default property alias content: menu.content + property Item visualParent: null + property alias status: popup.status + + // Symbian specific API + property bool platformInverted: false + + function open() { + popup.open() + } + + function close() { + popup.close() + } + + visible: false + implicitWidth: { + if (screen.width < screen.height) + screen.width - 2 * platformStyle.paddingLarge + else + privateStyle.dialogMaxSize + } + + Popup { + id: popup + + animationDuration: 250 + state: "Hidden" + anchors.centerIn: parent + visible: status != DialogStatus.Closed + enabled: status == DialogStatus.Open + width: root.width + height: menu.height + platformInverted: root.platformInverted + + onFaderClicked: { + privateStyle.play(Android.PopupClose) + close() + } + + MenuContent { + id: menu + containingPopup: popup + width: parent.width + platformInverted: root.platformInverted + onItemClicked: popup.close() + } + + states: [ + State { + name: "Hidden" + when: status == DialogStatus.Closing || status == DialogStatus.Closed + PropertyChanges { target: popup; opacity: 0 } + }, + State { + name: "Visible" + when: status == DialogStatus.Opening || status == DialogStatus.Open + PropertyChanges { target: popup; opacity: 1 } + } + ] + + transitions: [ + Transition { + from: "Visible"; to: "Hidden" + SequentialAnimation { + NumberAnimation { properties: "opacity"; duration: popup.animationDuration; easing.type: Easing.Linear } + PropertyAction { target: popup; property: "status"; value: DialogStatus.Closed } + } + }, + Transition { + from: "Hidden"; to: "Visible" + SequentialAnimation { + NumberAnimation { properties: "opacity"; duration: popup.animationDuration; easing.type: Easing.Linear } + PropertyAction { target: popup; property: "status"; value: DialogStatus.Open } + } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Dialog.qml b/mardrone/imports/com/nokia/android.1.1/Dialog.qml new file mode 100644 index 0000000..bb0da32 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Dialog.qml @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property alias title: titleItem.children + property alias content: contentItem.children + property alias buttons: buttonItem.children + property alias visualParent: dialog.visualParent + property alias status: dialog.status + + // Symbian specific API + property bool platformInverted: false + // read-only + property int platformContentMaximumWidth: dialog.width + // read-only + property int platformContentMaximumHeight: + dialog.maxHeight() - titleBar.height - buttonItem.height + + property alias privateTitleHeight: titleBar.height + property alias privateButtonsHeight: buttonItem.height + + signal accepted + signal rejected + signal clickedOutside + + function open() { + dialog.open() + dialog.focus = true + } + + function accept() { + if (status == DialogStatus.Open) { + dialog.close() + accepted() + } + } + + function reject() { + if (status == DialogStatus.Open) { + dialog.close() + rejected() + } + } + + function close() { + dialog.close() + } + + visible: false + + Popup { + id: dialog + + function defaultHeight() { + if (root.height > 0) + return root.height + else + return titleBar.height + contentItem.childrenRect.height + buttonItem.height + } + + function defaultWidth() { + return root.width > 0 ? root.width : maxWidth() + } + + function maxWidth() { + return Math.min(screen.width - 2 * platformStyle.paddingMedium, privateStyle.dialogMaxSize) + } + + function maxHeight() { + return inputContext.visible + ? Math.min(privateStyle.dialogMaxSize, screen.height-inputContext.height) + : Math.min(privateStyle.dialogMaxSize, screen.height-2*platformStyle.paddingMedium); + } + + function minWidth() { + return Math.min(screen.displayWidth, screen.displayHeight) - 2 * platformStyle.paddingMedium + } + + function minHeight() { + return inputContext.visible + ? Math.min(privateStyle.dialogMinSize, screen.height-inputContext.height) + : Math.min(privateStyle.dialogMinSize, screen.height-2*platformStyle.paddingMedium); + } + + function updateSize() { + width = Math.max(Math.min(defaultWidth(), maxWidth()), minWidth()) + height = Math.max(Math.min(defaultHeight(), maxHeight()), minHeight()) + } + + Connections { target: inputContext; onVisibleChanged: updateTimer.start() } + Connections { target: screen; onHeightChanged: updateTimer.start(); onWidthChanged: updateTimer.start() } + Connections { target: root; onHeightChanged: updateTimer.start(); onWidthChanged: updateTimer.start() } + + Component.onCompleted: updateTimer.start() + + Timer { + id: updateTimer + interval: 1; repeat: false + onTriggered: dialog.updateSize() + } + + onFaderClicked: root.clickedOutside() + + width: 0 // Defined in updateSize() + height: 0 // Defined in updateSize() + + state: "Hidden" + visible: true + anchors.centerIn: parent + animationDuration: 250 + platformInverted: root.platformInverted + + // Consume all key events that are not processed by children + Keys.onPressed: event.accepted = true + Keys.onReleased: event.accepted = true + + BorderImage { + source: privateStyle.imagePath("qtg_fr_popup", root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + anchors.fill: parent + } + + Item { + id: titleBar + + height: platformStyle.graphicSizeSmall + 2 * platformStyle.paddingLarge + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + BorderImage { + source: privateStyle.imagePath("qtg_fr_popup_heading", root.platformInverted) + border { left: 40; top: 0; right: 40; bottom: 0 } + anchors.fill: parent + } + + Item { + id: titleItem + + clip: true + anchors { + fill: parent + } + } + } + + Item { + id: contentItem + + clip: true + anchors { + top: titleBar.bottom + left: parent.left + right: parent.right + bottom: buttonItem.top + } + } + + Item { + id: buttonItem + + height: childrenRect.height + clip: true + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + + states: [ + State { + name: "Visible" + when: status == DialogStatus.Opening || status == DialogStatus.Open + PropertyChanges { target: dialog; opacity: 1.0; scale: 1; } + }, + State { + name: "Hidden" + when: status == DialogStatus.Closing || status == DialogStatus.Closed + PropertyChanges { target: dialog; opacity: 0.0; scale: 0.9; } + } + ] + + transitions: [ + Transition { + from: "Visible"; to: "Hidden" + SequentialAnimation { + ScriptAction {script: status = DialogStatus.Closing } + ParallelAnimation { + NumberAnimation { + properties: "opacity" + duration: dialog.animationDuration + easing.type: Easing.Linear + } + NumberAnimation { + property: "scale" + duration: dialog.animationDuration + easing.type: Easing.InQuad + } + } + ScriptAction {script: status = DialogStatus.Closed } + } + }, + Transition { + from: "Hidden"; to: "Visible" + SequentialAnimation { + ScriptAction { script: status = DialogStatus.Opening } + PropertyAction { target: dialog; property: "visible"; value: true} + ParallelAnimation { + NumberAnimation { + properties: "opacity" + duration: dialog.animationDuration + easing.type: Easing.Linear + } + NumberAnimation { + property: "scale" + duration: dialog.animationDuration + easing.type: Easing.OutQuad + } + } + ScriptAction { script: status = DialogStatus.Open } + } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Fader.qml b/mardrone/imports/com/nokia/android.1.1/Fader.qml new file mode 100644 index 0000000..0f01fac --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Fader.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 + +Item { + id: root + + property real dimm: 0.8 + property int animationDuration: 500 + property Item visualParent: null + property bool platformInverted: false // supports inverted, but looks the same + + signal clicked + + anchors.fill: parent + anchors.bottomMargin: inputContext.visible ? inputContext.height : 0 + state: "Hidden" + + Rectangle { + id: fader + + anchors { top: parent.top; left: parent.left; right: parent.right } + height: parent.height + parent.anchors.bottomMargin + opacity: 0.0 + color: "black" + + //eat mouse events + MouseArea { + id: mouseEventEater + anchors.fill: parent + enabled: fader.opacity != 0.0 + onClicked: root.clicked() + } + } + + states: [ + State { + name: "Visible" + PropertyChanges { target: fader; opacity: dimm } + }, + State { + name: "Hidden" + PropertyChanges { target: fader; opacity: 0.0 } + } + ] + + transitions: [ + Transition { + from: "Hidden"; to: "Visible" + NumberAnimation { properties: "opacity"; duration: animationDuration; easing.type: Easing.Linear } + }, + Transition { + from: "Visible"; to: "Hidden" + NumberAnimation { properties: "opacity"; duration: animationDuration; easing.type: Easing.Linear } + } + ] +} diff --git a/mardrone/imports/com/nokia/android.1.1/Label.qml b/mardrone/imports/com/nokia/android.1.1/Label.qml new file mode 100644 index 0000000..ada093a --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Label.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 + +Text { + property bool platformInverted: false + color: platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + font.family: platformStyle.fontFamilyRegular + font.pixelSize: platformStyle.fontSizeMedium +} diff --git a/mardrone/imports/com/nokia/android.1.1/ListHeading.qml b/mardrone/imports/com/nokia/android.1.1/ListHeading.qml new file mode 100644 index 0000000..e27080e --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ListHeading.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + property alias paddingItem: paddingItem // Read-only + property bool platformInverted: false + + implicitWidth: ListView.view ? ListView.view.width : screen.width + implicitHeight: platformStyle.graphicSizeSmall + + BorderImage { + source: privateStyle.imagePath("qtg_fr_list_heading_normal", root.platformInverted) + border { left: 28; top: 0; right: 28; bottom: 0 } + smooth: true + anchors.fill: parent + } + + Item { + // non-visible item to create a padding boundary that content items can bind to + id: paddingItem + anchors { + fill: parent + leftMargin: platformStyle.paddingLarge + rightMargin: privateStyle.scrollBarThickness + topMargin: platformStyle.paddingSmall + bottomMargin: platformStyle.paddingSmall + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ListItem.qml b/mardrone/imports/com/nokia/android.1.1/ListItem.qml new file mode 100644 index 0000000..072e6a4 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ListItem.qml @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + property string mode: "normal" // Read-only + property alias paddingItem: paddingItem // Read-only + + property bool enabled: true + property bool subItemIndicator: false + property bool platformInverted: false + + signal clicked + signal pressAndHold + + implicitWidth: ListView.view ? ListView.view.width : screen.width + implicitHeight: platformStyle.graphicSizeLarge + + Item { + id: background + anchors.fill: parent + + Rectangle { + height: 1 + color: root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledMid + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } + Loader { + id: faderLoader + opacity: 0 + anchors.fill: background + sourceComponent: root.mode != "normal" && root.mode != "pressed" ? fader : undefined + } + + BorderImage { + id: highlight + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + opacity: 0 + anchors.fill: background + } + } + + Component { + id: fader + + BorderImage { + source: privateStyle.imagePath("qtg_fr_list_" + mode, root.platformInverted) + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + enabled: root.enabled + onPressed: { + android.listInteractionMode = Android.TouchInteraction + internal.state = "Pressed" + } + onClicked: { + internal.state = "" + root.clicked() + } + onCanceled: { + internal.state = "Canceled" + } + onPressAndHold: { + internal.state = "PressAndHold" + } + onReleased: { + internal.state = "" + } + onExited: { + internal.state = "" + } + } + + Loader { + id: iconLoader + sourceComponent: root.subItemIndicator ? subItemIcon : undefined + anchors { + right: parent.right + rightMargin: privateStyle.scrollBarThickness + verticalCenter: parent.verticalCenter + } + } + + Component { + id: subItemIcon + + Image { + source: privateStyle.imagePath( + root.enabled ? "qtg_graf_drill_down_indicator" + : "qtg_graf_drill_down_indicator_disabled", + root.platformInverted) + mirror: LayoutMirroring.enabled + sourceSize.width: platformStyle.graphicSizeSmall + sourceSize.height: platformStyle.graphicSizeSmall + } + } + + Keys.onReleased: { + if (!event.isAutoRepeat && root.enabled) { + if (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + event.accepted = true + internal.state = "Focused" + } + } + } + + Keys.onPressed: { + if (!event.isAutoRepeat) { + switch (event.key) { + case Qt.Key_Select: + case Qt.Key_Enter: + case Qt.Key_Return: { + if (android.listInteractionMode != Android.KeyNavigation) + android.listInteractionMode = Android.KeyNavigation + else + if (root.enabled) { + highlight.source = privateStyle.imagePath("qtg_fr_list_pressed", + root.platformInverted) + highlight.opacity = 1 + releasedEffect.restart() + root.clicked() + } + event.accepted = true + break + } + + case Qt.Key_Up: { + if (android.listInteractionMode != Android.KeyNavigation) { + android.listInteractionMode = Android.KeyNavigation + internal.state = "Focused" + ListView.view.positionViewAtIndex(index, ListView.Beginning) + } else + ListView.view.decrementCurrentIndex() + event.accepted = true + break + } + + case Qt.Key_Down: { + if (android.listInteractionMode != Android.KeyNavigation) { + android.listInteractionMode = Android.KeyNavigation + ListView.view.positionViewAtIndex(index, ListView.Beginning) + internal.state = "Focused" + } else + ListView.view.incrementCurrentIndex() + event.accepted = true + break + } + default: { + event.accepted = false + break + } + } + } + + if (event.key == Qt.Key_Up || event.key == Qt.Key_Down) + android.privateListItemKeyNavigation(ListView.view) + } + + ListView.onRemove: SequentialAnimation { + PropertyAction { target: root; property: "ListView.delayRemove"; value: true } + ParallelAnimation { + SequentialAnimation { + PauseAnimation { duration: 50 } + NumberAnimation { + target: root + property: "height" + to: 0 + duration: 200 + easing.type: Easing.OutQuad + } + } + NumberAnimation { + target: root + property: "opacity" + from: 1 + to: 0 + duration: 200 + easing.type: Easing.Linear + } + } + PropertyAction { target: root; property: "ListView.delayRemove"; value: false } + } + + ListView.onAdd: SequentialAnimation { + PropertyAction { target: root; property: "height"; value: 0 } + ParallelAnimation { + NumberAnimation { + target: root + property: "height" + to: root.height + duration: 250 + easing.type: Easing.OutQuad + } + NumberAnimation { + target: root + property: "opacity" + from: 0 + to: 1 + duration: 250 + easing.type: Easing.Linear + } + } + } + + SequentialAnimation { + id: releasedEffect + PropertyAnimation { + target: highlight + property: "opacity" + to: 0 + easing.type: Easing.Linear + duration: 150 + } + } + + Item { + // non-visible item to create a padding boundary that content items can bind to + id: paddingItem + anchors { + fill: parent + leftMargin: platformStyle.paddingLarge + rightMargin: iconLoader.status == Loader.Ready ? + privateStyle.scrollBarThickness + iconLoader.width + platformStyle.paddingMedium : + privateStyle.scrollBarThickness + topMargin: platformStyle.paddingLarge + bottomMargin: platformStyle.paddingLarge + } + } + + StateGroup { + id: internal + + function getMode() { + if (internal.state == "Pressed" || internal.state == "PressAndHold") + return "pressed" + else if (internal.state == "Focused") + return "highlighted" + else if (internal.state == "Disabled") + return "disabled" + else + return "normal" + } + + // Performance optimization: + // Use value assignment when property changes instead of binding to js function + onStateChanged: { root.mode = internal.getMode() } + + function press() { + privateStyle.play(Android.BasicItem) + highlight.source = privateStyle.imagePath("qtg_fr_list_pressed", root.platformInverted) + highlight.opacity = 1 + if (root.ListView.view) + root.ListView.view.currentIndex = index + } + + function release() { + if (android.listInteractionMode != Android.KeyNavigation) + privateStyle.play(Android.BasicItem) + releasedEffect.restart() + } + + function releaseHold() { + releasedEffect.restart() + } + + function hold() { + root.pressAndHold() + } + + function disable() { + faderLoader.opacity = 1 + } + + function focus() { + faderLoader.opacity = 1 + } + + function canceled() { + releasedEffect.restart() + } + + states: [ + State { name: "Pressed" }, + State { name: "PressAndHold" }, + State { name: "Disabled"; when: !root.enabled }, + State { name: "Focused"; when: (root.ListView.isCurrentItem && + android.listInteractionMode == Android.KeyNavigation) }, + State { name: "Canceled" }, + State { name: "" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: internal.press() } + }, + Transition { + from: "Pressed" + to: "PressAndHold" + ScriptAction { script: internal.hold() } + }, + Transition { + from: "PressAndHold" + to: "" + ScriptAction { script: internal.releaseHold() } + }, + Transition { + to: "" + ScriptAction { script: internal.release() } + }, + Transition { + to: "Disabled" + ScriptAction { script: internal.disable() } + }, + Transition { + to: "Focused" + ScriptAction { script: internal.focus() } + }, + Transition { + to: "Canceled" + ScriptAction { script: internal.canceled() } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ListItemText.qml b/mardrone/imports/com/nokia/android.1.1/ListItemText.qml new file mode 100644 index 0000000..2469139 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ListItemText.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 + +Text { + id: root + property string mode: "normal" + property string role: "Title" + property bool platformInverted: false + + // Also role "Heading" taken into account although not explicitely used in evaluations below + font { + family: platformStyle.fontFamilyRegular + pixelSize: (role == "Title" || role == "SelectionTitle") ? platformStyle.fontSizeLarge : platformStyle.fontSizeSmall + weight: (role == "SubTitle" || role == "SelectionSubTitle") ? Font.Light : Font.Normal + } + color: internal.normalColor + elide: Text.ElideRight + horizontalAlignment: root.role != "Heading" ? Text.AlignLeft : Text.AlignRight + + // Performance optimization: + // Use value assignment when property changes instead of binding to js function + onModeChanged: { color = internal.getColor() } + + QtObject { + id: internal + + // Performance optmization: + // Use tertiary operations even though it doesn't look that good + property color colorMid: root.platformInverted ? platformStyle.colorNormalMidInverted + : platformStyle.colorNormalMid + property color colorLight: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + property color normalColor: (root.role == "SelectionSubTitle" || root.role == "SubTitle") + ? colorMid : colorLight + + function getColor() { + if (root.mode == "pressed") + return root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + else if (root.mode == "highlighted") + return root.platformInverted ? platformStyle.colorHighlightedInverted + : platformStyle.colorHighlighted + else if (root.mode == "disabled") + return root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + else + return normalColor + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Menu.qml b/mardrone/imports/com/nokia/android.1.1/Menu.qml new file mode 100644 index 0000000..6732e4b --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Menu.qml @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + default property alias content: menu.content + property Item visualParent: null + property alias status: popup.status + property bool platformInverted: false + + function open() { + popup.open() + } + + function close() { + popup.close() + } + + visible: false + + Popup { + id: popup + objectName: "OptionsMenu" + + y: screen.height - popup.height + animationDuration: 200 + state: "Hidden" + visible: status != DialogStatus.Closed + enabled: status == DialogStatus.Open + width: screen.width + height: menuContainer.height + clip: true + platformInverted: root.platformInverted + + onFaderClicked: { + privateStyle.play(Android.PopupClose) + close() + } + + BorderImage { + id: menuContainer + + property int borderSize: Math.round(platformStyle.borderSizeMedium * 1.5) + + source: privateStyle.imagePath("qtg_fr_popup_options", root.platformInverted) + border { left: borderSize; top: borderSize; right: borderSize; bottom: borderSize } + + width: parent.width + height: menu.height + 2 * platformStyle.paddingLarge + + MenuContent { + id: menu + containingPopup: popup + anchors { top: parent.top; left: parent.left; right: parent.right + topMargin: platformStyle.paddingLarge + leftMargin: platformStyle.paddingLarge + rightMargin: platformStyle.paddingLarge } + platformInverted: root.platformInverted + onItemClicked: popup.close() + } + + BorderImage { + source: privateStyle.imagePath("qtg_fr_popup_options_overlay", root.platformInverted) + anchors.fill: parent + border { left: menuContainer.borderSize; top: menuContainer.borderSize + right: menuContainer.borderSize; bottom: menuContainer.borderSize } + } + } + + states: [ + State { + name: "Hidden" + when: status == DialogStatus.Closing || status == DialogStatus.Closed + PropertyChanges { target: menuContainer; y: menuContainer.height; opacity: 0 } + }, + State { + name: "Visible" + when: status == DialogStatus.Opening || status == DialogStatus.Open + PropertyChanges { target: menuContainer; y: 0; opacity: 1 } + } + ] + + transitions: [ + Transition { + from: "Visible"; to: "Hidden" + SequentialAnimation { + ParallelAnimation { + NumberAnimation { + target: menuContainer + property: "y" + duration: popup.animationDuration + easing.type: Easing.Linear + } + NumberAnimation { + target: menuContainer + property: "opacity" + duration: popup.animationDuration + easing.type: Easing.Linear + } + } + PropertyAction { target: popup; property: "status"; value: DialogStatus.Closed } + } + }, + Transition { + from: "Hidden"; to: "Visible" + SequentialAnimation { + ParallelAnimation { + NumberAnimation { + target: menuContainer + property: "y" + duration: popup.animationDuration + easing.type: Easing.OutQuad + } + NumberAnimation { + target: menuContainer + property: "opacity" + duration: popup.animationDuration + easing.type: Easing.Linear + } + } + PropertyAction { target: popup; property: "status"; value: DialogStatus.Open } + } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/MenuContent.qml b/mardrone/imports/com/nokia/android.1.1/MenuContent.qml new file mode 100644 index 0000000..5e14c40 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/MenuContent.qml @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property alias content: contentArea.children + property Popup containingPopup: null + property bool platformInverted: false + + signal itemClicked() + + height: flickableArea.height + + QtObject { + id: internal + // Add padding to align content area top and bottom with rounded background graphics. + // Optionsmenu uses mask, thus no padding needed + property int clipMargin: containingPopup.objectName == "OptionsMenu" ? 0 : platformStyle.paddingSmall + property int preferredHeight: privateStyle.menuItemHeight * ((screen.width < screen.height) ? 5 : 3) + } + + BorderImage { + source: containingPopup.objectName == "OptionsMenu" + ? privateStyle.imagePath("qtg_fr_popup_options", root.platformInverted) + : privateStyle.imagePath("qtg_fr_popup", root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + anchors.fill: parent + } + + Item { + id: clipItem + y: internal.clipMargin + height: flickableArea.height - 2 * internal.clipMargin + width: root.width + clip: true + Flickable { + id: flickableArea + + property int index: 0 + property bool itemAvailable: (contentArea.children[0] != undefined) && (contentArea.children[0].children[0] != undefined) + property int itemHeight: itemAvailable ? Math.max(1, contentArea.children[0].children[0].height) : 1 + property int itemsHidden: Math.floor(contentY / itemHeight) + property int interactionMode: android.listInteractionMode + + y: -internal.clipMargin + height: contentArea.height; width: root.width + clip: true + contentHeight: (contentArea.children[0] != undefined) ? contentArea.children[0].height : 0 + contentWidth: width + + Item { + id: contentArea + + width: flickableArea.width + height: flickableArea.contentHeight > internal.preferredHeight + ? internal.preferredHeight - (internal.preferredHeight % flickableArea.itemHeight) + : flickableArea.contentHeight + + function setChildrenWidths() { + for (var i = 0; i < children.length; ++i) + children[i].width = width + } + + function connectChildren() { + for (var i = 0; i < children.length; ++i) { + if (children[i].clicked != undefined) + children[i].clicked.connect(root.itemClicked) + } + } + + onWidthChanged: setChildrenWidths() + onChildrenChanged: { + setChildrenWidths() + connectChildren() + } + } + + onVisibleChanged: { + enabled = visible + if (itemAvailable) + contentArea.children[0].children[0].focus = visible + contentY = 0 + index = 0 + } + + onItemsHiddenChanged: { + // Check that popup is really open in order to prevent unnecessary feedback + if (containingPopup.status == DialogStatus.Open + && android.listInteractionMode == Android.TouchInteraction) + privateStyle.play(Android.ItemScroll) + } + + onInteractionModeChanged: { + if (android.listInteractionMode == Android.KeyNavigation) { + contentY = 0 + scrollBar.flash(Android.FadeOut) + if (itemAvailable) + contentArea.children[0].children[index].focus = true + } else if (android.listInteractionMode == Android.TouchInteraction) { + index = 0 + } + } + + Keys.onPressed: { + if (itemAvailable && (event.key == Qt.Key_Down || event.key == Qt.Key_Up)) { + if (event.key == Qt.Key_Down && index < contentArea.children[0].children.length - 1) { + index++ + if (index * itemHeight > contentY + height - itemHeight) { + contentY = index * itemHeight - height + itemHeight + } + } else if (event.key == Qt.Key_Up && index > 0) { + index-- + if (index * itemHeight < contentY) { + contentY = index * itemHeight + } + } + scrollBar.flash(Android.FadeOut) + contentArea.children[0].children[index].focus = true + event.accepted = true + } + } + } + } + + ScrollBar { + id: scrollBar + objectName: "scrollBar" + flickableItem: flickableArea + interactive: false + visible: flickableArea.height < flickableArea.contentHeight + anchors { + top: clipItem.top + bottom: clipItem.bottom + right: clipItem.right + } + platformInverted: root.platformInverted + } + + Connections { + target: containingPopup + onStatusChanged: { + if (containingPopup.status == DialogStatus.Open) + scrollBar.flash(Android.FadeInFadeOut) + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/MenuItem.qml b/mardrone/imports/com/nokia/android.1.1/MenuItem.qml new file mode 100644 index 0000000..d93018c --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/MenuItem.qml @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property alias text: textArea.text + + // Symbian specific API + property bool platformInverted: false + property bool platformSubItemIndicator: false + property real platformLeftMargin: platformStyle.paddingLarge + + signal clicked + + width: parent.width; height: privateStyle.menuItemHeight + + QtObject { + id: internal + + function bg_postfix() { + if (activeFocus && android.listInteractionMode == Android.KeyNavigation) + return "highlighted" + else if (mouseArea.pressed && mouseArea.containsMouse && !mouseArea.canceled) + return "pressed" + else + return "popup_normal" + } + + function textColor() { + if (!enabled) + return root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + else if (activeFocus && android.listInteractionMode == Android.KeyNavigation) + return root.platformInverted ? platformStyle.colorHighlightedInverted + : platformStyle.colorHighlighted + else if (mouseArea.pressed && mouseArea.containsMouse) + return root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + else + return root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + } + } + + BorderImage { + source: privateStyle.imagePath("qtg_fr_list_" + internal.bg_postfix(), root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + anchors.fill: parent + } + + Text { + id: textArea + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: platformLeftMargin + right: iconLoader.status == Loader.Ready ? iconLoader.left : parent.right + rightMargin: iconLoader.status == Loader.Ready ? platformStyle.paddingMedium : privateStyle.scrollBarThickness + } + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + color: internal.textColor() + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + } + + Loader { + id: iconLoader + sourceComponent: root.platformSubItemIndicator ? subItemIcon : undefined + anchors { + right: parent.right + rightMargin: privateStyle.scrollBarThickness + verticalCenter: parent.verticalCenter + } + } + + Component { + id: subItemIcon + + Image { + source: privateStyle.imagePath("qtg_graf_drill_down_indicator", platformInverted) + sourceSize.width: platformStyle.graphicSizeSmall + sourceSize.height: platformStyle.graphicSizeSmall + mirror: LayoutMirroring.enabled + } + } + + + MouseArea { + id: mouseArea + + property bool canceled: false + + anchors.fill: parent + + onPressed: { + canceled = false + android.listInteractionMode = Android.TouchInteraction + privateStyle.play(Android.BasicItem) + } + onClicked: { + if (!canceled) + root.clicked() + } + onReleased: { + if (!canceled) + privateStyle.play(Android.PopupClose) + } + onExited: canceled = true + } + + Keys.onPressed: { + event.accepted = true + switch (event.key) { + case Qt.Key_Select: + case Qt.Key_Enter: + case Qt.Key_Return: { + if (!event.isAutoRepeat) { + if (android.listInteractionMode != Android.KeyNavigation) + android.listInteractionMode = Android.KeyNavigation + else + root.clicked() + } + break + } + + case Qt.Key_Up: { + if (android.listInteractionMode != Android.KeyNavigation) { + android.listInteractionMode = Android.KeyNavigation + if (ListView.view != null) + ListView.view.positionViewAtIndex(index, ListView.Beginning) + } else { + if (ListView.view != null) + ListView.view.decrementCurrentIndex() + else + event.accepted = false + } + break + } + + case Qt.Key_Down: { + if (android.listInteractionMode != Android.KeyNavigation) { + android.listInteractionMode = Android.KeyNavigation + if (ListView.view != null) + ListView.view.positionViewAtIndex(index, ListView.Beginning) + } else { + if (ListView.view != null) + ListView.view.incrementCurrentIndex() + else + event.accepted = false + } + break + } + case Qt.Key_Left: + case Qt.Key_Right: { + // If this MenuItem belongs to Menu invoke highlight + // in other cases consume event but do nothing + if (ListView.view == null && android.listInteractionMode != Android.KeyNavigation) + android.listInteractionMode = Android.KeyNavigation + break + } + default: { + event.accepted = false + break + } + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/MenuLayout.qml b/mardrone/imports/com/nokia/android.1.1/MenuLayout.qml new file mode 100644 index 0000000..87df7a3 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/MenuLayout.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 + +Column { + signal clicked + + Component.onCompleted: { + for (var i = 0; i < children.length; ++i) { + if (children[i].clicked != undefined) + children[i].clicked.connect(clicked) + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Page.qml b/mardrone/imports/com/nokia/android.1.1/Page.qml new file mode 100644 index 0000000..af2dec4 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Page.qml @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + // The status of the page. One of the following: + // PageStatus.Inactive - the page is not visible + // PageStatus.Activating - the page is transitioning into becoming the active page + // PageStatus.Active - the page is the current active page + // PageStatus.Deactivating - the page is transitioning into becoming inactive + property int status: PageStatus.Inactive + + property PageStack pageStack + + // Defines orientation lock for a page + property int orientationLock: PageOrientation.Automatic + + property Item tools: null + + visible: false + + width: visible && parent ? parent.width : internal.previousWidth + height: visible && parent ? parent.height : internal.previousHeight + + onWidthChanged: internal.previousWidth = visible ? width : internal.previousWidth + onHeightChanged: internal.previousHeight = visible ? height : internal.previousHeight + + onStatusChanged: { + if (status == PageStatus.Activating) + internal.orientationLockCheck(); + } + + onOrientationLockChanged: { + if (status == PageStatus.Activating || status == PageStatus.Active) + internal.orientationLockCheck(); + } + + // This is needed to make a parentless Page component visible in the Designer of QtCreator. + // It's done here instead of binding the visible property because binding it to a script + // block returning false would cause an element on the Page not to end up focused despite + // specifying focus=true inside the active focus scope. The focus would be gained and lost + // in createObject. + Component.onCompleted: if (!parent) visible = true + + QtObject { + id: internal + property int previousWidth: 0 + property int previousHeight: 0 + + function isScreenInPortrait() { + return screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted; + } + + function isScreenInLandscape() { + return screen.currentOrientation == Screen.Landscape || screen.currentOrientation == Screen.LandscapeInverted; + } + + function orientationLockCheck() { + switch (orientationLock) { + case PageOrientation.Automatic: + screen.allowedOrientations = Screen.Default + break + case PageOrientation.LockPortrait: + screen.allowedOrientations = Screen.Portrait + break + case PageOrientation.LockLandscape: + screen.allowedOrientations = Screen.Landscape + break + case PageOrientation.LockPrevious: + screen.allowedOrientations = screen.currentOrientation + break + case PageOrientation.Manual: + default: + // Do nothing + // In manual mode it is expected that orientation is set + // explicitly to "screen.allowedOrientations" by the user. + break + } + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/PageStack.js b/mardrone/imports/com/nokia/android.1.1/PageStack.js new file mode 100644 index 0000000..32f3136 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/PageStack.js @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Page stack - items are page containers. +var pageStack = []; + +// Returns the page stack depth. +function getDepth() { + return pageStack.length; +} + +// Pushes a page on the stack. +function push(page, properties, replace, immediate) { + // page order sanity check + if ((!replace && page == currentPage) + || (replace && pageStack.length > 1 + && page == pageStack[pageStack.length - 2].page)) { + throw new Error("Cannot navigate so that the resulting page stack has two consecutive entries of the same page instance."); + } + + // figure out if more than one page is being pushed + var pages; + if (page instanceof Array) { + pages = page; + page = pages.pop(); + if (page.createObject === undefined && page.parent === undefined && typeof page != "string") { + properties = properties || page.properties; + page = page.page; + } + } + + // get the current container + var oldContainer = pageStack[pageStack.length - 1]; + + // pop the old container off the stack if this is a replace + if (oldContainer && replace) { + pageStack.pop(); + } + + // push any extra defined pages onto the stack + if (pages) { + var i; + for (i = 0; i < pages.length; i++) { + var tPage = pages[i]; + var tProps; + if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage != "string") { + tProps = tPage.properties; + tPage = tPage.page; + } + pageStack.push(initPage(tPage, tProps)); + } + } + + // initialize the page + var container = initPage(page, properties); + + // push the page container onto the stack + pageStack.push(container); + + depth = pageStack.length; + currentPage = container.page; + + // perform page transition + immediate = immediate || !oldContainer; + var orientationChange = false; + if (oldContainer) { + orientationChange = orientationChanges(oldContainer.page, container.page); + oldContainer.pushExit(replace, immediate, orientationChange); + } + + // sync tool bar + var tools = container.page.tools || null; + if (toolBar) { + toolBar.setTools(tools, immediate ? "set" : replace ? "replace" : "push"); + } + + container.pushEnter(immediate, orientationChange); + return container.page; +} + +// Initializes a page and its container. +function initPage(page, properties) { + var container = containerComponent.createObject(root); + + var pageComp; + if (page.createObject) { + // page defined as component + pageComp = page; + } else if (typeof page == "string") { + // page defined as string (a url) + pageComp = Qt.createComponent(page); + } + if (pageComp) { + if (pageComp.status == Component.Error) { + throw new Error("Error while loading page: " + pageComp.errorString()); + } else { + // instantiate page from component + page = pageComp.createObject(container); + } + } + + container.page = page; + container.owner = page.parent; + + // the page has to be reparented if + if (page.parent != container) { + page.parent = container; + } + + if (page.pageStack !== undefined) { + page.pageStack = root; + } + + // copy properties to the page + for (var prop in properties) { + page[prop] = properties[prop]; + } + + return container; +} + +// Pops a page off the stack. +function pop(page, immediate) { + // make sure there are enough pages in the stack to pop + if (pageStack.length > 1) { + // pop the current container off the stack and get the next container + var oldContainer = pageStack.pop(); + var container = pageStack[pageStack.length - 1]; + if (page !== undefined) { + // an unwind target has been specified - pop until we find it + while (page != container.page && pageStack.length > 1) { + container.cleanup(); + pageStack.pop(); + container = pageStack[pageStack.length - 1]; + } + } + + depth = pageStack.length; + currentPage = container.page; + + // perform page transition + var orientationChange = orientationChanges(oldContainer.page, container.page); + oldContainer.popExit(immediate, orientationChange); + container.popEnter(immediate, orientationChange); + + // sync tool bar + var tools = container.page.tools || null; + if (toolBar) { + toolBar.setTools(tools, immediate ? "set" : "pop"); + } + return oldContainer.page; + } else { + return null; + } +} + +// Checks if the orientation changes between oldPage and newPage +function orientationChanges(oldPage, newPage) { + return newPage.orientationLock != PageOrientation.Automatic + && newPage.orientationLock != PageOrientation.LockPrevious + && newPage.orientationLock != oldPage.orientationLock +} + +// Clears the page stack. +function clear() { + var container; + while (container = pageStack.pop()) { + container.cleanup(); + } + depth = 0; + currentPage = null; +} + +// Iterates through all pages in the stack (top to bottom) to find a page. +function find(func) { + for (var i = pageStack.length - 1; i >= 0; i--) { + var page = pageStack[i].page; + if (func(page)) { + return page; + } + } + return null; +} + diff --git a/mardrone/imports/com/nokia/android.1.1/PageStack.qml b/mardrone/imports/com/nokia/android.1.1/PageStack.qml new file mode 100644 index 0000000..17e107b --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/PageStack.qml @@ -0,0 +1,444 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// The PageStack item defines a container for pages and a stack-based +// navigation model. Pages can be defined as QML items or components. + +import QtQuick 1.1 +import "." 1.1 + +import "PageStack.js" as Engine + +Item { + id: root + + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + + property int depth: Engine.getDepth() + property Item currentPage: null + property ToolBar toolBar + property variant initialPage + + // Indicates whether there is an ongoing page transition. + property bool busy: internal.ongoingTransitionCount > 0 + + // Pushes a page on the stack. + // The page can be defined as a component, item or string. + // If an item is used then the page will get re-parented. + // If a string is used then it is interpreted as a url that is used to load a page component. + // + // The page can also be given as an array of pages. In this case all those pages will be pushed + // onto the stack. The items in the stack can be components, items or strings just like for single + // pages. Additionally an object can be used, which specifies a page and an optional properties + // property. This can be used to push multiple pages while still giving each of them properties. + // When an array is used the transition animation will only be to the last page. + // + // The properties argument is optional and allows defining a map of properties to set on the page. + // If the immediate argument is true then no transition animation is performed. + // Returns the page instance. + function push(page, properties, immediate) { + return Engine.push(page, properties, false, immediate); + } + + // Pops a page off the stack. + // If page is specified then the stack is unwound to that page, to unwind to the first page specify + // page as null. If the immediate argument is true then no transition animation is performed. + // Returns the page instance that was popped off the stack. + function pop(page, immediate) { + return Engine.pop(page, immediate); + } + + // Replaces a page on the stack. + // See push() for details. + function replace(page, properties, immediate) { + return Engine.push(page, properties, true, immediate); + } + + // Clears the page stack. + function clear() { + return Engine.clear(); + } + + // Iterates through all pages (top to bottom) and invokes the specified function. + // If the specified function returns true the search stops and the find function + // returns the page that the iteration stopped at. If the search doesn't result + // in any page being found then null is returned. + function find(func) { + return Engine.find(func); + } + + // Called when the page stack visibility changes. + onVisibleChanged: { + if (currentPage) { + internal.setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive); + if (visible) + currentPage.visible = currentPage.parent.visible = true; + } + } + + onInitialPageChanged: { + if (initialPage) { + if (depth == 0) + push(initialPage, null, true) + else if (depth == 1) + replace(initialPage, null, true) + else + console.log("Cannot update PageStack.initialPage") + } + } + + Component.onCompleted: { + if (initialPage && depth == 0) + push(initialPage, null, true) + } + + QtObject { + id: internal + + // The number of ongoing transitions. + property int ongoingTransitionCount: 0 + + // Sets the page status. + function setPageStatus(page, status) { + if (page != null) { + if (page.status !== undefined) { + if (status == PageStatus.Active && page.status == PageStatus.Inactive) + page.status = PageStatus.Activating; + else if (status == PageStatus.Inactive && page.status == PageStatus.Active) + page.status = PageStatus.Deactivating; + + page.status = status; + } + } + } + } + + // Component for page containers. + Component { + id: containerComponent + + Item { + id: container + + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + + // The states correspond to the different possible positions of the container. + state: "Hidden" + + // The page held by this container. + property Item page: null + + // The owner of the page. + property Item owner: null + + // The width of the longer screen dimension + property int screenWidth: Math.max(screen.width, screen.height) + + // Duration of transition animation (in ms) + property int transitionDuration: 200 + + // Flag that indicates the container should be cleaned up after the transition has ended. + property bool cleanupAfterTransition: false + + // Flag that indicates if page transition animation is running + property bool transitionAnimationRunning: false + + // State to be set after previous state change animation has finished + property string pendingState: "none" + + // Ensures that transition finish actions are executed + // in case the object is destroyed before reaching the + // end state of an ongoing transition + Component.onDestruction: { + if (transitionAnimationRunning) + transitionEnded(); + } + + // Sets pending state as current if state change is delayed + onTransitionAnimationRunningChanged: { + if (!transitionAnimationRunning && pendingState != "none") { + state = pendingState; + pendingState = "none"; + } + } + + // Handles state change depening on transition animation status + function setState(newState) { + if (transitionAnimationRunning) + pendingState = newState; + else + state = newState; + } + + // Performs a push enter transition. + function pushEnter(immediate, orientationChanges) { + if (!immediate) { + if (orientationChanges) + setState("LandscapeRight"); + else + setState("Right"); + } + setState(""); + page.visible = true; + if (root.visible && immediate) + internal.setPageStatus(page, PageStatus.Active); + } + + // Performs a push exit transition. + function pushExit(replace, immediate, orientationChanges) { + if (orientationChanges) + setState(immediate ? "Hidden" : "LandscapeLeft"); + else + setState(immediate ? "Hidden" : "Left"); + if (root.visible && immediate) + internal.setPageStatus(page, PageStatus.Inactive); + if (replace) { + if (immediate) + cleanup(); + else + cleanupAfterTransition = true; + } + } + + // Performs a pop enter transition. + function popEnter(immediate, orientationChanges) { + if (!immediate) + state = orientationChanges ? "LandscapeLeft" : "Left"; + setState(""); + page.visible = true; + if (root.visible && immediate) + internal.setPageStatus(page, PageStatus.Active); + } + + // Performs a pop exit transition. + function popExit(immediate, orientationChanges) { + if (orientationChanges) + setState(immediate ? "Hidden" : "LandscapeRight"); + else + setState(immediate ? "Hidden" : "Right"); + + if (root.visible && immediate) + internal.setPageStatus(page, PageStatus.Inactive); + if (immediate) + cleanup(); + else + cleanupAfterTransition = true; + } + + // Called when a transition has started. + function transitionStarted() { + transitionAnimationRunning = true; + internal.ongoingTransitionCount++; + if (root.visible) + internal.setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating); + } + + // Called when a transition has ended. + function transitionEnded() { + if (state != "") + state = "Hidden"; + if (root.visible) + internal.setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive); + + internal.ongoingTransitionCount--; + transitionAnimationRunning = false; + if (cleanupAfterTransition) + cleanup(); + } + + states: [ + // Explicit properties for default state. + State { + name: "" + PropertyChanges { target: container; visible: true; opacity: 1 } + }, + // Start state for pop entry, end state for push exit. + State { + name: "Left" + PropertyChanges { target: container; x: -width / 2; opacity: 0 } + }, + // Start state for pop entry, end state for push exit + // when exiting portrait and entering landscape. + State { + name: "LandscapeLeft" + PropertyChanges { target: container; x: -screenWidth / 2; opacity: 0 } + }, + // Start state for push entry, end state for pop exit. + State { + name: "Right" + PropertyChanges { target: container; x: width / 2; opacity: 0 } + }, + // Start state for push entry, end state for pop exit + // when exiting portrait and entering landscape. + State { + name: "LandscapeRight" + PropertyChanges { target: container; x: screenWidth / 2; opacity: 0 } + }, + // Inactive state. + State { + name: "Hidden" + PropertyChanges { target: container; visible: false } + } + ] + + transitions: [ + // Push exit transition + Transition { + from: ""; to: "Left" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + }, + // Pop entry transition + Transition { + from: "Left"; to: "" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + }, + // Push exit transition landscape + Transition { + from: ""; to: "LandscapeLeft" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + }, + // Pop entry transition landscape + Transition { + from: "LandscapeLeft"; to: "" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + }, + // Pop exit transition + Transition { + from: ""; to: "Right" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + // Workaround for transition animation bug causing ghost view with page pop transition animation + // TODO: Root cause still unknown + PropertyAnimation {} + ScriptAction { script: transitionEnded() } + } + }, + // Push entry transition + Transition { + from: "Right"; to: "" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + }, + // Pop exit transition landscape + Transition { + from: ""; to: "LandscapeRight" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + // Workaround for transition animation bug causing ghost view with page pop transition animation + // TODO: Root cause still unknown + PropertyAnimation {} + ScriptAction { script: transitionEnded() } + } + }, + // Push entry transition landscape + Transition { + from: "LandscapeRight"; to: "" + SequentialAnimation { + ScriptAction { script: transitionStarted() } + ParallelAnimation { + PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration } + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration } + } + ScriptAction { script: transitionEnded() } + } + } + ] + + // Cleans up the container and then destroys it. + function cleanup() { + if (page != null) { + if (page.status == PageStatus.Active) + internal.setPageStatus(page, PageStatus.Inactive); + if (owner != container) { + // container is not the owner of the page - re-parent back to original owner + page.visible = false; + page.parent = owner; + } + } + container.destroy(); + } + } + } +} + diff --git a/mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml b/mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml new file mode 100644 index 0000000..a9f515f --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Window { + id: root + + property bool showStatusBar: true + property bool showToolBar: true + property variant initialPage + property alias pageStack: stack + + property bool platformSoftwareInputPanelEnabled: false + + Component.onCompleted: { + contentArea.initialized = true + if (initialPage && stack.depth == 0) + stack.push(initialPage, null, true) + } + + onInitialPageChanged: { + if (initialPage && contentArea.initialized) { + if (stack.depth == 0) + stack.push(initialPage, null, true) + else if (stack.depth == 1) + stack.replace(initialPage, null, true) + } + } + + onOrientationChangeStarted: { + statusBar.orientation = screen.currentOrientation + } + + Item { + id: contentArea + + property bool initialized: false + + anchors { + top: sbar.bottom; bottom: sip.top; + left: parent.left; right: parent.right; + } + + PageStack { + id: stack + anchors.fill: parent + toolBar: tbar + } + } + + StatusBar { + id: sbar + + width: parent.width + state: root.showStatusBar ? "Visible" : "Hidden" + platformInverted: root.platformInverted + + states: [ + State { + name: "Visible" + PropertyChanges { target: sbar; y: 0; opacity: 1 } + }, + State { + name: "Hidden" + PropertyChanges { target: sbar; y: -height; opacity: 0 } + } + ] + + transitions: [ + Transition { + from: "Hidden"; to: "Visible" + ParallelAnimation { + NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad } + NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + }, + Transition { + from: "Visible"; to: "Hidden" + ParallelAnimation { + NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.Linear } + NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + } + ] + } + + Item { + id: sip + + anchors { bottom: parent.bottom; left: parent.left; right: parent.right } + + Behavior on height { PropertyAnimation { duration: 200 } } + + states: [ + State { + name: "Visible"; when: inputContext.visible && root.platformSoftwareInputPanelEnabled + PropertyChanges { target: sip; height: inputContext.height } + }, + + State { + name: "Hidden"; when: root.showToolBar + PropertyChanges { target: sip; height: tbar.height } + }, + + State { + name: "HiddenInFullScreen"; when: !root.showToolBar + PropertyChanges { target: sip; height: 0 } + } + ] + } + + ToolBar { + id: tbar + + width: parent.width + state: root.showToolBar ? "Visible" : "Hidden" + platformInverted: root.platformInverted + + states: [ + State { + name: "Visible" + PropertyChanges { target: tbar; y: parent.height - height; opacity: 1 } + }, + State { + name: "Hidden" + PropertyChanges { target: tbar; y: parent.height; opacity: 0 } + } + ] + + transitions: [ + Transition { + from: "Hidden"; to: "Visible" + ParallelAnimation { + NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad } + NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + }, + Transition { + from: "Visible"; to: "Hidden" + ParallelAnimation { + NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.Linear } + NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear } + } + } + ] + } + + // event preventer when page transition is active + MouseArea { + anchors.fill: parent + enabled: pageStack.busy + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Popup.qml b/mardrone/imports/com/nokia/android.1.1/Popup.qml new file mode 100644 index 0000000..3071785 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Popup.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 +import "AppManager.js" as Utils + +Item { + id: root + + property Item visualParent + property int status: DialogStatus.Closed + property int animationDuration: 500 + property Item fader + property bool platformInverted: false + + signal faderClicked + + function open() { + if (status == DialogStatus.Open || status == DialogStatus.Opening) + return + + var notify = false + if (!fader) { + fader = faderComponent.createObject(visualParent ? visualParent : Utils.rootObject()) + notify = true + } + + fader.animationDuration = root.animationDuration + root.parent = fader + status = DialogStatus.Opening + fader.state = "Visible" + + if (notify) + platformPopupManager.privateNotifyPopupOpen() + } + + function close() { + if (status != DialogStatus.Closed) { + status = DialogStatus.Closing + if (fader) + fader.state = "Hidden" + } + } + + onStatusChanged: { + if (status == DialogStatus.Closed && fader) { + // Temporarily setting root window as parent + // otherwise transition animation jams + root.parent = null + fader.destroy() + fader = null // Prevent reuse in open() + root.parent = parentCache.oldParent + platformPopupManager.privateNotifyPopupClose() + } + } + + Component.onCompleted: { + parentCache.oldParent = parent + } + + //if this is not given, application may crash in some cases + Component.onDestruction: { + if (parentCache.oldParent != null) { + parent = parentCache.oldParent + } + } + + QtObject { + id: parentCache + property QtObject oldParent: null + } + + //This eats mouse events when popup area is clicked + MouseArea { + anchors.fill: parent + } + + Component { + id: faderComponent + + Fader { + platformInverted: root.platformInverted + onClicked: root.faderClicked() + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ProgressBar.qml b/mardrone/imports/com/nokia/android.1.1/ProgressBar.qml new file mode 100644 index 0000000..e8aefcc --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ProgressBar.qml @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import Qt.labs.components 1.1 +import "." 1.1 + +Item { + id: root + + // Common Public API + property alias minimumValue: model.minimumValue + property alias maximumValue: model.maximumValue + property alias value: model.value + property bool indeterminate: false + + // Symbian specific API + property bool platformInverted: false + + implicitWidth: Math.max(50, screen.width / 2) // TODO: use screen.displayWidth + implicitHeight: privateStyle.sliderThickness + + BorderImage { + id: background + + source: privateStyle.imagePath("qtg_fr_progressbar_track", root.platformInverted) + border { left: platformStyle.borderSizeMedium; top: 0; right: platformStyle.borderSizeMedium; bottom: 0 } + anchors.fill: parent + } + + Loader { + id: progressBarContent + + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + + states: [ + State { + name: "indeterminate" + when: root.indeterminate + PropertyChanges { target: progressBarContent; sourceComponent: indeterminateContent; anchors.fill: parent } + }, + State { + name: "determinate" + when: !root.indeterminate + PropertyChanges { target: progressBarContent; sourceComponent: determinateContent } + AnchorChanges { + target: progressBarContent + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + } + } + ] + } + + + Component { + id: indeterminateContent + + Item { + anchors.fill: parent + + Item { + id: indeterminateImageMask + + // Mask margins prevent indeterminateImage to appear outside the rounded + // frame corners, hardcoded 3 has been instructed by UX + anchors { fill: parent; leftMargin: 3; rightMargin: 3 } + clip: true + + Image { + id: indeterminateImage + + x: parent.x + height: parent.height + width: parent.width + height // height is the amount of horizontal movement + fillMode: Image.TileHorizontally + source: privateStyle.imagePath(root.platformInverted ? "qtg_graf_progressbar_wait_inverse" + : "qtg_graf_progressbar_wait") + + NumberAnimation on x { + loops: Animation.Infinite + running: true + from: 0 + to: -indeterminateImage.height // see indeterminateImage.width + easing.type: Easing.Linear + duration: privateStyle.sliderThickness * 30 + } + } + } + + BorderImage { + id: indeterminateOverlay + + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_progressbar_overlay", root.platformInverted) + border { + left: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + top: 0 + bottom: 0 + } + } + } + } + + Component { + id: determinateContent + + Item { + id: progressMask + + width: model.position + clip: true + + BorderImage { + id: progress + + source: privateStyle.imagePath("qtg_fr_progressbar_fill", root.platformInverted) + border { + left: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + top: 0 + bottom: 0 + } + height: parent.height + width: root.width + } + } + } + + RangeModel { + id: model + minimumValue: 0.0 + maximumValue: 1.0 + positionAtMinimum: 0.0 + positionAtMaximum: background.width + } +} + diff --git a/mardrone/imports/com/nokia/android.1.1/QueryDialog.qml b/mardrone/imports/com/nokia/android.1.1/QueryDialog.qml new file mode 100644 index 0000000..a46fa35 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/QueryDialog.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +CommonDialog { + id: root + objectName: "root" + + property string message + property string acceptButtonText + property string rejectButtonText + property alias icon: root.titleIcon // for backwards compatibility + + onAcceptButtonTextChanged: internal.updateButtonTexts() + onRejectButtonTextChanged: internal.updateButtonTexts() + + onStatusChanged: { + if (status == DialogStatus.Open) + scrollBar.flash() + } + + onButtonClicked: { + if (acceptButtonText && index == 0) + accepted() + else + rejected() + } + + content: Item { + height: { + if (root.height > 0) + return Math.min(Math.max(privateStyle.dialogMinSize, root.height) - privateTitleHeight - privateButtonsHeight, root.platformContentMaximumHeight) + else + return Math.min(label.paintedHeight, root.platformContentMaximumHeight) + } + width: parent.width + + Item { + anchors { + top: parent.top; topMargin: platformStyle.paddingLarge + bottom: parent.bottom; bottomMargin: platformStyle.paddingLarge + left: parent.left; leftMargin: platformStyle.paddingLarge + right: parent.right + } + + Flickable { + id: flickable + width: parent.width + height: parent.height + anchors { left: parent.left; top: parent.top } + contentHeight: label.paintedHeight + flickableDirection: Flickable.VerticalFlick + clip: true + interactive: contentHeight > height + + Text { + id: label + anchors { right: parent.right; rightMargin: privateStyle.scrollBarThickness } + width: flickable.width - privateStyle.scrollBarThickness + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + color: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + wrapMode: Text.WordWrap + text: root.message + horizontalAlignment: Text.AlignLeft + } + } + + ScrollBar { + id: scrollBar + height: parent.height + anchors { top: flickable.top; right: flickable.right } + flickableItem: flickable + interactive: false + orientation: Qt.Vertical + platformInverted: root.platformInverted + } + } + } + + QtObject { + id: internal + + function updateButtonTexts() { + var newButtonTexts = [] + if (acceptButtonText) + newButtonTexts.push(acceptButtonText) + if (rejectButtonText) + newButtonTexts.push(rejectButtonText) + root.buttonTexts = newButtonTexts + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/RadioButton.qml b/mardrone/imports/com/nokia/android.1.1/RadioButton.qml new file mode 100644 index 0000000..729c9ad --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/RadioButton.qml @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import Qt.labs.components 1.1 +import "." 1.1 + +Item { + id: root + + // Common Public API + property alias checked: checkable.checked + property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed" + signal clicked + property alias text: label.text + + // Symbian specific API + property alias platformExclusiveGroup: checkable.exclusiveGroup + property bool platformInverted: false + + QtObject { + id: internal + objectName: "internal" + property color disabledColor: root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + property color pressedColor: root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + property color normalColor: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + + function toggle() { + clickedEffect.restart() + checkable.toggle() + root.clicked() + } + + function icon_postfix() { + if (pressed) + return "pressed" + else if (root.checked) { + if (!root.enabled) + return "disabled_selected" + else + return "normal_selected" + } else { + if (!root.enabled) + return "disabled_unselected" + else + return "normal_unselected" + } + } + } + + StateGroup { + id: stateGroup + + states: [ + State { name: "Pressed" }, + State { name: "KeyPressed" }, + State { name: "Canceled" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: privateStyle.play(Android.BasicItem) } + }, + Transition { + from: "Pressed" + to: "" + ScriptAction { script: privateStyle.play(Android.CheckBox) } + ScriptAction { script: internal.toggle() } + }, + Transition { + from: "KeyPressed" + to: "" + ScriptAction { script: internal.toggle() } + } + ] + } + + implicitWidth: privateStyle.textWidth(label.text, label.font) + platformStyle.paddingMedium + privateStyle.buttonSize + implicitHeight: privateStyle.buttonSize + + Image { + id: image + source: privateStyle.imagePath("qtg_graf_radiobutton_" + internal.icon_postfix(), + root.platformInverted) + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + sourceSize.width: privateStyle.buttonSize + sourceSize.height: privateStyle.buttonSize + + MouseArea { + id: mouseArea + anchors.fill: parent + + onPressed: stateGroup.state = "Pressed" + onReleased: stateGroup.state = "" + onClicked: stateGroup.state = "" + onExited: stateGroup.state = "Canceled" + onCanceled: { + // Mark as canceled + stateGroup.state = "Canceled" + // Reset state. Can't expect a release since mouse was ungrabbed + stateGroup.state = "" + } + } + } + + Text { + id: label + elide: Text.ElideRight + anchors.left: image.right + anchors.leftMargin: platformStyle.paddingMedium + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + horizontalAlignment: Text.AlignLeft + + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + color: root.enabled ? (root.pressed ? internal.pressedColor : internal.normalColor) + : internal.disabledColor + } + + ParallelAnimation { + id: clickedEffect + SequentialAnimation { + PropertyAnimation { + target: image + property: "scale" + from: 1.0 + to: 0.8 + easing.type: Easing.Linear + duration: 50 + } + PropertyAnimation { + target: image + property: "scale" + from: 0.8 + to: 1.0 + easing.type: Easing.OutQuad + duration: 170 + } + } + } + + Keys.onPressed: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "KeyPressed" + event.accepted = true + } + } + + Keys.onReleased: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "" + event.accepted = true + } + } + + Checkable { + id: checkable + value: root.text + enabled: true + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/RectUtils.js b/mardrone/imports/com/nokia/android.1.1/RectUtils.js new file mode 100644 index 0000000..00e0e5e --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/RectUtils.js @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +.pragma library + +function rectContainsPoint(rect, x, y) { + return rect.x < x + && x < rect.x + rect.width + && rect.y < y + && y < rect.y + rect.height; +} + +function rectIntersectsRect(rect1, rect2) { + return !(rect2.x > rect1.x + rect1.width || rect2.x + rect2.width < rect1.x || + rect2.y > rect1.y + rect1.height || rect2.y + rect2.height < rect1.y); +} + +// returns true if rect1 contains rect2 +function rectContainsRect(rect1, rect2) { + return (rect1.x < rect2.x && rect2.x + rect2.width < rect1.x + rect1.width && + rect1.y < rect2.y && rect2.y + rect2.height < rect1.y + rect1.height); +} + +function manhattan(point1, point2) { + return Math.abs(point1.x - point2.x) + Math.abs(point1.y - point2.y) +} diff --git a/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml b/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml new file mode 100644 index 0000000..e356c55 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "SectionScroller.js" as Sections +import "." 1.1 + +Item { + id: root + + property Flickable flickableItem: null + property int orientation: Qt.Vertical + property bool interactive: true + property int policy: Android.ScrollBarWhenScrolling + property bool privateSectionScroller: false + property bool platformInverted: false + + //implicit values for qml designer when no Flickable is present + implicitHeight: privateStyle.scrollBarThickness * (orientation == Qt.Vertical ? 3 : 1) + implicitWidth: privateStyle.scrollBarThickness * (orientation == Qt.Horizontal ? 3 : 1) + height: { + if (flickableItem && orientation == Qt.Vertical) + return Math.floor(Math.round(flickableItem.height) - anchors.topMargin - anchors.bottomMargin) + return undefined + } + width: { + if (flickableItem && orientation == Qt.Horizontal) + return Math.floor(Math.round(flickableItem.width) - anchors.leftMargin - anchors.rightMargin) + return undefined + } + opacity: internal.rootOpacity + + onFlickableItemChanged: internal.initSectionScroller(); + onPrivateSectionScrollerChanged: internal.initSectionScroller(); + + //For showing explicitly a ScrollBar if policy is Android.ScrollBarWhenScrolling + function flash(type) { + if (policy == Android.ScrollBarWhenScrolling && internal.scrollBarNeeded) { + flashEffect.type = (type == undefined) ? Android.FadeOut : type + flashEffect.restart() + } + } + + Connections { + target: screen + onCurrentOrientationChanged: flash() + } + + Connections { + target: root.privateSectionScroller ? flickableItem : null + onModelChanged: internal.initSectionScroller() + } + + Loader { + id: listItemInteraction + sourceComponent: flickableItem && flickableItem.hasOwnProperty("currentIndex") && flickableItem.activeFocus ? listItemInteractionComponent : undefined + } + + Component { + id: listItemInteractionComponent + Item { + Connections { + target: symbian + onListInteractionModeChanged: flash() + onPrivateListItemKeyNavigation: flash() + } + } + } + + QtObject { + id: internal + property int hideTimeout: 500 + property int pageStepY: flickableItem ? Math.floor(flickableItem.visibleArea.heightRatio * flickableItem.contentHeight) : NaN + property int pageStepX: flickableItem ? Math.floor(flickableItem.visibleArea.widthRatio * flickableItem.contentWidth) : NaN + property int handleY: flickableItem ? Math.floor(handle.y / flickableItem.height * flickableItem.contentHeight) : NaN + property int handleX: flickableItem ? Math.floor(handle.x / flickableItem.width * flickableItem.contentWidth) : NaN + property int maximumY: flickableItem ? Math.floor(Math.min(flickableItem.contentHeight - root.height, flickableItem.contentY)) : NaN + property int maximumX: flickableItem ? Math.floor(Math.min(flickableItem.contentWidth - root.width, flickableItem.contentX)) : NaN + property bool scrollBarNeeded: root.visible && hasScrollableContent() + //Sets currentSection to empty string when flickableItem.currentSection is null + property string currentSection: flickableItem ? flickableItem.currentSection || "" : "" + //To be able to pressed on trackMouseArea, opacity needs to be greater than 0 + property real rootOpacity: root.privateSectionScroller ? 0.01 : 0 + + // The following properties are used for calculating handle position and size: + // Minimum allowed handle length + property real minHandleLength: 3 * Math.floor(privateStyle.scrollBarThickness) + // Scroll bar "container" length + property real totalLength: !flickableItem ? NaN : (orientation == Qt.Vertical ? root.height : root.width) + property real relativeHandlePosition: !flickableItem ? NaN : (orientation == Qt.Vertical + ? flickableItem.visibleArea.yPosition + : flickableItem.visibleArea.xPosition) + property real lengthRatio: !flickableItem ? NaN : (orientation == Qt.Vertical + ? flickableItem.visibleArea.heightRatio + : flickableItem.visibleArea.widthRatio) + // Scroll bar handle length under normal circumstances + property real staticHandleLength: Math.max(minHandleLength, lengthRatio * totalLength) + // Adjustment factor needed to calculate correct position, which is needed because + // relativeHandlePosition and lengthRatio are assuming default handle length. Dividend is + // the maximum handle position taking into account restricted minimum handle length, whereas + // divisor is the maximum handle position if default handle size would be used. + property real handlePositionAdjustment: (totalLength - staticHandleLength) / (totalLength * (1 - lengthRatio)) + property real handlePosition + handlePosition: { + // handle's position, which is adjusted to take into account non-default handle + // length and restricted never to exceed flickable boundaries + var pos = totalLength * relativeHandlePosition * handlePositionAdjustment + return Math.min(Math.max(0, pos), totalLength - minHandleLength) + } + property real dynamicHandleLength + dynamicHandleLength: { + // dynamic handle length, which may differ from static due to bounds behavior, i.e. + // handle gets shorter when content exceeds flickable boundaries + var length = 0 + if (relativeHandlePosition >= 1 - lengthRatio) // overflow + length = 1 - relativeHandlePosition + else if (relativeHandlePosition < 0) // underflow + length = lengthRatio + relativeHandlePosition + else + length = lengthRatio + return Math.max(minHandleLength, length * totalLength) + } + + // Show flash effect in case if no flicking (flickableItem.moving) has occured + // but there is a need to indicate scrollable content. + onScrollBarNeededChanged: { + if (stateGroup.state == "") { + if (scrollBarNeeded) + flash() + else + idleEffect.restart() + } + } + + /** + * Checks whether ScrollBar is needed or not + * based on Flickable visibleArea height and width ratios + */ + function hasScrollableContent() { + if (!flickableItem) + return false + var ratio = orientation == Qt.Vertical ? flickableItem.visibleArea.heightRatio : flickableItem.visibleArea.widthRatio + return ratio < 1.0 && ratio > 0 + } + /** + * Does Page by Page movement of flickableItem + * when ScrollBar Track is being clicked/pressed + * + * @see #moveToLongTapPosition + */ + function doPageStep() { + if (orientation == Qt.Vertical) { + if (trackMouseArea.mouseY > (handle.height / 2 + handle.y)) { + flickableItem.contentY += pageStepY + flickableItem.contentY = maximumY + } + else if (trackMouseArea.mouseY < (handle.height / 2 + handle.y)) { + flickableItem.contentY -= pageStepY + flickableItem.contentY = Math.max(0, flickableItem.contentY) + } + } else { + if (trackMouseArea.mouseX > (handle.width / 2 + handle.x)) { + flickableItem.contentX += pageStepX + flickableItem.contentX = maximumX + } + else if (trackMouseArea.mouseX < (handle.width / 2 + handle.x)) { + flickableItem.contentX -= pageStepX + flickableItem.contentX = Math.max(0, flickableItem.contentX) + } + } + } + /** + * Does movement of flickableItem + * when ScrollBar Handle is being dragged + */ + function moveToHandlePosition() { + if (orientation == Qt.Vertical) + flickableItem.contentY = handleY + else + flickableItem.contentX = handleX + } + /** + * Moves flickableItem's content according to given mouseArea movement + * when mouseArea is pressed long + * Tries to position the handle and content in center of mouse position enough + * + * @see #doPageStep + */ + function moveToLongTapPosition(mouseArea) { + if (orientation == Qt.Vertical) { + if (Math.abs(mouseArea.mouseY - (handle.height / 2 + handle.y)) < privateStyle.scrollBarThickness) + return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect + if (mouseArea.mouseY > (handle.height / 2 + handle.y)) { + flickableItem.contentY += Math.floor(privateStyle.scrollBarThickness) + flickableItem.contentY = maximumY + } + else if (mouseArea.mouseY < (handle.height / 2 + handle.y)) { + flickableItem.contentY -= Math.floor(privateStyle.scrollBarThickness) + flickableItem.contentY = Math.floor(Math.max(0, flickableItem.contentY)) + } + } else { + if (Math.abs(mouseArea.mouseX - (handle.width / 2 + handle.x)) < privateStyle.scrollBarThickness) + return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect + if (mouseArea.mouseX > (handle.width / 2 + handle.x)) { + flickableItem.contentX += Math.floor(privateStyle.scrollBarThickness) + flickableItem.contentX = maximumX + } + else if (mouseArea.mouseX < (handle.width / 2 + handle.x)) { + flickableItem.contentX -= Math.floor(privateStyle.scrollBarThickness) + flickableItem.contentX = Math.floor(Math.max(0, flickableItem.contentX)) + } + } + } + + function adjustContentPosition(y) { + if (y < 0 || y > trackMouseArea.height) + return; + + var sect = Sections.closestSection(y / trackMouseArea.height); + var idx = Sections.indexOf(sect); + currentSection = sect; + flickableItem.positionViewAtIndex(idx, ListView.Beginning); + } + + function initSectionScroller() { + if (root.privateSectionScroller && flickableItem && flickableItem.model) + Sections.initSectionData(flickableItem); + } + } + + BorderImage { + id: track + objectName: "track" + source: privateStyle.imagePath((orientation == Qt.Vertical + ? "qtg_fr_scrollbar_v_track_normal" + : "qtg_fr_scrollbar_h_track_normal"), + root.platformInverted) + visible: interactive + anchors.fill: parent + border.right: orientation == Qt.Horizontal ? 7 : 0 + border.left: orientation == Qt.Horizontal ? 7 : 0 + border.top: orientation == Qt.Vertical ? 7 : 0 + border.bottom: orientation == Qt.Vertical ? 7 : 0 + onVisibleChanged: { idleEffect.complete(); flashEffect.complete() } + } + + Loader { + id: sectionScrollBackground + anchors.right: trackMouseArea.right + width: flickableItem ? flickableItem.width : 0 + height: platformStyle.fontSizeMedium * 5 + sourceComponent: root.privateSectionScroller ? sectionScrollComponent : null + } + + Component { + id: sectionScrollComponent + BorderImage { + id: indexFeedbackBackground + objectName: "indexFeedbackBackground" + source: privateStyle.imagePath("qtg_fr_popup_transparent", root.platformInverted) + border { left: platformStyle.borderSizeMedium; top: platformStyle.borderSizeMedium; right: platformStyle.borderSizeMedium; bottom: platformStyle.borderSizeMedium } + visible: trackMouseArea.pressed + anchors.fill: parent + Text { + id: indexFeedbackText + objectName: "indexFeedbackText" + color: root.platformInverted ? platformStyle.colorNormalDarkInverted // intentionally dark inverted + : platformStyle.colorNormalLight + anchors { + left: parent.left; + leftMargin: platformStyle.paddingLarge; + right: parent.right; + rightMargin: platformStyle.paddingLarge; + verticalCenter: parent.verticalCenter + } + font { + family: platformStyle.fontFamilyRegular; + pixelSize: indexFeedbackText.text.length == 1 ? platformStyle.fontSizeMedium * 4 : platformStyle.fontSizeMedium * 2; + capitalization: indexFeedbackText.text.length == 1 ? Font.AllUppercase : Font.MixedCase + } + text: internal.currentSection + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + } + states: [ + State { + when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) < 0 + AnchorChanges { + target: sectionScrollBackground + anchors { verticalCenter: undefined; top: track.top; bottom: undefined } + } + }, + State { + when: (handle.y + (handle.height / 2)) + (sectionScrollBackground.height / 2) >= track.height + AnchorChanges { + target: sectionScrollBackground + anchors { verticalCenter: undefined; top: undefined; bottom: track.bottom } + } + }, + State { + when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) >= 0 + AnchorChanges { + target: sectionScrollBackground + anchors { verticalCenter: handle.verticalCenter; top: undefined; bottom: undefined } + } + } + ] + } + } + + // MouseArea for the move content "page by page" by tapping and scroll to press-and-hold position + MouseArea { + id: trackMouseArea + objectName: "trackMouseArea" + property bool longPressed: false + enabled: root.privateSectionScroller || interactive + anchors { + top: root.privateSectionScroller ? parent.top : undefined; + bottom: root.privateSectionScroller ? parent.bottom : undefined; + right: root.privateSectionScroller ? parent.right : undefined; + fill: root.privateSectionScroller ? undefined : (flickableItem ? track : undefined) + } + width: root.privateSectionScroller ? privateStyle.scrollBarThickness * 3 : undefined + drag { + target: root.privateSectionScroller ? sectionScrollBackground : undefined + // axis is set XandY to prevent flickable from stealing the mouse event + // SectionScroller is anchored to the right side of the mouse area so the user + // won't be able to drag it along the X axis + axis: root.privateSectionScroller ? Drag.XandYAxis : 0 + minimumY: root.privateSectionScroller ? (flickableItem ? flickableItem.y : 0) : 0 + maximumY: root.privateSectionScroller ? (flickableItem ? (trackMouseArea.height - sectionScrollBackground.height) : 0) : 0 + } + + onPressAndHold: { + if (!root.privateSectionScroller) + longPressed = true + } + + onReleased: longPressed = false + + onPositionChanged: { + if (root.privateSectionScroller) + internal.adjustContentPosition(trackMouseArea.mouseY); + } + + onPressedChanged: { + if (root.privateSectionScroller && trackMouseArea.pressed) + internal.adjustContentPosition(trackMouseArea.mouseY); + } + } + Timer { + id: pressAndHoldTimer + running: trackMouseArea.longPressed + interval: 50 + repeat: true + onTriggered: { internal.moveToLongTapPosition(trackMouseArea); privateStyle.play(Android.SensitiveSlider) } + } + + BorderImage { + id: handle + objectName: "handle" + source: privateStyle.imagePath(handleFileName(), root.platformInverted) + x: orientation == Qt.Horizontal ? internal.handlePosition : NaN + y: orientation == Qt.Vertical ? internal.handlePosition : NaN + height: orientation == Qt.Vertical ? internal.dynamicHandleLength : root.height + width: orientation == Qt.Horizontal ? internal.dynamicHandleLength : root.width + border.right: orientation == Qt.Horizontal ? 7 : 0 + border.left: orientation == Qt.Horizontal ? 7 : 0 + border.top: orientation == Qt.Vertical ? 7 : 0 + border.bottom: orientation == Qt.Vertical ? 7 : 0 + + function handleFileName() { + var fileName = (orientation == Qt.Vertical ? "qtg_fr_scrollbar_v_handle_" : + "qtg_fr_scrollbar_h_handle_") + if (!interactive) + fileName += "indicative" + else if (handleMouseArea.pressed) + fileName += "pressed" + else + fileName += "normal" + return fileName + } + } + + MouseArea { + id: handleMouseArea + objectName: "handleMouseArea" + property real maxDragY: flickableItem ? flickableItem.height - handle.height - root.anchors.topMargin - root.anchors.bottomMargin : NaN + property real maxDragX: flickableItem ? flickableItem.width - handle.width - root.anchors.leftMargin - root.anchors.rightMargin : NaN + enabled: interactive && !root.privateSectionScroller + width: orientation == Qt.Vertical ? 3 * privateStyle.scrollBarThickness : handle.width + height: orientation == Qt.Horizontal ? 3 * privateStyle.scrollBarThickness : handle.height + anchors { + verticalCenter: flickableItem ? handle.verticalCenter : undefined + horizontalCenter: flickableItem ? handle.horizontalCenter : undefined + } + drag { + target: handle + axis: orientation == Qt.Vertical ? Drag.YAxis : Drag.XAxis + minimumY: 0 + maximumY: maxDragY + minimumX: 0 + maximumX: maxDragX + } + onPositionChanged: internal.moveToHandlePosition() + } + + PropertyAnimation { + id: indicateEffect + + function play() { + flashEffect.stop() + idleEffect.stop() + restart() + } + + target: root + property: "opacity" + to: 1 + duration: 0 + } + SequentialAnimation { + id: idleEffect + + function play() { + indicateEffect.stop() + if (internal.scrollBarNeeded && root.policy == Android.ScrollBarWhenScrolling) + restart() + } + + PauseAnimation { duration: root.interactive ? 1500 : 0 } + PropertyAnimation { + target: root + property: "opacity" + to: internal.rootOpacity + duration: internal.hideTimeout + } + } + SequentialAnimation { + id: flashEffect + property int type: Android.FadeOut + + PropertyAnimation { + target: root + property: "opacity" + to: 1 + duration: (flashEffect.type == Android.FadeInFadeOut) ? internal.hideTimeout : 0 + } + PropertyAnimation { + target: root + property: "opacity" + to: internal.rootOpacity + duration: internal.hideTimeout + } + } + StateGroup { + id: stateGroup + states: [ + State { + name: "Move" + when: handleMouseArea.pressed + }, + State { + name: "Stepping" + when: trackMouseArea.longPressed + }, + State { + name: "Step" + when: trackMouseArea.pressed && !trackMouseArea.longPressed && !root.privateSectionScroller + }, + State { + name: "Indicate" + when: (internal.scrollBarNeeded && flickableItem.moving) || + (trackMouseArea.pressed && root.privateSectionScroller) + }, + State { + name: "" + } + ] + transitions: [ + Transition { + to: "Move" + ScriptAction { script: privateStyle.play(Android.BasicSlider) } + ScriptAction { script: indicateEffect.play() } + }, + Transition { + to: "Step" + ScriptAction { script: internal.doPageStep() } + ScriptAction { script: privateStyle.play(Android.BasicSlider) } + ScriptAction { script: indicateEffect.play() } + }, + Transition { + from: "Step" + to: "Stepping" + ScriptAction { script: indicateEffect.play() } + }, + Transition { + from: "Move" + to: "Indicate" + ScriptAction { script: privateStyle.play(Android.BasicSlider) } + ScriptAction { script: indicateEffect.play() } + }, + Transition { + to: "Indicate" + ScriptAction { script: indicateEffect.play() } + }, + Transition { + from: "Move" + to: "" + ScriptAction { script: privateStyle.play(Android.BasicSlider) } + ScriptAction { script: idleEffect.play() } + }, + Transition { + to: "" + ScriptAction { script: idleEffect.play() } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml b/mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml new file mode 100644 index 0000000..4ba220f --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + //Common Public API + property Flickable flickableItem: null + + // Symbian specific API + property bool platformInverted: false + + Item { + id: decorators + property bool completed: false + + Loader { + sourceComponent: decorators.completed ? vertical : undefined + } + Loader { + sourceComponent: decorators.completed ? horizontal : undefined + } + Component { + id: horizontal + ScrollBar { + parent: flickableItem + flickableItem: root.flickableItem + orientation: Qt.Horizontal + interactive: false + platformInverted: root.platformInverted + anchors { + left: flickableItem.left + bottom: flickableItem.bottom + rightMargin: height + } + } + } + Component { + id: vertical + ScrollBar { + parent: flickableItem + flickableItem: root.flickableItem + orientation: Qt.Vertical + interactive: false + platformInverted: root.platformInverted + anchors { top: flickableItem.top; right: flickableItem.right } + } + } + Component.onDestruction: { + decorators.completed = false + } + } + onFlickableItemChanged: { + decorators.completed = flickableItem ? true : false + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/SectionScroller.js b/mardrone/imports/com/nokia/android.1.1/SectionScroller.js new file mode 100644 index 0000000..7b987fe --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/SectionScroller.js @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var _sectionData = []; +var _sections = []; + +function initSectionData(list) { + if (!list || !list.model) return; + _sectionData = []; + _sections = []; + var current = ""; + var prop = list.section.property; + + for (var i = 0, count = list.model.count; i < count; i++) { + var item = list.model.get(i); + if (item[prop] !== current) { + current = item[prop]; + _sections.push(current); + _sectionData.push({ index: i, header: current }); + } + } +} + +function closestSection(pos) { + var tmp = (_sections.length) * pos; + var val = Math.ceil(tmp) // TODO: better algorithm + val = val < 2 ? 1 : val; + return _sections[val-1]; +} + +function indexOf(sectionName) { + var val = _sectionData[_sections.indexOf(sectionName)].index; + return val === 0 || val > 0 ? val : -1; +} diff --git a/mardrone/imports/com/nokia/android.1.1/SectionScroller.qml b/mardrone/imports/com/nokia/android.1.1/SectionScroller.qml new file mode 100644 index 0000000..0b165d7 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/SectionScroller.qml @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "SectionScroller.js" as Sections +import "." 1.1 + +Item { + id: root + + property ListView listView + property bool platformInverted: false + + onListViewChanged: { + if (listView && listView.model) + internal.initDirtyObserver(); + } + + Connections { + target: listView + onModelChanged: { + if (listView && listView.model) + internal.initDirtyObserver(); + } + } + + ScrollBar { + id: scrollBar + parent: listView + flickableItem: listView + privateSectionScroller: true + platformInverted: root.platformInverted + anchors { + top: parent ? parent.top : undefined + right: parent ? parent.right : undefined + } + } + + QtObject { + id: internal + + function initDirtyObserver() { + Sections.initSectionData(listView); + function dirtyObserver() { + if (!internal.modelDirty) { + internal.modelDirty = true; + dirtyTimer.running = true; + } + } + + if (listView.model.countChanged) + listView.model.countChanged.connect(dirtyObserver); + + if (listView.model.itemsChanged) + listView.model.itemsChanged.connect(dirtyObserver); + + if (listView.model.itemsInserted) + listView.model.itemsInserted.connect(dirtyObserver); + + if (listView.model.itemsMoved) + listView.model.itemsMoved.connect(dirtyObserver); + + if (listView.model.itemsRemoved) + listView.model.itemsRemoved.connect(dirtyObserver); + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml b/mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml new file mode 100644 index 0000000..6761534 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +CommonDialog { + id: root + + // Common API + property alias model: listView.model + property int selectedIndex: -1 + property Component delegate: defaultDelegate + + privateCloseIcon: true + + Component { + id: defaultDelegate + + MenuItem { + platformInverted: root.platformInverted + text: modelData + onClicked: { + selectedIndex = index + root.accept() + } + + Keys.onPressed: { + if (event.key == Qt.Key_Up || event.key == Qt.Key_Down) + scrollBar.flash() + } + } + } + + content: Item { + id: contentItem + + function preferredHeight() { + // Need to create artifical binding to listView.delegate because of QTBUG-19037 + var dummy = listView.delegate + var multiplier = screen.height > screen.width ? 0.45 : 0.6 + var maxHeight = Math.floor(multiplier * screen.height / privateStyle.menuItemHeight) + * privateStyle.menuItemHeight + return Math.min(maxHeight, listView.count * privateStyle.menuItemHeight) + } + + height: preferredHeight() + width: root.platformContentMaximumWidth + + Item { + // Clipping item with bottom margin added to align content with rounded background graphics + id: clipItem + anchors.fill: parent + anchors.bottomMargin: platformStyle.paddingSmall + clip: true + ListView { + id: listView + + currentIndex : -1 + width: contentItem.width + height: contentItem.height + delegate: root.delegate + clip: true + + Keys.onPressed: { + if (event.key == Qt.Key_Up || event.key == Qt.Key_Down + || event.key == Qt.Key_Left || event.key == Qt.Key_Right + || event.key == Qt.Key_Select || event.key == Qt.Key_Enter + || event.key == Qt.Key_Return) { + android.listInteractionMode = Android.KeyNavigation + listView.currentIndex = 0 + event.accepted = true + } + } + } + } + ScrollBar { + id: scrollBar + flickableItem: listView + interactive: false + visible: listView.contentHeight > contentItem.height + platformInverted: root.platformInverted + anchors { top: clipItem.top; right: clipItem.right } + } + } + + onClickedOutside: { + privateStyle.play(Android.PopupClose) + reject() + } + + onStatusChanged: { + if (status == DialogStatus.Opening) { + if (listView.currentItem != null) { + listView.currentItem.focus = false + } + listView.currentIndex = -1 + listView.positionViewAtIndex(0, ListView.Beginning) + } + else if (status == DialogStatus.Open) { + listView.focus = true + if (listView.contentHeight > contentItem.height) + scrollBar.flash(Android.FadeInFadeOut) + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml b/mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml new file mode 100644 index 0000000..d249d1d --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +ListItem { + id: root + property string title: "" + property string subTitle: "" + + implicitHeight: background.height + 2 * platformStyle.paddingLarge + + onModeChanged: { + if (root.mode == "pressed") { + pressed.source = privateStyle.imagePath("qtg_fr_choice_list_pressed", root.platformInverted) + pressed.opacity = 1 + } else { + releasedEffect.restart() + } + } + + BorderImage { + id: background + height: privateStyle.menuItemHeight - platformStyle.paddingSmall // from layout spec. + anchors { + left: parent.left + leftMargin: platformStyle.paddingLarge + right: parent.right + rightMargin: privateStyle.scrollBarThickness + verticalCenter: parent.verticalCenter + } + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + source: privateStyle.imagePath("qtg_fr_choice_list_" + internal.getBackground(), + root.platformInverted) + + BorderImage { + id: pressed + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + opacity: 0 + anchors.fill: parent + } + + Column { + anchors { + verticalCenter: background.verticalCenter + right: indicator.left + rightMargin: platformStyle.paddingMedium + left: background.left + leftMargin: platformStyle.paddingLarge + } + + Loader { + anchors.left: parent.left + sourceComponent: title != "" ? titleText : undefined + width: parent.width // elide requires explicit width + } + + Loader { + anchors.left: parent.left + sourceComponent: subTitle != "" ? subTitleText : undefined + width: parent.width // elide requires explicit width + } + } + Image { + id: indicator + source: root.mode == "disabled" ? privateStyle.imagePath("qtg_graf_choice_list_indicator_disabled", + root.platformInverted) + : privateStyle.imagePath("qtg_graf_choice_list_indicator", + root.platformInverted) + sourceSize.width: platformStyle.graphicSizeSmall + sourceSize.height: platformStyle.graphicSizeSmall + anchors { + right: background.right + rightMargin: platformStyle.paddingSmall + verticalCenter: parent.verticalCenter + } + } + } + + Component { + id: titleText + ListItemText { + mode: root.mode + role: "SelectionTitle" + text: root.title + platformInverted: root.platformInverted + } + } + Component { + id: subTitleText + ListItemText { + mode: root.mode + role: "SelectionSubTitle" + text: root.subTitle + platformInverted: root.platformInverted + } + } + + QtObject { + id: internal + function getBackground() { + if (root.mode == "highlighted") + return "highlighted" + else if (root.mode == "disabled") + return "disabled" + else + return "normal" + } + } + + SequentialAnimation { + id: releasedEffect + PropertyAnimation { + target: pressed + property: "opacity" + to: 0 + easing.type: Easing.Linear + duration: 150 + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Slider.qml b/mardrone/imports/com/nokia/android.1.1/Slider.qml new file mode 100644 index 0000000..2910553 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Slider.qml @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 +import Qt.labs.components 1.1 as QtComponents + +Item { + id: root + + // Common Public API + property alias stepSize: model.stepSize + property alias minimumValue: model.minimumValue + property alias maximumValue: model.maximumValue + property alias value: model.value + property int orientation: Qt.Horizontal + property bool pressed: track.inputActive + property bool valueIndicatorVisible: false + property string valueIndicatorText: "" + property alias inverted: model.inverted + + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + + // Symbian specific + property bool platformInverted: false + signal valueChanged(real value) + implicitWidth: orientation == Qt.Horizontal ? 150 : privateStyle.menuItemHeight + implicitHeight: orientation == Qt.Horizontal ? privateStyle.menuItemHeight : 150 + + onActiveFocusChanged: { + if (!root.activeFocus) + track.activeKey = undefined + } + + QtComponents.RangeModel { + id: model + value: 0.0 + stepSize: 0.0 + minimumValue: 0.0 + maximumValue: 1.0 + positionAtMinimum: 0 + positionAtMaximum: orientation == Qt.Horizontal ? track.width - handle.width : track.height - handle.height + onValueChanged: root.valueChanged(value) + } + + BorderImage { + id: track + objectName: "track" + property bool tapOnTrack: false + property variant activeKey + property bool inputActive: handleMouseArea.pressed || !!track.activeKey + onInputActiveChanged: { + if (!valueIndicatorVisible) + return + if (inputActive) + valueIndicator.show = true + else + indicatorTimer.restart() + } + + states: [ + State { + name: "Horizontal" + when: orientation == Qt.Horizontal + + PropertyChanges { + target: track + height: privateStyle.sliderThickness + width: undefined + source: privateStyle.imagePath("qtg_fr_slider_h_track_normal", root.platformInverted) + border { + left: 20 + right: 20 + top: 0 + bottom: 0 + } + smooth: true + } + + AnchorChanges { + target: track + anchors { + left: root.left + right: root.right + top: undefined + bottom: undefined + horizontalCenter: undefined + verticalCenter: root.verticalCenter + } + } + }, + State { + name: "Vertical" + when: orientation == Qt.Vertical + + PropertyChanges { + target: track + height: undefined + width: privateStyle.sliderThickness + source: privateStyle.imagePath("qtg_fr_slider_v_track_normal", root.platformInverted) + border { + left: 0 + right: 0 + top: 20 + bottom: 20 + } + smooth: true + } + + AnchorChanges { + target: track + anchors { + left: undefined + right: undefined + top: root.top + bottom: root.bottom + horizontalCenter: root.horizontalCenter + verticalCenter: undefined + } + } + } + ] + + anchors.leftMargin: platformStyle.paddingMedium + anchors.rightMargin: platformStyle.paddingMedium + anchors.topMargin: platformStyle.paddingMedium + anchors.bottomMargin: platformStyle.paddingMedium + + MouseArea { + id: trackMouseArea + objectName: "trackMouseArea" + + anchors.fill: parent + + onClicked: { + if (track.tapOnTrack) { + if (orientation == Qt.Horizontal) { + if (handle.x > (mouseX - handle.width / 2)) { + model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize + handle.x = model.position + } else { + model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize + handle.x = model.position + } + } else { + if (handle.y > (mouseY - handle.height / 2)) { + model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize + handle.y = model.position + } else { + model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize + handle.y = model.position + } + } + } + } + + Image { + id: handle + objectName: "handle" + x: orientation == Qt.Horizontal ? (handleMouseArea.pressed ? x : model.position) : 0 + y: orientation == Qt.Horizontal ? 0 : (handleMouseArea.pressed ? y : model.position) + + sourceSize.height: platformStyle.graphicSizeSmall + sourceSize.width: platformStyle.graphicSizeSmall + + anchors.verticalCenter: orientation == Qt.Horizontal ? parent.verticalCenter : undefined + anchors.horizontalCenter: orientation == Qt.Horizontal ? undefined : parent.horizontalCenter + + source: { + var handleIcon = "qtg_graf_slider_" + + (orientation == Qt.Horizontal ? "h" : "v") + + "_handle_" + + (handleMouseArea.pressed ? "pressed" : "normal") + privateStyle.imagePath(handleIcon, root.platformInverted) + } + + onXChanged: valueIndicator.position() + onYChanged: valueIndicator.position() + + MouseArea { + id: handleMouseArea + objectName: "handleMouseArea" + + height: platformStyle.graphicSizeMedium + width: platformStyle.graphicSizeMedium + anchors.verticalCenter: orientation == Qt.Horizontal ? parent.verticalCenter : undefined + anchors.horizontalCenter: orientation == Qt.Horizontal ? undefined : parent.horizontalCenter + + drag.target: handle + drag.axis: Drag.XandYAxis + drag.minimumX: orientation == Qt.Horizontal ? model.positionAtMinimum : 0 + drag.maximumX: orientation == Qt.Horizontal ? model.positionAtMaximum : 0 + drag.minimumY: orientation == Qt.Horizontal ? 0 : model.positionAtMinimum + drag.maximumY: orientation == Qt.Horizontal ? 0 : model.positionAtMaximum + + onPositionChanged: model.position = orientation == Qt.Horizontal ? handle.x : handle.y + onPressed: privateStyle.play(Android.BasicSlider) + onReleased: privateStyle.play(Android.BasicSlider) + } + } + } + } + + Keys.onPressed: { + internal.handleKeyPressEvent(event) + } + + Keys.onReleased: { + internal.handleKeyReleaseEvent(event) + } + + Component { + id: valueIndicatorComponent + ToolTip { + text: root.valueIndicatorText == "" ? model.value : root.valueIndicatorText + platformInverted: root.platformInverted + } + } + + Loader { + id: valueIndicator + + property int spacing: 2 * platformStyle.paddingLarge + // Must match with the "maxWidth" padding defined in ToolTip + property int toolTipPadding: platformStyle.paddingLarge + property bool show: false + sourceComponent: valueIndicator.show ? valueIndicatorComponent : undefined + onLoaded: position() + + function position() { + if (!valueIndicatorVisible || status != Loader.Ready) + return + + var point = null + if (orientation == Qt.Horizontal) { + point = root.mapFromItem(track, handle.x + handle.width / 2 - valueIndicator.item.width / 2, 0) + + // Check if valueIndicator will be positioned beyond the right or + // left boundaries and adjust if needed to keep it fully + // visible on screen. In case the valueIndicator is so wide that it + // does not fit the screen, it's positioned to left of the screen. + var rightStop = screen.width - toolTipPadding + var valueIndicatorLeftEdge = root.mapToItem(null, point.x, 0) + var valueIndicatorRightEdge = root.mapToItem(null, point.x + valueIndicator.item.width, 0) + + if (valueIndicatorLeftEdge.x < toolTipPadding) + point.x = root.mapFromItem(null, toolTipPadding, 0).x + else if (valueIndicatorRightEdge.x > rightStop) + point.x = root.mapFromItem(null, rightStop - valueIndicator.item.width, 0).x + + valueIndicator.item.x = point.x + valueIndicator.item.y = point.y - valueIndicator.spacing - valueIndicator.item.height + } else { + point = root.mapFromItem(track, 0, handle.y + handle.height / 2 - valueIndicator.item.height / 2) + valueIndicator.item.x = point.x - valueIndicator.spacing - valueIndicator.item.width + valueIndicator.item.y = point.y + } + } + + Timer { + id: indicatorTimer + interval: 750 + onTriggered: { + if (!track.inputActive) + valueIndicator.show = false + } + } + } + + QtObject { + id: internal + + function handleKeyPressEvent(keyEvent) { + var oldValue = model.value + if (orientation == Qt.Horizontal) { + if (keyEvent.key == Qt.Key_Left) { + model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize + } else if (keyEvent.key == Qt.Key_Right) { + model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize + } + } else { //Vertical + if (keyEvent.key == Qt.Key_Up) { + model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize + } else if (keyEvent.key == Qt.Key_Down) { + model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize + } + } + if (oldValue != model.value) + keyEvent.accepted = true + + if (keyEvent.accepted || + keyEvent.key == Qt.Key_Select || + keyEvent.key == Qt.Key_Return || + keyEvent.key == Qt.Key_Enter) + track.activeKey = keyEvent.key + } + + function handleKeyReleaseEvent(keyEvent) { + if (track.activeKey == keyEvent.key) { + if (!keyEvent.isAutoRepeat) + track.activeKey = undefined + keyEvent.accepted = true + } + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/StatusBar.qml b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml new file mode 100644 index 0000000..073dc6e --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + implicitWidth: screen.width + implicitHeight: privateStyle.statusBarHeight + property bool platformInverted: false + +// QtObject { +// id: priv +// objectName: "priv" + +// property bool clickedOpensStatusPanel: android.s60Version == Android.SV_S60_5_2 ? true : false +// property int contentHeight: Math.round(privateStyle.statusBarHeight * 18 / 26) +// property int paddingSmallOneQuarter: Math.round(platformStyle.paddingSmall / 4) +// property int paddingSmallThreeQuarters: Math.round(platformStyle.paddingSmall * 3 / 4) + +// function signalWidthPercentage(signalStrength) { +// if (signalStrength < 10) +// return 0; +// else if (signalStrength < 20) +// return 1/5; +// else if (signalStrength < 30) +// return 2/5; +// else if (signalStrength < 60) +// return 3/5; +// else if (signalStrength < 100) +// return 4/5; +// else +// return 1; +// } +// } + +// MouseArea { +// id: mouseArea +// anchors.fill: parent + +// onClicked: { +// if (priv.clickedOpensStatusPanel) { +// privateStyle.play(Android.PopUp) +// platformPopupManager.privateShowIndicatorPopup() +// } +// } +// onPressed: { +// if (!priv.clickedOpensStatusPanel) { +// privateStyle.play(Android.PopUp) +// platformPopupManager.privateShowIndicatorPopup() +// // reset MouseArea state since status panel window eats the release event +// android.privateSendMouseRelease(mouseArea) +// } +// } +// } + +// BorderImage { +// source: privateStyle.imagePath("qtg_fr_statusbar", root.platformInverted) +// anchors.fill: parent +// width: parent.width + +// UniversalIndicators { +// height: indicatorHeight +// anchors { +// right: networkMode.left +// rightMargin: priv.paddingSmallOneQuarter +// verticalCenter: parent.verticalCenter +// } + +// width: priv.paddingSmallOneQuarter * 2 + 3 * indicatorWidth + +// indicatorColor: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// indicatorWidth: priv.contentHeight // same as height +// indicatorHeight: priv.contentHeight +// indicatorPadding: priv.paddingSmallOneQuarter +// maxIndicatorCount: 3 +// } + +// // icon for network signal type e.g. 3G, GPRS etc +// NetworkIndicator { +// id: networkMode +// height: priv.contentHeight +// width: priv.contentHeight // same as height +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: offline ? batteryBackground.left : signalBackground.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// color: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// } +// // signal strength +// Image { +// id: signalBackground +// visible: !networkMode.offline +// sourceSize.height: priv.contentHeight +// sourceSize.width: Math.round(privateStyle.statusBarHeight * 19 / 26) +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: batteryBackground.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath("qtg_graf_signal_level_bg", root.platformInverted) +// Item { +// id: signalLevelItem +// anchors.left: parent.left +// anchors.top: parent.top +// height: parent.height +// width: priv.signalWidthPercentage(privateNetworkInfo.networkSignalStrength) * parent.width +// clip: true +// LayoutMirroring.enabled: false + +// Image { +// sourceSize.width: signalBackground.sourceSize.width +// sourceSize.height: signalBackground.sourceSize.height +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath("qtg_graf_signal_level_full", root.platformInverted) +// } +// } +// } +// // battery indicator +// Image { +// id: batteryBackground +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: timeItem.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// sourceSize.height: priv.contentHeight +// sourceSize.width: Math.round(privateStyle.statusBarHeight * 24 / 26) +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ? +// "qtg_graf_battery_level_psm_bg" : +// "qtg_graf_battery_level_bg"), root.platformInverted) + +// Item { +// id: batteryLevel + +// property int animatedLevel + +// anchors.left: parent.left +// anchors.top: parent.top +// width: Math.round(privateStyle.statusBarHeight +// * ((privateBatteryInfo.charging ? Math.floor(animatedLevel / 100) : +// privateBatteryInfo.batteryLevel) + 2) / 13) +// height: parent.height +// clip: true +// LayoutMirroring.enabled: false + +// Image { +// sourceSize.width: batteryBackground.sourceSize.width +// sourceSize.height: batteryBackground.sourceSize.height + +// fillMode: Image.PreserveAspectFit + +// // Battery state mappings: Levels 0 and 1 are low, 2-4 are medium, 5-7 are full. +// // Currently all levels use same graphics with white color. + +// source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ? +// "qtg_graf_battery_level_psm_full" : +// "qtg_graf_battery_level_full"), root.platformInverted) +// } +// } + +// Image { +// // power save mode indicator +// anchors.fill: parent +// sourceSize.width: parent.sourceSize.width +// sourceSize.height: parent.sourceSize.height +// source: privateStyle.imagePath("qtg_graf_battery_psm") +// visible: privateBatteryInfo.powerSaveModeEnabled +// } + +// NumberAnimation { +// id: batteryChargingAnimation +// loops: Animation.Infinite +// running: privateBatteryInfo.charging +// target: batteryLevel +// property: "animatedLevel" +// // Use bigger range (compared to 0-7) in order to make the animation smoother. +// from: 0 +// to: 799 +// duration: 3500 +// } +// } +// // clock +// Text { +// id: timeItem +// width: Math.round(privateStyle.statusBarHeight * 44 / 26) +// color: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: parent.right +// anchors.rightMargin: platformStyle.paddingSmall +// horizontalAlignment: Text.AlignRight +// text: android.currentTime +// font { +// family: platformStyle.fontFamilyRegular; +// pixelSize: priv.contentHeight +// weight: Font.Light +// } +// } +// } +} + diff --git a/mardrone/imports/com/nokia/android.1.1/StatusBar.qml~ b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml~ new file mode 100644 index 0000000..d9853db --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml~ @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { +// id: root + +// implicitWidth: screen.width +// implicitHeight: privateStyle.statusBarHeight + property bool platformInverted: false + +// QtObject { +// id: priv +// objectName: "priv" + +// property bool clickedOpensStatusPanel: android.s60Version == Android.SV_S60_5_2 ? true : false +// property int contentHeight: Math.round(privateStyle.statusBarHeight * 18 / 26) +// property int paddingSmallOneQuarter: Math.round(platformStyle.paddingSmall / 4) +// property int paddingSmallThreeQuarters: Math.round(platformStyle.paddingSmall * 3 / 4) + +// function signalWidthPercentage(signalStrength) { +// if (signalStrength < 10) +// return 0; +// else if (signalStrength < 20) +// return 1/5; +// else if (signalStrength < 30) +// return 2/5; +// else if (signalStrength < 60) +// return 3/5; +// else if (signalStrength < 100) +// return 4/5; +// else +// return 1; +// } +// } + +// MouseArea { +// id: mouseArea +// anchors.fill: parent + +// onClicked: { +// if (priv.clickedOpensStatusPanel) { +// privateStyle.play(Android.PopUp) +// platformPopupManager.privateShowIndicatorPopup() +// } +// } +// onPressed: { +// if (!priv.clickedOpensStatusPanel) { +// privateStyle.play(Android.PopUp) +// platformPopupManager.privateShowIndicatorPopup() +// // reset MouseArea state since status panel window eats the release event +// android.privateSendMouseRelease(mouseArea) +// } +// } +// } + +// BorderImage { +// source: privateStyle.imagePath("qtg_fr_statusbar", root.platformInverted) +// anchors.fill: parent +// width: parent.width + +// UniversalIndicators { +// height: indicatorHeight +// anchors { +// right: networkMode.left +// rightMargin: priv.paddingSmallOneQuarter +// verticalCenter: parent.verticalCenter +// } + +// width: priv.paddingSmallOneQuarter * 2 + 3 * indicatorWidth + +// indicatorColor: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// indicatorWidth: priv.contentHeight // same as height +// indicatorHeight: priv.contentHeight +// indicatorPadding: priv.paddingSmallOneQuarter +// maxIndicatorCount: 3 +// } + +// // icon for network signal type e.g. 3G, GPRS etc +// NetworkIndicator { +// id: networkMode +// height: priv.contentHeight +// width: priv.contentHeight // same as height +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: offline ? batteryBackground.left : signalBackground.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// color: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// } +// // signal strength +// Image { +// id: signalBackground +// visible: !networkMode.offline +// sourceSize.height: priv.contentHeight +// sourceSize.width: Math.round(privateStyle.statusBarHeight * 19 / 26) +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: batteryBackground.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath("qtg_graf_signal_level_bg", root.platformInverted) +// Item { +// id: signalLevelItem +// anchors.left: parent.left +// anchors.top: parent.top +// height: parent.height +// width: priv.signalWidthPercentage(privateNetworkInfo.networkSignalStrength) * parent.width +// clip: true +// LayoutMirroring.enabled: false + +// Image { +// sourceSize.width: signalBackground.sourceSize.width +// sourceSize.height: signalBackground.sourceSize.height +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath("qtg_graf_signal_level_full", root.platformInverted) +// } +// } +// } +// // battery indicator +// Image { +// id: batteryBackground +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: timeItem.left +// anchors.rightMargin: priv.paddingSmallThreeQuarters +// sourceSize.height: priv.contentHeight +// sourceSize.width: Math.round(privateStyle.statusBarHeight * 24 / 26) +// fillMode: Image.PreserveAspectFit +// source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ? +// "qtg_graf_battery_level_psm_bg" : +// "qtg_graf_battery_level_bg"), root.platformInverted) + +// Item { +// id: batteryLevel + +// property int animatedLevel + +// anchors.left: parent.left +// anchors.top: parent.top +// width: Math.round(privateStyle.statusBarHeight +// * ((privateBatteryInfo.charging ? Math.floor(animatedLevel / 100) : +// privateBatteryInfo.batteryLevel) + 2) / 13) +// height: parent.height +// clip: true +// LayoutMirroring.enabled: false + +// Image { +// sourceSize.width: batteryBackground.sourceSize.width +// sourceSize.height: batteryBackground.sourceSize.height + +// fillMode: Image.PreserveAspectFit + +// // Battery state mappings: Levels 0 and 1 are low, 2-4 are medium, 5-7 are full. +// // Currently all levels use same graphics with white color. + +// source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ? +// "qtg_graf_battery_level_psm_full" : +// "qtg_graf_battery_level_full"), root.platformInverted) +// } +// } + +// Image { +// // power save mode indicator +// anchors.fill: parent +// sourceSize.width: parent.sourceSize.width +// sourceSize.height: parent.sourceSize.height +// source: privateStyle.imagePath("qtg_graf_battery_psm") +// visible: privateBatteryInfo.powerSaveModeEnabled +// } + +// NumberAnimation { +// id: batteryChargingAnimation +// loops: Animation.Infinite +// running: privateBatteryInfo.charging +// target: batteryLevel +// property: "animatedLevel" +// // Use bigger range (compared to 0-7) in order to make the animation smoother. +// from: 0 +// to: 799 +// duration: 3500 +// } +// } +// // clock +// Text { +// id: timeItem +// width: Math.round(privateStyle.statusBarHeight * 44 / 26) +// color: root.platformInverted ? platformStyle.colorNormalDarkInverted +// : platformStyle.colorNormalLight +// anchors.verticalCenter: parent.verticalCenter +// anchors.right: parent.right +// anchors.rightMargin: platformStyle.paddingSmall +// horizontalAlignment: Text.AlignRight +// text: android.currentTime +// font { +// family: platformStyle.fontFamilyRegular; +// pixelSize: priv.contentHeight +// weight: Font.Light +// } +// } +// } +} + diff --git a/mardrone/imports/com/nokia/android.1.1/Switch.qml b/mardrone/imports/com/nokia/android.1.1/Switch.qml new file mode 100644 index 0000000..207d5c0 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Switch.qml @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + // Common Public API + property bool checked: false + property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed" + || stateGroup.state == "Dragging" + + // Symbian specific API + property bool platformInverted: false + + signal clicked + + QtObject { + id: internal + objectName: "internal" + + function toggle() { + root.checked = !root.checked + root.clicked() + } + } + + StateGroup { + id: stateGroup + + states: [ + State { name: "Pressed" }, + State { name: "Released" }, + State { name: "KeyPressed" }, + State { name: "Dragging" }, + State { name: "Canceled" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: privateStyle.play(Android.BasicItem) } + }, + Transition { + from: "Released, Dragging" + to: "" + ScriptAction { script: privateStyle.play(Android.CheckBox) } + ScriptAction { script: internal.toggle() } + }, + Transition { + from: "KeyPressed" + to: "" + ScriptAction { script: internal.toggle() } + } + ] + } + + implicitWidth: track.width + implicitHeight: privateStyle.switchButtonHeight + + Image { + id: track + + function trackPostfix() { + if (!root.enabled && root.checked) + return "disabled_on" + else if (!root.enabled && !root.checked) + return "disabled_off" + else + return "track" + } + + source: privateStyle.imagePath("qtg_graf_switchbutton_" + track.trackPostfix(), + root.platformInverted) + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + sourceSize.width: Android.UndefinedSourceDimension + sourceSize.height: privateStyle.switchButtonHeight + scale: root.LayoutMirroring.enabled ? -1 : 1 + } + + MouseArea { + id: mouseArea + + property real lastX + + function isChecked() { + if (root.LayoutMirroring.enabled) + return (handle.x + handle.width / 2 < track.x + (track.width / 2)) + else + return (handle.x + handle.width / 2 > track.x + (track.width / 2)) + } + function updateHandlePos() { + // The middle of the handle follows mouse, the handle is bound to the track + handle.x = Math.max(track.x, Math.min(mouseArea.lastX - handle.width / 2, + track.x + track.width - handle.width)) + } + + anchors.fill: track + onPressed: stateGroup.state = "Pressed" + onReleased: stateGroup.state = "Released" // releasing doesn't toggle yet, it is intermediate state + onClicked: stateGroup.state = "" + onCanceled: stateGroup.state = "Canceled" + onPositionChanged: { + mouseArea.lastX = mouse.x + if (mouseArea.drag.active) + updateHandlePos() + } + drag { + // The handle is moved manually but MouseArea can be used to decide when dragging + // should start (QApplication::startDragDistance). A dummy target needs to be bound or + // dragging won't get activated. + target: Item { visible: false } + + axis: Drag.XandYAxis + minimumY: 0; maximumY: 0 // keep dragging active eventhough only x axis switches + minimumX: track.x; maximumX: mouseArea.drag.minimumX + track.width - handle.width + onActiveChanged: { + if (mouseArea.drag.active) { + updateHandlePos() + stateGroup.state = "Dragging" + } + else { + stateGroup.state = (root.checked != isChecked()) ? "" : "Canceled" + } + } + } + } + + Item { + id: fill + + clip: true + anchors.left: track.left + anchors.right: handle.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + height: privateStyle.switchButtonHeight + visible: root.enabled + + Image { + source: privateStyle.imagePath("qtg_graf_switchbutton_fill", + root.platformInverted) + anchors.left: parent.left + anchors.top: parent.top + height: parent.height + } + } + + Image { + id: handle + + source: privateStyle.imagePath("qtg_graf_switchbutton_" + + (root.pressed ? "handle_pressed" : "handle_normal"), + root.platformInverted) + anchors.verticalCenter: root.verticalCenter + sourceSize.width: privateStyle.switchButtonHeight + sourceSize.height: privateStyle.switchButtonHeight + visible: root.enabled + + states: [ + State { + name: "Off" + when: !mouseArea.drag.active && !checked + PropertyChanges { + target: handle + restoreEntryValues: false + x: root.LayoutMirroring.enabled ? mouseArea.drag.maximumX : mouseArea.drag.minimumX + } + }, + State { + name: "On" + when: !mouseArea.drag.active && checked + PropertyChanges { + target: handle + restoreEntryValues: false + x: root.LayoutMirroring.enabled ? mouseArea.drag.minimumX : mouseArea.drag.maximumX + } + } + ] + + transitions: [ + Transition { + to: "Off" + SmoothedAnimation {properties: "x"; easing.type: Easing.InOutQuad; duration: 200 } + }, + Transition { + to: "On" + SmoothedAnimation {properties: "x"; easing.type: Easing.InOutQuad; duration: 200 } + } + ] + } + + Keys.onPressed: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "KeyPressed" + event.accepted = true + } + } + + + Keys.onReleased: { + if (!event.isAutoRepeat && (event.key == Qt.Key_Select + || event.key == Qt.Key_Return + || event.key == Qt.Key_Enter)) { + stateGroup.state = "" + event.accepted = true + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TabBar.qml b/mardrone/imports/com/nokia/android.1.1/TabBar.qml new file mode 100644 index 0000000..86f050a --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TabBar.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + default property alias content: tabBarLayout.data + property alias layout: tabBarLayout + property bool platformInverted: false + + implicitWidth: Math.max(50, screen.width) + implicitHeight: screen.width < screen.height ? privateStyle.tabBarHeightPortrait : privateStyle.tabBarHeightLandscape + + BorderImage { + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_tab_bar", root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + } + + TabBarLayout { + id: tabBarLayout + anchors.fill: parent + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml b/mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml new file mode 100644 index 0000000..b258357 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + Component.onCompleted: priv.layoutChildren() + onChildrenChanged: priv.layoutChildren() + onWidthChanged: priv.layoutChildren() + onHeightChanged: priv.layoutChildren() + + Keys.onPressed: { + if (event.key == Qt.Key_Right || event.key == Qt.Key_Left) { + if (event.key == Qt.Key_Right || priv.mirrored) { + var oldIndex = priv.currentButtonIndex() + if (oldIndex != root.children.length - 1) { + priv.tabGroup.currentTab = root.children[oldIndex + 1].tab + event.accepted = true + } + } else if (event.key == Qt.Key_Left || priv.mirrored) { + var oldIndex = priv.currentButtonIndex() + if (oldIndex != 0) { + priv.tabGroup.currentTab = root.children[oldIndex - 1].tab + event.accepted = true + } + } + } + } + + focus: true + + QtObject { + id: priv + property Item firstButton: root.children.length > 0 ? root.children[0] : null + property Item firstTab: firstButton ? (firstButton.tab != null ? firstButton.tab : null) : null + property Item tabGroup: firstTab ? (firstTab.parent ? firstTab.parent.parent : null) : null + property bool mirrored: root.LayoutMirroring.enabled + + onMirroredChanged: layoutChildren() + + function currentButtonIndex() { + for (var i = 0; i < root.children.length; ++i) { + if (root.children[i].tab == tabGroup.currentTab) + return i + } + return -1 + } + + function layoutChildren() { + var childCount = root.children.length + var contentWidth = 0 + var contentHeight = 0 + if (childCount != 0) { + var itemWidth = root.width / childCount + var itemIndex = mirrored ? childCount - 1 : 0 + var increment = mirrored ? - 1 : 1 + + for (var i = 0; i < childCount; ++i, itemIndex += increment) { + var child = root.children[itemIndex] + child.x = i * itemWidth + child.y = 0 + child.width = itemWidth + child.height = root.height + + if (child.implicitWidth != undefined) { + contentWidth = Math.max(contentWidth, child.implicitWidth * childCount) + contentHeight = Math.max(contentHeight, child.implicitHeight) + } + } + } + root.implicitWidth = contentWidth + root.implicitHeight = contentHeight + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TabButton.qml b/mardrone/imports/com/nokia/android.1.1/TabButton.qml new file mode 100644 index 0000000..fce0fb0 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TabButton.qml @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 +import "AppManager.js" as Utils + +Item { + id: root + + // Common Public API + property Item tab + property bool checked: internal.tabGroup != null && internal.tabGroup.currentTab == tab + property bool pressed: stateGroup.state == "Pressed" && mouseArea.containsMouse + property alias text: label.text + property alias iconSource: imageLoader.source + + signal clicked + + // Symbian specific API + property bool platformInverted: false + + implicitWidth: Math.max(2 * imageLoader.width, 2 * platformStyle.paddingMedium + privateStyle.textWidth(label.text, label.font)) + implicitHeight: internal.portrait ? privateStyle.tabBarHeightPortrait : privateStyle.tabBarHeightLandscape + + QtObject { + id: internal + + property Item tabGroup: Utils.findParent(tab, "currentTab") + property bool portrait: screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted + + function press() { + privateStyle.play(Android.BasicButton) + } + function click() { + root.clicked() + privateStyle.play(Android.BasicButton) + if (internal.tabGroup) + internal.tabGroup.currentTab = tab + } + + function isButtonRow(item) { + return (item && + item.hasOwnProperty("checkedButton") && + item.hasOwnProperty("privateDirection") && + item.privateDirection == Qt.Horizontal) + } + + function imageName() { + // If the parent of a TabButton is ButtonRow, segmented-style graphics + // are used to create a seamless row of buttons. Otherwise normal + // TabButton graphics are utilized. + var mirror = root.LayoutMirroring.enabled // To create binding + if (isButtonRow(parent)) + return parent.privateGraphicsName(root, 1) + else + return "qtg_fr_tab_" + } + + function modeName() { + if (isButtonRow(parent)) { + return parent.privateModeName(root, 1) + } else { + return root.checked ? "active" : "passive_normal" + } + } + + function modeNamePressed() { + if (isButtonRow(parent)) { + return "pressed" + } else { + return "passive_pressed" + } + } + } + + StateGroup { + id: stateGroup + + states: [ + State { + name: "Pressed" + PropertyChanges { target: pressedGraphics; opacity: 1 } + }, + State { name: "Canceled" } + ] + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: internal.press() } + }, + Transition { + from: "Pressed" + to: "" + NumberAnimation { + target: pressedGraphics + property: "opacity" + to: 0 + duration: 150 + easing.type: Easing.Linear + } + ScriptAction { script: internal.click() } + } + ] + } + + BorderImage { + id: background + + source: privateStyle.imagePath(internal.imageName() + internal.modeName(), root.platformInverted) + anchors.fill: parent + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + } + + BorderImage { + id: pressedGraphics + + source: privateStyle.imagePath(internal.imageName() + internal.modeNamePressed(), root.platformInverted) + anchors.fill: parent + opacity: 0 + + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + } + + Text { + id: label + + objectName: "label" + // hide in landscape if icon is present + visible: !(iconSource.toString() && !internal.portrait) + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + leftMargin: platformStyle.paddingMedium + rightMargin: platformStyle.paddingMedium + bottomMargin: iconSource.toString() + ? platformStyle.paddingSmall + : Math.ceil((parent.height - label.height) / 2.0) + } + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + font { + family: platformStyle.fontFamilyRegular; + pixelSize: platformStyle.fontSizeSmall + weight: Font.Light + } + color: { + if (root.pressed) + root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + else if (root.checked) + root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + else + root.platformInverted ? platformStyle.colorNormalMidInverted + : platformStyle.colorNormalMid + } + } + + Loader { + // imageLoader acts as wrapper for Image and Icon items. The Image item is + // shown when the source points to a image (jpg, png). Icon item is used for + // locigal theme icons which are colorised. + id: imageLoader + + property url source + property string iconId: privateStyle.isTabBarIcon(source) ? source.toString() : "" + + width : platformStyle.graphicSizeSmall + height : platformStyle.graphicSizeSmall + sourceComponent: { + if (iconId) + return iconComponent + if (source.toString()) + return imageComponent + return undefined + } + anchors { + top: parent.top + topMargin : !parent.text || !internal.portrait + ? Math.floor((parent.height - imageLoader.height) / 2.0) + : platformStyle.paddingSmall + horizontalCenter: parent.horizontalCenter + } + + Component { + id: imageComponent + + Image { + id: image + + objectName: "image" + source: imageLoader.iconId ? "" : imageLoader.source + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectFit + smooth: true + anchors.fill: parent + } + } + + Component { + id: iconComponent + + Icon { + id: icon + + objectName: "icon" + anchors.fill: parent + iconColor: label.color + iconName: imageLoader.iconId + } + } + } + + MouseArea { + id: mouseArea + + onPressed: stateGroup.state = "Pressed" + onReleased: stateGroup.state = "" + onCanceled: { + // Mark as canceled + stateGroup.state = "Canceled" + // Reset state + stateGroup.state = "" + } + onExited: { + if (pressed) + stateGroup.state = "Canceled" + } + + anchors.fill: parent + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TabGroup.js b/mardrone/imports/com/nokia/android.1.1/TabGroup.js new file mode 100644 index 0000000..da1c654 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TabGroup.js @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var allContainers = [] +var allContents = [] + +function hasContainer(content) { + for (var i = 0; i < allContainers.length; ++i) { + if (allContainers[i].children[0] == content) + return true + } + return false +} + +function addContent(content) { + for (var i = 0; i < allContents.length; ++i) { + if (allContents[i] == content) + return false + } + allContents.push(content) + return true +} + +function ensureContainers() { + var somethingChanged = false + + // check if we need to create a container + for (var i = 0; i < root.privateContents.length; ++i) { + var content = root.privateContents[i] + addContent(content) + if (!hasContainer(content)) { + var newContainer = tabContainerComponent.createObject(containerHost) + content.parent = newContainer + allContainers.push(newContainer) + somethingChanged = true + } + } + return somethingChanged +} + +function addTab(content) { + if (addContent(content)) { + var newContainer = tabContainerComponent.createObject(containerHost) + content.parent = newContainer + allContainers.push(newContainer) + } +} + +function removeTab(content) { + var foundIndex = -1 + for (var i = 0; i < allContents.length && foundIndex == -1; ++i) { + if (allContents[i] == content) + foundIndex = i + } + + if (foundIndex != -1) + allContents.splice(foundIndex, 1) + + if (hasContainer(content)) + content.parent = null // this causes deletion of container +} + +function removeContainer(container) { + var foundIndex = -1 + for (var i = 0; i < allContainers.length && foundIndex == -1; ++i) { + if (allContainers[i] == container) + foundIndex = i + } + + if (foundIndex != -1) { + var deletedContainer = allContainers[foundIndex] + if (deletedContainer.children.length > 0) + removeTab(deletedContainer.children[0]) + allContainers.splice(foundIndex, 1) + deletedContainer.destroy() + } +} + diff --git a/mardrone/imports/com/nokia/android.1.1/TabGroup.qml b/mardrone/imports/com/nokia/android.1.1/TabGroup.qml new file mode 100644 index 0000000..e9ae445 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TabGroup.qml @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 +import "TabGroup.js" as Engine + +Item { + id: root + property Item currentTab + + property list privateContents + // Qt defect: cannot have list as default property + default property alias privateContentsDefault: root.privateContents + property bool platformAnimated: true + + onChildrenChanged: { + // [0] is containerHost + if (children.length > 1) + Engine.addTab(children[1]) + } + + onPrivateContentsChanged: { + Engine.ensureContainers() + } + + Component.onCompleted: { + // Set first tabs as current if currentTab is not set by application + if (currentTab == null && containerHost.children[0] && containerHost.children[0].children[0]) + currentTab = containerHost.children[0].children[0] + priv.complete = true; + } + + Item { + id: containerHost + objectName: "containerHost" + anchors.fill: parent + } + + Component { + id: tabContainerComponent + Item { + id: tabContainerItem + + onChildrenChanged: { + if (children.length == 0) + Engine.removeContainer(tabContainerItem) + + else if (children.length == 1) { + children[0].width = width + children[0].height = height + // tab content created. set the first tab as current (if not set before, and if + // child is added after TabGroup has completed) + if (priv.complete && root.currentTab == null) + root.currentTab = children[0] + } + } + + onWidthChanged: { + if (children.length > 0) + children[0].width = width + } + + onHeightChanged: { + if (children.length > 0) + children[0].height = height + } + + Component.onDestruction: { + if (typeof(root) != "undefined" && !root.currentTab) { + // selected one deleted. try to activate the neighbour + var removedIndex = -1 + for (var i = 0; i < containerHost.children.length; i++) { + if (containerHost.children[i] == tabContainerItem) { + removedIndex = i + break + } + } + var newIndex = -1 + if (removedIndex != -1) { + if (removedIndex != containerHost.children.length - 1) + newIndex = removedIndex + 1 + else if (removedIndex != 0) + newIndex = removedIndex - 1 + } + + if (newIndex != -1) + root.currentTab = containerHost.children[newIndex].children[0] + else + root.currentTab = null + } + } + + function incomingDone() { + state = "" + if (priv.incomingPage) { + priv.incomingPage.status = PageStatus.Active + priv.incomingPage = null + } + } + + function outgoingDone() { + if (priv.outgoingPage) { + priv.outgoingPage.status = PageStatus.Inactive + priv.outgoingPage.visible = false + priv.outgoingPage = null + } + state = "Hidden" + } + + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + state: "Hidden" + + states: [ + State { name: ""; PropertyChanges { target: tabContainerItem; opacity: 1.0 } }, + State { name: "Incoming"; PropertyChanges { target: tabContainerItem; opacity: 1.0 } }, + State { name: "Outgoing"; PropertyChanges { target: tabContainerItem; opacity: 0.0 } }, + State { name: "Hidden"; PropertyChanges { target: tabContainerItem; opacity: 0.0 } } + ] + + transitions: [ + Transition { + to: "Incoming" + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: 250 } + ScriptAction { script: incomingDone() } + } + }, + Transition { + to: "Outgoing" + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: 250 } + ScriptAction { script: outgoingDone() } + } + } + ] + } + } + + QtObject { + id: priv + property bool reparenting: false + property bool complete: false + property Item currentTabContainer: root.currentTab ? root.currentTab.parent : null + property Item incomingPage + property Item outgoingPage + property bool animate + + function disableAnimations() { + animate = false + + // outgoingPage might have been found before the incomingPage changes the orientation + if (outgoingPage) + outgoingPage.parent.outgoingDone() + } + + onCurrentTabContainerChanged: { + animate = platformAnimated // updated on orientation change + screen.currentOrientationChanged.connect(priv.disableAnimations) + for (var i = 0; i < containerHost.children.length; i++) { + var tabContainer = containerHost.children[i] + var isNewTab = (tabContainer == currentTabContainer) + if (isNewTab) { + if (tabContainer.state != "") { + if (tabContainer.children[0].status != undefined) { + incomingPage = tabContainer.children[0] + incomingPage.status = PageStatus.Activating // triggers the orientation change + incomingPage.visible = true + if (incomingPage == outgoingPage) + outgoingPage = null + } + if (animate) + tabContainer.state = "Incoming" + else + tabContainer.incomingDone() + } + } else { + if (tabContainer.state != "Hidden") { + if (tabContainer.children.length > 0 && tabContainer.children[0].status != undefined) { + outgoingPage = tabContainer.children[0] + outgoingPage.status = PageStatus.Deactivating + if (incomingPage == outgoingPage) + incomingPage = null + } + if (animate) + tabContainer.state = "Outgoing" + else + tabContainer.outgoingDone() + } + } + } + screen.currentOrientationChanged.disconnect(priv.disableAnimations) + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextArea.qml b/mardrone/imports/com/nokia/android.1.1/TextArea.qml new file mode 100644 index 0000000..cb7ba88 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextArea.qml @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +FocusScopeItem { + id: root + + // Common public API + property alias font: textEdit.font + property alias cursorPosition: textEdit.cursorPosition + property alias horizontalAlignment: textEdit.horizontalAlignment + property alias inputMethodHints: textEdit.inputMethodHints + property alias verticalAlignment: textEdit.verticalAlignment + property alias readOnly: textEdit.readOnly + property alias selectedText: textEdit.selectedText + property alias selectionEnd: textEdit.selectionEnd + property alias selectionStart: textEdit.selectionStart + property alias text: textEdit.text + property alias textFormat: textEdit.textFormat + property alias wrapMode: textEdit.wrapMode + property bool errorHighlight: false + + function copy() { + textEdit.copy() + } + + function paste() { + textEdit.paste() + } + + function cut() { + textEdit.cut() + } + + function select(start, end) { + textEdit.select(start, end) + } + + function selectAll() { + textEdit.selectAll() + } + + function selectWord() { + textEdit.selectWord() + } + + function positionAt(x, y) { + var p = mapToItem(textEdit, x, y); + return textEdit.positionAt(p.x, p.y) + } + + function positionToRectangle(pos) { + var rect = textEdit.positionToRectangle(pos) + var point = mapFromItem(textEdit, rect.x, rect.y) + rect.x = point.x; rect.y = point.y + return rect; + } + + function openSoftwareInputPanel() { + textEdit.openSoftwareInputPanel() + } + + function closeSoftwareInputPanel() { + textEdit.closeSoftwareInputPanel() + } + + // API extensions + property alias placeholderText: placeholder.text + // TODO: Refactor implicit size when following bugs are resolved + // http://bugreports.qt.nokia.com/browse/QTBUG-14957 + // http://bugreports.qt.nokia.com/browse/QTBUG-16665 + // http://bugreports.qt.nokia.com/browse/QTBUG-16710 (fixed in Qt 4.7.2) + // http://bugreports.qt.nokia.com/browse/QTBUG-12305 (fixed in QtQuick1.1) + property real platformMaxImplicitWidth: -1 + property real platformMaxImplicitHeight: -1 + property bool platformInverted: false + property bool enabled: true // overriding due to QTBUG-15797 and related bugs + + implicitWidth: { + var preferredWidth = placeholder.visible ? placeholder.model.paintedWidth + : flick.contentWidth + preferredWidth = Math.max(preferredWidth, privy.minImplicitWidth) + preferredWidth += container.horizontalMargins + if (root.platformMaxImplicitWidth >= 0) + return Math.min(preferredWidth, root.platformMaxImplicitWidth) + return preferredWidth + } + + implicitHeight: { + var preferredHeight = placeholder.visible ? placeholder.model.paintedHeight + : flick.contentHeight + preferredHeight += container.verticalMargins + // layout spec gives minimum height (textFieldHeight) which includes required padding + preferredHeight = Math.max(privateStyle.textFieldHeight, preferredHeight) + if (root.platformMaxImplicitHeight >= 0) + return Math.min(preferredHeight, root.platformMaxImplicitHeight) + return preferredHeight + } + + onWidthChanged: { + // Detect when a width has been explicitly set. Needed to determine if the TextEdit should + // grow horizontally or wrap. I.e. in determining the model's width. There's no way to get + // notified of having an explicit width set. Therefore it's polled in widthChanged. + privy.widthExplicit = root.widthExplicit() + } + + Connections { + target: screen + onCurrentOrientationChanged: { + delayedEnsureVisible.start() + fade.start() + scroll.start() + } + } + + QtObject { + id: privy + // TODO: More consistent minimum width for empty TextArea than 20 * " " on current font? + property real minImplicitWidth: privateStyle.textWidth(" ", textEdit.font) + property bool widthExplicit: false + property bool wrap: privy.widthExplicit || root.platformMaxImplicitWidth >= 0 + property real wrapWidth: privy.widthExplicit ? root.width - container.horizontalMargins + : root.platformMaxImplicitWidth - container.horizontalMargins + function bg_postfix() { + if (root.errorHighlight) + return "error" + else if (root.readOnly || !root.enabled) + return "uneditable" + else if (textEdit.activeFocus) + return "highlighted" + else + return "editable" + } + } + + BorderImage { + id: background + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_textfield_" + privy.bg_postfix(), root.platformInverted) + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + smooth: true + } + + Item { + id: container + + property real horizontalMargins: container.anchors.leftMargin + + container.anchors.rightMargin + + flick.anchors.leftMargin + + flick.anchors.rightMargin + + property real verticalMargins: container.anchors.topMargin + + container.anchors.bottomMargin + + flick.anchors.topMargin + + flick.anchors.bottomMargin + + anchors { + fill: parent + leftMargin: platformStyle.paddingSmall; rightMargin: platformStyle.paddingSmall + topMargin: platformStyle.paddingMedium; bottomMargin: platformStyle.paddingMedium + } + + clip: true + + // TODO: Should placeholder also be scrollable? + Text { + id: placeholder + objectName: "placeholder" + + // TODO: See TODO: Refactor implicit size... + property variant model: Text { + font: textEdit.font + text: placeholder.text + visible: false + wrapMode: textEdit.wrapMode + horizontalAlignment: textEdit.horizontalAlignment + verticalAlignment: textEdit.verticalAlignment + opacity: 0 + + Binding { + when: privy.wrap + target: placeholder.model + property: "width" + value: privy.wrapWidth + } + } + + color: root.platformInverted ? platformStyle.colorNormalMidInverted + : platformStyle.colorNormalMid + font: textEdit.font + horizontalAlignment: textEdit.horizontalAlignment + verticalAlignment: textEdit.verticalAlignment + visible: { + if (text && (textEdit.model.paintedWidth == 0 && textEdit.model.paintedHeight <= textEdit.cursorRectangle.height)) + return (readOnly || !textEdit.activeFocus) + else + return false + } + wrapMode: textEdit.wrapMode + x: flick.x; y: flick.y + height: flick.height; width: flick.width + } + + Flickable { + id: flick + + property real tiny: Math.round(platformStyle.borderSizeMedium / 2) + + function ensureVisible(rect) { + if (Math.round(contentX) > Math.round(rect.x)) + contentX = rect.x + else if (Math.round(contentX + width) < Math.round(rect.x + rect.width)) + contentX = rect.x + rect.width - width + + if (Math.round(contentY) > Math.round(rect.y)) + contentY = rect.y + else if (Math.round(contentY + height) < Math.round(rect.y + rect.height)) + contentY = rect.y + rect.height - height + } + + anchors { + fill: parent + leftMargin: tiny + rightMargin: tiny + topMargin: tiny / 2 + bottomMargin: tiny / 2 + } + + boundsBehavior: Flickable.StopAtBounds + contentHeight: textEdit.model.paintedHeight + contentWidth: textEdit.model.paintedWidth + + (textEdit.wrapMode == TextEdit.NoWrap ? textEdit.cursorRectangle.width : 0) + interactive: root.enabled + + onHeightChanged: { + if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd) + ensureVisible(textEdit.cursorRectangle) + } + + onWidthChanged: { + if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd) + ensureVisible(textEdit.cursorRectangle) + } + + TextEdit { + id: textEdit + objectName: "textEdit" + + // TODO: See TODO: Refactor implicit size... + property variant model: TextEdit { + font: textEdit.font + text: textEdit.text + horizontalAlignment: textEdit.horizontalAlignment + verticalAlignment: textEdit.verticalAlignment + wrapMode: textEdit.wrapMode + visible: false + opacity: 0 + + // In Wrap mode, if the width is bound the text will always get wrapped at the + // set width. If the width is not bound the text won't get wrapped but + // paintedWidth will increase. + Binding { + when: privy.wrap + target: textEdit.model + property: "width" + value: privy.wrapWidth + } + } + activeFocusOnPress: false + enabled: root.enabled + focus: true + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + color: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalDark + cursorVisible: activeFocus && !selectedText + selectedTextColor: root.platformInverted ? platformStyle.colorNormalDarkInverted + : platformStyle.colorNormalLight + selectionColor: root.platformInverted ? platformStyle.colorTextSelectionInverted + : platformStyle.colorTextSelection + textFormat: TextEdit.AutoText + wrapMode: TextEdit.Wrap + height: flick.height + width: flick.width + // TODO: Make bug report? + // Called too early (before TextEdit size is adjusted) delay ensureVisible call a bit + onCursorRectangleChanged: delayedEnsureVisible.start() + onActiveFocusChanged: { + if (activeFocus) { + horizontal.flash() + vertical.flash() + } + } + onEnabledChanged: { + if (!enabled) { + deselect() + // De-focusing requires setting focus elsewhere, in this case editor's parent + if (root.parent) + root.parent.forceActiveFocus() + } + } + + Keys.forwardTo: touchController + + TextTouchController { + id: touchController + + // selection handles require touch area geometry to differ from TextEdit's geometry + anchors { + top: editor.top; topMargin: -container.verticalMargins / 2 + left: editor.left; leftMargin: -container.horizontalMargins / 2 + } + height: Math.max(root.height, flick.contentHeight + container.verticalMargins * 2) + width: Math.max(root.width, flick.contentWidth + container.horizontalMargins * 2) + editorScrolledX: flick.contentX - (container.horizontalMargins / 2) + editorScrolledY: flick.contentY - (container.verticalMargins / 2) + copyEnabled: textEdit.selectedText + cutEnabled: !textEdit.readOnly && textEdit.selectedText + platformInverted: root.platformInverted + Component.onCompleted: flick.movementEnded.connect(touchController.flickEnded) + Connections { target: screen; onCurrentOrientationChanged: touchController.updateGeometry() } + Connections { + target: textEdit + onHeightChanged: touchController.updateGeometry() + onWidthChanged: touchController.updateGeometry() + onHorizontalAlignmentChanged: touchController.updateGeometry() + onVerticalAlignmentChanged: touchController.updateGeometry() + onWrapModeChanged: touchController.updateGeometry() + onFontChanged: touchController.updateGeometry() + } + } + } + + PropertyAnimation { id: fade; target: textEdit; property: "opacity"; from: 0; to: 1; duration: 250 } + PropertyAnimation { id: scroll; target: flick; property: "contentY"; duration: 250 } + } + + ScrollBar { + id: vertical + anchors { + top: flick.top; + topMargin: -flick.tiny / 2; + left: flick.right; + } + flickableItem: flick + interactive: false + orientation: Qt.Vertical + height: container.height + platformInverted: root.platformInverted + } + + ScrollBar { + id: horizontal + anchors { + left: flick.left; + leftMargin: -flick.tiny; + bottom: container.bottom; + bottomMargin: -flick.tiny / 2 + rightMargin: vertical.opacity ? vertical.width : 0 + } + flickableItem: flick + interactive: false + orientation: Qt.Horizontal + width: container.width + platformInverted: root.platformInverted + } + + Timer { + id: delayedEnsureVisible + interval: 1 + onTriggered: flick.ensureVisible(textEdit.cursorRectangle) + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml b/mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml new file mode 100644 index 0000000..9a4e500 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 1.1 +import "." 1.1 +import "AppManager.js" as AppManager + +Item { + id: root + + property Item editor: null + property bool copyEnabled: false + property bool cutEnabled: false + property bool platformInverted: false + + function show() { + // Show menu only if some of the options is available + if(root.copyEnabled || root.cutEnabled || editor.canPaste) { + parent = AppManager.rootObject(); + root.visible = true; + calculatePosition(); + } + } + + function hide() { + root.visible = false; + } + + function calculatePosition() { + if (editor && root.visible) { + var rectStart = editor.positionToRectangle(editor.selectionStart); + var rectEnd = editor.positionToRectangle(editor.selectionEnd); + + var posStart = editor.mapToItem(parent, rectStart.x, rectStart.y); + var posEnd = editor.mapToItem(parent, rectEnd.x, rectEnd.y); + + var selectionCenterX = (posEnd.x + posStart.x) / 2; + if (posStart.y != posEnd.y) + // we have multiline selection so center to the screen + selectionCenterX = parent.width / 2; + + var contextMenuMargin = 10; // the space between the context menu and the line above/below + var contextMenuAdjustedRowHeight = row.height + contextMenuMargin; + var tempY = posStart.y - contextMenuAdjustedRowHeight; + var minVerticalPos = internal.textArea ? internal.textArea.mapToItem(parent, 0, 0).y - row.height : 0 + + if (tempY < minVerticalPos) + tempY = minVerticalPos + + if (tempY < 0) + tempY = 0 + + root.x = Math.max(0, Math.min(selectionCenterX - row.width / 2, parent.width - row.width)); + root.y = tempY; + } + } + + onVisibleChanged: { + if (visible) { + internal.editorSceneXChanged.connect(internal.editorMoved) + internal.editorSceneYChanged.connect(internal.editorMoved) + } else { + internal.editorSceneXChanged.disconnect(internal.editorMoved) + internal.editorSceneYChanged.disconnect(internal.editorMoved) + } + } + + x: 0; y: 0 + visible: false + + Binding { target: internal; property: "editorSceneX"; value: AppManager.sceneX(root.editor); when: root.visible && (root.editor != null) } + Binding { target: internal; property: "editorSceneY"; value: AppManager.sceneY(root.editor); when: root.visible && (root.editor != null) } + + QtObject { + id: internal + + property real editorSceneX + property real editorSceneY + property Item textArea: null + + function editorMoved() { + root.calculatePosition() + } + + Component.onCompleted: textArea = AppManager.findParent(editor, "errorHighlight") + } + + ButtonRow { + id: row + + function visibleButtonCount() { + var count = 0 + if (copyButton.visible) ++count + if (cutButton.visible) ++count + if (pasteButton.visible) ++count + return count + } + + exclusive: false + width: Math.round(privateStyle.buttonSize * 1.5) * visibleButtonCount() + + onWidthChanged: calculatePosition() + onHeightChanged: calculatePosition() + + Button { + id: copyButton + iconSource: privateStyle.imagePath("qtg_toolbar_copy", root.platformInverted) + visible: root.copyEnabled + platformInverted: root.platformInverted + onClicked: editor.copy() + } + Button { + id: cutButton + iconSource: privateStyle.imagePath("qtg_toolbar_cut", root.platformInverted) + visible: root.cutEnabled + platformInverted: root.platformInverted + onClicked: { + editor.cut() + root.visible = false + } + } + Button { + id: pasteButton + iconSource: privateStyle.imagePath("qtg_toolbar_paste", root.platformInverted) + visible: editor.canPaste + platformInverted: root.platformInverted + onClicked: { + editor.paste() + root.visible = false + } + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextField.qml b/mardrone/imports/com/nokia/android.1.1/TextField.qml new file mode 100644 index 0000000..4b96760 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextField.qml @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +FocusScope { + id: root + + // Common Public API + property alias placeholderText: placeholder.text + property alias inputMethodHints: textInput.inputMethodHints + property alias font: textInput.font + property alias cursorPosition: textInput.cursorPosition + property alias readOnly: textInput.readOnly + property alias echoMode: textInput.echoMode + property alias acceptableInput: textInput.acceptableInput + property alias inputMask: textInput.inputMask + property alias validator: textInput.validator + property alias selectedText: textInput.selectedText + property alias selectionEnd: textInput.selectionEnd + property alias selectionStart: textInput.selectionStart + property alias text: textInput.text + property bool errorHighlight: !acceptableInput + property alias maximumLength: textInput.maximumLength + + function copy() { + textInput.copy() + } + + function paste() { + textInput.paste() + } + + function cut() { + textInput.cut() + } + + function select(start, end) { + textInput.select(start, end) + } + + function selectAll() { + textInput.selectAll() + } + + function selectWord() { + textInput.selectWord() + } + + function positionAt(x) { + return textInput.positionAt(mapToItem(textInput, x, 0).x) + } + + function positionToRectangle(pos) { + var rect = textInput.positionToRectangle(pos) + rect.x = mapFromItem(textInput, rect.x, 0).x + return rect; + } + + function openSoftwareInputPanel() { + textInput.openSoftwareInputPanel() + } + + function closeSoftwareInputPanel() { + textInput.closeSoftwareInputPanel() + } + + // API extensions + implicitWidth: platformLeftMargin + platformRightMargin + flick.tiny * 2 + + Math.max(privateStyle.textWidth(text, textInput.font), priv.minWidth) + implicitHeight: Math.max(privateStyle.textFieldHeight, + 2 * platformStyle.paddingMedium + privateStyle.fontHeight(textInput.font)) + + property bool enabled: true // overriding due to QTBUG-15797 and related bugs + + property real platformLeftMargin: platformStyle.paddingSmall + property real platformRightMargin: platformStyle.paddingSmall + property bool platformInverted: false + + // Private data + QtObject { + id: priv + // Minimum width is either placeholder text lenght or 5 spaces on current font. + // Using placeholder text lenght as minimum width prevents implicit sized item from + // shrinking on focus gain. + property real minWidth: placeholder.text ? privateStyle.textWidth(placeholder.text, textInput.font) + : privateStyle.textWidth(" ", textInput.font) + + function bg_postfix() { + if (root.errorHighlight) + return "error" + else if (root.readOnly || !root.enabled) + return "uneditable" + else if (textInput.activeFocus) + return "highlighted" + else + return "editable" + } + } + + BorderImage { + id: frame + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_textfield_" + priv.bg_postfix(), root.platformInverted) + border { + left: platformStyle.borderSizeMedium + top: platformStyle.borderSizeMedium + right: platformStyle.borderSizeMedium + bottom: platformStyle.borderSizeMedium + } + smooth: true + } + + Item { + id: container + + anchors { + left: root.left; leftMargin: root.platformLeftMargin + right: root.right; rightMargin: root.platformRightMargin + verticalCenter : root.verticalCenter + } + clip: true + height: root.height + + Flickable { + id: flick + + property real tiny: Math.round(platformStyle.borderSizeMedium / 2) + + function ensureVisible(rect) { + if (rect.x >= 0 && Math.round(contentX) > Math.round(rect.x)) + contentX = rect.x + else if (Math.round(contentX + width) < Math.round(rect.x + rect.width)) + contentX = rect.x + rect.width - width + } + + anchors { + left: parent.left; leftMargin: tiny + right: parent.right; rightMargin: tiny + verticalCenter : parent.verticalCenter + } + + boundsBehavior: Flickable.StopAtBounds + contentWidth: textInput.paintedWidth + height: textInput.paintedHeight + + onWidthChanged: ensureVisible(textInput.cursorRectangle) + + TextInput { + id: textInput; objectName: "textInput" + + property real paintedWidth: textInput.model.paintedWidth + property real paintedHeight: textInput.model.paintedHeight + + // TODO: See TODO: Refactor implicit size... + property variant model: Text { + font: textInput.font + text: textInput.text + horizontalAlignment: textInput.horizontalAlignment + visible: false + opacity: 0 + } + + activeFocusOnPress: false + cursorVisible: activeFocus && !selectedText + enabled: root.enabled + color: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalDark + focus: true + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + selectedTextColor: root.platformInverted ? platformStyle.colorNormalDarkInverted + : platformStyle.colorNormalLight + selectionColor: root.platformInverted ? platformStyle.colorTextSelectionInverted + : platformStyle.colorTextSelection + width: Math.max(flick.width, paintedWidth) + + onEnabledChanged: { + if (!enabled) { + deselect() + // De-focusing requires setting focus elsewhere, in this case editor's parent + if (root.parent) + root.parent.forceActiveFocus() + } + } + + onCursorPositionChanged: flick.ensureVisible(textInput.cursorRectangle) + + Keys.forwardTo: touchController + + TextTouchController { + id: touchController + + // selection handles require touch area geometry to differ from TextInput's geometry + anchors { + left: parent.left; + leftMargin: -flick.tiny + verticalCenter : parent.verticalCenter + } + height: root.height + width: Math.max(root.width, flick.contentWidth + flick.tiny * 2) + editorScrolledX: flick.contentX - container.anchors.leftMargin - flick.tiny + editorScrolledY: 0 + copyEnabled: textInput.selectedText + cutEnabled: !textInput.readOnly && textInput.selectedText + platformInverted: root.platformInverted + Component.onCompleted: flick.movementEnded.connect(touchController.flickEnded) + Connections { target: screen; onCurrentOrientationChanged: touchController.updateGeometry() } + Connections { + target: textInput + onHeightChanged: touchController.updateGeometry() + onWidthChanged: touchController.updateGeometry() + onHorizontalAlignmentChanged: touchController.updateGeometry() + onFontChanged: touchController.updateGeometry() + } + } + } + } + + Text { + id: placeholder; objectName: "placeholder" + anchors { + left: parent.left; leftMargin: flick.tiny + right: parent.right; rightMargin: flick.tiny + verticalCenter : parent.verticalCenter + } + color: root.platformInverted ? platformStyle.colorNormalMidInverted + : platformStyle.colorNormalMid + font: textInput.font + visible: (!textInput.activeFocus || readOnly) && !textInput.text && text + } + + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml b/mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml new file mode 100644 index 0000000..5781b6e --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "AppManager.js" as AppManager + +Magnifier { + id: root + + property Item editor: parent.editor + // Defines the center of the content to be magnified in X axis + // Note: the center of the content to be magnified in Y axis is determined by the cursor position. + property variant contentCenter: Qt.point(0, 0) //editor's coordinate system + property bool platformInverted: false + + function show() { + parent = AppManager.rootObject(); + sourceRect = internal.calculateSourceGeometry(); + internal.calculatePosition(); + root.visible = true; + } + + function hide() { + root.visible = false; + } + + sourceRect: Qt.rect(0, 0, 0, 0) + visible: false + scaleFactor: 1.5 + maskFileName: root.platformInverted ? ":/graphics_1_1/qtg_graf_magnifier_mask_inverse.svg" + : ":/graphics_1_1/qtg_graf_magnifier_mask.svg" + overlayFileName: root.platformInverted ? ":/graphics_1_1/qtg_graf_magnifier_inverse.svg" + : ":/graphics_1_1/qtg_graf_magnifier.svg" + + onContentCenterChanged: internal.updateSourceRect() + + onSourceRectChanged: if (visible) internal.calculatePosition() + + Connections { + target: editor + onCursorPositionChanged: internal.updateSourceRect + } + + // Private + QtObject { + id: internal + + function updateSourceRect () { + if (visible) + sourceRect = internal.calculateSourceGeometry(); + } + + function calculatePosition() { + width = sourceRect.width * scaleFactor; + height = sourceRect.height * scaleFactor; + + var magnifierMargin = -platformStyle.paddingLarge; // the offset between the magnifier and top of the line + var pos = parent.mapFromItem(editor, + contentCenter.x - width/2, + contentCenter.y - sourceRect.height/2 - height - magnifierMargin); + + root.x = pos.x; + root.y = pos.y; + } + + // Calculates and returns the source geometry of the content to be magnified in scene coordinates + function calculateSourceGeometry() { + var magniferSize = Qt.size(platformStyle.graphicSizeMedium * 2, platformStyle.graphicSizeMedium * 2); + var sourceSize = Qt.size(magniferSize.width / scaleFactor, magniferSize.height / scaleFactor); + var contentCenterScene = editor.mapToItem(null, contentCenter.x, contentCenter.y); + + var rect = Qt.rect(contentCenterScene.x - sourceSize.width / 2, + contentCenterScene.y - sourceSize.height / 2, + sourceSize.width, sourceSize.height); + return rect; + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml b/mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml new file mode 100644 index 0000000..dd53941 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "RectUtils.js" as Utils + +Item { + id: root + + property alias imageSource: image.source + property int editorPos: 0 //character position of the handle in the editor document + property Item editor + // the default size of the touch area should come from some style constant + property variant touchAreaSize: Qt.size(platformStyle.graphicSizeMedium, platformStyle.graphicSizeMedium) + property alias showImage: image.visible + property variant viewPortRect: Qt.rect(0, 0, 0, 0) // item's coordinates + //parent's coordinate system + property variant center: Qt.point(root.x + root.width / 2, root.y + root.height / 2) + + // Point is in Item's coordinate system + function containsPoint(point) { + var touchAreaRect = Qt.rect(touchArea.x, touchArea.y, touchArea.width, touchArea.height); + + if (!Utils.rectContainsRect(viewPortRect, touchAreaRect) + && Utils.rectIntersectsRect(viewPortRect, touchAreaRect)) { + touchAreaRect.x = Math.max(touchAreaRect.x, viewPortRect.x); + touchAreaRect.x = Math.min(touchAreaRect.x, viewPortRect.x + viewPortRect.width - touchAreaRect.width); + touchAreaRect.y = Math.max(touchAreaRect.y, viewPortRect.y); + touchAreaRect.y = Math.min(touchAreaRect.y, viewPortRect.y + viewPortRect.height - touchAreaRect.height); + } + + effectiveTouchArea.x = touchAreaRect.x; + effectiveTouchArea.y = touchAreaRect.y; + + if (Utils.rectContainsPoint(touchAreaRect, point.x, point.y)) + return true; + + return false; + } + + // Point is in parent's coordinate system + function pointDistanceFromCenter(point) { + return Utils.manhattan(point, root.center); + } + + function updateGeometry() { + var rect = root.editor.positionToRectangle(editorPos); + var pos = root.editor.mapToItem(root.parent, rect.x, rect.y); + root.x = pos.x; + root.y = pos.y; + root.width = rect.width; + root.height = rect.height; + } + + onEditorPosChanged: root.updateGeometry() + + Item { + id: touchArea + + visible: false + anchors.centerIn: root + width: root.touchAreaSize.width + height: Math.max(root.touchAreaSize.height, root.height) + } + + Item { + id: effectiveTouchArea + + visible: false + width: root.touchAreaSize.width + height: Math.max(root.touchAreaSize.height, root.height) + } + + BorderImage { + id: image + + property real tiny: Math.round(platformStyle.borderSizeMedium / 2) + + anchors { + left: root.objectName == "SelectionEnd" ? root.right : undefined + right: root.objectName == "SelectionBegin" ? root.left : undefined + leftMargin: root.objectName == "SelectionEnd" ? -editor.cursorRectangle.width : 0 + top: root.top + topMargin: -tiny / 2 + 1 + bottom: root.bottom + bottomMargin: -tiny / 2 + } + + border { + left: 0 + top: tiny + right: 0 + bottom: tiny + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/TextTouchController.qml b/mardrone/imports/com/nokia/android.1.1/TextTouchController.qml new file mode 100644 index 0000000..0a2948b --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/TextTouchController.qml @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 +import "RectUtils.js" as Utils + + +MouseArea { + id: root + + property Item editor: parent + property alias touchTools: touchToolsLoader.item + property real editorScrolledX: 0 + property real editorScrolledY: 0 + property bool copyEnabled: false + property bool cutEnabled: false + property bool platformInverted: false + + enabled: !editor.inputMethodComposing + + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + + function updateGeometry() { + if (touchTools) { + touchTools.handleBegin.updateGeometry(); + touchTools.handleEnd.updateGeometry(); + touchTools.contextMenu.calculatePosition(); // Update context menu position + } + } + + function flickEnded() { + if (internal.editorHasSelection && internal.selectionVisible()) + touchTools.contextMenu.show(); + else + touchTools.contextMenu.hide(); + } + + onPressed: { + if (!touchTools) + touchToolsLoader.sourceComponent = touchToolsComponent + + internal.currentTouchPoint = root.mapToItem(editor, mouse.x, mouse.y); + + if (internal.currentTouchPoint.x < 0) + internal.currentTouchPoint.x = 0 + + if (internal.currentTouchPoint.y < 0) + internal.currentTouchPoint.y = 0 + + if (internal.tapCounter == 0) + internal.touchPoint = internal.currentTouchPoint; + + editor.forceActiveFocus(); + touchTools.contextMenu.hide(); + internal.handleMoved = false; + + touchTools.handleBegin.viewPortRect = internal.mapViewPortRectToHandle(touchTools.handleBegin); + touchTools.handleEnd.viewPortRect = internal.mapViewPortRectToHandle(touchTools.handleEnd); + + internal.pressedHandle = internal.handleForPoint({x: internal.currentTouchPoint.x, y: internal.currentTouchPoint.y}); + + if (internal.pressedHandle != null) { + internal.handleGrabbed(); + // Position cursor at the pressed selection handle + // TODO: Add bug ID!! + var tempStart = editor.selectionStart + var tempEnd = editor.selectionEnd + if (internal.pressedHandle == touchTools.handleBegin) { + editor.cursorPosition = editor.selectionStart + editor.select(tempEnd, tempStart); + } else { + editor.cursorPosition = editor.selectionEnd + editor.select(tempStart, tempEnd); + } + } + } + + onClicked: { + ++internal.tapCounter; + if (internal.tapCounter == 1) { + internal.onSingleTap(); + clickTimer.start(); + } else if (internal.tapCounter == 2 && clickTimer.running) { + internal.onDoubleTap(); + clickTimer.restart(); + } else if (internal.tapCounter == 3 && clickTimer.running) + internal.onTripleTap(); + } + + onPressAndHold: { + clickTimer.stop(); + internal.tapCounter = 0; + internal.longTap = true + if (!internal.handleMoved) { + if (internal.pressedHandle == null) { + // position the cursor under the long tap and make the cursor handle grabbed + editor.select(editor.cursorPosition, editor.cursorPosition); + editor.cursorPosition = editor.positionAt(internal.touchPoint.x,internal.touchPoint.y); + internal.pressedHandle = touchTools.handleEnd; + if (editor.readOnly) + touchTools.magnifier.hide(); + internal.handleGrabbed(); + } + touchTools.contextMenu.hide(); + } + + if (!editor.readOnly || internal.editorHasSelection) + touchTools.magnifier.show(); + } + + onReleased: { + touchTools.magnifier.hide(); + + mouseGrabDisabler.setKeepMouseGrab(root, false); + internal.forcedSelection = false; + + if ((internal.pressedHandle != null && internal.handleMoved) || + (internal.longTap && !editor.readOnly) || + (internal.pressedHandle != null && internal.longTap)) + touchTools.contextMenu.show(); + internal.longTap = false; + } + + onPositionChanged: { + + internal.currentTouchPoint = root.mapToItem(editor, mouse.x, mouse.y); + + if (internal.pressedHandle != null) { + internal.hitTestPoint = {x:internal.currentTouchPoint.x, y:internal.currentTouchPoint.y}; + + var newPosition = editor.positionAt(internal.hitTestPoint.x, internal.hitTestPoint.y); + if (newPosition >= 0 && newPosition != editor.cursorPosition) { + if (internal.hasSelection) { + var anchorPos = internal.pressedHandle == touchTools.handleBegin ? editor.selectionEnd + : editor.selectionStart + if (editor.selectionStart == editor.cursorPosition) + anchorPos = editor.selectionEnd; + else if (editor.selectionEnd == editor.cursorPosition) + anchorPos = editor.selectionStart; + editor.select(anchorPos, newPosition); + } else { + editor.cursorPosition = newPosition; + } + if (!editor.readOnly || internal.editorHasSelection) + touchTools.magnifier.show(); + internal.handleMoved = true; + } + } + } + + Connections { + target: editor + onTextChanged: internal.onEditorTextChanged + } + + // Private + QtObject { + id: internal + + property bool forcedSelection: false + property bool hasSelection: editorHasSelection || forcedSelection + property bool editorHasSelection: editor.selectionStart != editor.selectionEnd + property bool handleMoved: false + property bool longTap: false + property int tapCounter: 0 + property variant pressedHandle: null + property variant hitTestPoint: Qt.point(0, 0) + property variant touchPoint: Qt.point(0, 0) + property variant currentTouchPoint: Qt.point(0, 0) + + function onSingleTap() { + if (!internal.handleMoved) { + // need to deselect, because if the cursor position doesn't change the selection remains + // even after setting to cursorPosition + editor.deselect(); + editor.cursorPosition = editor.positionAt(internal.touchPoint.x, internal.touchPoint.y); + touchTools.contextMenu.hide(); + if (!editor.readOnly) + editor.openSoftwareInputPanel() + } + } + + function onDoubleTap() { + // assume that the 1st click positions the cursor + editor.selectWord(); + touchTools.contextMenu.show(); + } + + function onTripleTap() { + editor.selectAll(); + touchTools.contextMenu.show(); + } + + function onEditorTextChanged() { + if (touchTools && !internal.editorHasSelection) + touchTools.contextMenu.hide(); + } + + function handleGrabbed() { + mouseGrabDisabler.setKeepMouseGrab(root, true); + internal.hitTestPoint = {x:internal.currentTouchPoint.x, y:internal.currentTouchPoint.y}; + + internal.forcedSelection = internal.editorHasSelection; + } + + function mapViewPortRectToHandle(handle) { + var position = editor.mapToItem(handle, root.editorScrolledX, root.editorScrolledY); + var rect = Qt.rect(position.x, position.y, root.width, root.height); + return rect; + } + + // point is in Editor's coordinate system + function handleForPoint(point) { + var pressed = null; + + if (!editor.readOnly || editorHasSelection) { // to avoid moving the cursor handle in read only editor + // Find out which handle is being moved + if (touchTools.handleBegin.visible && + touchTools.handleBegin.containsPoint(editor.mapToItem(touchTools.handleBegin, point.x, point.y))) { + pressed = touchTools.handleBegin; + } + if (touchTools.handleEnd.containsPoint(editor.mapToItem(touchTools.handleEnd, point.x, point.y))) { + var useArea = true; + if (pressed != null) { + var distance1 = touchTools.handleBegin.pointDistanceFromCenter(point); + var distance2 = touchTools.handleEnd.pointDistanceFromCenter(point); + + if (distance1 < distance2) + useArea = false; + } + if (useArea) + pressed = touchTools.handleEnd; + } + } + return pressed; + } + + function selectionVisible() { + var startRect = editor.positionToRectangle(editor.selectionStart); + var endRect = editor.positionToRectangle(editor.selectionEnd); + var selectionRect = Qt.rect(startRect.x, startRect.y, endRect.x - startRect.x + endRect.width, endRect.y - startRect.y + endRect.height); + var viewPortRect = Qt.rect(editorScrolledX, editorScrolledY, editor.width, editor.height); + + return Utils.rectIntersectsRect(selectionRect, viewPortRect) || + Utils.rectContainsRect(viewPortRect, selectionRect) || + Utils.rectContainsRect(selectionRect, viewPortRect); + } + } + + Loader { + id: touchToolsLoader + } + + Component { + id: touchToolsComponent + + Item { + id: touchTools + property alias handleBegin: selBegin + property alias handleEnd: selEnd + property alias contextMenu: cxtMenu + property alias magnifier: magnif + + TextSelectionHandle { + id: selBegin + + objectName: "SelectionBegin" + editor: root.editor + imageSource: privateStyle.imagePath("qtg_fr_textfield_handle_start", root.platformInverted) + editorPos: editor.selectionStart + visible: editor.selectionStart != editor.selectionEnd + } + + TextSelectionHandle { // also acts as the cursor handle when no selection + id: selEnd + + objectName: "SelectionEnd" + editor: root.editor + imageSource: privateStyle.imagePath("qtg_fr_textfield_handle_end", root.platformInverted) + editorPos: editor.selectionEnd + visible: true + showImage: internal.hasSelection //show image only in selection mode + } + + TextContextMenu { + id: cxtMenu + + editor: root.editor + platformInverted: root.platformInverted + copyEnabled: root.copyEnabled + cutEnabled: root.cutEnabled + } + + TextMagnifier { + id: magnif + + editor: root.editor + contentCenter: internal.hitTestPoint + platformInverted: root.platformInverted + + } + } + + } + + MouseGrabDisabler { + id: mouseGrabDisabler + } + + Timer { + id: clickTimer + + interval: 400; repeat: false + onTriggered: { + running = false; + internal.tapCounter = 0; + } + } + + Connections { + target: root.editor + onActiveFocusChanged: { + // On focus loss dismiss menu, selection and VKB + if (!root.editor.activeFocus) { + if (touchTools) + touchTools.contextMenu.hide() + root.editor.select(root.editor.cursorPosition, root.editor.cursorPosition) + root.editor.closeSoftwareInputPanel() + } + } + } + + Keys.onPressed: { + if (!touchTools) + touchToolsLoader.sourceComponent = touchToolsComponent + + if (internal.editorHasSelection && event.modifiers & Qt.ShiftModifier + && (event.key == Qt.Key_Left || event.key == Qt.Key_Right + || event.key == Qt.Key_Up || event.key == Qt.Key_Down)) + touchTools.contextMenu.show() + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ToolBar.qml b/mardrone/imports/com/nokia/android.1.1/ToolBar.qml new file mode 100644 index 0000000..5de815c --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ToolBar.qml @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property Item tools: null + + // The transition type. One of the following: + // set an instantaneous change (default) + // push follows page stack push animation + // pop follows page stack pop animation + // replace follows page stack replace animation + property string transition: "set" + + // Symbian specific API + property bool platformInverted: false + + // Sets the tools with a transition. + function setTools(tools, transition) { + stateGroup.state = tools ? "" : "Hidden" + if (tools) + tools.height = root.height + priv.transition = transition + root.tools = tools + } + + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + + implicitWidth: Math.max(50, screen.width) + implicitHeight: (screen.width < screen.height) + ? privateStyle.toolBarHeightPortrait + : privateStyle.toolBarHeightLandscape + + BorderImage { + id: background + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_toolbar", root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + } + + //Prevents mouse events from propagating to elements below the ToolBar + MouseArea { + anchors.fill: parent + } + + Item { + id: container + anchors.fill: parent + } + + onToolsChanged: { + priv.performTransition(priv.transition || transition) + priv.transition = undefined + } + + Component { + id: containerComponent + + Item { + id: item + + anchors.fill: parent + + // The states correspond to the different possible positions of the item. + state: "Hidden" + + // The tools held by this item. + property Item tools: null + // The owner of the tools. + property Item owner: null + + states: [ + + // Active state + State { + name: "" + PropertyChanges { target: item; visible: true; opacity: 1 } + }, + + // Start state for pop entry, end state for push exit. + State { + name: "Prev" + PropertyChanges { target: item; opacity: 0.0 } + }, + // Start state for push entry, end state for pop exit. + State { + name: "Next" + PropertyChanges { target: item; opacity: 0.0 } + }, + // Start state for replace entry. + State { + name: "Front" + PropertyChanges { target: item; opacity: 0.0 } + }, + // End state for replace exit. + State { + name: "Back" + PropertyChanges { target: item; opacity: 0.0 } + }, + // Inactive state. + State { + name: "Hidden" + PropertyChanges { target: item; visible: false } + StateChangeScript { + script: { + if (item.tools) { + // re-parent back to original owner + tools.visible = false + tools.parent = owner + + // reset item + item.tools = item.owner = null + } + } + } + } + ] + + transitions: [ + // Pop entry and push exit transition. + Transition { + from: ""; to: "Prev"; reversible: true + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.InCubic; duration: priv.transitionDuration / 2 } + PauseAnimation { duration: priv.transitionDuration / 2 } + ScriptAction { script: if (state == "Prev") state = "Hidden" } + } + }, + // Push entry and pop exit transition. + Transition { + from: ""; to: "Next"; reversible: true + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.InCubic; duration: priv.transitionDuration / 2 } + PauseAnimation { duration: priv.transitionDuration / 2 } + ScriptAction { script: if (state == "Next") state = "Hidden" } + } + }, + Transition { + // Replace entry transition. + from: "Front"; to: "" + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.InOutExpo; duration: priv.transitionDuration } + } + }, + Transition { + // Replace exit transition. + from: ""; to: "Back" + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.InOutExpo; duration: priv.transitionDuration } + ScriptAction { script: if (state == "Back") state = "Hidden" } + } + } + ] + } + } + + QtObject { + id: priv + + property Item currentComponent: null + + // Alternating components used for transitions. + property Item compA: null + property Item compB: null + + // The transition to perform next. + property variant transition + + // Duration of transition animation (in ms) + property int transitionDuration: 500 + + // Performs a transition between tools in the toolbar. + function performTransition(transition) { + // lazily create components if they have not been created + if (!priv.currentComponent) { + priv.compA = containerComponent.createObject(container) + priv.compB = containerComponent.createObject(container) + priv.currentComponent = priv.compB + } + + // no transition if the tools are unchanged + if (priv.currentComponent.tools == tools) + return + + // select component states based on the transition animation + var transitions = { + "set": { "new": "", "old": "Hidden" }, + "push": { "new": "Next", "old": "Prev" }, + "pop": { "new": "Prev", "old": "Next" }, + "replace": { "new": "Front", "old": "Back" } + } + + var animation = transitions[transition] + // initialize the free component + var component = priv.currentComponent == priv.compA ? priv.compB : priv.compA + component.state = "Hidden" + if (tools) { + component.tools = tools + component.owner = tools.parent + tools.parent = component + tools.visible = true + if (tools.layoutChildren != undefined && typeof tools.layoutChildren == 'function' ) + tools.layoutChildren() + } + + // perform transition + priv.currentComponent.state = animation["old"] + if (tools) { + component.state = animation["new"] + component.state = "" + } + priv.currentComponent = component + } + } + + StateGroup { + id: stateGroup + states: [ + // Inactive state. + State { + name: "Hidden" + PropertyChanges { target: root; height: 0; opacity: 0.0 } + } + ] + + transitions: [ + // Transition between active and inactive states. + Transition { + from: ""; to: "Hidden"; reversible: true + SequentialAnimation { + PropertyAnimation { properties: "opacity"; easing.type: Easing.InOutExpo; duration: priv.transitionDuration } + PropertyAnimation { properties: "height"; duration: 10 } + } + } + ] + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml b/mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml new file mode 100644 index 0000000..49dfa0d --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// ToolBarLayout is a container for items on a toolbar that automatically +// implements an appropriate layout for its children. + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property bool backButton: true // deprecated + onBackButtonChanged: { console.log("warning: ToolBarLayout.backButton is deprecated.") } + + implicitWidth: screen.width + implicitHeight: internal.defaultHeightToolBar + + visible: false + + Connections { + target: privateStyle + onLayoutParametersChanged: internal.layoutChildren() + } + Connections { + target: screen + onCurrentOrientationChanged: internal.layoutChildren() + } + + QtObject { + id: internal + objectName: "internal" + + property bool portrait: screen.width < screen.height + + // These are the dynamic layout parameters used by the toolbar layout. + property real defaultHeightToolBar: portrait ? + privateStyle.toolBarHeightPortrait : privateStyle.toolBarHeightLandscape + property real defaultHeightToolButton: privateStyle.toolBarHeightLandscape + property real outerMarginHorizontal: portrait ? + 0 : (2 * platformStyle.paddingLarge) + property real outerMarginButtonRowLong: portrait ? + platformStyle.paddingLarge : (3 * platformStyle.paddingLarge) + property real innerSpacingTextButtonSingle: portrait ? + platformStyle.paddingMedium + (3 * platformStyle.paddingLarge) : (3 * platformStyle.paddingLarge) + property real innerSpacingTextButtonDouble: portrait ? + platformStyle.paddingSmall : (3 * platformStyle.paddingLarge) + property real innerSpacingButtonRowTwoChildren: portrait ? + platformStyle.paddingMedium : (3 * platformStyle.paddingLarge) + property real innerSpacingButtonRowLong: portrait ? + platformStyle.paddingMedium : platformStyle.paddingLarge + property real centerSpacingTextButtonDouble: platformStyle.paddingLarge + + function isIconButton(item) { + return item.hasOwnProperty("iconSource") + && item.hasOwnProperty("text") + && item.text == "" + } + + function isTextToolButton(item) { + // ToolButton has both iconSource and flat property, + // Button only has iconSource + return (item.hasOwnProperty("iconSource") + && item.iconSource == "" + && item.hasOwnProperty("flat")) + } + + function isButtonRow(item) { + return item.hasOwnProperty("checkedButton") + } + + function buttonWidth(child) { + if ((isTextToolButton(child)) || !(child.hasOwnProperty("implicitWidth"))) { + // ImplicitWidth for the ToolButton returns wrong value right after + // orientation change, and also we want to override its own + // layout width calculation, so use the actual width + return child.width + } + return child.implicitWidth + } + + function centerOffset(outerLength, innerLength) { + // calculate the offset of the leading edge of a centered child item + return Math.floor((outerLength - innerLength) / 2.0) + } + + function widthTextButtonSingle(leftMargin, innerSpacing) { + // calculate the remaining width for a centered item + var outerContents = leftMargin + innerSpacing + return root.width - (outerContents * 2) + } + + function widthTextButtonDouble(leftMargin, innerSpacing, centerSpacing) { + // calculate the space available when there are two items with a center + // margin, and share it between the two + var outerContents = leftMargin + innerSpacing + return Math.round((root.width - (outerContents * 2) - centerSpacing) / 2.0) + } + + function widthButtonRowLong(leftButton, rightButton, itemMargin, innerSpacing, outerMargin) { + // calculate the width of a long button row, which is used in the case that there are more + // than three icons. If either left or right button is present, allocate the itemMargin to + // ensure that there is sufficient space; otherwise we can use the special + // outerMargin value + var leftContents = leftButton ? itemMargin + innerSpacing : outerMargin + var rightContents = rightButton ? itemMargin + innerSpacing : outerMargin + return root.width - leftContents - rightContents + } + + function layoutChildren() { + var numChildren = children.length + if (parent == null || root.width == 0 || numChildren == 0) + return + + for (var i = 0; i < numChildren; ++i) { + // make sure all the children have correct parent, height, and y + children[i].parent = root + if (isButtonRow(children[i])) { + var buttonRow = children[i] + // ButtonRow frame height is always tool bar's height in + // landscape, regardless of the current orientation, so we need + // to override the heights of the buttons within (because it's a + // Row, so its height is based on its children) + for (var j = 0; j < buttonRow.children.length; ++j) { + buttonRow.children[j].implicitHeight = defaultHeightToolButton + } + } + // child's vertical center always goes to middle of the toolbar + var childHeight = children[i].hasOwnProperty("implicitHeight") ? + children[i].implicitHeight : children[i].height + children[i].y = root.y + centerOffset(root.implicitHeight, childHeight) + } + + // detect whether we have left and or right items. we need to lay out + // the remaining children (that are not left or right items) whether they + // are tool buttons, text buttons or a button row + var leftItem = isIconButton(children[0]) ? + children[0] : undefined + var rightItem = isIconButton(children[numChildren-1]) ? + children[numChildren-1] : undefined + var childrenRemaining = numChildren - (leftItem != undefined ? 1 : 0) - (rightItem != undefined ? 1 : 0) + var firstRemainingIndex = leftItem != undefined ? 1 : 0 + var lastRemainingIndex = rightItem != undefined ? (numChildren - 2) : (numChildren - 1) + + // precalculate the margins for the left and right items, we will work + // out child sizes assuming they are present + var leftMargin = outerMarginHorizontal + defaultHeightToolBar + var rightMargin = root.width - leftMargin + + // In the case of a lone remaining chlld, or in the case of 2 text + // buttons, we need to override the width + var overrideChildWidth = 0 + for (var p = firstRemainingIndex; p <= lastRemainingIndex; p++) { + var child = children[p] + overrideChildWidth = buttonWidth(child) + + // If necessary, we calculate and override the width first before we + // can calculate the x positions + if ((isTextToolButton(child) && childrenRemaining == 1) + || (isButtonRow(child) && child.children.length == 1)) { + // we treat a button row with a single item like a single tool button, + // but in landscape, calculate size as if there were two buttons + overrideChildWidth = portrait ? + widthTextButtonSingle(leftMargin, innerSpacingTextButtonSingle) : + widthTextButtonDouble(leftMargin, innerSpacingTextButtonDouble, innerSpacingTextButtonDouble) + + } else if (isTextToolButton(child) && childrenRemaining == 2) { + // special case of margins for two text buttons + overrideChildWidth = widthTextButtonDouble( + leftMargin, innerSpacingTextButtonDouble, centerSpacingTextButtonDouble) + + } else if (isButtonRow(child) && ((child.children.length == 2) + || (child.children.length > 2 && leftItem != undefined && rightItem != undefined))) { + // there are special margins if the button row has two children, + // or if it has more than two children and there is a left and + // a right item + overrideChildWidth = widthTextButtonSingle( + leftMargin, innerSpacingButtonRowTwoChildren) + + } else if (isButtonRow(child) && child.children.length > 2) { + // the long button row has special margins, which are used on + // either side if the side icon button is missing on that side. If the item is present, + // the leftMargin can be used on either side to leave space for either icon button + overrideChildWidth = widthButtonRowLong( + leftItem != undefined, + rightItem != undefined, + leftMargin, + innerSpacingButtonRowLong, + outerMarginButtonRowLong) + } + + child.width = overrideChildWidth + } + + if (numChildren == 1) { + var loneChild = children[0] + var loneChildWidth = buttonWidth(loneChild) + if (isButtonRow(loneChild)) { + loneChildWidth = overrideChildWidth + } + if (isIconButton(loneChild)) + loneChild.x = outerMarginHorizontal + else + loneChild.x = centerOffset(root.width, loneChildWidth) + return + } + + // we can easily calculate the positions of the left and right items, + // but if they are missing then correct the margins + if (leftItem != undefined){ + leftItem.x = outerMarginHorizontal + } else { + leftMargin = 0 + } + if (rightItem != undefined){ + rightItem.x = root.width - defaultHeightToolBar - outerMarginHorizontal + } else { + rightMargin = root.width + } + + if (!childrenRemaining) + return; + + if (childrenRemaining == 1) { + var loneChild = children[firstRemainingIndex] + var loneChildWidth = buttonWidth(loneChild) + if (isButtonRow(loneChild)) { + // ButtonRow should have the override width (but it won't have + // been updated yet) + loneChildWidth = overrideChildWidth + } + // lone child is always centered, unless it's a long button row on + // one side only + if (isButtonRow(loneChild) && loneChild.children.length >= 3 + && ((leftItem == undefined) != (rightItem == undefined))) { + loneChild.x = (leftItem != undefined) ? + (leftMargin + innerSpacingButtonRowLong) : outerMarginButtonRowLong + } else { + loneChild.x = centerOffset(root.width, loneChildWidth) + } + } else if (childrenRemaining == 2 && isTextToolButton(children[firstRemainingIndex])) { + // text buttons are distributed around the center with a center spacing + var midPoint = Math.floor(root.width / 2.0) + var halfSpacing = Math.round(platformStyle.paddingLarge / 2.0) + children[firstRemainingIndex].x = midPoint - halfSpacing - buttonWidth(children[firstRemainingIndex]) + children[firstRemainingIndex + 1].x = midPoint + halfSpacing + } else { + // icon buttons are deployed evenly in the remaining space, + // but we need to ensure that the spacings are integer values, + // and share the rounding error to ensure that they are centered + var remainingSpace = rightMargin - leftMargin + var spacingNotRounded = remainingSpace + for (var p = 0; p < childrenRemaining; p++) { + var nextChild = children[leftItem != undefined ? p + 1 : p] + spacingNotRounded -= buttonWidth(nextChild) + } + spacingNotRounded /= (childrenRemaining + 1) + var spacing = Math.floor(spacingNotRounded) + var totalRoundingError = (spacingNotRounded - spacing) * (childrenRemaining + 1) + var curPos = leftMargin + Math.floor(totalRoundingError / 2.0) + + for (var p = 0; p < childrenRemaining; p++) { + var nextChild = children[leftItem != undefined ? p + 1 : p] + curPos += spacing + nextChild.x = curPos + curPos += buttonWidth(nextChild) + } + } + } + } + + Component.onCompleted: internal.layoutChildren() + onParentChanged: internal.layoutChildren() + onChildrenChanged: internal.layoutChildren() + onImplicitWidthChanged: internal.layoutChildren() + onImplicitHeightChanged: internal.layoutChildren() +} diff --git a/mardrone/imports/com/nokia/android.1.1/ToolButton.qml b/mardrone/imports/com/nokia/android.1.1/ToolButton.qml new file mode 100644 index 0000000..65857e6 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ToolButton.qml @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import Qt.labs.components 1.1 +import "." 1.1 + +Item { + id: root + + // Common API + property alias checkable: checkableItem.enabled + property alias checked: checkableItem.checked + property bool enabled: true // overridden from base class + property alias text: label.text + property url iconSource + property bool flat: (!text && iconSource != "" && parent && !internal.isButtonRow(parent)) + property bool pressed: mouseArea.containsMouse && (stateGroup.state == "Pressed" || stateGroup.state == "PressAndHold") + + // Platform API + property alias platformExclusiveGroup: checkableItem.exclusiveGroup + property bool platformInverted: false + + // Common API + signal clicked + + // Platform API + signal platformReleased + signal platformPressAndHold + + onFlatChanged: { + background.visible = !flat || (checkableItem.enabled && checkableItem.checked && !internal.isButtonRow(parent)) + } + + implicitWidth: { + if (!text) + return internal.iconButtonWidth() + else if (iconSource == "") + return Math.max(internal.iconButtonWidth(), internal.textButtonWidth()) + else + return internal.iconButtonWidth() + internal.textButtonWidth() + } + implicitHeight: { + if ((iconSource != "") && !text) + // if there is just an icon, then it's full button height + return internal.iconButtonWidth() + else + // Otherwise frame height is always tool bar's height in landscape, regardless of the current orientation + return privateStyle.toolBarHeightLandscape + } + + BorderImage { + id: background + + source: privateStyle.imagePath(internal.imageName() + internal.modeName(), root.platformInverted) + border { + left: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + top: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + right: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + bottom: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + } + smooth: true + anchors.fill: parent + visible: !flat + + BorderImage { + id: highlight + border { + left: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + top: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + right: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + bottom: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0; + } + smooth: true + anchors.fill: background + opacity: 0 + } + } + + Image { + id: contentIcon + source: privateStyle.toolBarIconPath(iconSource, root.platformInverted) + visible: iconSource != "" + sourceSize.width: platformStyle.graphicSizeSmall + sourceSize.height: platformStyle.graphicSizeSmall + anchors { + centerIn: !text ? parent : undefined + verticalCenter: !text ? undefined : parent.verticalCenter + left: !text ? undefined : parent.left + leftMargin: !text ? 0 : 2 * platformStyle.paddingMedium + } + smooth: true + } + + Text { + id: label + clip: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge } + color: { + if (!root.enabled) + return root.platformInverted ? platformStyle.colorDisabledLightInverted + : platformStyle.colorDisabledLight + else if (stateGroup.state == "Pressed" || stateGroup.state == "PressAndHold") + return root.platformInverted ? platformStyle.colorPressedInverted + : platformStyle.colorPressed + else + return root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + } + visible: text + anchors { + top: parent.top; bottom: parent.bottom + left: iconSource != "" ? contentIcon.right : parent.left; right: parent.right + leftMargin: iconSource != "" ? platformStyle.paddingSmall : platformStyle.paddingMedium + rightMargin: platformStyle.paddingMedium + } + smooth: true + } + + MouseArea { + id: mouseArea + anchors.fill: parent + + onPressed: if (root.enabled) stateGroup.state = "Pressed" + onReleased: if (root.enabled) stateGroup.state = "" + onCanceled: { + if (root.enabled) { + // Mark as canceled + stateGroup.state = "Canceled" + // Reset state. Can't expect a release since mouse was ungrabbed + stateGroup.state = "" + } + } + onPressAndHold: if (stateGroup.state != "Canceled" && root.enabled) stateGroup.state = "PressAndHold" + onExited: if (root.enabled) stateGroup.state = "Canceled" + } + + QtObject { + id: internal + + property bool isFrameGraphic : imageName().search("_fr") > 0 + + function belongsToExclusiveGroup() { + return checkableItem.exclusiveGroup + || (root.parent + && root.parent.hasOwnProperty("checkedButton") + && root.parent.exclusive) + } + + function modeName() { + if (isButtonRow(parent)) + return parent.privateModeName(root, 1) + else if (!enabled) + return "disabled" + else if (flat && checkableItem.checked && !internal.isButtonRow(parent)) + return "latched" + else + return "normal" + } + + function iconButtonWidth() { + return (screen.width < screen.height) ? privateStyle.toolBarHeightPortrait : privateStyle.toolBarHeightLandscape + } + + function textButtonWidth() { + return platformStyle.paddingMedium * ((screen.width < screen.height) ? 15 : 25) + } + + function press() { + if (!belongsToExclusiveGroup()) { + if (checkableItem.enabled && checkableItem.checked) + privateStyle.play(Android.SensitiveButton) + else + privateStyle.play(Android.BasicButton) + } else if (checkableItem.enabled && !checkableItem.checked) { + privateStyle.play(Android.BasicButton) + } + + if (flat) + background.visible = true + highlight.source = privateStyle.imagePath(internal.imageName() + "pressed", root.platformInverted) + label.scale = 0.95 + contentIcon.scale = 0.95 + highlight.opacity = 1 + } + + function release() { + label.scale = 1 + contentIcon.scale = 1 + highlight.opacity = 0 + + if (((checkableItem.enabled && checkableItem.checked && !belongsToExclusiveGroup()) + || !checkableItem.enabled) && stateGroup.state != "Canceled") + privateStyle.play(Android.BasicButton) + + if (flat && isButtonRow(parent)) + visibleEffect.restart() //Background invisible + else if (flat && !checkableItem.enabled) + visibleEffect.restart() //Background invisible + else + clickedEffect.restart() //Background stays visible + + root.platformReleased() + } + + function click() { + checkableItem.toggle() + root.clicked() + } + + function hold() { + root.platformPressAndHold() + } + + function isButtonRow(item) { + return (item && + item.hasOwnProperty("checkedButton") && + item.hasOwnProperty("privateDirection") && + item.privateDirection == Qt.Horizontal) + } + + // The function imageName() handles fetching correct graphics for the ToolButton. + // If the parent of a ToolButton is ButtonRow, segmented-style graphics are used to create a + // seamless row of buttons. Otherwise normal ToolButton graphics are utilized. + function imageName() { + var mirror = root.LayoutMirroring.enabled // To create binding + if (isButtonRow(parent)) + return parent.privateGraphicsName(root, 1) + else + return (!flat || text || iconSource == "") ? "qtg_fr_toolbutton_text_" : "qtg_graf_toolbutton_" + } + } + + StateGroup { + id: stateGroup + + states: [ + State { name: "Pressed" }, + State { name: "PressAndHold" }, + State { name: "Canceled" } + ] + + transitions: [ + Transition { + to: "Pressed" + ScriptAction { script: internal.press() } + }, + Transition { + from: "Pressed" + to: "PressAndHold" + ScriptAction { script: internal.hold() } + }, + Transition { + from: "Pressed" + to: "" + ScriptAction { script: internal.release() } + ScriptAction { script: internal.click() } + }, + Transition { + from: "PressAndHold" + to: "" + ScriptAction { script: internal.release() } + ScriptAction { script: internal.click() } + }, + Transition { + from: "Pressed" + to: "Canceled" + ScriptAction { script: internal.release() } + }, + Transition { + from: "PressAndHold" + to: "Canceled" + ScriptAction { script: internal.release() } + } + ] + } + + ParallelAnimation { + id: clickedEffect + PropertyAnimation { + target: label + property: "scale" + from: 0.95 + to: 1.0 + easing.type: Easing.Linear + duration: 100 + } + PropertyAnimation { + target: contentIcon + property: "scale" + from: 0.95 + to: 1.0 + easing.type: Easing.Linear + duration: 100 + } + PropertyAnimation { + target: highlight + property: "opacity" + from: 1 + to: 0 + easing.type: Easing.Linear + duration: 150 + } + } + + SequentialAnimation { + id: visibleEffect + ParallelAnimation { + PropertyAnimation { + target: label + property: "scale" + from: 0.95 + to: 1.0 + easing.type: Easing.Linear + duration: 100 + } + PropertyAnimation { + target: contentIcon + property: "scale" + from: 0.95 + to: 1.0 + easing.type: Easing.Linear + duration: 100 + } + PropertyAnimation { + target: highlight + property: "opacity" + from: 1 + to: 0 + easing.type: Easing.Linear + duration: 150 + } + } + PropertyAction { + target: background + property: "visible" + value: false + } + } + + Checkable { + id: checkableItem + value: root.text + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/ToolTip.qml b/mardrone/imports/com/nokia/android.1.1/ToolTip.qml new file mode 100644 index 0000000..bae2155 --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/ToolTip.qml @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 1.1 +import "." 1.1 +import "AppManager.js" as AppView + +Item { + id: root + + // Public API + property alias font: label.font + property alias text: label.text + property variant target: null + property bool platformInverted: false + + implicitWidth: Math.min(privy.maxWidth, (privateStyle.textWidth(text, font) + privy.hMargin * 2)) + implicitHeight: privateStyle.fontHeight(font) + privy.vMargin * 2 + opacity: 0 + + function show() { + opacity = 1 + visible = true + } + + function hide() { + opacity = 0 + } + + Behavior on opacity { + PropertyAnimation { duration: 100 } + } + + Component.onCompleted: { + if (visible) + show() + } + + onOpacityChanged: { + if (opacity == 0) + visible = false + else + visible = true + } + + onVisibleChanged: { + if (visible) { + root.parent = AppView.rootObject() + privy.calculatePosition() + opacity = 1 + privy.targetSceneXChanged.connect(privy.targetMoved) + privy.targetSceneYChanged.connect(privy.targetMoved) + } else { + privy.targetSceneXChanged.disconnect(privy.targetMoved) + privy.targetSceneYChanged.disconnect(privy.targetMoved) + } + } + + Binding { target: privy; property: "targetSceneX"; value: AppView.sceneX(root.target); when: root.visible && (root.target != null) } + Binding { target: privy; property: "targetSceneY"; value: AppView.sceneY(root.target); when: root.visible && (root.target != null) } + + QtObject { + id: privy + + property real hMargin: platformStyle.paddingMedium * 2 + property real vMargin: platformStyle.paddingMedium + property real spacing: platformStyle.paddingLarge + property real maxWidth: screen.width - spacing * 2 + property real targetSceneX + property real targetSceneY + + function targetMoved() { + if (root.opacity == 1) + hide() + } + + function calculatePosition() { + if (!target) + return + + // Determine and set the main position for the ToolTip, order: top, right, left, bottom + var targetPos = root.parent.mapFromItem(target, 0, 0) + + // Top + if (targetPos.y >= (root.height + privy.vMargin + privy.spacing)) { + root.x = targetPos.x + (target.width / 2) - (root.width / 2) + root.y = targetPos.y - root.height - privy.vMargin + + // Right + } else if (targetPos.x <= (screen.width - target.width - privy.hMargin - root.width - privy.spacing)) { + root.x = targetPos.x + target.width + privy.hMargin; + root.y = targetPos.y + (target.height / 2) - (root.height / 2) + + // Left + } else if (targetPos.x >= (root.width + privy.hMargin + privy.spacing)) { + root.x = targetPos.x - root.width - privy.hMargin + root.y = targetPos.y + (target.height / 2) - (root.height / 2) + + // Bottom + } else { + root.x = targetPos.x + (target.width / 2) - (root.width / 2) + root.y = targetPos.y + target.height + privy.vMargin + } + + // Fine-tune the ToolTip position based on the screen borders + if (root.x > (screen.width - privy.spacing - root.width)) + root.x = screen.width - root.width - privy.spacing + + if (root.x < privy.spacing) + root.x = privy.spacing; + + if (root.y > (screen.height - root.height - privy.spacing)) + root.y = screen.height - root.height - privy.spacing + + if (root.y < privy.spacing) + root.y = privy.spacing + } + } + + BorderImage { + id: frame + anchors.fill: parent + source: privateStyle.imagePath("qtg_fr_tooltip", root.platformInverted) + border { left: 20; top: 20; right: 20; bottom: 20 } + } + + Text { + id: label + clip: true + color: root.platformInverted ? platformStyle.colorNormalLightInverted + : platformStyle.colorNormalLight + elide: Text.ElideRight + font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors { + fill: parent + leftMargin: privy.hMargin + rightMargin: privy.hMargin + topMargin: privy.vMargin + bottomMargin: privy.vMargin + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/Window.qml b/mardrone/imports/com/nokia/android.1.1/Window.qml new file mode 100644 index 0000000..efde5ca --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/Window.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.1 + +Item { + id: root + + property bool inPortrait: height > width + + signal orientationChangeAboutToStart + signal orientationChangeStarted + signal orientationChangeFinished + + // Symbian specific API + property bool platformInverted: false + + width: screen.width > 0 ? screen.width : screen.displayWidth + height: screen.height > 0 ? screen.height : screen.displayHeight + + Rectangle { + anchors.fill: parent + color: platformInverted ? platformStyle.colorBackgroundInverted + : platformStyle.colorBackground + } + + Binding { target: root; property: "height"; value: screen.height; when: screen.height > 0 } + Binding { target: root; property: "width"; value: screen.width; when: screen.width > 0 } + + Connections { + target: screen + onCurrentOrientationChanged: root.orientationChangeFinished() + onPrivateAboutToChangeOrientation: { + root.orientationChangeAboutToStart() + root.orientationChangeStarted() + } + } +} diff --git a/mardrone/imports/com/nokia/android.1.1/qmldir b/mardrone/imports/com/nokia/android.1.1/qmldir new file mode 100644 index 0000000..54b71bf --- /dev/null +++ b/mardrone/imports/com/nokia/android.1.1/qmldir @@ -0,0 +1,42 @@ +plugin androidplugin_1_1 +ApplicationWindow 1.1 ApplicationWindow.qml +BusyIndicator 1.1 BusyIndicator.qml +Button 1.1 Button.qml +ButtonColumn 1.1 ButtonColumn.qml +ButtonRow 1.1 ButtonRow.qml +CheckBox 1.1 CheckBox.qml +CommonDialog 1.1 CommonDialog.qml +ContextMenu 1.1 ContextMenu.qml +Dialog 1.1 Dialog.qml +Label 1.1 Label.qml +ListHeading 1.1 ListHeading.qml +ListItem 1.1 ListItem.qml +ListItemText 1.1 ListItemText.qml +Menu 1.1 Menu.qml +MenuItem 1.1 MenuItem.qml +MenuLayout 1.1 MenuLayout.qml +Page 1.1 Page.qml +PageStack 1.1 PageStack.qml +PageStackWindow 1.1 PageStackWindow.qml +ProgressBar 1.1 ProgressBar.qml +QueryDialog 1.1 QueryDialog.qml +RadioButton 1.1 RadioButton.qml +ScrollBar 1.1 ScrollBar.qml +ScrollDecorator 1.1 ScrollDecorator.qml +SectionScroller 1.1 SectionScroller.qml +SelectionDialog 1.1 SelectionDialog.qml +SelectionListItem 1.1 SelectionListItem.qml +Slider 1.1 Slider.qml +StatusBar 1.1 StatusBar.qml +Switch 1.1 Switch.qml +TabBar 1.1 TabBar.qml +TabBarLayout 1.1 TabBarLayout.qml +TabButton 1.1 TabButton.qml +TabGroup 1.1 TabGroup.qml +TextArea 1.1 TextArea.qml +TextField 1.1 TextField.qml +ToolBar 1.1 ToolBar.qml +ToolBarLayout 1.1 ToolBarLayout.qml +ToolButton 1.1 ToolButton.qml +ToolTip 1.1 ToolTip.qml +Window 1.1 Window.qml