diff options
Diffstat (limited to 'src/controls')
70 files changed, 11452 insertions, 0 deletions
diff --git a/src/controls/AbstractCheckable.qml b/src/controls/AbstractCheckable.qml new file mode 100644 index 00000000..206361e2 --- /dev/null +++ b/src/controls/AbstractCheckable.qml @@ -0,0 +1,149 @@ +import QtQuick 2.0 +import QtQuick.Controls.Private 1.0 + +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 + +/*! + \qmltype AbstractCheckable + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief An abstract representation of a checkable control + \qmlabstract +*/ + +Control { + id: abstractCheckable + + /*! + Emitted whenever the radio button is clicked. + */ + signal clicked + + /*! + \qmlproperty bool RadioButton::pressed + + This property is \c true if the radio button is pressed. + Set this property to manually invoke a mouse click. + */ + readonly property alias pressed: mouseArea.effectivePressed + + /*! + This property is \c true if the radio button is checked, and determines + whether \l checkedState is \c Qt.Checked or \c Qt.UnChecked. + + If \l partiallyCheckedEnabled is \c true, this property will be + \c false. + */ + property bool checked: false + + /*! + \qmlproperty bool RadioButton::containsMouse + + This property is \c true if the radio button currently contains the + mouse cursor. + */ + readonly property alias containsMouse: mouseArea.containsMouse + + /*! + This property is \c true if the radio button takes the focus when it is + pressed; \l{QQuickItem::forceActiveFocus()}{forceActiveFocus()} will be + called on the radio button. + */ + property bool activeFocusOnPress: false + + /*! + \qmlproperty ExclusiveGroup RadioButton::exclusiveGroup + + This property stores the ExclusiveGroup that the radio button belongs + to. + */ + property ExclusiveGroup exclusiveGroup: null + + /*! + This property holds the text that the label should display. + */ + property string text + + /*! \internal */ + property var __cycleStatesHandler: cycleRadioButtonStates + + /*! \internal */ + onExclusiveGroupChanged: { + if (exclusiveGroup) + exclusiveGroup.registerCheckable(abstractCheckable) + } + + MouseArea { + id: mouseArea + focus: true + anchors.fill: parent + hoverEnabled: true + enabled: !keyPressed + + property bool keyPressed: false + property bool effectivePressed: pressed && containsMouse || keyPressed + + onClicked: abstractCheckable.clicked(); + + onPressed: if (activeFocusOnPress) forceActiveFocus(); + + onReleased: { + if (containsMouse && (!exclusiveGroup || !checked)) + __cycleStatesHandler(); + } + } + + Keys.onPressed: { + if (event.key === Qt.Key_Space && !event.isAutoRepeat && !mouseArea.pressed) + mouseArea.keyPressed = true; + } + + Keys.onReleased: { + if (event.key === Qt.Key_Space && !event.isAutoRepeat && mouseArea.keyPressed) { + mouseArea.keyPressed = false; + __cycleStatesHandler(); + clicked(); + } + } +} diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml new file mode 100644 index 00000000..6a33c26f --- /dev/null +++ b/src/controls/ApplicationWindow.qml @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Window 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype ApplicationWindow + \inqmlmodule QtQuick.Controls 1.0 + \ingroup applicationwindow + \brief ApplicationWindow provides a top-level application window. + + AppliactionWindow is a \l Window, but adds convenience + for positioning items such as \l MenuBar, \l ToolBar and \l StatusBar in a platform + independent manner. + + \code + ApplicationWindow { + id: window + menuBar: MenuBar { + Menu { MenuItem {...} } + Menu { MenuItem {...} } + } + + toolBar: ToolBar { + RowLayout { + anchors.fill: parent + ToolButton{} + } + } + } + + TabFrame { + id: myContent + anchors.fill: parent + ... + } + } + \endcode +*/ + +Window { + id: root + + width: 320 + height: 240 + + /*! + \qmlproperty MenuBar ApplicationWindow::menuBar + + This property holds the \l MenuBar + + By default this value is not set. + */ + property MenuBar menuBar: null + + /*! + \qmlproperty Item ApplicationWindow::toolBar + + This property holds the tool bar \l Item. + + It can be set to any Item type but is generally used with \l ToolBar. + + By default this value is not set. When you set the toolBar Item, it will + be anchored automatically into the AppliacationWindow. + */ + property alias toolBar: toolBarArea.data + + /*! + \qmlproperty Item ApplicationWindow::statusBar + + This property holds the status bar \l Item. + + It can be set to any Item type but is generally used with \l StatusBar. + + By default this value is not set. When you set the toolBar Item, it will + be anchored automatically into the AppliacationWindow. + */ + property alias statusBar: statusBarArea.data + + /*! \internal */ + default property alias data: contentArea.data + property alias backgroundColor: syspal.window + + SystemPalette {id: syspal} + + Rectangle { + id: backgroundItem + anchors.fill: parent + color: backgroundColor + + Row { + id: toolBarArea + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + } + + Item { + id: contentArea + anchors.top: toolBarArea.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: statusBarArea.top + } + + Row { + id: statusBarArea + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + } + + states: State { + name: "hasMenuBar" + when: menuBar && !menuBar.isNative + + ParentChange { + target: menuBar + parent: backgroundItem + } + + PropertyChanges { + target: menuBar + x: 0 + y: 0 + width: backgroundItem.width + } + + AnchorChanges { + target: toolBarArea + anchors.top: menuBar.bottom + } + } + } +} diff --git a/src/controls/Button.qml b/src/controls/Button.qml new file mode 100644 index 00000000..529d602a --- /dev/null +++ b/src/controls/Button.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype Button + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief A normal button + + A normal command button. Similar to the QPushButton widget. + + The push button is perhaps the most commonly used widget in any graphical user interface. + Push (click) a button to command the computer to perform some action, or to answer a question. + Typical buttons are OK, Apply, Cancel, Close, Yes, No and Help. + + */ +BasicButton { + id: button + + /*! This property holds whether the push button is the default button. + Default buttons decide what happens when the user presses enter in a dialog without giving a button explicit focus. + Note : This property is currently ignored by Dialog + */ + property bool defaultbutton: false + + /*! This property holds the text shown on the button. + If the button has no text, the \l text property will be an empty string. */ + property string text + + /*! This property holds the icon shown on the button. + If the button has no icon, the \l iconSource property will be an empty string. */ + property url iconSource + + Accessible.name: text + + style: Qt.createComponent(Settings.THEME_PATH + "/ButtonStyle.qml", button) +} diff --git a/src/controls/CheckBox.qml b/src/controls/CheckBox.qml new file mode 100644 index 00000000..574f35e5 --- /dev/null +++ b/src/controls/CheckBox.qml @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import "Styles/Settings.js" as Settings +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype CheckBox + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief A checkbox with a text label + + A CheckBox is an option button that can be toggled on (checked) or off + (unchecked). Checkboxes are typically used to represent features in an + application that can be enabled or disabled without affecting others. + + The state of the checkbox can be set with the \l checked property. + + In addition to the checked and unchecked states, there is a third state: + partially checked. This state indicates that the + regular checked/unchecked state can not be determined; generally because of + other states that affect the checkbox. This state is useful when several + child nodes are selected in a treeview, for example. + + The partially checked state can be made available to the user by setting + \l partiallyCheckedEnabled to \c true, or set directly by setting + \l checkedState to \c Qt.PartiallyChecked. \l checkedState behaves + identically to \l checked when \l partiallyCheckedEnabled is \c false; + setting one will appropriately set the other. + + The text of the label shown next to the checkbox can be set with the \l text + property. + + Whenever a CheckBox is clicked, it emits the clicked() signal. +*/ + +AbstractCheckable { + id: checkBox + + /*! + This property indicates the current checked state of the checkbox. + + Possible values: + \c Qt.UnChecked - The checkbox is not checked (default). + \c Qt.Checked - The checkbox is checked. + \c Qt.PartiallyChecked - The checkbox is in a partially checked (or + "mixed") state. + */ + property int checkedState: checked ? Qt.Checked : Qt.Unchecked + + /*! + This property determines whether the \c Qt.PartiallyChecked state is + available. + + A checkbox may be in a partially checked state when the regular checked + state can not be determined. + + Setting \l checkedState to \c Qt.PartiallyChecked will implicitly set + this property to \c true. + + By default, this property is \c false. + */ + property bool partiallyCheckedEnabled: false + + /*! + True if onCheckedChanged should be ignored because we were reacting + to onCheckedStateChanged. + */ + property bool ignoreChecked: false + + style: Qt.createComponent(Settings.THEME_PATH + "/CheckBoxStyle.qml", checkBox) + + Accessible.role: Accessible.CheckBox + Accessible.name: text + + __cycleStatesHandler: cycleCheckBoxStates + + /*! \internal */ + onCheckedChanged: { + if (!ignoreChecked) + checkedState = checked ? Qt.Checked : Qt.Unchecked; + } + + /*! \internal */ + onCheckedStateChanged: { + ignoreChecked = true; + if (checkedState === Qt.PartiallyChecked) { + partiallyCheckedEnabled = true; + checked = false; + } else { + checked = checkedState === Qt.Checked; + } + ignoreChecked = false; + } + + /*! \internal */ + function cycleCheckBoxStates() { + if (!partiallyCheckedEnabled) { + checked = !checked; + } else { + switch (checkedState) { + case Qt.Unchecked: checkedState = Qt.Checked; break; + case Qt.Checked: checkedState = Qt.PartiallyChecked; break; + case Qt.PartiallyChecked: checkedState = Qt.Unchecked; break; + } + } + } +} diff --git a/src/controls/ComboBox.qml b/src/controls/ComboBox.qml new file mode 100644 index 00000000..5458807b --- /dev/null +++ b/src/controls/ComboBox.qml @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype ComboBox + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief ComboBox is a combined button and popup list. The popup menu itself is platform + native, and cannot by styled from QML code. + + Add menu items to the comboBox by either adding MenuItem children inside the popup, or + assign it a ListModel (or both). + + The ComboBox contains the following API (in addition to the BasicButton API): + + ListModel model - this model will be used, in addition to MenuItem children, to + create items inside the popup menu + bool popupOpen - setting this property to 'true' will open the popup. + int selectedIndex - the index of the selected item in the popup menu. + string selectedText - the text of the selected menu item. + + Example 1: + + \qml + ComboBox { + model: ListModel { + id: menuItems + ListElement { text: "Banana"; color: "Yellow" } + ListElement { text: "Apple"; color: "Green" } + ListElement { text: "Coconut"; color: "Brown" } + } + width: 200 + onSelectedIndexChanged: console.debug(selectedText + ", " + menuItems.get(selectedIndex).color) + } + \endqml + + Example 2: + + \qml + ComboBox { + width: 200 + MenuItem { + text: "Pineapple" + onSelected: console.debug(text) + + } + MenuItem { + text: "Grape" + onSelected: console.debug(text) + } + } + \endqml +*/ + +Control { + id: comboBox + + default property alias menuItems: popup.menuItems + property alias model: popup.model + property alias textRole: popup.textRole + property bool popupOpen: false + + property alias selectedIndex: popup.selectedIndex + readonly property alias selectedText: popup.selectedText + + readonly property bool pressed: mouseArea.pressed || popup.popupVisible + property alias containsMouse: mouseArea.containsMouse + + style: Qt.createComponent(Settings.THEME_PATH + "/ComboBoxStyle.qml", comboBox) + + Accessible.role: Accessible.ComboBox + + MouseArea { + id: mouseArea + anchors.fill: parent + onPressedChanged: if (pressed) popup.show() + } + + ExclusiveGroup { id: eg } + + StyleItem { id: styleItem } + Component.onCompleted: { + if (selectedIndex === -1) + selectedIndex = 0 + if (styleItem.style == "mac") { + popup.x -= 10 + popup.y += 4 + popup.font.pointSize = 13 + } + } + + ContextMenu { + id: popup + + style: __style.popupStyle + + // 'centerSelectedText' means that the menu will be positioned + // so that the selected text' top left corner will be at x, y. + property bool centerSelectedText: true + + property int x: 0 + property int y: centerSelectedText ? 0 : comboBox.height + minimumWidth: comboBox.width + + function finalizeItem(item) { + item.action.checkable = true + item.action.exclusiveGroup = eg + } + + function show() { + comboBox.popupOpen = true + menuItems[comboBox.selectedIndex].checked = true + currentIndex = comboBox.selectedIndex + showPopup(x, y, centerSelectedText ? comboBox.selectedIndex : 0, comboBox) + } + + onMenuClosed: popupOpen = false + } + + // The key bindings below will only be in use when popup is + // not visible. Otherwise, native popup key handling will take place: + Keys.onSpacePressed: { + if (!popupOpen) + popup.show() + else + popupOpen = false + } + Keys.onUpPressed: { if (selectedIndex > 0) selectedIndex-- } + Keys.onDownPressed: { if (selectedIndex < model.count - 1) selectedIndex++ } +} diff --git a/src/controls/ContextMenu.qml b/src/controls/ContextMenu.qml new file mode 100644 index 00000000..42c2adcb --- /dev/null +++ b/src/controls/ContextMenu.qml @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 + +Menu { + id: root + property string selectedText: menuItems[selectedIndex] ? menuItems[selectedIndex].text : "" + property string textRole + + onModelChanged: if (Component.status === Component.Ready && model !== undefined) rebuildMenu() + + Component.onCompleted: if (model !== undefined) rebuildMenu() + + onSelectedIndexChanged: { + if (0 <= selectedIndex && selectedIndex < menuItems.length) + menuItems[selectedIndex].triggered() + } + + function rebuildMenu() + { + clearMenuItems(); + + var nativeModel = root.hasNativeModel() + + if (model !== undefined) { + var modelCount = nativeModel ? root.modelCount() : model.count; + for (var j = 0 ; j < modelCount; ++j) { + var textValue + if (nativeModel) { + textValue = root.modelTextAt(j); + } else { + if (textRole !== "") + textValue = model.get(j)[textRole] + else if (model.count > 0 && root.model.get && root.model.get(0)) { + // ListModel with one role + var listElement = root.model.get(0) + var oneRole = true + var roleName = "" + var roleCount = 0 + for (var role in listElement) { + if (!roleName || role === "text") + roleName = role + ++roleCount + } + if (roleCount > 1 && roleName !== "text") { + oneRole = false + console.log("Warning: No textRole set for ComboBox.") + break + } + + if (oneRole) { + root.textRole = roleName + textValue = root.model.get(j)[textRole] + } + } + } + + var item = addMenuItem(textValue) + if (root["finalizeItem"]) + finalizeItem(item) + } + } + + menuItemsChanged() + } +} diff --git a/src/controls/GroupBox.qml b/src/controls/GroupBox.qml new file mode 100644 index 00000000..2e5b35d6 --- /dev/null +++ b/src/controls/GroupBox.qml @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype GroupBox + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief GroupBox provides a group box frame with a title + + A group box provides a frame, a title on top and displays various other controls inside itself. Group boxes can also be checkable. + + Child controls in checkable group boxes are enabled or disabled depending on whether or not the group box is checked. + + You can minimize the space consumption of a group box by enabling the flat property. + In most styles, enabling this property results in the removal of the left, right and bottom edges of the frame. + + GroupBox doesn't automatically lay out the child controls (which are often \l{CheckBox}{CheckBoxes} or \l{RadioButton}{RadioButtons} but can be any controls). + The following example shows how we can set up a GroupBox with a column: + + \qml + GroupBox { + title: "Package selection" + adjustToContentSize: true + Column { + CheckBox { + text: "Update system" + } + CheckBox { + text: "Update applications" + } + CheckBox { + text: "Update documentation" + } + } + } + \endqml +*/ + +Item { + id: groupbox + + /*! + This property holds the group box title text. + + There is no default title text. + */ + property string title + + /*! + This property holds whether the group box is painted flat or has a frame. + + A group box usually consists of a surrounding frame with a title at the top. + If this property is enabled, only the top part of the frame is drawn in most styles; + otherwise, the whole frame is drawn. + + By default, this property is disabled, i.e., group boxes are not flat unless explicitly specified. + + \note In some styles, flat and non-flat group boxes have similar representations and may not be as + distinguishable as they are in other styles. + */ + property bool flat: false + + /*! + This property holds whether the group box has a checkbox in its title. + + If this property is true, the group box displays its title using a checkbox in place of an ordinary label. + If the checkbox is checked, the group box's children are enabled; otherwise, they are disabled and inaccessible. + + By default, group boxes are not checkable. + */ + property bool checkable: false + + /*! + \qmlproperty bool GroupBox::checked + + This property holds whether the group box is checked. + + If the group box is checkable, it is displayed with a check box. If the check box is checked, the group + box's children are enabled; otherwise, the children are disabled and are inaccessible to the user. + + By default, checkable group boxes are also checked. + */ + property alias checked: check.checked + + /*! + This property holds the width of the content. + */ + readonly property real contentWidth: content.childrenRect.width + + /*! + This property holds the height of the content. + */ + readonly property real contentHeight: content.childrenRect.height + + /*! + This property holds whether the group box resizes itself to fit the contents. + + By default, group boxes do not resize itself to fit the contents. + + \note When adjustToContentSize is enabled, children cannot be anchored. + */ + property bool adjustToContentSize: false + + /*! \internal */ + property Component style: Qt.createComponent(Settings.THEME_PATH + "/GroupBoxStyle.qml", groupbox) + + /*! \internal */ + default property alias data: content.data + + /*! \internal */ + property alias __checkbox: check + + implicitWidth: Math.max(200, contentWidth + (loader.item ? loader.item.implicitWidth: 0) ) + implicitHeight: contentHeight + (loader.item ? loader.item.implicitHeight : 0) + 4 + + Accessible.role: Accessible.Grouping + Accessible.name: title + + Loader { + id: loader + property alias control: groupbox + anchors.fill: parent + property int topMargin: title.length > 0 || checkable ? 22 : 4 + property int bottomMargin: 4 + property int leftMargin: 4 + property int rightMargin: 4 + sourceComponent: style + onLoaded: item.z = -1 + } + + CheckBox { + id: check + checked: true + visible: checkable + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: loader.topMargin + } + + Item { + id:content + z: 1 + focus: true + anchors.topMargin: loader.topMargin + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.bottomMargin: 8 + anchors.fill: parent + enabled: (!checkable || checkbox.checked) + } +} diff --git a/src/controls/Label.qml b/src/controls/Label.qml new file mode 100644 index 00000000..0dd5dec2 --- /dev/null +++ b/src/controls/Label.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 + +/*! + \qmltype Label + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief Label is a text display. + + In addition to the normal \l Text element, Label follows the font and + color scheme of the system. + Use the \c text property to assign a text to the label. For other properties + check \l Text. + + A simple label looks like this: + \qml + Label { + text: "Hello world" + } + \endqml + + You can use the properties of \l Text to change the appearance + of the text as desired: + \qml + Label { + text: "Hello world" + font.pixelSize: 22 + font.italic: true + color: "steelblue" + } + \endqml + + \sa Text, TextField, TextEdit +*/ + +Text { + /*! + \qmlproperty string Label::text + + The text to display. Use this property to get and set it. + */ + + id: label + font.pixelSize: 11 + color: pal.text + SystemPalette {id:pal} +} diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml new file mode 100644 index 00000000..3176436f --- /dev/null +++ b/src/controls/Menu.qml @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype Menu + \inqmlmodule QtQuick.Controls 1.0 + \inherits MenuItem + \brief Menu provides a menu component for use in menu bars, context menus, and other popup menus. + + \code + Menu { + text: "Edit" + + MenuItem { + text: "Cut" + shortcut: "Ctrl+X" + onTriggered: ... + } + + MenuItem { + text: "Copy" + shortcut: "Ctrl+C" + onTriggered: ... + } + + MenuItem { + text: "Paste" + shortcut: "Ctrl+V" + onTriggered: ... + } + + MenuSeparator { } + + Menu { + text: "More Stuff" + + MenuItem { + text: "Do Nothing" + } + } + } + \endcode + + \sa MenuBar, MenuItem, MenuSeparator +*/ +MenuPrivate { + id: root + + property Component style: Qt.createComponent(Settings.THEME_PATH + "/MenuStyle.qml", root) + + //! internal + property var menuBar: null + //! internal + property int currentIndex: -1 + + //! internal + menuContentItem: Loader { + sourceComponent: menuComponent + active: !root.isNative && root.popupVisible + focus: true + } + + //! internal + property Component menuComponent: Loader { + id: menuFrameLoader + + property Style __style: styleLoader.item + property Component menuItemStyle: __style ? __style.menuItem : null + + property var control: root + property alias contentWidth: column.width + property alias contentHeight: column.height + + property int subMenuXPos: width + (item && item["subMenuOverlap"] || 0) + + visible: status === Loader.Ready + sourceComponent: __style ? __style.frame : undefined + + Loader { + id: styleLoader + active: !root.isNative + sourceComponent: root.style + onStatusChanged: { + if (status === Loader.Error) + console.error("Failed to load Style for", root) + } + } + + focus: true + Keys.forwardTo: menuBar ? [menuBar] : [] + Keys.onEscapePressed: root.dismissMenu() + + Keys.onDownPressed: { + if (root.currentIndex < 0) { + root.currentIndex = 0 + return + } + + for (var i = root.currentIndex + 1; + i < root.menuItems.length && !canBeHovered(i); i++) + ; + } + + Keys.onUpPressed: { + for (var i = root.currentIndex - 1; + i >= 0 && !canBeHovered(i); i--) + ; + } + + function canBeHovered(index) { + var item = itemsRepeater.itemAt(index) + if (!item["isSeparator"] && item.enabled) { + root.currentIndex = index + return true + } + return false + } + + Keys.onLeftPressed: { + if (root.parentMenu) + closeMenu() + } + + Keys.onRightPressed: { + var item = itemsRepeater.itemAt(root.currentIndex) + if (item && item.hasSubmenu) { + item.menuItem.showPopup(menuFrameLoader.subMenuXPos, 0, -1, item) + item.menuItem.currentIndex = 0 + } + } + + Keys.onSpacePressed: menuFrameLoader.triggerAndDismiss() + Keys.onReturnPressed: menuFrameLoader.triggerAndDismiss() + Keys.onEnterPressed: menuFrameLoader.triggerAndDismiss() + + function triggerAndDismiss() { + var item = itemsRepeater.itemAt(root.currentIndex) + if (item && !item.isSeparator) { + root.selectedIndex = root.currentIndex + item.menuItem.trigger() + root.dismissMenu() + } + } + + Binding { + // Make sure the styled frame is in the background + target: menuFrameLoader.item + property: "z" + value: menuMouseArea.z - 1 + } + + MouseArea { + id: menuMouseArea + anchors.fill: parent + hoverEnabled: true + + onExited: root.currentIndex = -1 // TODO Test for any submenu open + + // Each menu item has its own mouse area, and for events to be + // propagated to the menu mouse area, they need to be embedded. + Column { + id: column + + Repeater { + id: itemsRepeater + model: root.menuItems + + Loader { + id: menuItemLoader + + property var menuItem: modelData + property int contentWidth: column.width + property int contentHeight: column.height + property bool isSeparator: menuItem ? !menuItem.hasOwnProperty("text") : false + property bool hasSubmenu: menuItem ? !!menuItem["menuItems"] : false + property bool selected: !isSeparator && root.currentIndex === index + + sourceComponent: menuFrameLoader.menuItemStyle + enabled: !isSeparator && !!menuItem && menuItem.enabled + + MouseArea { + id: itemMouseArea + width: menuFrameLoader.width + height: parent.height + y: menuItemLoader.item ? menuItemLoader.item.y : 0 // Adjust mouse area for style offset + hoverEnabled: true + + onClicked: { + if (hasSubmenu) + menuItem.closeMenu() + menuFrameLoader.triggerAndDismiss() + } + + onEntered: { + if (menuItemLoader.hasSubmenu && !menuItem.popupVisible) + openMenuTimer.start() + } + + onExited: { + if (!pressed && menuItemLoader.hasSubmenu) + closeMenuTimer.start() + } + + onPositionChanged: root.currentIndex = index + + Connections { + target: menuMouseArea + onEntered: { + if (!itemMouseArea.containsMouse && menuItemLoader.hasSubmenu) + closeMenuTimer.start() + } + } + } + + Timer { + id: openMenuTimer + interval: 50 + onTriggered: { + if (itemMouseArea.containsMouse) + menuItem.showPopup(menuFrameLoader.subMenuXPos, 0, -1, menuItemLoader) + } + } + + Timer { + id: closeMenuTimer + interval: 1 + onTriggered: { + if (menuMouseArea.containsMouse && !itemMouseArea.pressed && !itemMouseArea.containsMouse) + menuItem.closeMenu() + } + } + + Binding { + target: menuItem + property: "__visualItem" + value: menuItemLoader + } + } + } + + onWidthChanged: { + for (var i = 0; i < children.length; i++) { + var item = children[i]["item"] + if (item) + item.implicitWidth = Math.max(root.minimumWidth, implicitWidth) + } + } + } + } + } +} diff --git a/src/controls/MenuBar.qml b/src/controls/MenuBar.qml new file mode 100644 index 00000000..db1cb3f6 --- /dev/null +++ b/src/controls/MenuBar.qml @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype MenuBar + \inqmlmodule QtQuick.Controls 1.0 + \inherits Item + \brief The MenuBar item provides a horizontal menu bar. + + \code + MenuBar { + Menu { + text: "File" + MenuItem { text: "Open..." } + MenuItem { text: "Close" } + } + + Menu { + text: "Edit" + MenuItem { text: "Cut" } + MenuItem { text: "Copy" } + MenuItem { text: "Paste" } + } + } + \endcode + + \sa ApplicationWindow::menuBar +*/ + +MenuBarPrivate { + id: root + + property Component style: Qt.createComponent(Settings.THEME_PATH + "/MenuBarStyle.qml", root) + + height: !isNative ? topLoader.height : 0 + data: [ + Loader { + id: topLoader + sourceComponent: menuBarComponent + active: !root.isNative + focus: true + }, + + Component { + id: menuBarComponent + + Loader { + id: menuBarLoader + + property Style __style: styleLoader.item + property Component menuItemStyle: __style ? __style.menuItem : null + + property var control: root + onStatusChanged: if (status === Loader.Error) console.error("Failed to load panel for", root) + + visible: status === Loader.Ready + active: !root.isNative + sourceComponent: __style ? __style.frame : undefined + + Loader { + id: styleLoader + sourceComponent: root.style + onStatusChanged: { + if (status === Loader.Error) + console.error("Failed to load Style for", root) + } + } + + property int openedMenuIndex: -1 + property bool preselectMenuItem: false + property alias contentHeight: row.height + + Binding { + // Make sure the styled menu bar is in the background + target: menuBarLoader.item + property: "z" + value: menuMouseArea.z - 1 + } + + focus: openedMenuIndex !== -1 + + Keys.onLeftPressed: { + if (openedMenuIndex > 0) { + preselectMenuItem = true + openedMenuIndex-- + } + } + + Keys.onRightPressed: { + if (openedMenuIndex < root.menus.length - 1) { + preselectMenuItem = true + openedMenuIndex++ + } + } + + MouseArea { + id: menuMouseArea + anchors.fill: parent + hoverEnabled: true + + Row { + id: row + + Repeater { + id: itemsRepeater + model: root.menus + Loader { + id: menuItemLoader + + property var menuItem: root.isNative ? null : modelData + property bool selected: menuItem.popupVisible || itemMouseArea.pressed || menuBarLoader.openedMenuIndex === index + + sourceComponent: menuBarLoader.menuItemStyle + + MouseArea { + id: itemMouseArea + anchors.fill:parent + hoverEnabled: true + + onClicked: { + menuBarLoader.preselectMenuItem = false + menuBarLoader.openedMenuIndex = index + } + onPositionChanged: { + if ((pressed || menuMouseArea.pressed || menuBarLoader.openedMenuIndex !== -1) + && menuBarLoader.openedMenuIndex !== index) { + menuBarLoader.openedMenuIndex = index + menuBarLoader.preselectMenuItem = false + } + } + } + + Connections { + target: menuBarLoader + onOpenedMenuIndexChanged: { + if (menuBarLoader.openedMenuIndex === index) { + menuItem.showPopup(0, root.height, 0, menuItemLoader) + if (menuBarLoader.preselectMenuItem) + menuItem.currentIndex = 0 + } else { + menuItem.closeMenu() + } + } + } + + Connections { + target: menuItem + onMenuClosed: { + if (menuBarLoader.openedMenuIndex === index) + menuBarLoader.openedMenuIndex = -1 + } + } + + Binding { + target: menuItem + property: "menuBar" + value: menuBarLoader + } + } + } + } + } + } + } + ] +} diff --git a/src/controls/Page.qml b/src/controls/Page.qml new file mode 100644 index 00000000..c7d08c29 --- /dev/null +++ b/src/controls/Page.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 + +/*! + \qmltype Page + \inqmlmodule QtQuick.Controls 1.0 + \ingroup navigation + \brief A Page is an Item you can push on a PageStack + + A \a Page is the main Item pushed onto a \l PageStack. It normally contains a discrete + set of information and interaction elements meant for the user to solve a specific task, and + contains properties to use when working with a PageStack. + See \l PageStack for more information. + + \qml + \endqml +*/ + +Item { + id: root + + /*! \readonly + The status of the page. It can have one of the following values: + \list + \li \c PageStatus.Inactive: the page is not visible + \li \c PageStatus.Activating: the page is transitioning into becoming an active page on the stack + \li \c PageStatus.Active: the page is on top of the stack + \li \c PageStatus.Deactivating: the page is transitioning into becoming inactive + \endlist */ + readonly property alias status: root.__status + /*! \readonly + This property contains the PageStack the page is in. If the page is not inside + a PageStack, \a pageStack will be \c null. */ + readonly property alias pageStack: root.__pageStack + /*! \readonly + This property contains the index of the page inside \l{pageStack}{PageStack}, so + that \l{PageStack::get()}{pageStack.get(index)} will + return the page itself. If \l{Page::pageStack}{pageStack} is \c null, \a index + will be \c -1. */ + readonly property alias index: root.__index + /*! This property can be set to override the default animations used + during a page transition. To better understand how to use this + property, refer to the \l{PageStack#Transitions}{transition documentation} in PageStack. + \sa {PageStack::animations}{PageStack.animations} */ + property PageTransition pageTransition + + visible: false // PageStack will show/hide the page as needed + width: parent.width + height: parent.height + + // ********** PRIVATE API ********** + + /*! \internal */ + property int __status: PageStatus.Inactive + /*! \internal */ + property PageStack __pageStack: null + /*! \internal */ + property int __index: -1 +} diff --git a/src/controls/PageAnimation.qml b/src/controls/PageAnimation.qml new file mode 100644 index 00000000..ace0e2e4 --- /dev/null +++ b/src/controls/PageAnimation.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 + +ParallelAnimation { + id: root + /*! The name of the animation that is running. Can be one of the following: + \list + \li 'PushAnimation' + \li 'PopAnimation' + \li 'ReplaceAnimation' + \endlist + */ + property string name + /*! The page that is transitioning in. */ + property Item enterPage + /*! The page that is transitioning out */ + property Item exitPage + /*! Set to \c true if the animation is told to + fast-forward directly to its end-state */ + property bool immediate +} diff --git a/src/controls/PageStack.qml b/src/controls/PageStack.qml new file mode 100644 index 00000000..42483443 --- /dev/null +++ b/src/controls/PageStack.qml @@ -0,0 +1,991 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Private/PageStack.js" as JSArray + +/*! + \qmltype PageStack + \inherits Item + \inqmlmodule QtQuick.Controls 1.0 + + \brief The central component for building page-based applications + + PageStack implements a stack-based navigation model for an application. + A stack-based navigation model means that "pages" (discrete views of information) + are pushed onto a stack as the user navigates + deeper into the application page hierarchy. Similarily, the user can return back to + previous pages at a later point, which from a stack point of view means popping pages from the + top of the stack and re-activating them (make them visible on screen). + + Pages can - but do not have to - use \l{Page} as the root item. + \l{Page} defines a contract for how the page and the page stack works together. + Namely the page can be notified when it becomes active or inactive + through the \l{Page::status} {Page.status} property. Status will be + \c PageStatus.Activating when a + page is transitioning into being the current page on screen, and \c PageStatus.Active + once the transition stops. When it leaves the screen, it will be + \c PageStatus.Deactivating, and then \c PageStatus.Inactive. When the page is + inactive, it will be hidden. + + \section1 Using PageStack in an Application + Using the PageStack in the application is typically a simple matter of adding + the PageStack as a child to e.g the applications top-level + \l{http://doc.qt.nokia.com/latest/qml-item.html} {Item}. The stack is usually + anchored to the edges of the window, except at the top or bottom where it might + be anchored to a status bar, or some other similar UI component. + The stack can then be used by invoking its navigation methods. The first page + to show in the PageStack is commonly loaded assigning it to \l initialPage. + + \section1 Basic Navigation + There are three primary navigation operations in PageStack: push(), pop() and + replace (you replace by specifying argument \c replace to push()). + These correspond to classic stack operations where "push" adds a page to the + top of a stack, "pop" removes the top page from the stack, and "replace" is like a + pop followed by a push in that it replaces the topmost page on the stack with + a new page (but the applied transtition might be different). The topmost page + in the stack corresponds to the one that is \l{PageStack::currentPage} {current}, + i.e. the one that is visible on + screen. That means that "push" is the logical equivalent of navigating forward or + deeper into the application, "pop" is the equivalent of navigation back and + "replace" is the equivalent of replacing the current page with a different page. + + Sometimes it is necessary to go back more than a single step in the stack, e.g. + to return to a "main" page or some kind of section page in the application. + For this use case, pop() can be provided with a page to pop to. This is called + an "unwind" operation as the stack gets unwound to the specified page. If the + page is not found then the stack unwinds until there is only a single page in + the stack, which becomes the current page. To explicitly unwind to the bottom + of the stack it is recommended to use \l{pop()} {pop(null)}, though technically any + non-existent page will do. + + Given the stack [A, B, C]: + + \list + \li \l{push()}{push(D)} => [A, B, C, D] - "push" transition animation between C and D + \li pop() => [A, B] - "pop" transition animation between C and B + \li \l{push()}{push(D, replace)} => [A, B, D] - "replace" transition between C and D + \li \l{pop()}{pop(A)} => [A] - "pop" transition between C and A + \endlist + + Note that when the stack is empty, a push() will not perform a + transition animation because there is nothing to transition from (which will + typically happend during application start-up). A pop() on a stack with + depth 1 or 0 is a no-operation. If removing all pages from the stack is + needed, a separate function clear() is available. + + Calling push() returns the page that was pushed onto the stack. + Calling pop() returns the page that was popped off the stack. When pop() is + called in an unwind operation the top-most page (the first page that was + popped, which will also be the one transitioning out) is returned. + + \section1 Deep Linking + Deep linking means launching an application into a particular state. For example + a Newspaper application could be launched into showing a particular article, + bypassing the front page (and possible a section page) that would normally have + to be navigated through to get to the article in question. In terms of page + stacks deep linking means the ability to modify the state of the stack so that + you e.g. push a set of pages to the top of the stack, or that you completely reset + the stack to a given state. + + The API for deep linking in PageStack is the same as for basic navigation. If + you push an array instead of a single page then all the pages in that array will + be pushed onto the stack. The transition animation, however, will be conducted as + if only the last page in the array was pushed onto the stack. The normal semantics + of push() apply for deep linking, meaning that push() adds whatever you push onto + the stack. Note also that only the last item in the array will actually be loaded + (in case of a \l{http://doc.qt.nokia.com/latest/qml-url.html}{URL} or + \l{http://doc.qt.nokia.com/latest/qml-component.html}{Component}). + The rest will be lazy loaded as needed when entering + the screen upon subsequent calls to pop (or when requesting the page by using \a get). + + This gives us the following result, given the stack [A, B, C]: + + \list + \li \l{push()}{push([D, E, F])} => [A, B, C, D, E, F] - "push" transition animation between C and F + \li \l{push()}{push([D, E, F], replace)} => [A, B, D, E, F] - "replace" transition animation between C and F + \li clear(); \l{push()}{push([D, E, F])} => [D, E, F] - no transition animation (since the stack was empty) + \endlist + + \section1 Pushing pages + + A page you push onto the PageStack can be either a \l Page, a \l{http://doc.qt.nokia.com/latest/qml-url.html}{URL}, a + string with a URL, an \l{http://doc.qt.nokia.com/latest/qml-item.html}{Item}, or a + \l{http://doc.qt.nokia.com/latest/qml-component.html}{Component}. To push it, you assign it + to a property "page" inside a property list, and send it as argument to \l{PageStack::push}{push}: + + \code + pageStack.push({page: yourPage}) + \endcode + + The list can contain several properties that controls how the page should be pushed: + \list + \li \c page: This property is required, and holds the page you want to push. + \li \c properties: You can set a property list of QML properties that should be assigned + to the page upon push. These properties will be copied into the page at the + time the page is loaded, or about to become the current page (normally upon push). + \li \c immediate: Set this property to \c true to skip transition effects. When pushing + an array, you only need to set this property on the first element to make the + whole operation immediate. + \li \c replace: Set this property to replace the current page on the stack. When pushing + an array, you only need to set this property on the first element to replace + as many elements on the stack as inside the array. + \li \c destroyOnPop: Set this property to be explicit to whether or not PageStack should + destroy the page when its popped off the stack. By default (if \a destroyOnPop is + not specified), PageStack will destroy pages pushed as components or URLs. Pages + not destroyed will be reparented back to the original parents they had before being + pushed onto the stack, and hidden. If you need to set this property, do it with + care, so that pages are not leaked. + \endlist + + If the only argument needed is "page", you can also, as a short-hand + notation, do: + + \code + pageStack.push(yourPage). + \endcode + + You can push several pages in one go by using an array of property lists. This is + optimizing compared to pushing pages one by one, since PageStack then can load only the + last page in the list. The rest will be loaded as they are about to become + the current page (which happends when the stack is popped). The following example shows how + to push an array of pages: + + \code + pageStack.push([{page: yourPage1}, {page: yourPage2}]) + \endcode + + If inline pages/items are pushed, the page gets re-parented into an internal + container in the PageStack. When the page is later popped off, it gets + re-parented back to its original owner. If, however, a page is pushed + as a component or a URL, the actual page will be created as a page from that component. This + happens automatically when the page is about to become the current page in the stack. Ownership + over the item will then normally be taken by the PageStack. It will as such automatically + destroy the page when it is later popped off. The component that declared the page, by + contrast, remains in the ownership of the application and is not destroyed by the page stack. + You can override this behavior if needed by explicitly setting "destroyOnPop" in the list + argument given to push. + + If you specify the \c properties property to push, these properties will be copied into + the page at the time the page is loaded (in case of a component or URL), or instead when + its about to become the current page (in case of an inline item). This normally happends when + the page is pushed. The following example shows how this can be done: + + \code + pageStack.push({page: examplePage, properties: {fgcolor: "red", bgcolor: "blue"}}); + \endcode + + Note that if a page is declared in an item that is destroyed - even if a component + was used - then that page also gets destroyed. + This follows normal Qt parent-child destruction rules but sometimes comes as a surprise + for developers. In practice this means that if you declare a page B as a child of + page A and then do a replace from page A to page B, then page B will be destroyed when + page A was destroyed (as it was popped off the stack) and the application will effectively + be switching to a page that has been destroyed. + + \section1 Lifecycle + The page lifecycle goes from instantiation to inactive, activating, active, deactivating, + inactive, and when no longer needed, destruction. + It can move any number of times between inactive and active. When a page is activated, + it's visible on the screen and is considered to be the current item. A page + in a page stack that is not visible is not activated, even if the page is currently the + top-most page in the stack. When the stack becomes visible the page that is top-most gets + activated. Likewise if the page stack is then hidden the top-most page would be deactivated. + Popping the page off the top of the stack at this point would not result in further + deactivation since the page is not active. + + There is a \l{Page::status}{status} property that tracks the lifecycle. The value of status is + an enumeration with values \c PageStatus.Inactive, \c PageStatus.Activating, \c PageStatus.Active + and \c PageStatus.Deactivating. Combined with the normal \c Component.onComplete and + \c Component.onDestruction signals the entire lifecycle is thus: + + \list + \li Created: Component.onCompleted() + \li Activating: onStatusChanged (status is PageStatus.Activating) + \li Acivated: onStatusChanged (status is PageStatus.Active) + \li Deactivating: onStatusChanged (status is PageStatus.Deactivating) + \li Deactivated: onStatusChanged (status is PageStatus.Inactive) + \li Destruction: Component.onDestruction() + \endlist + + \section1 Finding Pages + Sometimes it is necessary to search for a page, e.g. in order to unwind the stack to + a page to which the application does not have a reference. This is facilitated using a + function find() in the page stack. The find() function takes a callback function as its + only argument. The callback gets invoked for each page in the stack (starting at the top). + If the callback returns true then it signals that a match has been found and the find() + function returns that page. If the callback fails to return true (i.e. no match is found) + then find() returns \c null. + + The code below searches for a page in the stack that has a name "foo" and then unwinds to + that page. Note that since find() returns null if no page is found and since pop unwinds to + the bottom of the stack if null is given as the target page, the code works well even in the + case that no matching page was found. + + \code + pageStack.pop(pageStack.find(function(page) { + return page.name == "foo"; + })); + \endcode + + You can also get to a page in the page stack using get(index). You should use + this function if your page depends on another page in the stack, as the function will + ensure that the page at the given index gets loaded before it is returned. + + \code + previousPage = pageStack.get(myPage.index - 1)); + \endcode + + \section1 Transitions + + A transition is performed whenever a page is pushed or popped, and consists of + two pages: enterPage and exitPage. The pagestack itself will never move pages + around, but instead delegate the job to an external animation set by the style + or the application developer. How pages should visually enter and leave the stack + is therefore completely controlled from the outside. + + When the transition starts, the pagestack will search for an animation that + matches the operation executed. There are three animations to choose + from: pushAnimation, popAnimation, and replaceAnimation. Each implements how + enterPage should animate in, and exitPage out. The animations are + collected inside a PageTransition object assigned to + \l {PageStack::pageTransition}{pageTransition}. By default, popAnimation and + replaceAnimation will be the same as PushAnimation, unless you set them + to something else. + + A simple fade transition could be implemented as: + + \qml + PageStack { + pageTransition: PageTransition { + function cleanupAnimation(properties) + { + properties.exitPage.opacity = 1 + } + + property Component pushAnimation: PageAnimation { + PropertyAnimation { + target: enterPage + property: "opacity" + from: 0 + to: 1 + } + PropertyAnimation { + target: exitPage + property: "opacity" + from: 1 + to: 0 + } + } + } + } + \endqml + + PushAnimation needs to inherit from PageAnimation, which is a ParallelAnimation that + contains the properties \c enterPage and \c exitPage. You set the target of your + inner animations to those pages. Since the same page instance can be pushed several + times to a pagestack, and since pages also can override transitions, your PageTransition + always need to override + \l {PageTransition::cleanupAnimation(properties)}{PageTransition.cleanupAnimation(properties)}. + Implement this function to reset any properties animated on the exitPage so that later + transitions can expect the pages to be in a default state. + + A more complex example could look like the following. Here, the pages slides in lying on the side before + they are rotated up in an upright position: + + \qml + PageStack { + pageTransition: PageTransition { + function cleanupAnimation(properties) + { + properties.exitPage.x = 0 + properties.exitPage.rotation = 0 + } + + property Component pushAnimation: PageAnimation { + SequentialAnimation { + ScriptAction { + script: enterPage.rotation = 90 + } + PropertyAnimation { + target: enterPage + property: "x" + from: enterPage.width + to: 0 + } + PropertyAnimation { + target: enterPage + property: "rotation" + from: 90 + to: 0 + } + } + PropertyAnimation { + target: exitPage + property: "x" + from: 0 + to: -exitPage.width + } + } + } + } + \endqml + + A single Page can also override the transition to use when itself is pushed or popped. This can + be done by just assigning another PageTransition object to \l{Page::pageTransition}{Page.pageTransition}. + + \section2 Advanced usage + + After PageStack finds the correct transition to use (it first checks + \l{Page::pageTransition}{Page.pageTransition}, then \l {PageStack::pageTransition}{pageTransition}) + it calls \l {PageTransition::getAnimation(properties)}{PageTransition.getAnimation(properties)}. + The base implementation of this function just looks for a property named \c properties.name inside + itself (root), which is how it finds \c {property Component pushAnimation} in the examples above. + + \code + function getAnimation(properties) + { + return root[properties.name] + } + \endcode + + You can override this function for your transition if you need extra logic to decide which + animation to run. You could for example introspect the pages, and return different animations + depending on the their internal state. PageStack will expect you to return a Component that + contains a PageAnimation, or a PageAnimation directly. The former is easier, as PageStack will + then create the animation and later destroy it when it's done, while avoiding any sideeffects + caused by the animation being alive long after it ran. Returning a PageAnimation directly + can be useful if you need to write some sort of animation caching for performance reasons. + As an optimization, you can also return \c null to signal that you just want to show/hide the pages + immediately without creating or running any animations. + + \c properties contains the same properties that will be assigned to the PageAnimation object by + PageStack. In fact, you can add more properties to this object during the call + if you need to initialize additional properties of your custom PageAnimation when the returned + component is instanciated. + + The following example shows how you can decide run-time which animation to use: + + \qml + PageTransition { + function getAnimation(properties) + { + return (properties.enterPage.index % 2) ? horizontalAnimation : verticalAnimation + } + + function cleanupAnimation(properties) + { + properties.exitPage.x = 0 + properties.exitPage.y = 0 + } + + property Component horizontalAnimation: PageAnimation { + PropertyAnimation { + target: enterPage + property: "x" + from: target.width + to: 0 + duration: 300 + } + PropertyAnimation { + target: exitPage + property: "x" + from: 0 + to: target.width + duration: 300 + } + } + + property Component verticalAnimation: PageAnimation { + PropertyAnimation { + target: enterPage + property: "y" + from: target.height + to: 0 + duration: 300 + } + PropertyAnimation { + target: exitPage + property: "y" + from: 0 + to: target.height + duration: 300 + } + } + } + \endqml +*/ + +Item { + id: root + + /*! \qmlproperty int QtQuickComponents.Mt1.0::PageStack::depth + \readonly + The number of pages currently pushed onto the stack. + */ + readonly property alias depth: root.__depth + + /*! \qmlproperty Item QtQuickComponents.Mt1.0::PageStack::currentPage + \readonly + The currently top-most page in the stack. + */ + readonly property alias currentPage: root.__currentPage + + /*! The first \l Page that should be shown when the PageStack is created. + \a initialPage can take same value as the first argument to \l{PageStack::push()} + {PageStack.push()}. Note that this is just a convenience for writing + \c{Component.onCompleted: pageStack.push(myInitialPage)} + + Examples: + + \list + \li initialPage: Qt.resolvedUrl("MyPage.qml") + \li initialPage: myItem + \li initialPage: {"page" : Qt.resolvedUrl("MyPage.qml"), "properties" : {"color" : "red"}} + \endlist + \sa push + */ + property variant initialPage: null + + /*! \readonly + \a busy is \c true if a page transition is running, and \c false otherwise. */ + readonly property bool busy: __currentTransition !== null + + /*! The animations to use for page transitions. + For better understanding on how to apply custom page transitions, read \l{Transitions}. + \sa {Page::animations}{Page.transitions} */ + property PageTransition pageTransition: PageSlideTransition {} + + /*! Pushes a page onto the stack. The function takes a property list as argument, which + should contain one or more of the following properties: + \list + \li \c page: This property is required, and holds the page you want to push. + It can be a \l Page, a \l{http://doc.qt.nokia.com/latest/qml-url.html}{URL}, a string with a + URL, an \l{http://doc.qt.nokia.com/latest/qml-item.html}{Item}, a + \l{http://doc.qt.nokia.com/latest/qml-component.html}{Component}. + \li \c properties: You can set a property list of QML properties that should be assigned + to the page upon push. These properties will be copied into the page at the + time the page is loaded (in case of a component or URL), or else the first time it + becomes the current page (normally upon push). + \li \c immediate: Set this property to \c true to skip transition effects. When pushing + an array, you only need to set this property on the first element to make the + whole operation immediate. + \li \c replace: Set this property to replace the current page on the stack. When pushing + an array, you only need to set this property on the first element to replace + as many elements on the stack as inside the array. + \li \c destroyOnPop: Set this property to be explicit to whether or not PageStack should + destroy the page when its popped off the stack. By default (if \a destroyOnPop is + not specified), PageStack will destroy pages pushed as components or URLs. Pages + not destroyed will be reparented back to the original parents they had before being + pushed onto the stack, and hidden. If you need to set this property, do it with + care, so that pages are not leaked. + \endlist + + You can also push an array of pages (property lists) if you need to push several pages + in one go. A transition will then only occur between the current page and the last + page in the list. The other pages will be deferred loaded until needed. + + Examples: + \list + \li pageStack.push({page:aPage}) + \li pageStack.push({page:aURL, immediate: true, replace: true}) + \li pageStack.push({page:aRectangle, properties:{color:"red"}}) + \li pageStack.push({page:aComponent, properties:{color:"red"}}) + \li pageStack.push({page:aComponent.createObject(), destroyOnPop:true}) + \li pageStack.push([{page:aPage, immediate:true}, {page:aURL}]) + \endlist + + Note: If the only argument needed is "page", you can also, as a short-hand + notation, do: \c{pageStack.push(aPage)}. + + Returns the page that became current. + + \sa initialPage + \sa {Pushing pages} + */ + function push(page) { + // Note: we support two different APIs in this function; The old meego API, and + // the new "property list" API. Hence the reason for hiding the fact that you + // can pass more arguments than shown in the signature: + if (__recursionGuard(true)) + return + var properties = arguments[1] + var immediate = arguments[2] + var replace = arguments[3] + var arrayPushed = (page instanceof Array) + var firstPage = arrayPushed ? page[0] : page + immediate = (immediate || JSArray.pageStack.length === 0) + + if (firstPage && firstPage.page && firstPage.hasOwnProperty("x") === false) { + // Property list API used: + immediate = immediate || firstPage.immediate + replace = replace || firstPage.replace + } + + // Create, and push, a new javascript object, called "element", onto the stack. + // This element contains all the information necessary to construct the page, and + // will, after loaded, also contain the loaded page: + if (arrayPushed) { + if (page.length === 0) + return + var outElement = replace ? JSArray.pop() : JSArray.current() + for (var i=0; i<page.length; ++i) + JSArray.push({"pageComponent" : page[i], loaded: false, index: __depth, properties: properties}); + } else { + outElement = replace ? JSArray.pop() : JSArray.current() + JSArray.push({"pageComponent" : page, loaded: false, index: __depth, properties: properties}) + } + + var currentElement = JSArray.current() + var transition = { + inElement: currentElement, + outElement: outElement, + transitionElement: currentElement, + immediate: immediate, + replace: replace, + push: true + } + __performPageTransition(transition) + __recursionGuard(false) + return __currentPage + } + + /*! Pops one or more pages off the stack. The function takes a property list as argument + which can contain one or more of the following properties: + \list + \li \c page: If specified, all pages down to (but not including) \a page will be + popped off. if \a page is \c null, all pages down to (but not including) the + first page will be popped. If not specified, only the current page will be + popped. + \li \c immediate: Set this property to \c true to skip transition effects. + \endlist + + Examples: + \list + \li pageStack.pop() + \li pageStack.pop({page:somePage, immediate: true}) + \li pageStack.pop({immediate: true}) + \li pageStack.pop(null) + \endlist + + Note: If the only argument needed is "page", you can also, as a short-hand + notation, do: \c{pageStack.pop(aPage)}. + + Returns the page that was popped off + \sa clear() + */ + function pop(page) { + if (__depth <= 1) + return null + if (page && page.hasOwnProperty("x") === false) { + // Property list API used: + var immediate = (page.immediate === true) + page = page.page + } else { + immediate = (arguments[1] === true) + } + + if (page === __currentPage) + return + + if (__recursionGuard(true)) + return + + var outElement = JSArray.pop() + var transitionElement = outElement + var inElement = JSArray.current() + + if (__depth > 1 && page !== undefined && page !== inElement.page) { + // Pop from the top until we find 'page', and return the corresponding + // element. Skip all non-loaded pages (except the first), since no one + // has any references to such pages anyway: + while (__depth > 1 && !JSArray.current().loaded) + JSArray.pop() + inElement = JSArray.current() + while (__depth > 1 && page !== inElement.page) { + JSArray.pop() + __cleanup(inElement) + while (__depth > 1 && !JSArray.current().loaded) + JSArray.pop() + inElement = JSArray.current() + } + } + + var transition = { + inElement: inElement, + outElement: outElement, + transitionElement: transitionElement, + immediate: immediate, + replace: false, + push: false + } + __performPageTransition(transition) + __recursionGuard(false) + return outElement.page; + } + + /*! Remove all pages from the stack. No animations will be applied. */ + function clear() { + if (__recursionGuard(true)) + return + if (__currentTransition) + __currentTransition.animation.complete() + __currentPage = null + var count = __depth + for (var i=0; i<count; ++i) { + var element = JSArray.pop() + if (element.page) + __cleanup(element); + } + __recursionGuard(false) + } + + /*! Search for a specific page inside the stack. \a func will + be called for each page in the stack (with the page as argument) + until the function returns true. Return value will be the page found. E.g: + find(function(page, index) { return page.isTheOne }) + Set \a onlySearchLoadedPages to \c true to not load pages that are + not loaded into memory */ + function find(func, onlySearchLoadedPages) { + for (var i=__depth-1; i>=0; --i) { + var element = JSArray.pageStack[i]; + if (onlySearchLoadedPages !== true) + __loadElement(element) + else if (!element.page) + continue + if (func(element.page)) + return element.page + } + return null; + } + + /*! Returns the page at position \a index in + the page stack. If \a dontLoad is true, the + page will not be forced to load (and \c null + will be returned if not yet loaded) */ + function get(index, dontLoad) + { + if (index < 0 || index >= JSArray.pageStack.length) + return null + var element = JSArray.pageStack[index] + if (dontLoad !== true) { + __loadElement(element) + return element.page + } else if (element.page) { + return element.page + } else { + return null + } + } + + /*! Immediately completes any ongoing transition. + /sa Animation.complete + */ + function completeTransition() + { + if (__recursionGuard(true)) + return + if (__currentTransition) + __currentTransition.animation.complete() + __recursionGuard(false) + } + + /********* DEPRECATED API *********/ + + /*! \internal + \deprecated Use Push() instead */ + function replace(page, properties, immediate) { + push(page, properties, immediate, true) + } + + /********* PRIVATE API *********/ + + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + + /*! \internal The currently top-most page in the stack. */ + property Item __currentPage: null + /*! \internal The number of pages currently pushed onto the stack. */ + property int __depth: 0 + /*! \internal Stores the transition info while a transition is ongoing */ + property var __currentTransition: null + /*! \internal Stops the user from pushing pages while preparing a transition */ + property bool __guard: false + + /*! \internal */ + Component.onCompleted: { + if (initialPage) + push(initialPage) + } + + /*! \internal */ + Component.onDestruction: { + if (__currentTransition) + __currentTransition.animation.complete() + __currentPage = null + } + + /*! \internal */ + function __recursionGuard(use) + { + if (use && __guard) { + console.warn("Warning: PageStack: You cannot push/pop recursively!") + console.trace() + return true + } + __guard = use + } + + /*! \internal */ + function __loadElement(element) + { + if (element.loaded) { + if (!element.page) { + element.page = invalidPageReplacement.createObject(root) + element.page.text = "\nError: The page has been deleted outside PageStack!" + } + return + } + if (!element.pageComponent) { + element.page = invalidPageReplacement.createObject(root) + element.page.text = "\nError: Invalid page (page was 'null'). " + + "This might indicate that the page was deleted outside PageStack!" + return + } + + var comp = __resolvePageComponent(element.pageComponent, element) + + // Assign properties to Page: + if (!element.properties) + element.properties = {} + element.properties.__index = element.index + element.properties.__pageStack = root + + if (comp.hasOwnProperty("createObject")) { + if (comp.status === Component.Error) { + element.page = invalidPageReplacement.createObject(root) + element.page.text = "\nError: Could not load: " + comp.errorString() + } else { + element.page = comp.createObject(root, element.properties) + // Destroy pages we create unless the user specified something else: + if (!element.hasOwnProperty("destroyOnPop")) + element.destroyOnPop = true + } + } else { + // comp is already an Item, so just reparent it into the pagestack: + element.page = comp + element.originalParent = parent + element.page.parent = root + for (var prop in element.properties) { + if (element.page.hasOwnProperty(prop)) + element.page[prop] = element.properties[prop]; + } + // Do not destroy pages we didn't create, unless the user specified something else: + if (!element.hasOwnProperty("destroyOnPop")) + element.destroyOnPop = false + } + + delete element.properties.__index + delete element.properties.__pageStack + element.loaded = true + } + + /*! \internal */ + function __resolvePageComponent(unknownObjectType, element) + { + // We need this extra resolve function since we dont really + // know what kind of object the user pushed. So we try to + // figure it out by inspecting the object: + if (unknownObjectType.hasOwnProperty("createObject")) { + return unknownObjectType + } else if (typeof unknownObjectType == "string") { + return Qt.createComponent(unknownObjectType) + } else if (unknownObjectType.hasOwnProperty("x")) { + return unknownObjectType + } else if (unknownObjectType.hasOwnProperty("page")) { + // INVARIANT: user pushed a JS-object + element.properties = unknownObjectType.properties + if (!unknownObjectType.page) + unknownObjectType.page = invalidPageReplacement + if (unknownObjectType.hasOwnProperty("destroyOnPop")) + element.destroyOnPop = unknownObjectType.destroyOnPop + return __resolvePageComponent(unknownObjectType.page, element) + } else { + // We cannot determine the type, so assume its a URL: + return Qt.createComponent(unknownObjectType) + } + } + + /*! \internal */ + function __cleanup(element) { + // INVARIANT: element has been removed from JSArray. Destroy its + // page, or reparent it back to the parent it had before it was pushed: + var page = element.page + if (element.destroyOnPop) { + page.destroy() + } else { + // Mark the page as no longer part of the PageStack. It + // might reenter on pop if pushed several times: + page.visible = false + __setPageStatus(page, PageStatus.Inactive) + if (page.hasOwnProperty("__pageStack")) + page.__pageStack = null + if (page.hasOwnProperty("__index")) + page.__index = -1 + if (element.originalParent) + page.parent = element.originalParent + } + } + + /*! \internal */ + function __setPageStatus(page, status) { + if (page.hasOwnProperty("__status")) + page.__status = status + } + + /*! \internal */ + function __performPageTransition(transition) + { + // Animate page in "outElement" out, and page in "inElement" in. Set a guard to protect + // the user from pushing new pages on signals that will fire while preparing for the transition + // (e.g Page.onCompleted, Page.onStatusChanged, Page.onIndexChanged etc). Otherwise, we will enter + // this function several times, which causes the pages to be half-way updated. + if (__currentTransition) + __currentTransition.animation.complete() + __loadElement(transition.inElement) + + transition.name = transition.replace ? "replaceAnimation" : (transition.push ? "pushAnimation" : "popAnimation") + var enterPage = transition.inElement.page + transition.enterPage = enterPage + + // Since a page can be pushed several times, we need to update its properties: + enterPage.parent = root + if (enterPage.hasOwnProperty("__pageStack")) + enterPage.__pageStack = root + if (enterPage.hasOwnProperty("__index")) + enterPage.__index = transition.inElement.index + __currentPage = enterPage + + if (!transition.outElement) { + // A transition consists of two pages, but we got just one. So just show the page: + enterPage.visible = true + __setPageStatus(enterPage, PageStatus.Activating) + __setPageStatus(enterPage, PageStatus.Active) + return + } + + var exitPage = transition.outElement.page + transition.exitPage = exitPage + if (enterPage === exitPage) + return + + __searchForAnimationIn(transition.transitionElement.page, transition) + if (!transition.animation) + __searchForAnimationIn(root, transition) + if (!transition.animation) { + console.warn("Warning: PageStack: no", transition.name, "found!") + return + } + + if (enterPage.anchors.fill || exitPage.anchors.fill) + console.warn("Warning: PageStack: cannot transition a page that is anchored!") + + __currentTransition = transition + __setPageStatus(exitPage, PageStatus.Deactivating) + enterPage.visible = true + __setPageStatus(enterPage, PageStatus.Activating) + transition.animation.runningChanged.connect(animationFinished) + transition.animation.start() + // NB! For empty animations, "animationFinished" is already + // executed at this point, leaving __animation === null: + if (transition.immediate === true && transition.animation) + transition.animation.complete() + } + + /*! \internal */ + function __searchForAnimationIn(obj, transition) + { + var t = obj.pageTransition + if (t) { + transition.pageTransition = t + transition.properties = { + "name":transition.name, + "enterPage":transition.enterPage, + "exitPage":transition.exitPage, + "immediate":transition.immediate } + var anim = t.getAnimation(transition.properties) + if (anim.createObject) { + anim = anim.createObject(null, transition.properties) + anim.runningChanged.connect(function(){ if (anim.running === false) anim.destroy() }) + } + transition.animation = anim + } + } + + /*! \internal */ + function animationFinished() + { + if (!__currentTransition || __currentTransition.animation.running) + return + + __currentTransition.animation.runningChanged.disconnect(animationFinished) + __currentTransition.exitPage.visible = false + __setPageStatus(__currentTransition.exitPage, PageStatus.Inactive); + __setPageStatus(__currentTransition.enterPage, PageStatus.Active); + __currentTransition.properties.animation = __currentTransition.animation + __currentTransition.pageTransition.cleanupAnimation(__currentTransition.properties) + + if (!__currentTransition.push || __currentTransition.replace) + __cleanup(__currentTransition.outElement) + + __currentTransition = null + } + + /*! \internal */ + property Component invalidPageReplacement: Component { + Text { + width: parent.width + height: parent.height + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + } +} diff --git a/src/controls/PageTransition.qml b/src/controls/PageTransition.qml new file mode 100644 index 00000000..bd69dc5a --- /dev/null +++ b/src/controls/PageTransition.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 + +/*! + \qmltype PageTransition + \inqmlmodule QtQuick.Controls 1.0 + + \brief The component for managing page transitions + + See the documentation for the \l {QtQuick.Controls1::PageStack} {PageStack} + component. + +*/ +QtObject { + id: root + + function getAnimation(properties) + { + return root[properties.name] + } + + function cleanupAnimation(properties) + { + console.warn("Warning: PageTransition: the current transition did not override " + + "cleanupAnimation(properties). This can cause the exit page to " + + "be left in a state that makes it unusable for further usage!") + } + + property Component pushAnimation: PageAnimation {} + property Component popAnimation: root["pushAnimation"] + property Component replaceAnimation: root["pushAnimation"] +} diff --git a/src/controls/ProgressBar.qml b/src/controls/ProgressBar.qml new file mode 100644 index 00000000..9d67b690 --- /dev/null +++ b/src/controls/ProgressBar.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype ProgressBar + \inqmlmodule QtQuick.Controls 1.0 + \ingroup indicators + \brief A progress bar + + The ProgressBar is used to give an indication of the progress of an operation. + \l value is updated regularly and must be between \l minimumValue and \l maximumValue. + +*/ + +Control { + id: progressbar + + /*! This property holds the progress bar's current value. + Attempting to change the current value to one outside the minimum-maximum + range has no effect on the current value. + The default value is \c 0 + */ + property real value: 0 + + /*! This property is the progress bar's minimum value + The \l value is clamped to this value. + The default value is \c 0 + */ + property real minimumValue: 0 + + /*! This property is the progress bar's maximum value + The \l value is clamped to this value. + If maximumValue is smaller than \l minimumValue, \l minimumValue will be enforced. + The default value is \c 1 + */ + property real maximumValue: 1 + + /*! This property toggles indeterminate mode. + When the actual progress is unknown, use this option. + The progress bar will be animated as a busy indicator instead. + The default value is \c false + */ + property bool indeterminate: false + + /*! \qmlproperty enum orientation + + This property holds the orientation of the progress bar. + + \list + \li Qt.Horizontal - Horizontal orientation. (Default) + \li Qt.Vertical - Vertical orientation. + \endlist + */ + property int orientation: Qt.Horizontal + + /*! \internal */ + style: Qt.createComponent(Settings.THEME_PATH + "/ProgressBarStyle.qml", progressbar) + + /*! \internal */ + onMaximumValueChanged: setValue(value) + /*! \internal */ + onMinimumValueChanged: setValue(value) + /*! \internal */ + Component.onCompleted: setValue(value) + /*! \internal */ + onValueChanged: setValue(value) + + Accessible.role: Accessible.ProgressBar + Accessible.name: value + + implicitWidth: orientation === Qt.Horizontal ? 200 : (__panel ? __panel.implicitHeight : 0) + implicitHeight: orientation === Qt.Horizontal ? (__panel ? __panel.implicitHeight : 0) : 200 + + /* \internal */ + function setValue(v) { + var newval = parseFloat(v) + if (!isNaN(newval)) { + // we give minimumValue priority over maximum if they are inconsistent + if (newval > maximumValue) { + if (maximumValue >= minimumValue) + newval = maximumValue; + else + newval = minimumValue + } else if (v < minimumValue) { + newval = minimumValue + } + value = newval + } + } +} diff --git a/src/controls/RadioButton.qml b/src/controls/RadioButton.qml new file mode 100644 index 00000000..ce819164 --- /dev/null +++ b/src/controls/RadioButton.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import "Styles/Settings.js" as Settings + +// jb : Size should not depend on background, we should make it consistent + +/*! + \qmltype RadioButton + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief A radio button with a text label + + A RadioButton is an option button that can be switched on (checked) or off + (unchecked). Radio buttons typically present the user with a "one of many" + choice. In a group of radio buttons, only one radio button at a time can be + checked; if the user selects another button, the previously selected button + is switched off. +*/ + +AbstractCheckable { + id: radioButton + + Accessible.role: Accessible.RadioButton + + /*! + The style that should be applied to the radio button. Custom style + components can be created with: + + \codeline Qt.createComponent("path/to/style.qml", radioButtonId); + */ + style: Qt.createComponent(Settings.THEME_PATH + "/RadioButtonStyle.qml", radioButton) + + __cycleStatesHandler: function() { checked = !checked; } +} diff --git a/src/controls/ScrollArea.qml b/src/controls/ScrollArea.qml new file mode 100644 index 00000000..8a48303e --- /dev/null +++ b/src/controls/ScrollArea.qml @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype ScrollArea + \inqmlmodule QtQuick.Controls 1.0 + \ingroup navigation + \brief The ScrollArea class provides a scrolling view onto another Item. + + A ScrollArea can be used either instead of a \l Flickable or to decorate an + existing Flickable. Depending on the platform it will add scroll bars and + a content frame. + + Only one Item can be a direct child of the ScrollArea and the child is implicitly anchored + to fill the scroll view. + + Example: + \code + ScrollArea { + Image { imageSource: "largeImage.png" } + } + \endcode + + In the previous example the Image item will implicitly get scroll behavior as if it was + used within a \l Flickable. The width and height of the child item will be used to + define the size of the content area. + + Example: + \code + ScrollArea { + ListView { + ... + } + } + \endcode + + In this case the content size of the ScrollArea will simply mirror that of its contained + \l flickableItem. + +*/ + +FocusScope { + id: root + implicitWidth: 200 + implicitHeight: 100 + + /*! + This property tells the scroll area if it should render + a frame around it's content. + + The default value is \c false + */ + property bool frame: false + + /*! + This property controls if there should be a highlight + around the frame when the ScrollArea has input focus. + + The default value is \c false + + \note This property is only applicable on some platforms, such + as Mac OS. + */ + property bool highlightOnFocus: false + + /*! + \qmlproperty Item ScrollArea::viewport + + The viewport determines the current "window" on to the contentItem. + In other words it clips it and the size of the viewport tells you + how much of the content area is visible. + */ + property alias viewport: viewportItem + + /*! + \qmlproperty Item ScrollArea::flickableItem + + The flickableItem of the ScrollArea. If the contentItem provided + to the ScrollArea is a Flickable, it will be the \l contentItem. + */ + readonly property alias flickableItem: internal.flickableItem + + /*! + The contentItem of the ScrollArea. This is set by the user. + + Note that the definition of contentItem is somewhat different to that + of a Flickable, where the contentItem is implicitly created. + */ + default property Item contentItem + + /*! \internal */ + property Item __scroller: scroller + /*! \internal */ + property int __scrollBarTopMargin: 0 + /*! \internal */ + property alias horizontalScrollBar: scroller.horizontalScrollBar + /*! \internal */ + property alias verticalScrollBar: scroller.verticalScrollBar + + /*! \internal */ + onContentItemChanged: { + + if (contentItem.hasOwnProperty("contentY") && // Check if flickable + contentItem.hasOwnProperty("contentHeight")) { + internal.flickableItem = contentItem // "Use content if it is a flickable + } else { + internal.flickableItem = flickableComponent.createObject(this) + contentItem.parent = flickableItem.contentItem + } + internal.flickableItem.parent = viewportItem + internal.flickableItem.anchors.fill = viewportItem + } + + + children: Item { + id: internal + + property Flickable flickableItem + + Binding { + target: flickableItem + property: "contentHeight" + when: contentItem !== flickableItem + value: contentItem ? contentItem.height : 0 + } + + Binding { + target: flickableItem + when: contentItem !== flickableItem + property: "contentWidth" + value: contentItem ? contentItem.width : 0 + } + + Connections { + target: flickableItem + + onContentYChanged: { + scroller.blockUpdates = true + scroller.verticalScrollBar.value = flickableItem.contentY + scroller.blockUpdates = false + } + + onContentXChanged: { + scroller.blockUpdates = true + scroller.horizontalScrollBar.value = flickableItem.contentX + scroller.blockUpdates = false + } + + } + + anchors.fill: parent + + Component { + id: flickableComponent + Flickable {} + } + + WheelArea { + id: wheelArea + parent: flickableItem + + // ### Note this is needed due to broken mousewheel behavior in Flickable. + + anchors.fill: parent + + property int acceleration: 40 + property int flickThreshold: 20 + property double speedThreshold: 3 + property double ignored: 0.001 // ## flick() does not work with 0 yVelocity + property int maxFlick: 400 + + property bool horizontalRecursionGuard: false + property bool verticalRecursionGuard: false + + horizontalMaximumValue: flickableItem ? flickableItem.contentWidth - viewport.width : 0 + verticalMaximumValue: flickableItem ? flickableItem.contentHeight - viewport.height : 0 + + Connections { + target: flickableItem + onContentYChanged: { + wheelArea.verticalRecursionGuard = true + wheelArea.verticalValue = flickableItem.contentY + wheelArea.verticalRecursionGuard = false + } + onContentXChanged: { + wheelArea.horizontalRecursionGuard = true + wheelArea.horizontalValue = flickableItem.contentX + wheelArea.horizontalRecursionGuard = false + } + } + + onVerticalValueChanged: { + if (!verticalRecursionGuard) { + if (flickableItem.contentY < flickThreshold && verticalDelta > speedThreshold) { + flickableItem.flick(ignored, Math.min(maxFlick, acceleration * verticalDelta)) + } else if (flickableItem.contentY > flickableItem.contentHeight + - flickThreshold - viewport.height && verticalDelta < -speedThreshold) { + flickableItem.flick(ignored, Math.max(-maxFlick, acceleration * verticalDelta)) + } else { + flickableItem.contentY = verticalValue + } + } + } + + onHorizontalValueChanged: { + if (!horizontalRecursionGuard) + flickableItem.contentX = horizontalValue + } + } + + ScrollAreaHelper { + id: scroller + anchors.fill: parent + property int frameWidth: frame ? styleitem.pixelMetric("defaultframewidth") : 0 + property bool outerFrame: !frame || !styleitem.styleHint("frameOnlyAroundContents") + property int scrollBarSpacing: outerFrame ? 0 : styleitem.pixelMetric("scrollbarspacing") + property int verticalScrollbarOffset: verticalScrollBar.visible && !verticalScrollBar.isTransient ? + verticalScrollBar.width + scrollBarSpacing : 0 + property int horizontalScrollbarOffset: horizontalScrollBar.visible && !horizontalScrollBar.isTransient ? + horizontalScrollBar.height + scrollBarSpacing : 0 + + StyleItem { + id: styleitem + elementType: "frame" + sunken: true + visible: frame + anchors.fill: parent + anchors.rightMargin: scroller.outerFrame ? 0 : scroller.verticalScrollbarOffset + anchors.bottomMargin: scroller.outerFrame ? 0 : scroller.horizontalScrollbarOffset + } + + Item { + id: viewportItem + anchors.fill: styleitem + anchors.margins: scroller.frameWidth + anchors.rightMargin: scroller.frameWidth + (scroller.outerFrame ? scroller.verticalScrollbarOffset : 0) + anchors.bottomMargin: scroller.frameWidth + (scroller.outerFrame ? scroller.horizontalScrollbarOffset : 0) + clip: true + } + } + FocusFrame { visible: highlightOnFocus && area.activeFocus } + } +} diff --git a/src/controls/Slider.qml b/src/controls/Slider.qml new file mode 100644 index 00000000..9c274463 --- /dev/null +++ b/src/controls/Slider.qml @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype Slider + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief Slider provides a vertical or horizontal slider control. + + The slider is the classic control for providing a bounded value. It lets + the user move a slider handle along a horizontal or vertical groove + and translates the handle's position into a value within the legal range. + + \code + Slider { + onValueChanged: print(value) + } + \endcode + + The Slider value is by default in the range [0, 1]. If integer values are + needed, you can set the \l stepSize. +*/ + +Control { + id: slider + + /*! + \qmlproperty enum Slider::orientation + + This property holds the layout orientation of the slider. + The default value is \c Qt.Horizontal + */ + property int orientation: Qt.Horizontal + + /*! + \qmlproperty double Slider::minimumValue + + This property holds the minimum value of the Slider + The default value is \c 0.0 + */ + property alias minimumValue: range.minimumValue + + /*! + \qmlproperty double Slider::maximumValue + + This property holds the maximum value of the Slider + The default value is \c 1.0 + */ + property alias maximumValue: range.maximumValue + + /*! \internal */ + property alias inverted: range.inverted + + + /*! + \qmlproperty bool Slider::updateValueWhileDragging + + This property indicates if the current \l value should update while + the user is moving the slider handle or only when the button has been released. + The property can for instance be used if changing the slider value can be + time consuming. + + The default value is \c true + */ + property bool updateValueWhileDragging: true + + /*! + \qmlproperty bool Slider::pressed + + This property indicates if slider handle is currently being pressed + */ + property alias pressed: mouseArea.pressed + + /*! + \qmlproperty double Slider::stepSize + + This property indicates the slider step size. + + A value of 0 indicates that the value of the slider operates in a + continuous range between \l minimumValue and \l maximumValue. + + Any non 0 value indicates a discrete stepSize. The following example + will generate a slider with integer values in the range [0-5] + + \qml + Slider { + maximumValue: 5.0 + stepSize: 1.0 + } + \endqml + + The default value is \c 0 + */ + property alias stepSize: range.stepSize + + /*! + \qmlproperty double Slider::value + + This property holds the current value of the Slider + The default value is \c 0.0 + */ + property alias value: range.value + + /*! \internal */ + property bool containsMouse: mouseArea.containsMouse + + /*! + \qmlproperty bool Slider::activeFocusOnPress + + This property indicates if the Slider should receive active focus when + pressed. + */ + property bool activeFocusOnPress: false + + /*! + \qmlproperty bool Slider::tickmarksEnabled + + This property indicates if the Slider should display tickmarks + at step intervals. + + The default value is \c false + */ + property bool tickmarksEnabled: false + + /*! \internal */ + property string tickPosition: "Below" // "Above", "Below", "BothSides" + + Accessible.role: Accessible.Slider + Accessible.name: value + + /*! + \qmlmethod Slider::formatValue + + This method returns the current slider value in a way that is more suitable + for user display, such as the \l value rounded to only two decimal places. + + By default this function returns the nearest \c int value. + */ + + function formatValue(v) { + return Math.round(v); + } + + /* \internal */ + style: Qt.createComponent(Settings.THEME_PATH + "/SliderStyle.qml", slider) + + Keys.onRightPressed: value += (maximumValue - minimumValue)/10.0 + Keys.onLeftPressed: value -= (maximumValue - minimumValue)/10.0 + + RangeModel { + id: range + minimumValue: 0.0 + maximumValue: 1.0 + value: 0 + stepSize: 0.0 + inverted: false + + positionAtMinimum: 0 + positionAtMaximum: slider.width + } + + Item { id: fakeHandle } + + MouseArea { + id: mouseArea + + hoverEnabled: true + anchors.centerIn: parent + + width: parent.width + height: parent.height + + drag.target: fakeHandle + drag.axis: Drag.XAxis + drag.minimumX: range.positionAtMinimum + drag.maximumX: range.positionAtMaximum + + onPressed: { + if (activeFocusOnPress) + slider.focus = true; + + // Clamp the value + var newX = Math.max(mouse.x, drag.minimumX); + newX = Math.min(newX, drag.maximumX); + + // Debounce the press: a press event inside the handler will not + // change its position, the user needs to drag it. + + // Note this really messes up things for scrollbar + // if (Math.abs(newX - fakeHandle.x) > handleLoader.width / 2) + range.position = newX; + } + + onReleased: { + // If we don't update while dragging, this is the only + // moment that the range is updated. + if (!slider.updateValueWhileDragging) + range.position = fakeHandle.x; + } + } + + + + // Range position normally follow fakeHandle, except when + // 'updateValueWhileDragging' is false. In this case it will only follow + // if the user is not pressing the handle. + Binding { + when: updateValueWhileDragging || !mouseArea.pressed + target: range + property: "position" + value: fakeHandle.x + } + + // During the drag, we simply ignore position set from the range, this + // means that setting a value while dragging will not "interrupt" the + // dragging activity. + Binding { + when: !mouseArea.drag.active + target: fakeHandle + property: "x" + value: range.position + } + + + WheelArea { + id: wheelarea + anchors.fill: parent + horizontalMinimumValue: slider.minimumValue + horizontalMaximumValue: slider.maximumValue + verticalMinimumValue: slider.minimumValue + verticalMaximumValue: slider.maximumValue + property double step: (slider.maximumValue - slider.minimumValue)/100 + + onVerticalWheelMoved: { + value += verticalDelta/4*step + } + + onHorizontalWheelMoved: { + value += horizontalDelta/4*step + } + } +} diff --git a/src/controls/SpinBox.qml b/src/controls/SpinBox.qml new file mode 100644 index 00000000..25c70fc6 --- /dev/null +++ b/src/controls/SpinBox.qml @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype SpinBox + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief SpinBox provides a spin box control. + + SpinBox allows the user to choose a value by clicking the up/down buttons or pressing up/down on the keyboard to increase/decrease + the value currently displayed. The user can also type the value in manually. + + By default the SpinBox provides discrete values in the range [0-99] with a \l stepSize of 1 and 0 \l decimals. + + \code + SpinBox { + id: spinbox + } + \endcode + + Note that if you require decimal values you will need to set the \l decimals to a non 0 value. + + \code + SpinBox { + id: spinbox + decimals: 2 + } + \endcode + +*/ + +Control { + id: spinbox + + /*! + The value of this SpinBox, clamped to \l minimumValue and \l maximumValue. + + The default value is \c 0 + */ + property real value: 0 + + /*! + The minimum value of the SpinBox range. + The \l value is clamped to this value. + + The default value is \c 0 + */ + property real minimumValue: 0 + + /*! + The maximum value of the SpinBox range. + The \l value is clamped to this value. If maximumValue is smaller than + \l minimumValue, \l minimumValue will be enforced. + + The default value is \c 99 + */ + property real maximumValue: 99 + + /*! + The amount by which the \l value is incremented/decremented when a + spin button is pressed. + + The default value is 1.0. + */ + property real stepSize: 1.0 + + /*! The suffix for the value. I.e "cm" */ + property string suffix + + /*! The prefix for the value. I.e "$" */ + property string prefix + + /*! This property indicates the amount of decimals. + Note that if you enter more decimals than specified, they will + be truncated to the specified amount of decimal places. + The default value is \c 0 + */ + property int decimals: 0 + + /*! \qmlproperty font SpinBox::font + + This property indicates the current font used by the SpinBox. + */ + property alias font: input.font + + /*! This property indicates if the Spinbox should get active + focus when pressed. + The default value is \c true + */ + property bool activeFocusOnPress: true + + /*! \internal */ + style: Qt.createComponent(Settings.THEME_PATH + "/SpinBoxStyle.qml", spinbox) + + /*! \internal */ + function __increment() { + input.setValue(input.text) + value += stepSize + if (value > maximumValue) + value = maximumValue + input.text = value.toFixed(decimals) + } + + /*! \internal */ + function __decrement() { + input.setValue(input.text) + value -= stepSize + if (value < minimumValue) + value = minimumValue + input.text = value.toFixed(decimals) + } + + /*! \internal */ + readonly property bool __upEnabled: value != maximumValue; + /*! \internal */ + readonly property bool __downEnabled: value != minimumValue; + /*! \internal */ + readonly property alias __upPressed: mouseUp.pressed + /*! \internal */ + readonly property alias __downPressed: mouseDown.pressed + /*! \internal */ + property alias __upHovered: mouseUp.containsMouse + /*! \internal */ + property alias __downHovered: mouseDown.containsMouse + /*! \internal */ + property alias __containsMouse: mouseArea.containsMouse + /*! \internal */ + property alias __text: input.text + /*! \internal */ + readonly property int __contentHeight: Math.max(input.implicitHeight, 20) + /*! \internal */ + readonly property int __contentWidth: suffixItem.implicitWidth + + Math.max(maxSizeHint.implicitWidth, + minSizeHint.implicitWidth) + + prefixItem.implicitWidth + Text { + id: maxSizeHint + text: maximumValue.toFixed(decimals) + font: input.font + visible: false + } + + Text { + id: minSizeHint + text: minimumValue.toFixed(decimals) + font: input.font + visible: false + } + + /*! \internal */ + onDecimalsChanged: input.setValue(value) + /*! \internal */ + onMaximumValueChanged: input.setValue(value) + /*! \internal */ + onMinimumValueChanged: input.setValue(value) + /*! \internal */ + Component.onCompleted: input.setValue(value) + /*! \internal */ + onValueChanged: input.setValue(value) + + Accessible.name: input.text + Accessible.role: Accessible.SpinBox + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onPressed: if (activeFocusOnPress) input.forceActiveFocus() + } + + Row { + id: textLayout + anchors.fill: parent + spacing: 1 + clip: true + anchors.leftMargin: __panel ? __panel.leftMargin : 0 + anchors.topMargin: __panel ? __panel.topMargin : 0 + anchors.rightMargin: __panel ? __panel.rightMargin: 0 + anchors.bottomMargin: __panel ? __panel.bottomMargin: 0 + + Text { + id: prefixItem + text: prefix + color: __panel ? __panel.foregroundColor : "black" + anchors.verticalCenter: parent.verticalCenter + renderType: Text.NativeRendering + } + + TextInput { + id: input + anchors.verticalCenter: parent.verticalCenter + activeFocusOnPress: spinbox.activeFocusOnPress + function setValue(v) { + var newval = parseFloat(v) + + if (!isNaN(newval)) { + // we give minimumValue priority over maximum if they are inconsistent + if (newval > maximumValue && maximumValue >= minimumValue) + newval = maximumValue + else if (v < minimumValue) + newval = minimumValue + newval = newval.toFixed(decimals) + spinbox.value = parseFloat(newval) + input.text = newval + } else { + input.text = parseFloat(spinbox.value) + } + } + + horizontalAlignment: __panel ? __panel.horizontalTextAlignment : Qt.AlignLeft + verticalAlignment: __panel ? __panel.verticalTextAlignment : Qt.AlignVCenter + selectByMouse: true + + validator: DoubleValidator { bottom: minimumValue; top: maximumValue; } + onAccepted: setValue(input.text) + color: __panel ? __panel.foregroundColor : "black" + selectionColor: __panel ? __panel.selectionColor : "black" + selectedTextColor: __panel ? __panel.selectedTextColor : "black" + + opacity: parent.enabled ? 1 : 0.5 + renderType: Text.NativeRendering + } + Text { + id: suffixItem + text: suffix + color: __panel ? __panel.foregroundColor : "black" + anchors.verticalCenter: parent.verticalCenter + renderType: Text.NativeRendering + } + } + + // Spinbox increment button + + MouseArea { + id: mouseUp + hoverEnabled: true + + property var upRect: __panel ? __panel.upRect : null + + anchors.left: parent.left + anchors.top: parent.top + + anchors.leftMargin: upRect ? upRect.x : 0 + anchors.topMargin: upRect ? upRect.y : 0 + + width: upRect ? upRect.width : 0 + height: upRect ? upRect.height : 0 + + onClicked: __increment() + + property bool autoincrement: false; + onReleased: autoincrement = false + Timer { running: mouseUp.pressed; interval: 350 ; onTriggered: mouseUp.autoincrement = true } + Timer { running: mouseUp.autoincrement; interval: 60 ; repeat: true ; onTriggered: __increment() } + } + + // Spinbox decrement button + + MouseArea { + id: mouseDown + hoverEnabled: true + + onClicked: __decrement() + property var downRect: __panel ? __panel.downRect : null + + anchors.left: parent.left + anchors.top: parent.top + + anchors.leftMargin: downRect ? downRect.x : 0 + anchors.topMargin: downRect ? downRect.y : 0 + + width: downRect ? downRect.width : 0 + height: downRect ? downRect.height : 0 + + property bool autoincrement: false; + onReleased: autoincrement = false + Timer { running: mouseDown.pressed; interval: 350 ; onTriggered: mouseDown.autoincrement = true } + Timer { running: mouseDown.autoincrement; interval: 60 ; repeat: true ; onTriggered: __decrement() } + } + + Keys.onUpPressed: __increment() + Keys.onDownPressed: __decrement() +} diff --git a/src/controls/Splitter.qml b/src/controls/Splitter.qml new file mode 100644 index 00000000..16796f1a --- /dev/null +++ b/src/controls/Splitter.qml @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 as Private + +/*! + \qmltype Splitter + \inqmlmodule QtQuick.Controls 1.0 + \brief Splitter is a component that lays out items horisontally or + vertically with a draggable splitter between each item. +*/ + +/* +* +* Splitter +* +* Splitter is a component that lays out items horisontally or +* vertically with a draggable splitter between each item. +* +* There will always be one (and only one) item in the Splitter that is 'expanding'. +* Being expanding means that the item will get all the remaining space when other +* items have been laid out according to their own width and height. +* By default, the last visible child of the Splitter will be expanding, but +* this can changed by setting Layout.horizontalSizePolicy to \c Layout.Expanding. +* Since the expanding item will automatically be resized to fit the extra space, it +* will ignore explicit assignments to width and height. +* +* A handle can belong to the item on the left/top side, or the right/bottom side, of the +* handle. Which one depends on the expaning item. If the expanding item is to the right +* of the handle, the handle will belong to the item on the left. If it is to the left, it +* will belong to the item on the right. This will again control which item that gets resized +* when the user drags a handle, and which handle that gets hidden when an item is told to hide. +* +* The Splitter contains the following API: +* +* int orientation - the orientation of the splitter. Can be either Qt.Horizontal +* or Qt.Vertical. +* Component handleDelegate - delegate that will be instanciated between each +* child item. Inside the delegate, the following properties are available: +* int handleIndex - specifies the index of the splitter handle. The handle +* between the first and the second item will get index 0, the next handle index 1 etc. +* bool containsMouse - the mouse hovers the handle. +* bool pressed: the handle is being pressed. +* bool dragged: the handle is being dragged. +* +* Splitter supports setting Layout properties on child items, which means that you +* can control minimumWidth, minimumHeight, maximumWidth and maximumHeight (in addition +* to horizontalSizePolicy/verticalSizePolicy) for each child. +* +* Example: +* +* To create a Splitter with three items, and let +* the center item be expanding, one could do the following: +* +* Splitter { +* anchors.fill: parent +* orientation: Qt.Horizontal +* +* Rectangle { +* width: 200 +* Layout.maximumWidth: 400 +* color: "gray" +* } +* Rectangle { +* id: centerItem +* Layout.minimumWidth: 50 +* Layout.horizontalSizePolicy: Layout.Expanding +* color: "darkgray" +* } +* Rectangle { +* width: 200 +* color: "gray" +* } +* } +*/ + +Item { + id: root + property int orientation: Qt.Horizontal + + property Component handleDelegate: + Rectangle{ + width: 1 + height: 1 + color: Qt.darker(pal.window, 1.5) + } + + // **** PRIVATE **** + + clip: true + default property alias __items: splitterItems.children + property alias __handles: splitterHandles.children + Component.onCompleted: d.init() + onWidthChanged: d.updateLayout() + onHeightChanged: d.updateLayout() + + SystemPalette { id: pal } + + QtObject { + id: d + property bool horizontal: orientation == Qt.Horizontal + property string minimum: horizontal ? "minimumWidth" : "minimumHeight" + property string maximum: horizontal ? "maximumWidth" : "maximumHeight" + property string offset: horizontal ? "x" : "y" + property string otherOffset: horizontal ? "y" : "x" + property string size: horizontal ? "width" : "height" + property string otherSize: horizontal ? "height" : "width" + + property int expandingIndex: -1 + property bool updateLayoutGuard: true + + function init() + { + for (var i=0; i<__items.length; ++i) { + var item = __items[i]; + item.widthChanged.connect(d.updateLayout); + item.heightChanged.connect(d.updateLayout); + item.Layout.maximumWidthChanged.connect(d.updateLayout); + item.Layout.minimumWidthChanged.connect(d.updateLayout); + item.Layout.maximumHeightChanged.connect(d.updateLayout); + item.Layout.minimumHeightChanged.connect(d.updateLayout); + item.Layout.horizontalSizePolicyChanged.connect(d.updateExpandingIndex) + item.Layout.verticalSizePolicyChanged.connect(d.updateExpandingIndex) + d.listenForVisibleChanged(item) + if (i < __items.length-1) + handleLoader.createObject(splitterHandles, {"handleIndex":i}); + } + + d.updateExpandingIndex() + d.updateLayoutGuard = false + d.updateLayout() + } + + function listenForVisibleChanged(item) { + item.visibleChanged.connect(function() { + if (!root.visible) + return + if (item.visible) { + // Try to keep all items within the SplitterRow. When an item + // has been hidden, the expanding item might no longer be large enough + // to give away space to the new items width. So we need to resize: + var overflow = d.accumulatedSize(0, __items.length, true) - root[d.size]; + if (overflow > 0) + item[d.size] -= overflow + } + updateExpandingIndex() + }); + } + + function updateExpandingIndex() + { + var policy = (root.orientation === Qt.Horizontal) ? "horizontalSizePolicy" : "verticalSizePolicy" + for (var i=__items.length-1; i>=0; --i) { + if (__items[i].visible && __items[i].Layout[policy] === Layout.Expanding) { + d.expandingIndex = i + break; + } + } + + if (i === -1) { + for (i=__items.length-1; i>0; --i) { + if (__items[i].visible) + break; + } + } + + d.expandingIndex = i + d.updateLayout() + } + + function accumulatedSize(firstIndex, lastIndex, includeExpandingMinimum) + { + // Go through items and handles, and + // calculate their acummulated width. + var w = 0 + for (var i=firstIndex; i<lastIndex; ++i) { + var item = __items[i] + if (item.visible) { + if (i !== d.expandingIndex) + w += item[d.size]; + else if (includeExpandingMinimum && item.Layout[minimum] !== undefined) + w += item.Layout[minimum] + } + + var handle = __handles[i] + if (handle && __items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) + w += handle[d.size] + } + return w + } + + function updateLayout() + { + // This function will reposition both handles and + // items according to the their width/height: + if (__items.length === 0) + return; + if (d.updateLayoutGuard === true) + return + d.updateLayoutGuard = true + + // Ensure all items within their min/max: + for (var i=0; i<__items.length; ++i) { + if (i !== d.expandingIndex) { + var item = __items[i]; + if (item.Layout[maximum] !== undefined) { + if (item[d.size] > item.Layout[maximum]) + item[d.size] = item.Layout[maximum] + } + if (item.Layout[minimum] !== undefined) { + if (item[d.size] < item.Layout[minimum]) + item[d.size] = item.Layout[minimum] + } + } + } + + // Set size of expanding item to remaining available space: + var expandingItem = __items[expandingIndex] + var min = expandingItem.Layout[minimum] !== undefined ? expandingItem.Layout[minimum] : 0 + expandingItem[d.size] = Math.max(min, root[d.size] - d.accumulatedSize(0, __items.length, false)) + + // Then, position items and handles according to their width: + var lastVisibleItem, lastVisibleHandle, handle + var implicitSize = min - expandingItem[d.size] + + for (i=0; i<__items.length; ++i) { + // Position item to the right of the previous visible handle: + item = __items[i]; + if (item.visible) { + item[d.offset] = lastVisibleHandle ? lastVisibleHandle[d.offset] + lastVisibleHandle[d.size] : 0 + item[d.otherOffset] = 0 + item[d.otherSize] = root[d.otherSize] + implicitSize += item[d.size] + lastVisibleItem = item + } + + // Position handle to the right of the previous visible item. We use an alterative way of + // checking handle visibility because that property might not have updated correctly yet: + handle = __handles[i] + if (handle && __items[i + ((d.expandingIndex > i) ? 0 : 1)].visible) { + handle[d.offset] = lastVisibleItem[d.offset] + Math.max(0, lastVisibleItem[d.size]) + handle[d.otherOffset] = 0 + handle[d.otherSize] = root[d.otherSize] + implicitSize += handle[d.size] + lastVisibleHandle = handle + } + } + + if (root.orientation === Qt.horizontal) { + root.implicitWidth = implicitSize + root.implicitHeight = 0 + } else { + root.implicitWidth = 0 + root.implicitHeight = implicitSize + } + + d.updateLayoutGuard = false + } + } + + Component { + id: handleLoader + Loader { + id: itemHandle + property int handleIndex: -1 + property alias containsMouse: mouseArea.containsMouse + property alias pressed: mouseArea.pressed + property bool dragged: mouseArea.drag.active + + visible: __items[handleIndex + ((d.expandingIndex > handleIndex) ? 0 : 1)].visible + sourceComponent: handleDelegate + onWidthChanged: d.updateLayout() + onHeightChanged: d.updateLayout() + onXChanged: moveHandle() + onYChanged: moveHandle() + + MouseArea { + id: mouseArea + anchors.fill: parent + anchors.leftMargin: (parent.width <= 1) ? -2 : 0 + anchors.rightMargin: (parent.width <= 1) ? -2 : 0 + anchors.topMargin: (parent.height <= 1) ? -2 : 0 + anchors.bottomMargin: (parent.height <= 1) ? -2 : 0 + hoverEnabled: true + drag.target: parent + drag.axis: root.orientation === Qt.Horizontal ? Drag.XAxis : Drag.YAxis + cursorShape: root.orientation === Qt.Horizontal ? Qt.SplitHCursor : Qt.SplitVCursor + } + + function moveHandle() { + // Moving the handle means resizing an item. Which one, + // left or right, depends on where the expanding item is. + // 'updateLayout' will override in case new width violates max/min. + // And 'updateLayout will be triggered when an item changes width. + if (d.updateLayoutGuard) + return + + var leftHandle, leftItem, rightItem, rightHandle + var leftEdge, rightEdge, newWidth, leftStopX, rightStopX + var i + + if (d.expandingIndex > handleIndex) { + // Resize item to the left. + // Ensure that the handle is not crossing other handles. So + // find the first visible handle to the left to determine the left edge: + leftEdge = 0 + for (i=handleIndex-1; i>=0; --i) { + leftHandle = __handles[i] + if (leftHandle.visible) { + leftEdge = leftHandle[d.offset] + leftHandle[d.size] + break; + } + } + + // Ensure: leftStopX >= itemHandle[d.offset] >= rightStopX + var min = d.accumulatedSize(handleIndex+1, __items.length, true) + rightStopX = root[d.size] - min - itemHandle[d.size] + leftStopX = Math.max(leftEdge, itemHandle[d.offset]) + itemHandle[d.offset] = Math.min(rightStopX, Math.max(leftStopX, itemHandle[d.offset])) + + newWidth = itemHandle[d.offset] - leftEdge + leftItem = __items[handleIndex] + // The next line will trigger 'updateLayout': + leftItem[d.size] = newWidth + } else { + // Resize item to the right. + // Ensure that the handle is not crossing other handles. So + // find the first visible handle to the right to determine the right edge: + rightEdge = root[d.size] + for (i=handleIndex+1; i<__handles.length; ++i) { + rightHandle = __handles[i] + if (rightHandle.visible) { + rightEdge = rightHandle[d.offset] + break; + } + } + + // Ensure: leftStopX <= itemHandle[d.offset] <= rightStopX + min = d.accumulatedSize(0, handleIndex+1, true) + leftStopX = min - itemHandle[d.size] + rightStopX = Math.min((rightEdge - itemHandle[d.size]), itemHandle[d.offset]) + itemHandle[d.offset] = Math.max(leftStopX, Math.min(itemHandle[d.offset], rightStopX)) + + newWidth = rightEdge - (itemHandle[d.offset] + itemHandle[d.size]) + rightItem = __items[handleIndex+1] + // The next line will trigger 'updateLayout': + rightItem[d.size] = newWidth + } + } + } + } + + Item { + id: splitterItems + anchors.fill: parent + } + Item { + id: splitterHandles + anchors.fill: parent + } + +} diff --git a/src/controls/StatusBar.qml b/src/controls/StatusBar.qml new file mode 100644 index 00000000..965af47b --- /dev/null +++ b/src/controls/StatusBar.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype StatusBar + \inqmlmodule QtQuick.Controls 1.0 + \ingroup applicationwindow + \brief StatusBar is for containing status informating in your app + + The common way of using StatusBar is in relation to \l ApplicationWindow. + + Note that the StatusBar does not provide a layout of its own but requires you to + position its contents, for instance by creating a Row. + + \code + ApplicationWindow { + statusBar: StatusBar { + Label { + text: "Read Only" + anchors.centerIn: parent + } + } + } + \endcode +*/ + +Item { + id: statusbar + implicitHeight: 20 + implicitWidth: parent ? parent.width : style.implicitWidth + StyleItem { + id: style + anchors.fill: parent + elementType: "statusbar" + } +} diff --git a/src/controls/Tab.qml b/src/controls/Tab.qml new file mode 100644 index 00000000..014bb023 --- /dev/null +++ b/src/controls/Tab.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 + +/*! + \qmltype Tab + \inqmlmodule QtQuick.Controls 1.0 + \ingroup navigation + \brief Tab is doing bla...bla... +*/ + +Item { + id:tab + anchors.fill: parent + property string title + property int contentMargin + Accessible.role: Accessible.PageTab +} diff --git a/src/controls/TabFrame.qml b/src/controls/TabFrame.qml new file mode 100644 index 00000000..91ea18f6 --- /dev/null +++ b/src/controls/TabFrame.qml @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles/Settings.js" as Settings + +/*! + \qmltype TabFrame + \inqmlmodule QtQuick.Controls 1.0 + \ingroup navigation + \brief Represents a control that contains multiple items that share the same space on the screen. + +*/ + +Item { + id: tabWidget + width: 100 + height: 100 + + property int current: 0 + property int count: 0 + property bool frame: true + property bool tabsVisible: true + property string position: "North" + default property alias tabs : stack.children + property Component style: Qt.createComponent(Settings.THEME_PATH + "/TabFrameStyle.qml", tabWidget) + property var __styleItem: loader.item + + onCurrentChanged: __setOpacities() + Component.onCompleted: __setOpacities() + + function __setOpacities() { + var tabCount = 0; + for (var i = 0; i < stack.children.length; ++i) { + stack.children[i].visible = (i == current ? true : false) + // count real tabs - ignore for instance Repeater + if (stack.children[i].Accessible.role == Accessible.PageTab) + ++tabCount; + } + tabWidget.count = tabCount; + } + + Component { + id: tabcomp + Tab {} + } + + function addTab(component, title) { + var tab = tabcomp.createObject(this); + component.createObject(tab) + tab.parent = stack + tab.title = title + __setOpacities() + return tab + } + + function removeTab(id) { + var tab = tabs[id] + tab.destroy() + if (current > 0) + current-=1 + } + + Loader { + id: loader + sourceComponent: style + property var control: tabWidget + } + + Loader { + id: frameLoader + + anchors.fill: parent + anchors.topMargin: tabbarItem && tabsVisible && position == "North" ? Math.max(0, tabbarItem.height - stack.baseOverlap) : 0 + anchors.bottomMargin: tabbarItem && tabsVisible && position == "South" ? Math.max(0, tabbarItem.height - stack.baseOverlap) : 0 + sourceComponent: frame && loader.item ? loader.item.frame : null + property var control: tabWidget + + Item { + id: stack + anchors.fill: parent + anchors.margins: (frame ? frameWidth : 0) + anchors.topMargin: anchors.margins + (style =="mac" ? 6 : 0) + anchors.bottomMargin: anchors.margins + (style =="mac" ? 6 : 0) + property int frameWidth + property string style + property int baseOverlap + } + onLoaded: { item.z = -1 } + } + + TabBar { + id: tabbarItem + tabFrame: tabWidget + style: loader.item + anchors.top: parent.top + anchors.left: tabWidget.left + anchors.right: tabWidget.right + } + + states: [ + State { + name: "South" + when: position == "South" && tabbarItem != undefined + PropertyChanges { + target: tabbarItem + anchors.topMargin: tabbarItem.height + } + AnchorChanges { + target: tabbarItem + anchors.top: undefined + anchors.bottom: tabWidget.bottom + } + } + ] +} diff --git a/src/controls/TableView.qml b/src/controls/TableView.qml new file mode 100644 index 00000000..bcfc8d8b --- /dev/null +++ b/src/controls/TableView.qml @@ -0,0 +1,676 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype TableView + \inqmlmodule QtQuick.Controls 1.0 + \ingroup views + \brief Provides a list view with scroll bars, styling and header sections. + + \image tableview.png + + A TableView is similar to \l ListView and adds scroll bars, selection and + resizable header sections. As with \l ListView, data for each row is provided through a \l model: + + \code + ListModel { + id: libraryModel + ListElement{ title: "A Masterpiece" ; author: "Gabriel" } + ListElement{ title: "Brilliance" ; author: "Jens" } + ListElement{ title: "Outstanding" ; author: "Frederik" } + } + \endcode + + You provide title and size of a column header + by adding a \l TableViewColumn to the default \l header property + as demonstrated below. + \code + + TableView { + TableViewColumn{ role: "title" ; title: "Title" ; width: 100 } + TableViewColumn{ role: "author" ; title: "Author" ; width: 200 } + model: libraryModel + } + \endcode + + The header sections are attached to values in the \l model by defining + the model role they attach to. Each property in the model, will + then be shown in their corresponding column. + + You can customize the look by overriding the \l itemDelegate, + \l rowDelegate or \l headerDelegate properties. + + The view itself does not provide sorting. This has to + be done on the model itself. However you can provide sorting + on the model and enable sort indicators on headers. + +\list + \li sortColumn - The index of the currently selected sort header + \li sortIndicatorVisible - If sort indicators should be enabled + \li sortIndicatorDirection - "up" or "down" depending on state +\endlist +*/ + +ScrollArea { + id: root + + /*! This property holds the model providing data for the list. + + The model provides the set of data that is used to create the items in the view. + Models can be created directly in QML using ListModel, XmlListModel or VisualItemModel, + or provided by C++ model classes. \sa ListView::model + + Example model: + + \code + model: ListModel { + ListElement{ column1: "value 1" ; column2: "value 2" } + ListElement{ column1: "value 3" ; column2: "value 4" } + } + \endcode */ + property variant model + + width: 200 + height: 200 + + /*! \internal */ + __scrollBarTopMargin: styleitem.style == "mac" ? headerrow.height : 0 + + /*! This property sets if the frame should paint the focus frame around its contents. + The default value is \c false. + \note Only certain platforms such as Mac OS X will be affected by this property */ + property bool highlightOnFocus: false + + /*! This property is set to \c true if the view alternates the row color. + The default value is \c true. */ + property bool alternateRowColor: true + + /*! This property determines if the header is visible. + The default value is \c true. */ + property bool headerVisible: true + + /*! This property defines a delegate to draw a specific cell. + + In the item delegate you have access to the following special properties: + \list + \li itemHeight - default platform size of item + \li itemWidth - default platform width of item + \li itemSelected - if the row is currently selected + \li itemValue - The text for this item + \li itemForeground - The default text color for an item + \endlist + Example: + \code + itemDelegate: Item { + Text { + anchors.verticalCenter: parent.verticalCenter + color: itemForeground + elide: Text.ElideRight + text: itemValue + } + } + \endcode */ + property Component itemDelegate: standardDelegate + + /*! This property defines a delegate to draw a row. */ + property Component rowDelegate: rowDelegate + + /*! This property defines a delegate to draw a header. */ + property Component headerDelegate: headerDelegate + + /*! \qmlproperty color TableView::backgroundColor + + This property sets the background color of the viewport. + The default value is the base color of the SystemPalette. */ + property alias backgroundColor: colorRect.color + + /*! This property sets if the frame should be visible. + The default value is \c true. */ + frame: true + + /*! Index of the currently selected sort column + The default value is \c 0. */ + property int sortColumn + + /*! This property shows or hides the sort indicator + The default value is \c false. + \note The view itself does not sort the data. */ + property bool sortIndicatorVisible: false + + /*! This sets the sorting direction of the sort indicator + The allowed values are: + \list + \li "up" + \li "down" - the default + \endlist */ + property string sortIndicatorDirection: "down" + + /*! \qmlproperty Component TableView::header + This property contains the TableViewHeader items */ + default property alias header: listView.columnheader + + /*! \qmlproperty Component TableView::contentHeader + This is the content header of the TableView */ + property alias contentHeader: listView.header + + /*! \qmlproperty Component TableView::contentFooter + This is the content footer of the TableView */ + property alias contentFooter: listView.footer + + /*! \qmlproperty Item TableView::currentItem + This is the current item of the TableView */ + property alias currentItem: listView.currentItem + + /*! \qmlproperty int TableView::count + The current number of rows */ + property alias count: listView.count + + /*! \qmlproperty string TableView::section + The section of the view. \sa ListView::section */ + readonly property alias section: listView.section + + /*! \qmlproperty int TableView::currentIndex + The current row index of the view. */ + property alias currentIndex: listView.currentIndex + + Accessible.role: Accessible.Table + + /*! \qmlsignal TableView::activated() + Emitted when a new row is selected by the user. */ + signal activated + + /*! \internal */ + function __decrementCurrentIndex() { + __scroller.blockUpdates = true; + listView.decrementCurrentIndex(); + __scroller.blockUpdates = false; + } + + /*! \internal */ + function __incrementCurrentIndex() { + __scroller.blockUpdates = true; + listView.incrementCurrentIndex(); + __scroller.blockUpdates = false; + } + + ListView { + id: listView + anchors.topMargin: tableHeader.height + anchors.fill: parent + + flickableDirection: Flickable.HorizontalFlick + SystemPalette { + id: palette + colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled + } + + Rectangle { + id: colorRect + parent: viewport + anchors.fill: parent + color: palette.base + z: -1 + } + + StyleItem { + id: itemstyle + elementType: "item" + visible: false + } + + MouseArea { + id: mousearea + + anchors.fill: listView + + property bool autoincrement: false + property bool autodecrement: false + + onReleased: { + autoincrement = false + autodecrement = false + } + + // Handle vertical scrolling whem dragging mouse outside boundraries + Timer { running: mousearea.autoincrement && __scroller.verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __incrementCurrentIndex()} + Timer { running: mousearea.autodecrement && __scroller.verticalScrollBar.visible; repeat: true; interval: 20 ; onTriggered: __decrementCurrentIndex()} + + onPositionChanged: { + if (mouseY > listView.height && pressed) { + if (autoincrement) return; + autodecrement = false; + autoincrement = true; + } else if (mouseY < 0 && pressed) { + if (autodecrement) return; + autoincrement = false; + autodecrement = true; + } else { + autoincrement = false; + autodecrement = false; + } + var y = Math.min(flickableItem.contentY + listView.height - 5, Math.max(mouseY + flickableItem.contentY, flickableItem.contentY)); + var newIndex = listView.indexAt(0, y); + if (newIndex >= 0) + listView.currentIndex = listView.indexAt(0, y); + } + + onPressed: { + listView.forceActiveFocus() + var x = Math.min(flickableItem.contentWidth - 5, Math.max(mouseX + flickableItem.contentX, 0)) + var y = Math.min(flickableItem.contentHeight - 5, Math.max(mouseY + flickableItem.contentY, 0)) + listView.currentIndex = listView.indexAt(x, y) + } + + onDoubleClicked: { root.activated() } + + // Note by prevent stealing we are keeping the flickable from + // eating our mouse press events + preventStealing: true + } + + // Fills extra rows with alternate color + Column { + id: rowfiller + property int rowHeight: flickableItem.contentHeight/count + property int paddedRowCount: height/rowHeight + property int count: flickableItem.count + y: flickableItem.contentHeight + width: parent.width + visible: flickableItem.contentHeight > 0 && alternateRowColor + height: viewport.height - flickableItem.contentHeight + Repeater { + model: visible ? parent.paddedRowCount : 0 + Loader { + width: rowfiller.width + height: rowfiller.rowHeight + sourceComponent: root.rowDelegate + property bool itemAlternateBackground: (index + count) % 2 === 1 + property bool itemSelected: false + property variant model: listView.model + property variant modelData: null + } + } + } + + property list<TableViewColumn> columnheader + highlightFollowsCurrentItem: true + model: root.model + + Keys.onUpPressed: root.decrementCurrentIndex() + Keys.onDownPressed: root.incrementCurrentIndex() + + Keys.onPressed: { + if (event.key === Qt.Key_PageUp) { + verticalScrollBar.value = __scroller.verticalScrollBar.value - listView.height + } else if (event.key === Qt.Key_PageDown) + verticalScrollBar.value = __scroller.verticalScrollBar.value + listView.height + } + + Keys.onReturnPressed: root.activated(); + + delegate: Item { + id: rowitem + width: row.width + height: rowstyle.height + + property int rowIndex: model.index + property bool itemAlternateBackground: alternateRowColor && rowIndex % 2 == 1 + property variant itemModelData: typeof modelData == "undefined" ? null : modelData + property variant itemModel: model + + Loader { + id: rowstyle + // row delegate + sourceComponent: root.rowDelegate + // Row fills the view width regardless of item size + // But scrollbar should not adjust to it + width: parent.width + __scroller.horizontalScrollBar.width + x: flickableItem.contentX + + property bool itemAlternateBackground: rowitem.itemAlternateBackground + property bool itemSelected: rowitem.ListView.isCurrentItem + property int index: rowitem.rowIndex + property variant model: listView.model + property variant modelData: rowitem.itemModelData + property variant itemModel: rowitem.itemModel + } + Row { + id: row + anchors.left: parent.left + height: parent.height + Repeater { + id: repeater + model: root.header.length + Loader { + id: itemDelegateLoader + height: parent.height + visible: header[index].visible + sourceComponent: header[index].delegate ? header[index].delegate : itemDelegate + property variant model: listView.model + property variant role: header[index].role + property variant modelData: itemModelData + + width: header[index].width + + function getValue() { + if (header[index].role.length && itemModel.hasOwnProperty(header[index].role)) + return itemModel[header[index].role] // Qml ListModel and QAbstractItemModel + else if (modelData != undefined && modelData.hasOwnProperty(header[index].role)) + return modelData[header[index].role] // QObjectList / QObject + else if (modelData != undefined) + return modelData // Models without role + else + return "" + } + property variant itemValue: getValue() + property bool itemSelected: rowitem.ListView.isCurrentItem + property color itemForeground: itemSelected ? rowstyleitem.highlightedTextColor : rowstyleitem.textColor + property int rowIndex: rowitem.rowIndex + property int columnIndex: index + property int itemElideMode: header[index].elideMode + property int itemTextAlignment: header[index].horizontalAlignment + } + } + onWidthChanged: listView.contentWidth = width + } + } + + Text{ id:text } + + Item { + id: tableHeader + clip: true + parent: __scroller + visible: headerVisible + anchors.top: parent.top + anchors.margins: viewport.anchors.margins + anchors.rightMargin: __scroller.frameWidth + + (__scroller.outerFrame && __scrollBarTopMargin ? 0 : __scroller.verticalScrollBar.width + + __scroller.scrollBarSpacing) + + anchors.left: parent.left + anchors.right: parent.right + + height: headerVisible ? headerrow.height : 0 + + Behavior on height { NumberAnimation{ duration: 80 } } + + Row { + id: headerrow + x: -listView.contentX + + Repeater { + id: repeater + + property int targetIndex: -1 + property int dragIndex: -1 + + model: header.length + + delegate: Item { + z:-index + width: header[index].width + visible: header[index].visible + height: headerStyle.height + + Loader { + id: headerStyle + sourceComponent: root.headerDelegate + anchors.left: parent.left + anchors.right: parent.right + property string itemValue: header[index].title + property string itemSort: (sortIndicatorVisible && index == sortColumn) ? (sortIndicatorDirection == "up" ? "up" : "down") : ""; + property bool itemPressed: headerClickArea.pressed + property bool itemContainsMouse: headerClickArea.containsMouse + property string itemPosition: header.length === 1 ? "only" : + index===header.length-1 ? "end" : + index===0 ? "beginning" : "" + } + Rectangle{ + id: targetmark + width: parent.width + height:parent.height + opacity: (index == repeater.targetIndex && repeater.targetIndex != repeater.dragIndex) ? 0.5 : 0 + Behavior on opacity { NumberAnimation{duration:160}} + color: palette.highlight + } + + MouseArea{ + id: headerClickArea + drag.axis: Qt.YAxis + hoverEnabled: true + anchors.fill: parent + onClicked: { + if (sortColumn == index) + sortIndicatorDirection = sortIndicatorDirection === "up" ? "down" : "up" + sortColumn = index + } + // Here we handle moving header sections + // NOTE: the direction is different from the master branch + // so this indicates that I am using an invalid assumption on item ordering + onPositionChanged: { + if (pressed) { // only do this while dragging + for (var h = header.length-1 ; h >= 0 ; --h) { + if (drag.target.x > headerrow.children[h].x) { + repeater.targetIndex = h + break + } + } + } + } + + onPressed: { + repeater.dragIndex = index + draghandle.x = parent.x + } + + onReleased: { + if (repeater.targetIndex >= 0 && repeater.targetIndex != index ) { + // Rearrange the header sections + var items = new Array + for (var i = 0 ; i< header.length ; ++i) + items.push(header[i]) + items.splice(index, 1); + items.splice(repeater.targetIndex, 0, header[index]); + header = items + if (sortColumn == index) + sortColumn = repeater.targetIndex + } + repeater.targetIndex = -1 + } + drag.maximumX: 1000 + drag.minimumX: -1000 + drag.target: draghandle + } + + Loader { + id: draghandle + property string itemValue: header[index].title + property string itemSort: (sortIndicatorVisible && index == sortColumn) ? (sortIndicatorDirection == "up" ? "up" : "down") : ""; + property bool itemPressed: headerClickArea.pressed + property bool itemContainsMouse: headerClickArea.containsMouse + property string itemPosition + + parent: tableHeader + width: header[index].width + height: parent.height + sourceComponent: root.headerDelegate + visible: headerClickArea.pressed + opacity: 0.5 + } + + + MouseArea { + id: headerResizeHandle + property int offset: 0 + property int minimumSize: 20 + anchors.rightMargin: -width/2 + width: 16 ; height: parent.height + anchors.right: parent.right + onPositionChanged: { + var newHeaderWidth = header[index].width + (mouseX - offset) + header[index].width = Math.max(minimumSize, newHeaderWidth) + } + property bool found:false + + onDoubleClicked: { + var row + var minWidth = 0 + var listdata = listView.children[0] + for (row = 0 ; row < listdata.children.length ; ++row){ + var item = listdata.children[row+1] + if (item && item.children[1] && item.children[1].children[index] && + item.children[1].children[index].children[0].hasOwnProperty("implicitWidth")) + minWidth = Math.max(minWidth, item.children[1].children[index].children[0].implicitWidth) + } + if (minWidth) + header[index].width = minWidth + } + onPressedChanged: if (pressed) offset=mouseX + cursorShape: Qt.SplitHCursor + } + } + } + } + Loader { + id: loader + property string itemValue + property string itemSort + property bool itemPressed + property bool itemContainsMouse + property string itemPosition + + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: headerrow.bottom + anchors.rightMargin: -2 + sourceComponent: root.headerDelegate + width: root.width - headerrow.width + 2 + visible: root.header.length + z:-1 + } + + Component { + id: standardDelegate + Item { + height: Math.max(16, styleitem.implicitHeight) + property int implicitWidth: sizehint.paintedWidth + 4 + Text { + id: label + objectName: "label" + width: parent.width + anchors.margins: 6 + font: itemstyle.font + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: itemTextAlignment + anchors.verticalCenter: parent.verticalCenter + elide: itemElideMode + text: itemValue != undefined ? itemValue : "" + color: itemForeground + renderType: Text.NativeRendering + } + Text { + id: sizehint + font: label.font + text: itemValue ? itemValue : "" + visible: false + } + } + } + + Component { + id: nativeDelegate + // This gives more native styling, but might be less performant + StyleItem { + elementType: "item" + text: itemValue + selected: itemSelected + active: root.activeFocus + } + } + + Component { + id: headerDelegate + StyleItem { + elementType: "header" + activeControl: itemSort + raised: true + sunken: itemPressed + text: itemValue + hover: itemContainsMouse + hints: itemPosition + } + } + + Component { + id: rowDelegate + StyleItem { + id: rowstyle + elementType: "itemrow" + activeControl: itemAlternateBackground ? "alternate" : "" + selected: itemSelected ? true : false + height: Math.max(16, styleitem.implicitHeight) + active: root.activeFocus + } + } + + StyleItem { + id: styleitem + elementType: "header" + visible:false + contentWidth: 16 + contentHeight: font.pixelSize + } + + StyleItem { + id: rowstyleitem + property color textColor: styleHint("textColor") + property color highlightedTextColor: styleHint("highlightedTextColor") + elementType: "item" + visible: false + } + } + } +} diff --git a/src/controls/TableViewColumn.qml b/src/controls/TableViewColumn.qml new file mode 100644 index 00000000..f14b3f03 --- /dev/null +++ b/src/controls/TableViewColumn.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 + +/*! + \qmltype TableViewColumn + \inqmlmodule QtQuick.Controls 1.0 + \ingroup views + \brief Used by the \l TableView to define a column header. +*/ + +QtObject { + /*! The title text of the column. */ + property string title + + /*! The model \c role of the column. */ + property string role + + /*! The current width of the column + The default value depends on platform. */ + property int width: 160 + + /*! The horizontal offset of the column. */ + property int x + + /*! The visible status of the column.*/ + property bool visible: true + + /*! The text elide mode of the column. + Allowed values are: + \list + \li Text.AlignLeft - the default + \li Text.AligntRight + \li Text.AlignHCenter + \li Text.AlignJustify + \endlist + \sa Text::elide */ + property int elideMode: Text.ElideRight + + /*! The text elide mode of the column. + \sa Text::horizontalAlignment: */ + property int horizontalAlignment: Text.AlignLeft + + /*! The delegate of the column. This can be used to set the + \l TableView::itemDelegate for a specific column. */ + property Component delegate +} diff --git a/src/controls/TextArea.qml b/src/controls/TextArea.qml new file mode 100644 index 00000000..bcedbe75 --- /dev/null +++ b/src/controls/TextArea.qml @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +/*! + \qmltype TextArea + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief TextArea displays multiple lines of editable formatted text. + + It can display both plain and rich text. For example: + + \qml + TextArea { + width: 240 + text: "<b>Hello</b> <i>World!</i>" + } + \endqml + + Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can + be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely + from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). + + You can translate between cursor positions (characters from the start of the document) and pixel + points using positionAt() and positionToRectangle(). + + \sa TextField, TextEdit +*/ + +ScrollArea { + id: area + + /*! + \qmlproperty bool TextArea::activeFocusOnPress + + Whether the TextEdit should gain active focus on a mouse press. By default this is + set to true. + */ + property alias activeFocusOnPress: edit.activeFocusOnPress + + /*! + \qmlproperty url TextArea::baseUrl + + This property specifies a base URL which is used to resolve relative URLs + within the text. + + The default value is the url of the QML file instantiating the TextArea item. + */ + property alias baseUrl: edit.baseUrl + + /*! + \qmlproperty bool TextArea::canPaste + + Returns true if the TextArea is writable and the content of the clipboard is + suitable for pasting into the TextArea. + */ + readonly property alias canPaste: edit.canPaste + + /*! + \qmlproperty bool TextArea::canRedo + + Returns true if the TextArea is writable and there are \l {undo}{undone} + operations that can be redone. + */ + readonly property alias canRedo: edit.canRedo + + /*! + \qmlproperty bool TextArea::canUndo + + Returns true if the TextArea is writable and there are previous operations + that can be undone. + */ + readonly property alias canUndo: edit.canUndo + + /*! + \qmlproperty color TextArea::textColor + + The text color. + + \qml + TextArea { color: "orange" } + \endqml + */ + property alias textColor: edit.color + + /*! + \qmlproperty int TextArea::cursorPosition + The position of the cursor in the TextArea. + */ + property alias cursorPosition: edit.cursorPosition + + /*! \qmlproperty font TextArea::font + + The font of the TextArea. + */ + property alias font: edit.font + + /*! + \qmlproperty enumeration TextArea::horizontalAlignment + + Sets the alignment of the text within the TextArea item's width. + + By default, the horizontal text alignment follows the natural alignment of the text, + for example text that is read from left to right will be aligned to the left. + + The valid values for \c horizontalAlignment are: + \list + \li TextEdit.AlignLeft (Default) + \li TextEdit.AlignRight + \li TextEdit.AlignHCenter + \endlist + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextArea, use the read-only property \c effectiveHorizontalAlignment. + */ + property alias horizontalAlignment: edit.horizontalAlignment + + /*! + \qmlproperty enumeration TextArea::effectiveHorizontalAlignment + + Gets the effective horizontal alignment of the text within the TextArea item's width. + + To set/get the default horizontal alignment of TextArea, use the property \c horizontalAlignment. + + */ + readonly property alias effectiveHorizontalAlignment: edit.effectiveHorizontalAlignment + + /*! + \qmlproperty enumeration TextArea::verticalAlignment + + Sets the alignment of the text within the TextArea item's height. + + The valid values for \c verticalAlignment are: + \list + \li TextEdit.AlignTop + \li TextEdit.AlignBottom + \li TextEdit.AlignVCenter (Default) + \endlist + */ + property alias verticalAlignment: edit.verticalAlignment + + /*! + \qmlproperty enumeration TextArea::inputMethodHints + + Provides hints to the input method about the expected content of the text edit and how it + should operate. + + The value is a bit-wise combination of flags or Qt.ImhNone if no hints are set. + + The default value is \c Qt.ImhNone. + + Flags that alter behavior are: + + \list + \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords. + \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method + in any persistent storage like predictive user dictionary. + \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case + when a sentence ends. + \li Qt.ImhPreferNumbers - Numbers are preferred (but not required). + \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required). + \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required). + \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing. + + \li Qt.ImhDate - The text editor functions as a date field. + \li Qt.ImhTime - The text editor functions as a time field. + \endlist + + Flags that restrict input (exclusive flags) are: + + \list + \li Qt.ImhDigitsOnly - Only digits are allowed. + \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign. + \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed. + \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed. + \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed. + \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed. + \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed. + \endlist + + Masks: + + \list + \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used. + \endlist + */ + property alias inputMethodHints: edit.inputMethodHints + + /*! + \qmlproperty int TextArea::length + + Returns the total number of plain text characters in the TextArea item. + + As this number doesn't include any formatting markup it may not be the same as the + length of the string returned by the \l text property. + + This property can be faster than querying the length the \l text property as it doesn't + require any copying or conversion of the TextArea's internal string data. + */ + readonly property alias length: edit.length + + /*! + \qmlproperty int TextArea::lineCount + + Returns the total number of lines in the TextArea item. + */ + readonly property alias lineCount: edit.lineCount + + /*! + \qmlproperty bool TextArea::readOnly + + Whether the user can interact with the TextArea item. + + The difference from a disabled text field is that it will appear + to be active and text can be selected and copied. + + If this property is set to true the text cannot be edited by user interaction. + + By default this property is \c false. + */ + property alias readOnly: edit.readOnly + + /*! + \qmlproperty string TextArea::selectedText + + This read-only property provides the text currently selected in the + text edit. + */ + readonly property alias selectedText: edit.selectedText + + /*! + \qmlproperty int TextArea::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText + */ + readonly property alias selectionEnd: edit.selectionEnd + + /*! + \qmlproperty int TextArea::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText + */ + readonly property alias selectionStart: edit.selectionStart + + /*! + \qmlproperty bool TextArea::tabChangesFocus + + This property holds whether Tab changes focus or is accepted as input. + + Defaults to \c false. + */ + property bool tabChangesFocus: false + + /*! + \qmlproperty string TextArea::text + + The text to display. If the text format is AutoText the text edit will + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + */ + property alias text: edit.text + + /*! + \qmlproperty enumeration TextArea::textFormat + + The way the text property should be displayed. + + \list + \li TextEdit.AutoText + \li TextEdit.PlainText + \li TextEdit.RichText + \endlist + + The default is TextEdit.PlainText. If the text format is TextEdit.AutoText the text edit + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + */ + property alias textFormat: edit.textFormat + + /*! + \qmlproperty enumeration TextArea::wrapMode + + Set this property to wrap the text to the TextArea item's width. + The text will only wrap if an explicit width has been set. + + \list + \li TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. + \li TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. + \li TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \li TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist + + The default is \c TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. + */ + property alias wrapMode: edit.wrapMode + + /*! + \qmlsignal TextArea::linkActivated(string link) + + This signal is emitted when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. + */ + signal linkActivated(string link) + + /*! + \qmlmethod TextArea::append(string) + + Appends \a string as a new line to the end of the text area. + */ + function append (string) { + if (length) + string = "\n" + string + text += string + verticalScrollBar.value = verticalScrollBar.maximumValue + } + + /*! + \qmlmethod TextArea::copy() + + Copies the currently selected text to the system clipboard. + */ + function copy() { + edit.copy(); + } + + /*! + \qmlmethod TextArea::cut() + + Moves the currently selected text to the system clipboard. + */ + function cut() { + edit.cut(); + } + + /*! + \qmlmethod TextArea::deselect() + + Removes active text selection. + */ + function deselect() { + edit.deselect(); + } + + /*! + \qmlmethod string TextArea::getFormattedText(int start, int end) + + Returns the section of text that is between the \a start and \a end positions. + + The returned text will be formatted according the \l textFormat property. + */ + function getFormattedText(start, end) { + return edit.getFormattedText(start, end); + } + + /*! + \qmlmethod string TextArea::getText(int start, int end) + + Returns the section of text that is between the \a start and \a end positions. + + The returned text does not include any rich text formatting. + */ + function getText(start, end) { + return edit.getText(start, end); + } + + /*! + \qmlmethod TextArea::insert(int position, string text) + + Inserts \a text into the TextArea at position. + */ + function insert(position, text) { + edit.insert(position, text); + } + + /*! + \qmlmethod TextArea::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. + */ + function isRightToLeft(start, end) { + return edit.isRightToLeft(start, end); + } + + /*! + \qmlmethod TextArea::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextEdit.SelectCharacters. + + \list + \li TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \li TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified position and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextEdit.SelectCharacters) + moveCursorSelection(7, TextEdit.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. + */ + function moveCursorSelection(position, mode) { + edit.moveCursorSelection(position, mode); + } + + /*! + \qmlmethod TextArea::paste() + + Replaces the currently selected text by the contents of the system clipboard. + */ + function paste() { + edit.paste(); + } + + /*! + \qmlmethod int TextArea::positionAt(int x, int y) + + Returns the text position closest to pixel position (\a x, \a y). + + Position 0 is before the first character, position 1 is after the first character + but before the second, and so on until position \l {text}.length, which is after all characters. + */ + function positionAt(x, y) { + return edit.positionAt(x, y); + } + + /*! + \qmlmethod rectangle TextArea::positionToRectangle(position) + + Returns the rectangle at the given \a position in the text. The x, y, + and height properties correspond to the cursor that would describe + that position. + */ + function positionToRectangle(position) { + return edit.positionToRectangle(position); + } + + /*! + \qmlmethod TextArea::redo() + + Redoes the last operation if redo is \l {canRedo}{available}. + */ + function redo() { + edit.redo(); + } + + /*! + \qmlmethod string TextArea::remove(int start, int end) + + Removes the section of text that is between the \a start and \a end positions from the TextArea. + */ + function remove(start, end) { + return edit.remove(start, end); + } + + /*! + \qmlmethod TextArea::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd + */ + function select(start, end) { + edit.select(start, end); + } + + /*! + \qmlmethod TextArea::selectAll() + + Causes all text to be selected. + */ + function selectAll() { + edit.selectAll(); + } + + /*! + \qmlmethod TextArea::selectWord() + + Causes the word closest to the current cursor position to be selected. + */ + function selectWord() { + edit.selectWord(); + } + + /*! + \qmlmethod TextArea::undo() + + Undoes the last operation if undo is \l {canUndo}{available}. Deselects any + current selection, and updates the selection start to the current cursor + position. + */ + function undo() { + edit.undo(); + } + + /*! + \qmlproperty color ScrollArea:backgroundColor + + This property sets the background color of the viewport. + + The default value is the base color of the SystemPalette. + */ + property alias backgroundColor: colorRect.color + + /*! \internal */ + property int documentMargins: 4 + + width: 280 + height: 120 + + flickableItem.contentWidth: edit.paintedWidth + (2 * documentMargins) + frame: true + + Accessible.role: Accessible.EditableText + + // FIXME: probably implement text interface + Accessible.name: text + + TextEdit { + id: edit + focus: true + + SystemPalette { + id: palette + colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled + } + + Rectangle { + id: colorRect + parent: viewport + anchors.fill: parent + color: palette.base + z: -1 + } + + + renderType: Text.NativeRendering + + color: palette.text + selectionColor: palette.highlight + selectedTextColor: palette.highlightedText + wrapMode: TextEdit.WordWrap + width: area.viewport.width - (2 * documentMargins) + height: Math.max(area.viewport.height - (2 * documentMargins), paintedHeight + (2 * documentMargins)) + x: documentMargins + y: documentMargins + + selectByMouse: true + readOnly: false + + KeyNavigation.priority: KeyNavigation.BeforeItem + KeyNavigation.tab: area.tabChangesFocus ? area.KeyNavigation.tab : null + KeyNavigation.backtab: area.tabChangesFocus ? area.KeyNavigation.backtab : null + + // keep textcursor within scrollarea + onCursorPositionChanged: { + if (cursorRectangle.y >= flickableItem.contentY + viewport.height - 1.5*cursorRectangle.height - documentMargins) + flickableItem.contentY = cursorRectangle.y - viewport.height + 1.5*cursorRectangle.height + documentMargins + else if (cursorRectangle.y < flickableItem.contentY) + flickableItem.contentY = cursorRectangle.y + + if (cursorRectangle.x >= flickableItem.contentX + viewport.width - documentMargins) { + flickableItem.contentX = cursorRectangle.x - viewport.width + documentMargins + } else if (cursorRectangle.x < flickableItem.contentX) + flickableItem.contentX = cursorRectangle.x + } + onLinkActivated: area.linkActivated(link) + + MouseArea { + parent: area.viewport + anchors.fill: parent + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + } + + Keys.onPressed: { + if (event.key == Qt.Key_PageUp) { + verticalValue = verticalValue - area.height + } else if (event.key == Qt.Key_PageDown) + verticalValue = verticalValue + area.height + } + +} diff --git a/src/controls/TextField.qml b/src/controls/TextField.qml new file mode 100644 index 00000000..c209e532 --- /dev/null +++ b/src/controls/TextField.qml @@ -0,0 +1,564 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 +import "Styles" +import "Styles/Settings.js" as Settings + +/*! + \qmltype TextField + \inqmlmodule QtQuick.Controls 1.0 + \ingroup controls + \brief TextField displays a single line of editable plain text + + TextField is used to accept a line of text input. Input constraints + can be placed on a TextField item (for example, through a \l validator or \l inputMask), + and setting \l echoMode to an appropriate value enables TextField to be used for + a password input field. + + \sa TextArea, TextInput +*/ + +Control { + id: textfield + + /*! + \qmlproperty bool TextField::acceptableInput + + This property is always true unless a validator or input mask has been set. + If a validator or input mask has been set, this property will only be true + if the current text is acceptable to the validator or input mask as a final + string (not as an intermediate string). + + \sa validator, inputMask + + */ + readonly property alias acceptableInput: textInput.acceptableInput // read only + + /*! + \qmlproperty bool TextField::activeFocusOnPress + + Whether the TextField should gain active focus on a mouse press. By default this is + set to \c true. + */ + property alias activeFocusOnPress: textInput.activeFocusOnPress + + /*! + \qmlproperty bool TextField::canPaste + + Returns true if the TextField is writable and the content of the clipboard is + suitable for pasting into the TextField. + */ + readonly property alias canPaste: textInput.canPaste + + /*! + \qmlproperty bool TextField::canRedo + + Returns true if the TextField is writable and there are \l {undo}{undone} + operations that can be redone. + */ + readonly property alias canRedo: textInput.canRedo + + /*! + \qmlproperty bool TextField::canUndo + + Returns true if the TextField is writable and there are previous operations + that can be undone. + */ + readonly property alias canUndo: textInput.canUndo + + /*! + \qmlproperty color TextField::textColor + + The text color. + */ + property alias textColor: textInput.color + + /*! + \qmlproperty int TextField::cursorPosition + The position of the cursor in the TextField. + */ + property alias cursorPosition: textInput.cursorPosition + + /*! + \qmlproperty string TextField::displayText + + This is the text displayed in the TextField. + + If \l echoMode is set to TextInput::Normal, this holds the + same value as the TextField::text property. Otherwise, + this property holds the text visible to the user, while + the \l text property holds the actual entered text. + */ + readonly property alias displayText: textInput.displayText + + /*! + \qmlproperty enumeration TextField::echoMode + + Specifies how the text should be displayed in the TextField. + \list + \li TextInput.Normal - Displays the text as it is. (Default) + \li TextInput.Password - Displays asterisks instead of characters. + \li TextInput.NoEcho - Displays nothing. + \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered + while editing, otherwise displays asterisks. + \endlist + */ + property alias echoMode: textInput.echoMode + + /*! + \qmlproperty font TextField::font + + The font of the TextField. + */ + property alias font: textInput.font + + /*! + \qmlproperty enumeration TextField::horizontalAlignment + + Sets the alignment of the text within the TextField item's width. + + By default, the horizontal text alignment follows the natural alignment of the text, + for example text that is read from left to right will be aligned to the left. + + The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and + \c TextInput.AlignHCenter. + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextField, use the read-only property \c effectiveHorizontalAlignment. + */ + property alias horizontalAlignment: textInput.horizontalAlignment + + /*! + \qmlproperty enumeration TextField::effectiveHorizontalAlignment + + Gets the effective horizontal alignment of the text within the TextField item's width. + + To set/get the default horizontal alignment of TextField, use the property \c horizontalAlignment. + + */ + readonly property alias effectiveHorizontalAlignment: textInput.effectiveHorizontalAlignment + + /*! + \qmlproperty enumeration TextField::verticalAlignment + + Sets the alignment of the text within the TextField item's height. + + The valid valid values for \c verticalAlignment are \c TextInput.AlignTop, + \c TextInput.AlignBottom \c TextInput.AlignVCenter (default). + */ + property alias verticalAlignment: textInput.verticalAlignment + + /*! + \qmlproperty string TextField::inputMask + + Allows you to set an input mask on the TextField, restricting the allowable + text inputs. See QLineEdit::inputMask for further details, as the exact + same mask strings are used by TextField. + + \sa acceptableInput, validator + */ + property alias inputMask: textInput.inputMask + + /*! + \qmlproperty enumeration TextField::inputMethodHints + + Provides hints to the input method about the expected content of the text field and how it + should operate. + + The value is a bit-wise combination of flags, or Qt.ImhNone if no hints are set. + + The default value is Qt.ImhNone. + + Flags that alter behavior are: + + \list + \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords. + This is automatically set when setting echoMode to \c TextInput.Password. + \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method + in any persistent storage like predictive user dictionary. + \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case + when a sentence ends. + \li Qt.ImhPreferNumbers - Numbers are preferred (but not required). + \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required). + \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required). + \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing. + + \li Qt.ImhDate - The text editor functions as a date field. + \li Qt.ImhTime - The text editor functions as a time field. + \endlist + + Flags that restrict input (exclusive flags) are: + + \list + \li Qt.ImhDigitsOnly - Only digits are allowed. + \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign. + \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed. + \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed. + \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed. + \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed. + \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed. + \endlist + + Masks: + + \list + \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used. + \endlist + */ + property alias inputMethodHints: textInput.inputMethodHints + + /*! + \qmlproperty int TextField::length + + Returns the total number of characters in the TextField item. + + If the TextField has an inputMask the length will include mask characters and may differ + from the length of the string returned by the \l text property. + + This property can be faster than querying the length the \l text property as it doesn't + require any copying or conversion of the TextField's internal string data. + */ + readonly property alias length: textInput.length + + /*! + \qmlproperty int TextField::maximumLength + The maximum permitted length of the text in the TextField. + + If the text is too long, it is truncated at the limit. + */ + property alias maximumLength: textInput.maximumLength + + /*! + \qmlproperty string TextField::placeholderText + + The text that is shown in the text field when the text field is empty + and has no focus. + */ + property alias placeholderText: placeholderTextComponent.text + + /*! + \qmlproperty bool TextField::readOnly + + Sets whether user input can modify the contents of the TextField. + The difference from a disabled text field is that it will appear + to be active and text can be selected and copied. + + If readOnly is set to true, then user input will not affect the text + property. Any bindings or attempts to set the text property will still + work. + */ + property alias readOnly: textInput.readOnly + + /*! + \qmlproperty string TextField::selectedText + + This read-only property provides the text currently selected in the + text input. + + It is equivalent to the following snippet, but is faster and easier + to use. + + \js + myTextField.text.toString().substring(myTextField.selectionStart, myTextField.selectionEnd); + \endjs + */ + readonly property alias selectedText: textInput.selectedText + + /*! + \qmlproperty int TextField::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText + */ + readonly property alias selectionEnd: textInput.selectionEnd + + /*! + \qmlproperty int TextField::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText + */ + readonly property alias selectionStart: textInput.selectionStart + + /*! + \qmlproperty string TextField::text + + The text in the TextField. + */ + property alias text: textInput.text + + /*! + \qmlproperty Validator TextField::validator + + Allows you to set a validator on the TextField. When a validator is set + the TextField will only accept input which leaves the text property in + an or intermediate state. The accepted signal will only be sent + if the text is in an acceptable state when enter is pressed. + + Currently supported validators are IntValidator, DoubleValidator and + RegExpValidator. An example of using validators is shown below, which allows + input of integers between 11 and 31 into the text input: + + \code + import QtQuick 2.0 + import QtQuick.Controls 1.0 + + TextField { + validator: IntValidator {bottom: 11; top: 31;} + focus: true + } + \endcode + + \sa acceptableInput, inputMask + */ + property alias validator: textInput.validator + + /*! + \qmlsignal TextField::accepted() + + This signal is emitted when the Return or Enter key is pressed. + Note that if there is a \l validator or \l inputMask set on the text + field, the signal will only be emitted if the input is in an acceptable + state. + */ + signal accepted() + + /*! + \qmlmethod TextField::copy() + + Copies the currently selected text to the system clipboard. + */ + function copy() { + textInput.copy() + } + + /*! + \qmlmethod TextField::cut() + + Moves the currently selected text to the system clipboard. + */ + function cut() { + textInput.cut() + } + + /*! + \qmlmethod TextField::deselect() + + Removes active text selection. + */ + function deselect() { + textInput.deselect(); + } + + /*! + \qmlmethod string TextField::getText(int start, int end) + + Removes the section of text that is between the \a start and \a end positions from the TextField. + */ + function getText(start, end) { + return textInput.getText(start, end); + } + + /*! + \qmlmethod TextField::insert(int position, string text) + + Inserts \a text into the TextField at position. + */ + function insert(position, text) { + textInput.insert(position, text); + } + + /*! + \qmlmethod bool TextField::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. + */ + function isRightToLeft(start, end) { + return textInput.isRightToLeft(start, end); + } + + /*! + \qmlmethod TextField::paste() + + Replaces the currently selected text by the contents of the system clipboard. + */ + function paste() { + textInput.paste() + } + + /*! + \qmlmethod TextField::redo() + + Redoes the last operation if redo is \l {canRedo}{available}. + */ + function redo() { + textInput.redo(); + } + + /*! + \qmlmethod TextField::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd + */ + function select(start, end) { + textInput.select(start, end) + } + + /*! + \qmlmethod TextField::selectAll() + + Causes all text to be selected. + */ + function selectAll() { + textInput.selectAll() + } + + /*! + \qmlmethod TextField::selectWord() + + Causes the word closest to the current cursor position to be selected. + */ + function selectWord() { + textInput.selectWord() + } + + /*! + \qmlmethod TextField::undo() + + Undoes the last operation if undo is \l {canUndo}{available}. Deselects any + current selection, and updates the selection start to the current cursor + position. + */ + function undo() { + textInput.undo(); + } + + /*! \internal */ + property alias __containsMouse: mouseArea.containsMouse + + /*! \internal */ + property alias __contentHeight: textInput.contentHeight + + /*! \internal */ + property alias __contentWidth: textInput.contentWidth + + /*! \internal */ + style: Qt.createComponent(Settings.THEME_PATH + "/TextFieldStyle.qml", textInput) + + /*! \internal */ + onFocusChanged: { + if (textfield.activeFocus) + textInput.forceActiveFocus(); + } + + Accessible.name: text + Accessible.role: Accessible.EditableText + Accessible.description: placeholderText + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: textfield.forceActiveFocus() + } + + TextInput { + id: textInput + selectByMouse: true + selectionColor: __panel ? __panel.selectionColor : "darkred" + selectedTextColor: __panel ? __panel.selectedTextColor : "white" + + font: __panel ? __panel.font : font + anchors.leftMargin: __panel ? __panel.leftMargin : 0 + anchors.topMargin: __panel ? __panel.topMargin : 0 + anchors.rightMargin: __panel ? __panel.rightMargin : 0 + anchors.bottomMargin: __panel ? __panel.bottomMargin : 0 + + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + + color: __panel ? __panel.foregroundColor : "darkgray" + clip: true + renderType: Text.NativeRendering + + onAccepted: textfield.accepted() + } + + Text { + id: placeholderTextComponent + anchors.fill: textInput + font: textInput.font + horizontalAlignment: textInput.horizontalAlignment + verticalAlignment: textInput.verticalAlignment + opacity: !textInput.text.length && !textInput.activeFocus ? 1 : 0 + color: __panel ? __panel.placeholderTextColor : "darkgray" + clip: true + elide: Text.ElideRight + renderType: Text.NativeRendering + Behavior on opacity { NumberAnimation { duration: 90 } } + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } +} diff --git a/src/controls/ToolBar.qml b/src/controls/ToolBar.qml new file mode 100644 index 00000000..2c359239 --- /dev/null +++ b/src/controls/ToolBar.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 + +/*! + \qmltype ToolBar + \inqmlmodule QtQuick.Controls 1.0 + \ingroup applicationwindow + \brief ToolBar is for containing ToolButton and related controls. + + The common way of using ToolBar is in relation to \l ApplicationWindow. + It provides styling and is generally designed to work well with ToolButton as well + as other controls. + + Note that the ToolBar does not provide a layout of its own but requires you to + position its contents, for instance by creating a Row. + + \code + ApplicationWindow { + toolBar: ToolBar { + Row { + ToolButton { ... } + ToolButton { ... } + ToolButton { ... } + } + } + } + \endcode +*/ + +Item { + width: toolbar.width + height: toolbar.height + implicitHeight: toolbar.implicitHeight + implicitWidth: parent ? parent.width : toolbar.implicitWidth + Accessible.role: Accessible.ToolBar + StyleItem { + id: toolbar + anchors.fill: parent + elementType: "toolbar" + } +} diff --git a/src/controls/ToolButton.qml b/src/controls/ToolButton.qml new file mode 100644 index 00000000..d3fd441a --- /dev/null +++ b/src/controls/ToolButton.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 as Private +import "Styles/Settings.js" as Settings + +/*! + \qmltype ToolButton + \inqmlmodule QtQuick.Controls 1.0 + \ingroup applicationwindow + \brief ToolButton provides a button type that is typically used within a ToolBar + + ToolButton is functionally similar to \l Button but can provide a look that is more + suitable within a \l ToolBar. + + \code + ToolButton { + iconSource: "edit-cut.png" + } + \endcode +*/ + +Private.BasicButton { + id: button + + /*! The image label source. */ + property url iconSource + + /*! The label text. */ + property string text + + Image { + id: image + anchors.centerIn: parent + source: button.iconSource + width: Math.min(button.width, image.implicitWidth) + height: Math.min(button.height, image.implicitHeight) + fillMode: Image.PreserveAspectFit + } + Accessible.name: text + + style: Qt.createComponent(Settings.THEME_PATH + "/ToolButtonStyle.qml", button) +} diff --git a/src/controls/controls.pro b/src/controls/controls.pro new file mode 100644 index 00000000..7af5a965 --- /dev/null +++ b/src/controls/controls.pro @@ -0,0 +1,45 @@ +CXX_MODULE = qml +TARGET = plugin +TARGETPATH = QtQuick/Controls + +QT += qml quick widgets gui-private core-private + +QMAKE_DOCS = $$PWD/doc/qtquickcontrols.qdocconf + +QML_FILES = \ + AbstractCheckable.qml \ + ApplicationWindow.qml \ + Button.qml \ + CheckBox.qml \ + ComboBox.qml \ + ContextMenu.qml \ + GroupBox.qml \ + Label.qml \ + MenuBar.qml \ + Menu.qml \ + Page.qml \ + PageAnimation.qml \ + PageStack.qml \ + PageTransition.qml \ + ProgressBar.qml \ + RadioButton.qml \ + ScrollArea.qml \ + Slider.qml \ + SpinBox.qml \ + Splitter.qml \ + StatusBar.qml \ + Tab.qml \ + TabFrame.qml \ + TableView.qml \ + TableViewColumn.qml \ + TextArea.qml \ + TextField.qml \ + ToolBar.qml \ + ToolButton.qml + +include(plugin.pri) + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + + +load(qml_plugin) diff --git a/src/controls/doc/images/placeholder.png b/src/controls/doc/images/placeholder.png Binary files differnew file mode 100644 index 00000000..c64ff5bd --- /dev/null +++ b/src/controls/doc/images/placeholder.png diff --git a/src/controls/doc/images/tableview.png b/src/controls/doc/images/tableview.png Binary files differnew file mode 100644 index 00000000..e7e5e27d --- /dev/null +++ b/src/controls/doc/images/tableview.png diff --git a/src/controls/doc/qtquickcontrols.qdocconf b/src/controls/doc/qtquickcontrols.qdocconf new file mode 100644 index 00000000..3977be50 --- /dev/null +++ b/src/controls/doc/qtquickcontrols.qdocconf @@ -0,0 +1,90 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +# Name of the project. +project = QtQuickControls +description = Qt Quick Controls Documentation + +depends = qtqml qtquick qtwidgets qtdoc + +# Directories in which to search for files to document and images. +# By default set to the root directory of the project for sources +# and headers and qdoc will therefore generate output for each file. +# Images should be placed in <rootdir>/dic/images and examples in +# <rootdir>/examples. +# Paths are relative to the location of this file. + +exampledirs += ../../../examples/ + +headerdirs += ../ + +sourcedirs += ../ + +sources += ../../private/qstyleitem.cpp \ + ../../private/BasicButton.qml \ + ../../private/ButtonBehavior.qml \ + ../../private/FocusFrame.qml \ + ../../private/ModalPopupBehavior.qml \ + ../../private/PageSlideTransition.qml \ + ../../private/ScrollAreaHelper.qml \ + ../../private/ScrollBar.qml \ + ../../private/SplitterBase.qml \ + ../../private/TabBar.qml \ + ../../private/Control.qml \ + ../../styles/Style.qml \ + ../../styles/ButtonStyle.qml \ + ../../styles/CheckBoxStyle.qml \ + ../../styles/ComboBoxStyle.qml \ + ../../styles/GroupBoxStyle.qml \ + ../../styles/MenuBarItemStyle.qml \ + ../../styles/MenuBarStyle.qml \ + ../../styles/MenuFrameStyle.qml \ + ../../styles/MenuItemStyle.qml \ + ../../styles/ProgressBarStyle.qml \ + ../../styles/RadioButtonStyle.qml \ + ../../styles/ScrollAreaStyle.qml \ + ../../styles/ScrollBarStyle.qml \ + ../../styles/SliderStyle.qml \ + ../../styles/SpinBoxStyle.qml \ + ../../styles/TabFrameStyle.qml \ + ../../styles/TextFieldStyle.qml \ + ../../styles/ToolBarStyle.qml \ + ../../styles/ToolButtonStyle.qml + +imagedirs += images + +# The following parameters are for creating a qhp file, the qhelpgenerator +# program can convert the qhp file into a qch file which can be opened in +# Qt Assistant and/or Qt Creator. + +# Defines the name of the project. You cannot use operators (+, =, -) in +# the name. Properties for this project are set using a qhp.<projectname>.property +# format. +qhp.projects = qtquickcontrols + +# Sets the name of the output qhp file. +qhp.qtquickcontrols.file = qtquickcontrols.qhp + +# Namespace for the output file. This namespace is used to distinguish between +# different documentation files in Creator/Assistant. +qhp.qtquickcontrols.namespace = qtquickcontrols.100 + +# Title for the package, will be the main title for the package in +# Assistant/Creator. +qhp.qtquickcontrols.indexTitle = Qt Quick Controls + +# Extra files to add to the output which are not linked to from anywhere +# using a qdoc \l command. +#qhp.qtquickcontrols.extraFiles = style/qtquickcontrols.css + +# Only update the name of the project for the next variables. +qhp.qtquickcontrols.virtualFolder = qtquickcontrols + +qhp.qtquickcontrols.subprojects = qtquickcontrolsqmltypes styleqmltypes +qhp.qtquickcontrols.subprojects.qtquickcontrolsqmltypes.title = Qt Quick Controls QML Types +qhp.qtquickcontrols.subprojects.qtquickcontrolsqmltypes.indexTitle = Qt Quick Controls QML Types +qhp.qtquickcontrols.subprojects.qtquickcontrolsqmltypes.selectors = class fake:headerfile +qhp.qtquickcontrols.subprojects.qtquickcontrolsqmltypes.sortPages = true +qhp.qtquickcontrols.subprojects.styleqmltypes.title = Styles QML Types +qhp.qtquickcontrols.subprojects.styleqmltypes.indexTitle = Qt Quick Controls Styles QML Types +qhp.qtquickcontrols.subprojects.styleqmltypes.selectors = class fake:headerfile +qhp.qtquickcontrols.subprojects.styleqmltypes.sortPages = true diff --git a/src/controls/doc/src/applicationwindow.qdoc b/src/controls/doc/src/applicationwindow.qdoc new file mode 100644 index 00000000..f9e2d97f --- /dev/null +++ b/src/controls/doc/src/applicationwindow.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group applicationwindow + \title Application Window +*/ diff --git a/src/controls/doc/src/containers.qdoc b/src/controls/doc/src/containers.qdoc new file mode 100644 index 00000000..6a0ab02d --- /dev/null +++ b/src/controls/doc/src/containers.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group containers + \title Layout and Containers +*/ diff --git a/src/controls/doc/src/controls.qdoc b/src/controls/doc/src/controls.qdoc new file mode 100644 index 00000000..d1e4d052 --- /dev/null +++ b/src/controls/doc/src/controls.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group controls + \title Buttons and Controls +*/ diff --git a/src/controls/doc/src/indicators.qdoc b/src/controls/doc/src/indicators.qdoc new file mode 100644 index 00000000..f31dec9e --- /dev/null +++ b/src/controls/doc/src/indicators.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group indicators + \title Status Indicators +*/ diff --git a/src/controls/doc/src/menus.qdoc b/src/controls/doc/src/menus.qdoc new file mode 100644 index 00000000..ae8554ea --- /dev/null +++ b/src/controls/doc/src/menus.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group menus + \title Application Menus +*/ diff --git a/src/controls/doc/src/navigation.qdoc b/src/controls/doc/src/navigation.qdoc new file mode 100644 index 00000000..b1bd8ef8 --- /dev/null +++ b/src/controls/doc/src/navigation.qdoc @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group navigation + \title Application Navigation +*/ diff --git a/src/controls/doc/src/qtquickcontrols-examples.qdoc b/src/controls/doc/src/qtquickcontrols-examples.qdoc new file mode 100644 index 00000000..04fa1f4a --- /dev/null +++ b/src/controls/doc/src/qtquickcontrols-examples.qdoc @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group qtquickcontrols_examples + \ingroup all-examples + \title Qt Quick Controls Examples + \brief Demonstrates the ... functionality provided by Qt. +*/ diff --git a/src/controls/doc/src/qtquickcontrols-index.qdoc b/src/controls/doc/src/qtquickcontrols-index.qdoc new file mode 100644 index 00000000..1f3ab44e --- /dev/null +++ b/src/controls/doc/src/qtquickcontrols-index.qdoc @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtquickcontrols-index.html + \title Qt Quick Controls + + \brief The Qt Quick Controls module is an add-on module of Qt. + + \image placeholder.png + + \section1 Getting started + + The QML types can be imported into your applciation using the following import statement in your \c {.qml} file. + + \code + import QtQuick.Controls 1.0 + import QtQuick.Controls.Styles 1.0 + \endcode + + \section1 Components + + \section2 Application + \list + \li \l{Application Window}{Window} + \li \l{Application Menus}{Menus} + \li \l{Application Navigation}{Navigation} + \endlist + + \section2 Controls and Layout + \list + \li \l{Buttons and Controls} + \li \l{Layout and Containers} + \li \l{Tables} + \li \l{Status Indicators} + \endlist + + \section1 Related information + + \section2 Guides + \list + \li \l{Qt Quick Controls Overview} + \endlist + + \section2 Reference + \list + \li \l{Qt Quick Controls QML Types}{Qt Quick Controls QML Types} + \li \l{Qt Quick Controls Styles QML Types}{Qt Quick Controls Styles QML Types} + \endlist + + \section2 Examples + \list + \li \l{Qt Quick Controls Examples} + \endlist +*/ diff --git a/src/controls/doc/src/qtquickcontrols-overview.qdoc b/src/controls/doc/src/qtquickcontrols-overview.qdoc new file mode 100644 index 00000000..82da0b22 --- /dev/null +++ b/src/controls/doc/src/qtquickcontrols-overview.qdoc @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtquickcontrols-overview.html + \title Qt Quick Controls Overview + \brief A set of APIs for working with ... +*/ diff --git a/src/controls/doc/src/qtquickcontrols.qdoc b/src/controls/doc/src/qtquickcontrols.qdoc new file mode 100644 index 00000000..ddbfc5d6 --- /dev/null +++ b/src/controls/doc/src/qtquickcontrols.qdoc @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtQuick.Controls 1 + \title Qt Quick Controls QML Types + \ingroup qmlmodules + \brief Provides QML types for qt quick controls support. + + This is just a demo page. + + \section1 Overview + + The QML types for Qt Quick Controls support the basic use cases such as: + \list + \li sth 1, + \li sth 2, + \endlist + + \section1 QML types +*/ diff --git a/src/controls/doc/src/styles.qdoc b/src/controls/doc/src/styles.qdoc new file mode 100644 index 00000000..36dc302f --- /dev/null +++ b/src/controls/doc/src/styles.qdoc @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtQuick.Controls.Styles 1 + \title Qt Quick Controls Styles QML Types + \ingroup qmlmodules + \brief Provides QML types for qt quick controls styles support. + + This is just a demo page. + + \section1 Overview + + The QML types for Qt Quick Controls Styles support the basic use cases such as: + \list + \li sth 1, + \li sth 2, + \endlist + + \section1 QML types +*/ diff --git a/src/controls/doc/src/views.qdoc b/src/controls/doc/src/views.qdoc new file mode 100644 index 00000000..c92cb7da --- /dev/null +++ b/src/controls/doc/src/views.qdoc @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group views + \title Views + + \image placeholder.png +*/ diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp new file mode 100644 index 00000000..d8bdd9ef --- /dev/null +++ b/src/controls/plugin.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include <qqml.h> +#include "plugin_p.h" +#include "qtaction_p.h" +#include "qtexclusivegroup_p.h" +#include "qtmenu_p.h" +#include "qtmenubar_p.h" +#include "qquicklinearlayout_p.h" +#include "qpagestatus.h" +#include <qqmlextensionplugin.h> + +#include <qqmlengine.h> +#include <qquickimageprovider.h> +#include <QtWidgets/QApplication> +#include <QtQuick/QQuickWindow> +#include <QImage> + +QT_BEGIN_NAMESPACE + +// Load icons from desktop theme +class DesktopIconProvider : public QQuickImageProvider +{ +public: + DesktopIconProvider() + : QQuickImageProvider(QQuickImageProvider::Pixmap) + { + } + + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) + { + Q_UNUSED(requestedSize); + Q_UNUSED(size); + int pos = id.lastIndexOf('/'); + QString iconName = id.right(id.length() - pos); + int width = requestedSize.width(); + return QIcon::fromTheme(iconName).pixmap(width); + } +}; + +void StylePlugin::registerTypes(const char *uri) +{ + qmlRegisterType<QtAction>(uri, 1, 0, "Action"); + qmlRegisterType<QtExclusiveGroup>(uri, 1, 0, "ExclusiveGroup"); + qmlRegisterType<QtMenu>(uri, 1, 0, "MenuPrivate"); + qmlRegisterType<QtMenuBar>(uri, 1, 0, "MenuBarPrivate"); + qmlRegisterType<QtMenuItem>(uri, 1, 0, "MenuItem"); + qmlRegisterType<QtMenuSeparator>(uri, 1, 0, "MenuSeparator"); + qmlRegisterUncreatableType<QtMenuBase>(uri, 1, 0, "NativeMenuBase", + QLatin1String("Do not create objects of type NativeMenuBase")); + + qmlRegisterType<QQuickComponentsRowLayout>(uri, 1, 0, "RowLayout"); + qmlRegisterType<QQuickComponentsColumnLayout>(uri, 1, 0, "ColumnLayout"); + qmlRegisterUncreatableType<QQuickComponentsLayout>(uri, 1, 0, "Layout", + QLatin1String("Do not create objects of type Layout")); + + qmlRegisterUncreatableType<QPageStatus>(uri, 1, 0, "PageStatus", QLatin1String("Do not create objects of type PageStatus")); +} + +void StylePlugin::initializeEngine(QQmlEngine *engine, const char *uri) +{ + Q_UNUSED(uri); + engine->addImageProvider("desktoptheme", new DesktopIconProvider); +} + +QT_END_NAMESPACE diff --git a/src/controls/plugin.json b/src/controls/plugin.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/src/controls/plugin.json @@ -0,0 +1 @@ +{} diff --git a/src/controls/plugin.pri b/src/controls/plugin.pri new file mode 100644 index 00000000..5637e75c --- /dev/null +++ b/src/controls/plugin.pri @@ -0,0 +1,27 @@ +HEADERS += \ + $$PWD/qquicklayout_p.h \ + $$PWD/qquicklayoutengine_p.h \ + $$PWD/qquicklinearlayout_p.h \ + $$PWD/plugin_p.h \ + $$PWD/qtaction_p.h \ + $$PWD/qtexclusivegroup_p.h \ + $$PWD/qtmenu_p.h \ + $$PWD/qtmenubar_p.h \ + $$PWD/qtmenuitem_p.h \ + $$PWD/qtmenupopupwindow_p.h \ + $$PWD/qpagestatus.h + +SOURCES += \ + $$PWD/qquicklayout.cpp \ + $$PWD/qquicklayoutengine.cpp \ + $$PWD/qquicklinearlayout.cpp \ + $$PWD/plugin.cpp \ + $$PWD/qtaction.cpp \ + $$PWD/qtexclusivegroup.cpp \ + $$PWD/qtmenu.cpp \ + $$PWD/qtmenubar.cpp \ + $$PWD/qtmenuitem.cpp \ + $$PWD/qtmenupopupwindow.cpp + +OTHER_FILES += \ + $$PWD/plugin.json diff --git a/src/controls/plugin_p.h b/src/controls/plugin_p.h new file mode 100644 index 00000000..c6c3fee3 --- /dev/null +++ b/src/controls/plugin_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QSTYLEPLUGIN_P_H +#define QSTYLEPLUGIN_P_H + +#include <QQmlExtensionPlugin> +#include <QtCore/QTimer> +#include <QtWidgets/QFileSystemModel> + +QT_BEGIN_NAMESPACE + +class StylePlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.playground.qtquickcontrols.QQmlExtensionInterface" FILE "plugin.json") +public: + void registerTypes(const char *uri); + void initializeEngine(QQmlEngine *engine, const char *uri); +}; + +QT_END_NAMESPACE + +#endif // QSTYLEPLUGIN_P_H diff --git a/src/controls/plugins.qmltypes b/src/controls/plugins.qmltypes new file mode 100644 index 00000000..734c3878 --- /dev/null +++ b/src/controls/plugins.qmltypes @@ -0,0 +1,405 @@ +import QtQuick.tooling 1.1 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated with the command 'qmlplugindump QtDesktop 1.0 imports/'. + +Module { + Component { + name: "QDesktopItem" + prototype: "QObject" + exports: ["Desktop 1.0"] + Property { name: "screenWidth"; type: "int"; isReadonly: true } + Property { name: "screenHeight"; type: "int"; isReadonly: true } + Property { name: "availableWidth"; type: "int"; isReadonly: true } + Property { name: "availableHeight"; type: "int"; isReadonly: true } + Property { name: "screenCount"; type: "int"; isReadonly: true } + Signal { name: "screenGeometryChanged" } + Signal { name: "availableGeometryChanged" } + Method { + name: "screenGeometry" + type: "QRect" + Parameter { name: "screen"; type: "int" } + } + Method { + name: "availableGeometry" + type: "QRect" + Parameter { name: "screen"; type: "int" } + } + } + Component { + name: "QFileDialogItem" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["FileDialog 1.0"] + Property { name: "visible"; type: "bool" } + Property { name: "modal"; type: "bool" } + Property { name: "title"; type: "string" } + Property { name: "selectExisting"; type: "bool" } + Property { name: "selectMultiple"; type: "bool" } + Property { name: "selectFolder"; type: "bool" } + Property { name: "folder"; type: "string" } + Property { name: "nameFilters"; type: "QStringList" } + Property { name: "filePath"; type: "string"; isReadonly: true } + Property { name: "filePaths"; type: "QStringList"; isReadonly: true } + Signal { name: "modalityChanged" } + Signal { name: "accepted" } + Signal { name: "rejected" } + Method { name: "open" } + Method { name: "close" } + } + Component { + name: "QFileSystemModel" + prototype: "QAbstractItemModel" + exports: ["FileSystemModel 1.0"] + Property { name: "resolveSymlinks"; type: "bool" } + Property { name: "readOnly"; type: "bool" } + Property { name: "nameFilterDisables"; type: "bool" } + Signal { + name: "rootPathChanged" + Parameter { name: "newPath"; type: "string" } + } + Signal { + name: "fileRenamed" + Parameter { name: "path"; type: "string" } + Parameter { name: "oldName"; type: "string" } + Parameter { name: "newName"; type: "string" } + } + Signal { + name: "directoryLoaded" + Parameter { name: "path"; type: "string" } + } + } + Component { + name: "QQuickComponentsColumnLayout" + defaultProperty: "data" + prototype: "QQuickComponentsLinearLayout" + exports: ["ColumnLayout 1.0"] + } + Component { + name: "QQuickComponentsLayout" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["Layout 1.0"] + attachedType: "QQuickComponentsLayoutAttached" + Enum { + name: "SizePolicy" + values: { + "Fixed": 0, + "Expanding": 1 + } + } + } + Component { + name: "QQuickComponentsLayoutAttached" + prototype: "QObject" + Property { name: "minimumWidth"; type: "double" } + Property { name: "minimumHeight"; type: "double" } + Property { name: "maximumWidth"; type: "double" } + Property { name: "maximumHeight"; type: "double" } + Property { name: "verticalSizePolicy"; type: "QQuickComponentsLayout::SizePolicy" } + Property { name: "horizontalSizePolicy"; type: "QQuickComponentsLayout::SizePolicy" } + } + Component { + name: "QQuickComponentsLinearLayout" + defaultProperty: "data" + prototype: "QQuickComponentsLayout" + Property { name: "spacing"; type: "double" } + Signal { name: "orientationChanged" } + } + Component { + name: "QQuickComponentsPrivate" + prototype: "QObject" + exports: ["PrivateHelper 1.0"] + Method { + name: "showToolTip" + Parameter { name: "item"; type: "QQuickItem"; isPointer: true } + Parameter { name: "pos"; type: "QPointF" } + Parameter { name: "text"; type: "string" } + } + Method { name: "hideToolTip" } + } + Component { + name: "QQuickComponentsRowLayout" + defaultProperty: "data" + prototype: "QQuickComponentsLinearLayout" + exports: ["RowLayout 1.0"] + } + Component { + name: "QRangeModel" + prototype: "QObject" + exports: ["RangeModel 1.0"] + Property { name: "value"; type: "double" } + Property { name: "minimumValue"; type: "double" } + Property { name: "maximumValue"; type: "double" } + Property { name: "stepSize"; type: "double" } + Property { name: "position"; type: "double" } + Property { name: "positionAtMinimum"; type: "double" } + Property { name: "positionAtMaximum"; type: "double" } + Property { name: "inverted"; type: "bool" } + Signal { + name: "valueChanged" + Parameter { name: "value"; type: "double" } + } + Signal { + name: "positionChanged" + Parameter { name: "position"; type: "double" } + } + Signal { + name: "stepSizeChanged" + Parameter { name: "stepSize"; type: "double" } + } + Signal { + name: "invertedChanged" + Parameter { name: "inverted"; type: "bool" } + } + Signal { + name: "minimumChanged" + Parameter { name: "min"; type: "double" } + } + Signal { + name: "maximumChanged" + Parameter { name: "max"; type: "double" } + } + Signal { + name: "positionAtMinimumChanged" + Parameter { name: "min"; type: "double" } + } + Signal { + name: "positionAtMaximumChanged" + Parameter { name: "max"; type: "double" } + } + Method { name: "toMinimum" } + Method { name: "toMaximum" } + Method { + name: "setValue" + Parameter { name: "value"; type: "double" } + } + Method { + name: "setPosition" + Parameter { name: "position"; type: "double" } + } + Method { + name: "valueForPosition" + type: "double" + Parameter { name: "position"; type: "double" } + } + Method { + name: "positionForValue" + type: "double" + Parameter { name: "value"; type: "double" } + } + } + Component { + name: "QStyleItem" + defaultProperty: "data" + prototype: "QQuickPaintedItem" + exports: ["StyleItem 1.0"] + Property { name: "sunken"; type: "bool" } + Property { name: "raised"; type: "bool" } + Property { name: "active"; type: "bool" } + Property { name: "selected"; type: "bool" } + Property { name: "hasFocus"; type: "bool" } + Property { name: "on"; type: "bool" } + Property { name: "hover"; type: "bool" } + Property { name: "horizontal"; type: "bool" } + Property { name: "elementType"; type: "string" } + Property { name: "text"; type: "string" } + Property { name: "activeControl"; type: "string" } + Property { name: "info"; type: "string" } + Property { name: "style"; type: "string"; isReadonly: true } + Property { name: "hint"; type: "string" } + Property { name: "font"; type: "QFont"; isReadonly: true } + Property { name: "minimum"; type: "int" } + Property { name: "maximum"; type: "int" } + Property { name: "value"; type: "int" } + Property { name: "step"; type: "int" } + Property { name: "paintMargins"; type: "int" } + Property { name: "contentWidth"; type: "int" } + Property { name: "contentHeight"; type: "int" } + Signal { + name: "contentWidthChanged" + Parameter { name: "arg"; type: "int" } + } + Signal { + name: "contentHeightChanged" + Parameter { name: "arg"; type: "int" } + } + Method { + name: "pixelMetric" + type: "int" + Parameter { type: "string" } + } + Method { + name: "styleHint" + type: "QVariant" + Parameter { type: "string" } + } + Method { name: "updateSizeHint" } + Method { name: "updateItem" } + Method { + name: "hitTest" + type: "string" + Parameter { name: "x"; type: "int" } + Parameter { name: "y"; type: "int" } + } + Method { + name: "subControlRect" + type: "QRectF" + Parameter { name: "subcontrolString"; type: "string" } + } + Method { + name: "elidedText" + type: "string" + Parameter { name: "text"; type: "string" } + Parameter { name: "elideMode"; type: "int" } + Parameter { name: "width"; type: "int" } + } + Method { + name: "textWidth" + type: "int" + Parameter { type: "string" } + } + Method { + name: "hasThemeIcon" + type: "bool" + Parameter { type: "string" } + } + Method { + name: "setContentWidth" + Parameter { name: "arg"; type: "int" } + } + Method { + name: "setContentHeight" + Parameter { name: "arg"; type: "int" } + } + } + Component { + name: "QWheelArea" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["WheelArea 1.0"] + Property { name: "verticalDelta"; type: "double" } + Property { name: "horizontalDelta"; type: "double" } + Property { name: "horizontalMinimumValue"; type: "double" } + Property { name: "horizontalMaximumValue"; type: "double" } + Property { name: "verticalMinimumValue"; type: "double" } + Property { name: "verticalMaximumValue"; type: "double" } + Property { name: "horizontalValue"; type: "double" } + Property { name: "verticalValue"; type: "double" } + Property { name: "scrollSpeed"; type: "double" } + Signal { name: "verticalWheelMoved" } + Signal { name: "horizontalWheelMoved" } + } + Component { + name: "QWindowItem" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["Window 0.1", "Window 1.0"] + Property { name: "minimumHeight"; type: "int" } + Property { name: "maximumHeight"; type: "int" } + Property { name: "minimumWidth"; type: "int" } + Property { name: "maximumWidth"; type: "int" } + Property { name: "visible"; type: "bool" } + Property { name: "windowDecoration"; type: "bool" } + Property { name: "modal"; type: "bool" } + Property { name: "deleteOnClose"; type: "bool" } + Property { name: "windowState"; type: "Qt::WindowState" } + Property { name: "title"; type: "string" } + Signal { name: "modalityChanged" } + Method { name: "close" } + } + Component { + name: "QtMenu" + defaultProperty: "menuItems" + prototype: "QtMenuBase" + exports: ["Menu 1.0"] + Property { name: "text"; type: "string" } + Property { name: "model"; type: "QVariant" } + Property { name: "selectedIndex"; type: "int" } + Property { name: "hoveredIndex"; type: "int" } + Property { name: "menuItems"; type: "QtMenuBase"; isList: true; isReadonly: true } + Signal { name: "menuClosed" } + Signal { + name: "modelChanged" + Parameter { name: "newModel"; type: "QVariant" } + } + Signal { name: "rebuldMenu" } + Method { + name: "setModel" + Parameter { name: "newModel"; type: "QVariant" } + } + Method { name: "minimumWidth"; type: "int" } + Method { + name: "setMinimumWidth" + Parameter { name: "w"; type: "int" } + } + Method { + name: "showPopup" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + Parameter { name: "atActionIndex"; type: "int" } + } + Method { + name: "showPopup" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + Method { name: "hidePopup" } + Method { name: "clearMenuItems" } + Method { + name: "addMenuItem" + Parameter { name: "text"; type: "string" } + } + Method { + name: "itemTextAt" + type: "string" + Parameter { name: "index"; type: "int" } + } + Method { + name: "modelTextAt" + type: "string" + Parameter { name: "index"; type: "int" } + } + Method { name: "modelCount"; type: "int" } + Method { name: "hasNativeModel"; type: "bool" } + } + Component { + name: "QtMenuBar" + defaultProperty: "menus" + prototype: "QQuickItem" + exports: ["MenuBar 1.0"] + Property { name: "menus"; type: "QtMenu"; isList: true; isReadonly: true } + } + Component { + name: "QtMenuBase" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["NativeMenuBase 0.1"] + Property { name: "iconSource"; type: "QUrl" } + Property { name: "iconName"; type: "string" } + } + Component { + name: "QtMenuItem" + defaultProperty: "data" + prototype: "QtMenuBase" + exports: ["MenuItem 1.0"] + Property { name: "text"; type: "string" } + Property { name: "shortcut"; type: "string" } + Property { name: "checkable"; type: "bool" } + Property { name: "checked"; type: "bool" } + Property { name: "enabled"; type: "bool" } + Signal { name: "triggered" } + Signal { + name: "toggled" + Parameter { type: "bool" } + } + } + Component { + name: "QtMenuSeparator" + defaultProperty: "data" + prototype: "QtMenuBase" + exports: ["Separator 1.0"] + } +} diff --git a/src/controls/qmldir b/src/controls/qmldir new file mode 100644 index 00000000..b3de1110 --- /dev/null +++ b/src/controls/qmldir @@ -0,0 +1,30 @@ +module QtQuick.Controls +plugin plugin +ApplicationWindow 1.0 ApplicationWindow.qml +Button 1.0 Button.qml +CheckBox 1.0 CheckBox.qml +ComboBox 1.0 ComboBox.qml +ContextMenu 1.0 ContextMenu.qml +GroupBox 1.0 GroupBox.qml +Label 1.0 Label.qml +MenuBar 1.0 MenuBar.qml +Menu 1.0 Menu.qml +Page 1.0 Page.qml +PageAnimation 1.0 PageAnimation.qml +PageStack 1.0 PageStack.qml +PageTransition 1.0 PageTransition.qml +ProgressBar 1.0 ProgressBar.qml +RadioButton 1.0 RadioButton.qml +ScrollArea 1.0 ScrollArea.qml +Slider 1.0 Slider.qml +SpinBox 1.0 SpinBox.qml +Splitter 1.0 Splitter.qml +StatusBar 1.0 StatusBar.qml +Tab 1.0 Tab.qml +TabFrame 1.0 TabFrame.qml +TableView 1.0 TableView.qml +TableViewColumn 1.0 TableViewColumn.qml +TextArea 1.0 TextArea.qml +TextField 1.0 TextField.qml +ToolBar 1.0 ToolBar.qml +ToolButton 1.0 ToolButton.qml diff --git a/src/controls/qpagestatus.h b/src/controls/qpagestatus.h new file mode 100644 index 00000000..6ba8930a --- /dev/null +++ b/src/controls/qpagestatus.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QPAGESTATUS_H +#define QPAGESTATUS_H + +#include <QtQml> +#include <QtQuick> + +QT_BEGIN_NAMESPACE + +class QPageStatus : public QObject +{ + Q_OBJECT + Q_ENUMS(PageStatus) + +public: + enum PageStatus { + Inactive = 0, + Deactivating = 1, + Activating = 2, + Active = 3 + }; +}; + +QT_END_NAMESPACE + +#endif // QPAGESTATUS_H diff --git a/src/controls/qquicklayout.cpp b/src/controls/qquicklayout.cpp new file mode 100644 index 00000000..f0decf88 --- /dev/null +++ b/src/controls/qquicklayout.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qquicklayout_p.h" +#include <QEvent> +#include <QApplication> +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +static const qreal q_declarativeLayoutMaxSize = 10e8; + + +QQuickComponentsLayoutAttached::QQuickComponentsLayoutAttached(QObject *parent) + : QObject(parent), + m_minimumWidth(0), + m_minimumHeight(0), + m_maximumWidth(q_declarativeLayoutMaxSize), + m_maximumHeight(q_declarativeLayoutMaxSize), + m_verticalSizePolicy(QQuickComponentsLayout::Fixed), + m_horizontalSizePolicy(QQuickComponentsLayout::Fixed) +{ + +} + +void QQuickComponentsLayoutAttached::setMinimumWidth(qreal width) +{ + if (qIsNaN(width) || m_minimumWidth == width) + return; + + m_minimumWidth = width; + updateLayout(); + emit minimumWidthChanged(); +} + +void QQuickComponentsLayoutAttached::setMinimumHeight(qreal height) +{ + if (qIsNaN(height) || m_minimumHeight == height) + return; + + m_minimumHeight = height; + updateLayout(); + emit minimumHeightChanged(); +} + +void QQuickComponentsLayoutAttached::setMaximumWidth(qreal width) +{ + if (qIsNaN(width) || m_maximumWidth == width) + return; + + m_maximumWidth = width; + updateLayout(); + emit maximumWidthChanged(); +} + +void QQuickComponentsLayoutAttached::setMaximumHeight(qreal height) +{ + if (qIsNaN(height) || m_maximumHeight == height) + return; + + m_maximumHeight = height; + updateLayout(); + emit maximumHeightChanged(); +} + +void QQuickComponentsLayoutAttached::setVerticalSizePolicy(QQuickComponentsLayout::SizePolicy policy) +{ + if (m_verticalSizePolicy != policy) { + m_verticalSizePolicy = policy; + updateLayout(); + emit verticalSizePolicyChanged(); + } +} + +void QQuickComponentsLayoutAttached::setHorizontalSizePolicy(QQuickComponentsLayout::SizePolicy policy) +{ + if (m_horizontalSizePolicy != policy) { + m_horizontalSizePolicy = policy; + updateLayout(); + emit horizontalSizePolicyChanged(); + } +} + +void QQuickComponentsLayoutAttached::updateLayout() +{ + if (m_layout) + m_layout->invalidate(); +} + + + +QQuickComponentsLayout::QQuickComponentsLayout(QQuickItem *parent) + : QQuickItem(parent), + m_dirty(false) +{ + +} + +QQuickComponentsLayout::~QQuickComponentsLayout() +{ + +} + +void QQuickComponentsLayout::setupItemLayout(QQuickItem *item) +{ + QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(item); + QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); + info->m_layout = this; +} + +QQuickComponentsLayoutAttached *QQuickComponentsLayout::qmlAttachedProperties(QObject *object) +{ + return new QQuickComponentsLayoutAttached(object); +} + +bool QQuickComponentsLayout::event(QEvent *e) +{ + if (e->type() == QEvent::LayoutRequest) + reconfigureTopDown(); + + return QQuickItem::event(e); +} + +void QQuickComponentsLayout::invalidate() +{ + if (m_dirty) + return; + + QQuickComponentsLayout *layout = this; + QQuickComponentsLayout *parentLayout = 0; + + while (!layout->m_dirty) { + layout->m_dirty = true; + parentLayout = qobject_cast<QQuickComponentsLayout *>(layout->parentItem()); + + if (!parentLayout) + break; + else + layout = parentLayout; + } + + // just post events for top level layouts + if (!parentLayout) + QApplication::postEvent(layout, new QEvent(QEvent::LayoutRequest)); +} + +void QQuickComponentsLayout::reconfigureTopDown() +{ + const QList<QQuickItem *> &children = childItems(); + + reconfigureLayout(); + + foreach (QQuickItem *child, children) { + QQuickComponentsLayout *layout = qobject_cast<QQuickComponentsLayout *>(child); + + if (layout && layout->m_dirty) + layout->reconfigureTopDown(); + } + + m_dirty = false; +} + +void QQuickComponentsLayout::reconfigureLayout() +{ + +} + +QT_END_NAMESPACE diff --git a/src/controls/qquicklayout_p.h b/src/controls/qquicklayout_p.h new file mode 100644 index 00000000..d435f35f --- /dev/null +++ b/src/controls/qquicklayout_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QQUICKLAYOUT_P_H +#define QQUICKLAYOUT_P_H + +#include <QPointer> +#include <QQuickItem> + +QT_BEGIN_NAMESPACE + +class QQuickComponentsLayoutAttached; + + +class QQuickComponentsLayout : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(SizePolicy) + +public: + enum SizePolicy { + Fixed, + Expanding + }; + + explicit QQuickComponentsLayout(QQuickItem *parent = 0); + ~QQuickComponentsLayout(); + + static QQuickComponentsLayoutAttached *qmlAttachedProperties(QObject *object); + +protected: + void invalidate(); + bool event(QEvent *e); + void reconfigureTopDown(); + virtual void reconfigureLayout(); + void setupItemLayout(QQuickItem *item); + +private: + bool m_dirty; + + friend class QQuickComponentsLayoutAttached; +}; + + +class QQuickComponentsLayoutAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) + Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged) + Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) + Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged) + Q_PROPERTY(QQuickComponentsLayout::SizePolicy verticalSizePolicy READ verticalSizePolicy WRITE setVerticalSizePolicy NOTIFY verticalSizePolicyChanged) + Q_PROPERTY(QQuickComponentsLayout::SizePolicy horizontalSizePolicy READ horizontalSizePolicy WRITE setHorizontalSizePolicy NOTIFY horizontalSizePolicyChanged) + +public: + QQuickComponentsLayoutAttached(QObject *object); + + qreal minimumWidth() const { return m_minimumWidth; } + void setMinimumWidth(qreal width); + + qreal minimumHeight() const { return m_minimumHeight; } + void setMinimumHeight(qreal height); + + qreal maximumWidth() const { return m_maximumWidth; } + void setMaximumWidth(qreal width); + + qreal maximumHeight() const { return m_maximumHeight; } + void setMaximumHeight(qreal height); + + QQuickComponentsLayout::SizePolicy verticalSizePolicy() const { return m_verticalSizePolicy; } + void setVerticalSizePolicy(QQuickComponentsLayout::SizePolicy policy); + + QQuickComponentsLayout::SizePolicy horizontalSizePolicy() const { return m_horizontalSizePolicy; } + void setHorizontalSizePolicy(QQuickComponentsLayout::SizePolicy policy); + +signals: + void minimumWidthChanged(); + void minimumHeightChanged(); + void maximumWidthChanged(); + void maximumHeightChanged(); + void verticalSizePolicyChanged(); + void horizontalSizePolicyChanged(); + +protected: + void updateLayout(); + +private: + qreal m_minimumWidth; + qreal m_minimumHeight; + qreal m_maximumWidth; + qreal m_maximumHeight; + QQuickComponentsLayout::SizePolicy m_verticalSizePolicy; + QQuickComponentsLayout::SizePolicy m_horizontalSizePolicy; + QPointer<QQuickComponentsLayout> m_layout; + + friend class QQuickComponentsLayout; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickComponentsLayout) +QML_DECLARE_TYPEINFO(QQuickComponentsLayout, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKLAYOUT_P_H diff --git a/src/controls/qquicklayoutengine.cpp b/src/controls/qquicklayoutengine.cpp new file mode 100644 index 00000000..2ede7af9 --- /dev/null +++ b/src/controls/qquicklayoutengine.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qquicklayoutengine_p.h" + +QT_BEGIN_NAMESPACE + +/* + This function is a modification of qGeomCalc() included in "QtCore/kernel/qlayoutengine_p.h". + It is used as a helper function to handle linear layout recalculations for QQuickItems. + + chain contains input and output parameters describing the geometry. + count is the count of items in the chain; pos and space give the + interval (relative to parentWidget topLeft). +*/ +void qDeclarativeLayoutCalculate(QVector<QQuickComponentsLayoutInfo> &chain, int start, + int count, qreal pos, qreal space, qreal spacer) +{ + if (chain.count() == 0) + return; + + bool wannaGrow = false; + qreal totalStretch = 0; + qreal totalSpacing = 0; + qreal totalSizeHint = 0; + qreal totalMinimumSize = 0; + + const int end = start + count; + const int spacerCount = chain.count() - 1; + + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + data->done = false; + + totalStretch += data->stretch; + totalSizeHint += data->smartSizeHint(); + totalMinimumSize += data->minimumSize; + + // don't count last spacing + if (i < end - 1) + totalSpacing += data->effectiveSpacer(spacer); + + wannaGrow = (wannaGrow || data->expansive || data->stretch > 0); + } + + qreal extraSpace = 0; + + if (space < totalMinimumSize + totalSpacing) { + // Less space than minimumSize; take from the biggest first + qreal minSize = totalMinimumSize + totalSpacing; + + // shrink the spacers proportionally + if (spacer >= 0) { + spacer = minSize > 0 ? spacer * space / minSize : 0; + totalSpacing = spacer * spacerCount; + } + + QList<qreal> list; + + for (int i = start; i < end; i++) + list << chain.at(i).minimumSize; + + qSort(list); + + qreal spaceLeft = space - totalSpacing; + + qreal sum = 0; + int idx = 0; + qreal spaceUsed = 0; + qreal current = 0; + + while (idx < count && spaceUsed < spaceLeft) { + current = list.at(idx); + spaceUsed = sum + current * (count - idx); + sum += current; + ++idx; + } + + --idx; + + int items = count - idx; + qreal deficit = spaceUsed - spaceLeft; + qreal deficitPerItem = deficit / items; + qreal maxval = current - deficitPerItem; + + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + data->done = true; + + if (data->minimumSize > 0) + data->size = data->minimumSize; + else + data->size = qMin<qreal>(data->minimumSize, maxval); + } + } else if (space < totalSizeHint + totalSpacing) { + /* + Less space than smartSizeHint(), but more than minimumSize. + Currently take space equally from each, as in Qt 2.x. + Commented-out lines will give more space to stretchier + items. + */ + int n = count; + qreal spaceLeft = space - totalSpacing; + qreal overdraft = totalSizeHint - spaceLeft; + + // first give to the fixed ones + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (!data->done && data->minimumSize >= data->smartSizeHint()) { + data->done = true; + data->size = data->smartSizeHint(); + spaceLeft -= data->smartSizeHint(); + n--; + } + } + + bool finished = (n == 0); + + while (!finished) { + finished = true; + + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (data->done) + continue; + + qreal w = overdraft / n; + data->size = data->smartSizeHint() - w; + + if (data->size < data->minimumSize) { + data->done = true; + data->size = data->minimumSize; + finished = false; + overdraft -= data->smartSizeHint() - data->minimumSize; + n--; + break; + } + } + } + } else { // extra space + int n = count; + qreal spaceLeft = space - totalSpacing; + + // first give to the fixed ones, and handle non-expansiveness + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (data->done) + continue; + + if (data->maximumSize <= data->smartSizeHint() + || (wannaGrow && !data->expansive && data->stretch == 0) + || (!data->expansive && data->stretch == 0)) { + data->done = true; + data->size = data->smartSizeHint(); + spaceLeft -= data->size; + totalStretch -= data->stretch; + n--; + } + } + + extraSpace = spaceLeft; + + /* + Do a trial distribution and calculate how much it is off. + If there are more deficit pixels than surplus pixels, give + the minimum size items what they need, and repeat. + Otherwise give to the maximum size items, and repeat. + + Paul Olav Tvete has a wonderful mathematical proof of the + correctness of this principle, but unfortunately this + comment is too small to contain it. + */ + qreal surplus, deficit; + + do { + surplus = deficit = 0; + + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (data->done) + continue; + + extraSpace = 0; + + qreal w; + + if (totalStretch <= 0) + w = (spaceLeft / n); + else + w = (spaceLeft * data->stretch) / totalStretch; + + data->size = w; + + if (w < data->smartSizeHint()) + deficit += data->smartSizeHint() - w; + else if (w > data->maximumSize) + surplus += w - data->maximumSize; + } + + if (deficit > 0 && surplus <= deficit) { + // give to the ones that have too little + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (!data->done && data->size < data->smartSizeHint()) { + data->done = true; + data->size = data->smartSizeHint(); + spaceLeft -= data->smartSizeHint(); + totalStretch -= data->stretch; + n--; + } + } + } + + if (surplus > 0 && surplus >= deficit) { + // take from the ones that have too much + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + + if (!data->done && data->size > data->maximumSize) { + data->done = true; + data->size = data->maximumSize; + spaceLeft -= data->maximumSize; + totalStretch -= data->stretch; + n--; + } + } + } + } while (n > 0 && surplus != deficit); + + if (n == 0) + extraSpace = spaceLeft; + } + + /* + As a last resort, we distribute the unwanted space equally + among the spacers (counting the start and end of the chain). We + could, but don't, attempt a sub-pixel allocation of the extra + space. + */ + qreal extra = extraSpace / (spacerCount + 2); + qreal p = pos + extra; + + for (int i = start; i < end; i++) { + QQuickComponentsLayoutInfo *data = &chain[i]; + data->pos = p; + p += data->size; + p += data->effectiveSpacer(spacer) + extra; + } +} + +QT_END_NAMESPACE diff --git a/src/controls/qquicklayoutengine_p.h b/src/controls/qquicklayoutengine_p.h new file mode 100644 index 00000000..85eca8c3 --- /dev/null +++ b/src/controls/qquicklayoutengine_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QQUICKLAYOUTENGINE_P_H +#define QQUICKLAYOUTENGINE_P_H + +#include <QVector> + +QT_BEGIN_NAMESPACE + +struct QQuickComponentsLayoutInfo +{ + QQuickComponentsLayoutInfo() + : stretch(1), + sizeHint(0), + spacing(0), + minimumSize(0), + maximumSize(0), + expansive(true), + done(false), + pos(0), + size(0) + { } + + inline qreal smartSizeHint() { + return (stretch > 0) ? minimumSize : sizeHint; + } + + inline qreal effectiveSpacer(qreal value) const { + return value + spacing; + } + + qreal stretch; + qreal sizeHint; + qreal spacing; + qreal minimumSize; + qreal maximumSize; + bool expansive; + + // result + bool done; + qreal pos; + qreal size; +}; + +void qDeclarativeLayoutCalculate(QVector<QQuickComponentsLayoutInfo> &chain, int start, + int count, qreal pos, qreal space, qreal spacer); + +QT_END_NAMESPACE + +#endif // QQUICKLAYOUTENGINE_P_H diff --git a/src/controls/qquicklinearlayout.cpp b/src/controls/qquicklinearlayout.cpp new file mode 100644 index 00000000..015b8cba --- /dev/null +++ b/src/controls/qquicklinearlayout.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qquicklinearlayout_p.h" +#include "qquicklayoutengine_p.h" +#include <QtCore/qnumeric.h> + +/*! + \qmltype RowLayout + \instantiates QQuickComponentsRowLayout + \inqmlmodule QtQuick.Controls 1.0 + \brief RowLayout is doing bla...bla... +*/ + +/*! + \qmltype ColumnLayout + \instantiates QQuickComponentsColumnLayout + \inqmlmodule QtQuick.Controls 1.0 + \brief ColumnLayout is doing bla...bla... +*/ + +QT_BEGIN_NAMESPACE + +static const qreal q_declarativeLayoutDefaultSpacing = 4.0; + + +QQuickComponentsLinearLayout::QQuickComponentsLinearLayout(Orientation orientation, + QQuickItem *parent) + : QQuickComponentsLayout(parent), + m_spacing(q_declarativeLayoutDefaultSpacing), + m_orientation(orientation) +{ + +} + +qreal QQuickComponentsLinearLayout::spacing() const +{ + return m_spacing; +} + +void QQuickComponentsLinearLayout::setSpacing(qreal spacing) +{ + if (qIsNaN(spacing) || m_spacing == spacing) + return; + + m_spacing = spacing; + invalidate(); +} + +QQuickComponentsLinearLayout::Orientation QQuickComponentsLinearLayout::orientation() const +{ + return m_orientation; +} + +void QQuickComponentsLinearLayout::setOrientation(Orientation orientation) +{ + if (m_orientation == orientation) + return; + + m_orientation = orientation; + invalidate(); + + emit orientationChanged(); +} + +void QQuickComponentsLinearLayout::componentComplete() +{ + QQuickComponentsLayout::componentComplete(); + updateLayoutItems(); + invalidate(); +} + +void QQuickComponentsLinearLayout::updateLayoutItems() +{ + const QList<QQuickItem *> &children = childItems(); + qreal implicitWidth = 0; + qreal implicitHeight = 0; + foreach (QQuickItem *child, children) { + if (m_orientation == Horizontal) { + implicitWidth += child->implicitWidth(); + implicitHeight = qMax(implicitHeight, child->implicitHeight()); + } else { + implicitHeight += child->implicitHeight(); + implicitWidth = qMax(implicitWidth, child->implicitWidth()); + } + insertLayoutItem(child); + } + setImplicitWidth(implicitWidth); + setImplicitHeight(implicitHeight); +} + +void QQuickComponentsLinearLayout::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (isComponentComplete()) { + if (change == ItemChildAddedChange) + insertLayoutItem(value.item); + else if (change == ItemChildRemovedChange) + removeLayoutItem(value.item); + } + + QQuickComponentsLayout::itemChange(change, value); +} + +void QQuickComponentsLinearLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickComponentsLayout::geometryChanged(newGeometry, oldGeometry); + invalidate(); +} + +void QQuickComponentsLinearLayout::insertLayoutItem(QQuickItem *item) +{ + m_items.append(item); + setupItemLayout(item); + + invalidate(); + QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); + QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); +} + +void QQuickComponentsLinearLayout::removeLayoutItem(QQuickItem *item) +{ + if (!m_items.removeOne(item)) + return; + + invalidate(); + QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); + QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); +} + +void QQuickComponentsLinearLayout::onItemVisibleChanged() +{ + invalidate(); +} + +void QQuickComponentsLinearLayout::onItemDestroyed() +{ + if (!m_items.removeOne(static_cast<QQuickItem *>(sender()))) + return; + + invalidate(); +} + +void QQuickComponentsLinearLayout::reconfigureLayout() +{ + if (!isComponentComplete()) + return; + + const int count = m_items.count(); + + if (count == 0) + return; + + qreal totalSpacing = 0; + qreal totalSizeHint = 0; + qreal totalMinimumSize = 0; + qreal totalMaximumSize = 0; + + QVector<QQuickComponentsLayoutInfo> itemData; + + for (int i = 0; i < count; i++) { + QQuickItem *item = m_items.at(i); + QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(item); + QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); + + QQuickComponentsLayoutInfo data; + + if (item->isVisible()) { + if (m_orientation == Horizontal) { + data.sizeHint = item->implicitWidth(); + data.minimumSize = info->minimumWidth(); + data.maximumSize = info->maximumWidth(); + data.expansive = (info->horizontalSizePolicy() == QQuickComponentsLayout::Expanding); + data.stretch = info->horizontalSizePolicy() == Expanding ? 1.0 : 0; + } else { + data.sizeHint = item->implicitHeight(); + data.minimumSize = info->minimumHeight(); + data.maximumSize = info->maximumHeight(); + data.expansive = (info->verticalSizePolicy() == QQuickComponentsLayout::Expanding); + data.stretch = info->verticalSizePolicy() == Expanding ? 1.0 : 0; + } + + itemData.append(data); + // sum + totalSizeHint += data.sizeHint; + totalMinimumSize += data.minimumSize; + totalMaximumSize += data.maximumSize; + + // don't count last spacing + if (i < count - 1) + totalSpacing += data.spacing + m_spacing; + } + } + + qreal extent = m_orientation == Horizontal ? width() : height(); + qDeclarativeLayoutCalculate(itemData, 0, itemData.count(), 0, extent, m_spacing); + + int i = 0; + int id = 0; + while (i < count) { + QQuickItem *item = m_items.at(i++); + if (!item->isVisible()) + continue; + const QQuickComponentsLayoutInfo &data = itemData.at(id); + + if (m_orientation == Horizontal) { + item->setX(data.pos); + item->setY(height()/2 - item->height()/2); + item->setWidth(data.size); + } else { + item->setY(data.pos); + item->setX(width()/2 - item->width()/2); + item->setHeight(data.size); + } + ++id; + } + + // propagate hints to upper levels + QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(this); + QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); + + if (m_orientation == Horizontal) { + setImplicitWidth(totalSizeHint); + info->setMinimumWidth(totalMinimumSize + totalSpacing); + info->setMaximumWidth(totalMaximumSize + totalSpacing); + } else { + setImplicitHeight(totalSizeHint); + info->setMinimumHeight(totalMinimumSize + totalSpacing); + info->setMaximumHeight(totalMaximumSize + totalSpacing); + } +} + +QT_END_NAMESPACE diff --git a/src/controls/qquicklinearlayout_p.h b/src/controls/qquicklinearlayout_p.h new file mode 100644 index 00000000..9b71c896 --- /dev/null +++ b/src/controls/qquicklinearlayout_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QQUICKLINEARLAYOUT_P_H +#define QQUICKLINEARLAYOUT_P_H + +#include "qquicklayout_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickComponentsLinearLayout : public QQuickComponentsLayout +{ + Q_OBJECT + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + +public: + enum Orientation { + Vertical, + Horizontal + }; + + explicit QQuickComponentsLinearLayout(Orientation orientation, + QQuickItem *parent = 0); + ~QQuickComponentsLinearLayout() {} + + qreal spacing() const; + void setSpacing(qreal spacing); + + Orientation orientation() const; + void setOrientation(Orientation orientation); + + void componentComplete(); + +signals: + void spacingChanged(); + void orientationChanged(); + +protected: + void updateLayoutItems(); + void reconfigureLayout(); + void insertLayoutItem(QQuickItem *item); + void removeLayoutItem(QQuickItem *item); + void itemChange(ItemChange change, const ItemChangeData &data); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + +protected slots: + void onItemVisibleChanged(); + void onItemDestroyed(); + +private: + qreal m_spacing; + Orientation m_orientation; + QList<QQuickItem *> m_items; +}; + + +class QQuickComponentsRowLayout : public QQuickComponentsLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickComponentsRowLayout(QQuickItem *parent = 0) + : QQuickComponentsLinearLayout(Horizontal, parent) {} +}; + + +class QQuickComponentsColumnLayout : public QQuickComponentsLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickComponentsColumnLayout(QQuickItem *parent = 0) + : QQuickComponentsLinearLayout(Vertical, parent) {} +}; + +QT_END_NAMESPACE + +#endif // QQUICKLINEARLAYOUT_P_H diff --git a/src/controls/qtaction.cpp b/src/controls/qtaction.cpp new file mode 100644 index 00000000..debdd762 --- /dev/null +++ b/src/controls/qtaction.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtaction_p.h" +#include "qtexclusivegroup_p.h" + +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Action + \instantiates QtAction + \inqmlmodule QtQuick.Controls 1.0 + \brief Action provides an abstract user interface action that can be bound to items + + \sa MenuItem, Menu, ExclusiveGroup +*/ + +/*! + \qmlproperty string Action::text +*/ + +/*! + \qmlproperty url Action::iconSource +*/ + +/*! + \qmlproperty string Action::iconName +*/ + +/*! + \qmlproperty string Action::toolTip +*/ + +/*! + \qmlproperty bool Action::enabled +*/ + +/*! + \qmlproperty bool Action::checkable +*/ + +/*! + \qmlproperty bool Action::checked + +*/ + +/*! + \qmlproperty ExclusiveGroup Action::exclusiveGroup + + \sa ExclusiveGroup +*/ + +/*! + \qmlproperty string Action::shortcut +*/ + +/*! + \qmlproperty string Action::mnemonic +*/ + +QtAction::QtAction(QObject *parent) + : QObject(parent) + , m_enabled(true) + , m_checkable(false) + , m_checked(false) + , m_exclusiveGroup(0) +{ +} + +QtAction::~QtAction() +{ + setShortcut(QString()); + setMnemonic(QString()); +} + +void QtAction::setText(const QString &text) +{ + if (text == m_text) + return; + m_text = text; + emit textChanged(); +} + +bool qShortcutContextMatcher(QObject *, Qt::ShortcutContext) +{ + // the context matching is only interesting for non window-wide shortcuts + // it might be interesting to check for the action's window being active + // we currently only support the window wide focus so we can safely ignore this + return true; +} + +QString QtAction::shortcut() const +{ + return m_shortcut.toString(QKeySequence::NativeText); +} + +void QtAction::setShortcut(const QString &arg) +{ + QKeySequence sequence = QKeySequence::fromString(arg); + if (sequence == m_shortcut) + return; + + if (!m_shortcut.isEmpty()) + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, m_shortcut); + + m_shortcut = sequence; + + if (!m_shortcut.isEmpty()) { + Qt::ShortcutContext context = Qt::WindowShortcut; + QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_shortcut, context, qShortcutContextMatcher); + } + emit shortcutChanged(shortcut()); +} + +QString QtAction::mnemonic() const +{ + return m_mnemonic.toString(QKeySequence::NativeText); +} + +void QtAction::setMnemonic(const QString &mnem) +{ + QKeySequence sequence = QKeySequence::mnemonic(mnem); + if (m_mnemonic == sequence) + return; + + if (!m_mnemonic.isEmpty()) + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, m_mnemonic); + + m_mnemonic = sequence; + + if (!m_mnemonic.isEmpty()) { + Qt::ShortcutContext context = Qt::WindowShortcut; + QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_mnemonic, context, qShortcutContextMatcher); + } + emit mnemonicChanged(mnemonic()); +} + +void QtAction::setIconSource(const QUrl &iconSource) +{ + if (iconSource == m_iconSource) + return; + + m_iconSource = iconSource; + QString iconName = m_icon.name(); + m_icon = QIcon(m_iconSource.toLocalFile()); + if (!iconName.isEmpty()) + m_icon = QIcon::fromTheme(iconName, m_icon); + + emit iconSourceChanged(); + emit iconChanged(); +} + +QString QtAction::iconName() const +{ + return m_icon.name(); +} + +void QtAction::setIconName(const QString &iconName) +{ + if (iconName == m_icon.name()) + return; + + m_icon = QIcon::fromTheme(iconName, QIcon(m_iconSource.toLocalFile())); + emit iconNameChanged(); + emit iconChanged(); +} + +void QtAction::setToolTip(const QString &arg) +{ + if (m_toolTip != arg) { + m_toolTip = arg; + emit toolTipChanged(arg); + } +} + +void QtAction::setEnabled(bool e) +{ + if (e == m_enabled) + return; + m_enabled = e; + emit enabledChanged(); +} + +void QtAction::setCheckable(bool c) +{ + if (c == m_checkable) + return; + m_checkable = c; + emit checkableChanged(); +} + +void QtAction::setChecked(bool c) +{ + if (c == m_checked) + return; + m_checked = c; + emit toggled(m_checked); +} + +void QtAction::setExclusiveGroup(QtExclusiveGroup *eg) +{ + if (m_exclusiveGroup == eg) + return; + + if (m_exclusiveGroup) + m_exclusiveGroup->unregisterCheckable(this); + m_exclusiveGroup = eg; + if (m_exclusiveGroup) + m_exclusiveGroup->registerCheckable(this); + + emit exclusiveGroupChanged(); +} + +bool QtAction::event(QEvent *e) +{ + if (!m_enabled) + return false; + + if (e->type() != QEvent::Shortcut) + return false; + + QShortcutEvent *se = static_cast<QShortcutEvent *>(e); + + Q_ASSERT_X(se->key() == m_shortcut || se->key() == m_mnemonic, + "QtAction::event", + "Received shortcut event from incorrect shortcut"); + if (se->isAmbiguous()) { + qWarning("QtAction::event: Ambiguous shortcut overload: %s", se->key().toString(QKeySequence::NativeText).toLatin1().constData()); + return false; + } + + trigger(); + + return true; +} + +void QtAction::trigger() +{ + if (m_checkable) + setChecked(!m_checked); + + emit triggered(); +} + +QT_END_NAMESPACE diff --git a/src/controls/qtaction_p.h b/src/controls/qtaction_p.h new file mode 100644 index 00000000..1e44b003 --- /dev/null +++ b/src/controls/qtaction_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTACTION_H +#define QTACTION_H + +#include <QtCore/QObject> +#include <QtCore/QUrl> +#include <QtCore/QVariant> +#include <QtGui/QIcon> +#include <QtGui/qkeysequence.h> + +QT_BEGIN_NAMESPACE + +class QtExclusiveGroup; + +class QtAction : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QUrl iconSource READ iconSource WRITE setIconSource NOTIFY iconSourceChanged) + Q_PROPERTY(QString iconName READ iconName WRITE setIconName NOTIFY iconNameChanged) + Q_PROPERTY(QVariant __icon READ iconVariant NOTIFY iconChanged) + Q_PROPERTY(QString toolTip READ tooltip WRITE setToolTip NOTIFY toolTipChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged) + Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled) + + Q_PROPERTY(QtExclusiveGroup *exclusiveGroup READ exclusiveGroup WRITE setExclusiveGroup NOTIFY exclusiveGroupChanged) +#ifndef QT_NO_SHORTCUT + Q_PROPERTY(QString shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged) + Q_PROPERTY(QString mnemonic READ mnemonic WRITE setMnemonic NOTIFY mnemonicChanged) +#endif + +public: + explicit QtAction(QObject *parent = 0); + ~QtAction(); + + QString text() const { return m_text; } + void setText(const QString &text); + + QString shortcut() const; + void setShortcut(const QString &shortcut); + + QString mnemonic() const; + void setMnemonic(const QString &mnemonic); + + QString iconName() const; + void setIconName(const QString &iconName); + + QUrl iconSource() const { return m_iconSource; } + void setIconSource(const QUrl &iconSource); + + QString tooltip() const { return m_toolTip; } + void setToolTip(const QString &toolTip); + + bool isEnabled() const { return m_enabled; } + void setEnabled(bool e); + + bool isCheckable() const { return m_checkable; } + void setCheckable(bool c); + + bool isChecked() const { return m_checked; } + void setChecked(bool c); + + QtExclusiveGroup *exclusiveGroup() const { return m_exclusiveGroup; } + void setExclusiveGroup(QtExclusiveGroup * arg); + + QIcon icon() const { return m_icon; } + QVariant iconVariant() const { return QVariant(m_icon); } + void setIcon(QIcon icon) { m_icon = icon; emit iconChanged(); } + + bool event(QEvent *e); + +public Q_SLOTS: + void trigger(); + +Q_SIGNALS: + void triggered(); + void toggled(bool); + + void textChanged(); + void shortcutChanged(QString shortcut); + void mnemonicChanged(QString mnemonic); + + void iconChanged(); + void iconNameChanged(); + void iconSourceChanged(); + void toolTipChanged(QString arg); + void enabledChanged(); + void checkableChanged(); + + void exclusiveGroupChanged(); + +private: + QString m_text; + QUrl m_iconSource; + QIcon m_icon; + bool m_enabled; + bool m_checkable; + bool m_checked; + QtExclusiveGroup *m_exclusiveGroup; + QKeySequence m_shortcut; + QKeySequence m_mnemonic; + QString m_toolTip; +}; + +QT_END_NAMESPACE + +#endif // QTACTION_H diff --git a/src/controls/qtexclusivegroup.cpp b/src/controls/qtexclusivegroup.cpp new file mode 100644 index 00000000..fde2a9df --- /dev/null +++ b/src/controls/qtexclusivegroup.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtexclusivegroup_p.h" + +#include <QtCore/QVariant> +#include <QtCore/qdebug.h> + +#define CHECKED_PROPERTY "checked" + +QT_BEGIN_NAMESPACE + +static const char *checkableSignals[] = { + CHECKED_PROPERTY"Changed()", + "toggled(bool)", + "toggled()", + 0 +}; + +static bool isChecked(const QObject *o) +{ + if (!o) return false; + QVariant checkedVariant = o->property(CHECKED_PROPERTY); + return checkedVariant.isValid() && checkedVariant.toBool(); +} + +/*! + \qmltype ExclusiveGroup + \instantiates QtExclusiveGroup + \inqmlmodule QtQuick.Controls 1.0 + \ingroup containers + \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive. + + \code + ExclusiveGroup { id: radioInputGroup } + + Action { + id: dabRadioInput + text: "DAB" + exclusiveGroup: radioInputGroup + } + + Action { + id: fmRadioInput + text: "FM" + exclusiveGroup: radioInputGroup + } + + Action { + id: amRadioInput + text: "AM" + exclusiveGroup: radioInputGroup + } + + \endcode + + For an object, or control, to be compatible with \c ExclusiveGroup, it should have a \c checked + property, and either a \c checkedChanged, \c toggled(), or \c toggled(bool) signal. It also needs + to be registered with \c ExclusiveGroup::registerCheckable(object) when its \c ExclusiveGroup property is set. + + \sa Action, ButtonBehavior +*/ + +/*! + \qmlproperty QtObject ExclusiveGroup::current + + The currently selected object. +*/ + +/*! + \qmlmethod void ExclusiveGroup::registerCheckable(object) + + Register \c object to the exclusive group. + + You should only need to call this function when creating a component you want to be compatible with \c ExclusiveGroup. + + \sa ExclusiveGroup::unregisterCheckable(object) +*/ + +/*! + \qmlmethod void ExclusiveGroup::unregisterCheckable(object) + + Unregister \c object from the exclusive group. + + You should only need to call this function when creating a component you want to be compatible with \c ExclusiveGroup. + + \sa ExclusiveGroup::registerCheckable(object) +*/ + +QtExclusiveGroup::QtExclusiveGroup(QObject *parent) + : QObject(parent) +{ + int index = metaObject()->indexOfMethod("updateCurrent()"); + m_updateCurrentMethod = metaObject()->method(index); +} + +void QtExclusiveGroup::setCurrent(QObject * o) +{ + if (m_current == o) + return; + + if (m_current) + m_current->setProperty(CHECKED_PROPERTY, QVariant(false)); + m_current = o; + if (m_current) + m_current->setProperty(CHECKED_PROPERTY, QVariant(true)); + emit currentChanged(); +} + +void QtExclusiveGroup::updateCurrent() +{ + QObject *checkable = sender(); + if (isChecked(checkable)) + setCurrent(checkable); +} + +void QtExclusiveGroup::registerCheckable(QObject *o) +{ + for (const char **signalName = checkableSignals; *signalName; signalName++) { + int signalIndex = o->metaObject()->indexOfSignal(*signalName); + if (signalIndex != -1) { + QMetaMethod signalMethod = o->metaObject()->method(signalIndex); + connect(o, signalMethod, this, m_updateCurrentMethod, Qt::UniqueConnection); + connect(o, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterCheckable(QObject*)), Qt::UniqueConnection); + if (!m_current && isChecked(o)) + setCurrent(o); + return; + } + } + + qWarning() << "QtExclusiveGroup::registerCheckable(): Cannot register" << o; +} + +void QtExclusiveGroup::unregisterCheckable(QObject *o) +{ + for (const char **signalName = checkableSignals; *signalName; signalName++) { + int signalIndex = o->metaObject()->indexOfSignal(*signalName); + if (signalIndex != -1) { + QMetaMethod signalMethod = o->metaObject()->method(signalIndex); + if (disconnect(o, signalMethod, this, m_updateCurrentMethod)) { + disconnect(o, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterCheckable(QObject*))); + break; + } + } + } +} + +QT_END_NAMESPACE diff --git a/src/controls/qtexclusivegroup_p.h b/src/controls/qtexclusivegroup_p.h new file mode 100644 index 00000000..7d980bd0 --- /dev/null +++ b/src/controls/qtexclusivegroup_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTEXCLUSIVEGROUP_H +#define QTEXCLUSIVEGROUP_H + +#include <QtCore/QObject> +#include <QtCore/QMetaMethod> + +QT_BEGIN_NAMESPACE + +class QtExclusiveGroup : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QObject *current READ current WRITE setCurrent NOTIFY currentChanged) + +public: + explicit QtExclusiveGroup(QObject *parent = 0); + + QObject *current() const { return m_current; } + void setCurrent(QObject * o); + +public Q_SLOTS: + void registerCheckable(QObject *o); + void unregisterCheckable(QObject *o); + +Q_SIGNALS: + void currentChanged(); + +private Q_SLOTS: + void updateCurrent(); + +private: + QObject * m_current; + QMetaMethod m_updateCurrentMethod; +}; + +QT_END_NAMESPACE + +#endif // QTEXCLUSIVEGROUP_H diff --git a/src/controls/qtmenu.cpp b/src/controls/qtmenu.cpp new file mode 100644 index 00000000..bd7af0c2 --- /dev/null +++ b/src/controls/qtmenu.cpp @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtmenu_p.h" +#include "qdebug.h" +#include "qtaction_p.h" +#include "qtmenupopupwindow_p.h" +#include <qabstractitemmodel.h> + +#include "private/qguiapplication_p.h" +#include <QtGui/qpa/qplatformtheme.h> +#include <QtGui/qpa/qplatformmenu.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QtMenu + \internal + */ + +/*! + \qmltype MenuPrivate + \instantiates QtMenu + \internal + \inqmlmodule QtQuick.Controls 1.0 + */ + +/*! + \qmlproperty readonly list Menu::menuItems + \default +*/ + +/*! + \qmlproperty var Menu::model +*/ + +/*! + \qmlproperty int Menu::selectedIndex +*/ + +/*! + \qmlproperty font Menu::font + + Write-only. For styling purposes only. +*/ + +/*! + \qmlproperty readonly bool Menu::popupVisible +*/ + +/*! + \qmlmethod void Menu::showPopup(x, y, item, parent) + + Shows the popup related to this menu. It can block on some platforms, so test it accordingly. +*/ + +/*! + \qmlmethod void Menu::closeMenu() + + Closes current menu (and submenus) only. +*/ + +/*! + \qmlmethod void Menu::dismissMenu() + + Closes all menus related to this one, including its parent menu. +*/ + +QtMenu::QtMenu(QObject *parent) + : QtMenuItem(parent), + m_selectedIndex(-1), + m_highlightedIndex(0), + m_hasNativeModel(false), + m_minimumWidth(0), + m_popupWindow(0), + m_popupVisible(false) +{ + m_platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu(); + if (m_platformMenu) { + connect(m_platformMenu, SIGNAL(aboutToHide()), this, SLOT(closeMenu())); + if (platformItem()) + platformItem()->setMenu(m_platformMenu); + } +} + +QtMenu::~QtMenu() +{ + delete m_platformMenu; +} + +void QtMenu::updateText() +{ + if (m_platformMenu) + m_platformMenu->setText(text()); + QtMenuItem::updateText(); +} + +void QtMenu::setMinimumWidth(int w) +{ + if (w == m_minimumWidth) + return; + + m_minimumWidth = w; + if (m_platformMenu) + m_platformMenu->setMinimumWidth(w); + + emit minimumWidthChanged(); +} + +void QtMenu::setFont(const QFont &arg) +{ + if (m_platformMenu) + m_platformMenu->setFont(arg); +} + +void QtMenu::setSelectedIndex(int index) +{ + if (m_selectedIndex == index) + return; + + m_selectedIndex = index; + + if (m_selectedIndex >= 0 && m_selectedIndex < m_menuItems.size()) + if (QtMenuItem *item = qobject_cast<QtMenuItem *>(m_menuItems[m_selectedIndex])) + if (item->checkable()) + item->setChecked(true); + + emit selectedIndexChanged(); +} + +QQmlListProperty<QtMenuBase> QtMenu::menuItems() +{ + return QQmlListProperty<QtMenuBase>(this, 0, &QtMenu::append_menuItems, &QtMenu::count_menuItems, &QtMenu::at_menuItems, 0); +} + +void QtMenu::showPopup(qreal x, qreal y, int atItemIndex, QObject *reference) +{ + if (popupVisible()) + closeMenu(); + + setPopupVisible(true); + + // If atItemIndex is valid, x and y is specified from the + // the position of the corresponding QtMenuItem: + QtMenuItem *atItem = 0; + if (atItemIndex >= 0) + while (!atItem && atItemIndex < m_menuItems.size()) + atItem = qobject_cast<QtMenuItem *>(m_menuItems[atItemIndex++]); + + QQuickItem *item = qobject_cast<QQuickItem *>(reference); + + QQuickWindow *parentWindow = item ? item->window() : qobject_cast<QQuickWindow *>(reference); + if (!parentWindow) { + QQuickItem *parentAsItem = qobject_cast<QQuickItem *>(parent()); + parentWindow = visualItem() ? visualItem()->window() : // Menu as menu item case + parentAsItem ? parentAsItem->window() : 0; //Menu as context menu/popup case + } + + if (m_platformMenu) { + QPointF screenPosition(x, y); + if (item) + screenPosition = item->mapToScene(screenPosition); + m_platformMenu->showPopup(parentWindow, screenPosition.toPoint(), atItem ? atItem->platformItem() : 0); + } else { + m_popupWindow = new QtMenuPopupWindow(); + m_popupWindow->setParentWindow(parentWindow); + m_popupWindow->setMenuContentItem(m_menuContentItem); + connect(m_popupWindow, SIGNAL(visibleChanged(bool)), this, SLOT(windowVisibleChanged(bool))); + + if (parentWindow) { + if (item) { + QPointF pos = item->mapToItem(parentWindow->contentItem(), QPointF(x, y)); + x = pos.x(); + y = pos.y(); + } + + x += parentWindow->geometry().left(); + y += parentWindow->geometry().top(); + } + + QQuickItem *visualItem = atItem ? atItem->visualItem() : 0; + if (visualItem) { + QPointF pos = visualItem->position(); + x -= pos.x(); + y -= pos.y(); + m_popupWindow->setItemAt(visualItem); + } + + m_popupWindow->setGeometry(x, y, m_menuContentItem->width(), m_menuContentItem->height()); + m_popupWindow->show(); + m_popupWindow->setMouseGrabEnabled(true); // Needs to be done after calling show() + m_popupWindow->setKeyboardGrabEnabled(true); + } +} + +void QtMenu::closeMenu() +{ + setPopupVisible(false); + if (m_popupWindow) + m_popupWindow->setVisible(false); + emit menuClosed(); +} + +void QtMenu::dismissMenu() +{ + if (m_popupWindow) + m_popupWindow->dismissMenu(); +} + +void QtMenu::windowVisibleChanged(bool v) +{ + if (!v) { + if (qobject_cast<QtMenuPopupWindow *>(m_popupWindow->transientParent())) { + m_popupWindow->transientParent()->setMouseGrabEnabled(true); + m_popupWindow->transientParent()->setKeyboardGrabEnabled(true); + } + m_popupWindow->deleteLater(); + m_popupWindow = 0; + closeMenu(); + } +} + +void QtMenu::clearMenuItems() +{ + foreach (QtMenuBase *item, m_menuItems) { + if (m_platformMenu) + m_platformMenu->removeMenuItem(item->platformItem()); + delete item; + } + m_menuItems.clear(); +} + +QtMenuItem *QtMenu::addMenuItem(const QString &text) +{ + QtMenuItem *menuItem = new QtMenuItem(this); + menuItem->setText(text); + m_menuItems.append(menuItem); + if (QPlatformMenuItem *platformItem = menuItem->platformItem()) { + if (m_platformMenu) + m_platformMenu->insertMenuItem(platformItem, 0 /* append */); + + connect(platformItem, SIGNAL(activated()), this, SLOT(emitSelected())); + } + + if (m_menuItems.size() == 1) + // Inform QML that the selected action (0) now has changed contents: + emit selectedIndexChanged(); + + emit menuItemsChanged(); + return menuItem; +} + +void QtMenu::emitSelected() +{ + QPlatformMenuItem *platformItem = qobject_cast<QPlatformMenuItem *>(sender()); + if (!platformItem) + return; + + int index = -1; + foreach (QtMenuBase *item, m_menuItems) { + ++index; + if (item->platformItem() == platformItem) + break; + } + + setSelectedIndex(index); +} + +QString QtMenu::itemTextAt(int index) const +{ + QtMenuItem *mi = 0; + if (index >= 0 && index < m_menuItems.size() + && (mi = qobject_cast<QtMenuItem *>(m_menuItems.at(index)))) + return mi->text(); + else + return ""; +} + +QString QtMenu::modelTextAt(int index) const +{ + if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel*>(m_model.value<QObject*>())) { + return model->data(model->index(index, 0)).toString(); + } else if (m_model.canConvert(QVariant::StringList)) { + return m_model.toStringList().at(index); + } + return ""; +} + +int QtMenu::modelCount() const +{ + if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel*>(m_model.value<QObject*>())) { + return model->rowCount(); + } else if (m_model.canConvert(QVariant::StringList)) { + return m_model.toStringList().count(); + } + return -1; +} + +void QtMenu::append_menuItems(QQmlListProperty<QtMenuBase> *list, QtMenuBase *menuItem) +{ + QtMenu *menu = qobject_cast<QtMenu *>(list->object); + if (menu) { + menuItem->setParent(menu); + menu->m_menuItems.append(menuItem); + if (menu->m_platformMenu) + menu->m_platformMenu->insertMenuItem(menuItem->platformItem(), 0 /* append */); + } +} + +int QtMenu::count_menuItems(QQmlListProperty<QtMenuBase> *list) +{ + QtMenu *menu = qobject_cast<QtMenu *>(list->object); + if (menu) + return menu->m_menuItems.size(); + return 0; +} + +QtMenuBase *QtMenu::at_menuItems(QQmlListProperty<QtMenuBase> *list, int index) +{ + QtMenu *menu = qobject_cast<QtMenu *>(list->object); + if (menu && 0 <= index && index < menu->m_menuItems.size()) + return menu->m_menuItems[index]; + return 0; +} + + +void QtMenu::setModel(const QVariant &newModel) { + if (m_model != newModel) { + + // Clean up any existing connections + if (QAbstractItemModel *oldModel = qobject_cast<QAbstractItemModel*>(m_model.value<QObject*>())) { + disconnect(oldModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SIGNAL(rebuildMenu())); + } + + m_hasNativeModel = false; + m_model = newModel; + + if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel*>(newModel.value<QObject*>())) { + m_hasNativeModel = true; + connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SIGNAL(rebuildMenu())); + } else if (newModel.canConvert(QVariant::StringList)) { + m_hasNativeModel = true; + } + emit modelChanged(m_model); + } +} + +QT_END_NAMESPACE diff --git a/src/controls/qtmenu_p.h b/src/controls/qtmenu_p.h new file mode 100644 index 00000000..d34138ac --- /dev/null +++ b/src/controls/qtmenu_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTMENU_P_H +#define QTMENU_P_H +#include <QtCore/qglobal.h> +#include <QtQuick/QtQuick> +#include <QtQml/QtQml> +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/QVariant> +#include "qtmenuitem_p.h" + +QT_BEGIN_NAMESPACE + +class QPlatformMenu; +class QtMenuPopupWindow; + +class QtMenu : public QtMenuItem +{ + Q_OBJECT + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(int selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) + Q_PROPERTY(QFont font WRITE setFont) + Q_PROPERTY(QQmlListProperty<QtMenuBase> menuItems READ menuItems NOTIFY menuItemsChanged) + Q_CLASSINFO("DefaultProperty", "menuItems") + Q_PROPERTY(QQuickItem *menuContentItem READ menuContentItem WRITE setMenuContentItem NOTIFY menuContentItemChanged) + Q_PROPERTY(bool popupVisible READ popupVisible NOTIFY popupVisibleChanged) + +public: + QtMenu(QObject *parent = 0); + virtual ~QtMenu(); + + int selectedIndex() const { return m_selectedIndex; } + void setSelectedIndex(int index); + + QQmlListProperty<QtMenuBase> menuItems(); + + QPlatformMenu* platformMenu() { return m_platformMenu; } + + int minimumWidth() const { return m_minimumWidth; } + void setMinimumWidth(int w); + + void setFont(const QFont &font); + + Q_INVOKABLE void showPopup(qreal x, qreal y, int atActionIndex = -1, QObject *reference = 0); + Q_INVOKABLE void clearMenuItems(); + Q_INVOKABLE QtMenuItem *addMenuItem(const QString &text); + Q_INVOKABLE QString itemTextAt(int index) const; // TODO Remove, it's useless + Q_INVOKABLE QString modelTextAt(int index) const; + Q_INVOKABLE int modelCount() const; + + QVariant model() const { return m_model; } + Q_INVOKABLE bool hasNativeModel() const { return m_hasNativeModel; } + + QQuickItem *menuContentItem() const + { + return m_menuContentItem; + } + + bool popupVisible() const + { + return m_popupVisible; + } + +public Q_SLOTS: + void setModel(const QVariant &newModel); + void closeMenu(); + void dismissMenu(); + + void setMenuContentItem(QQuickItem * arg) + { + if (m_menuContentItem != arg) { + m_menuContentItem = arg; + emit menuContentItemChanged(arg); + } + } + + void setPopupVisible(bool arg) + { + if (m_popupVisible != arg) { + m_popupVisible = arg; + emit popupVisibleChanged(arg); + } + } + +Q_SIGNALS: + void menuClosed(); + void selectedIndexChanged(); + void modelChanged(const QVariant &newModel); + void rebuildMenu(); + void minimumWidthChanged(); + void menuItemsChanged(); + void menuContentItemChanged(QQuickItem * arg); + + void popupVisibleChanged(bool arg); + +protected: + bool isNative() { return m_platformMenu != 0; } + +protected Q_SLOTS: + void emitSelected(); + void updateText(); + void windowVisibleChanged(bool); + +private: + static void append_menuItems(QQmlListProperty<QtMenuBase> *list, QtMenuBase *menuItem); + static int count_menuItems(QQmlListProperty<QtMenuBase> *list); + static QtMenuBase *at_menuItems(QQmlListProperty<QtMenuBase> *list, int index); + + QPlatformMenu *m_platformMenu; + QList<QtMenuBase *> m_menuItems; + int m_selectedIndex; + int m_highlightedIndex; + bool m_hasNativeModel; + QVariant m_model; + int m_minimumWidth; + QtMenuPopupWindow *m_popupWindow; + QQuickItem * m_menuContentItem; + bool m_popupVisible; + + friend class QtMenuBase; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QtMenu) + +#endif // QTMENU_P_H diff --git a/src/controls/qtmenubar.cpp b/src/controls/qtmenubar.cpp new file mode 100644 index 00000000..ff4a2031 --- /dev/null +++ b/src/controls/qtmenubar.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtmenubar_p.h" + +#include "private/qguiapplication_p.h" +#include <QtGui/qpa/qplatformtheme.h> +#include <QtGui/qpa/qplatformmenu.h> + +QT_BEGIN_NAMESPACE + + +/*! + \class QtMenuBar + \internal + */ + +/*! + \qmltype MenuBarPrivate + \instantiates QtMenuBar + \internal + \inqmlmodule QtQuick.Controls 1.0 + */ +QtMenuBar::QtMenuBar(QQuickItem *parent) + : QQuickItem(parent) +{ + connect(this, SIGNAL(parentChanged(QQuickItem *)), this, SLOT(updateParent(QQuickItem *))); + m_platformMenuBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar(); +} + +QtMenuBar::~QtMenuBar() +{ +} + +QQmlListProperty<QtMenu> QtMenuBar::menus() +{ + return QQmlListProperty<QtMenu>(this, 0, &QtMenuBar::append_menu, &QtMenuBar::count_menu, &QtMenuBar::at_menu, 0); +} + +bool QtMenuBar::isNative() { + return m_platformMenuBar != 0; +} + +void QtMenuBar::updateParent(QQuickItem *newParent) +{ + QWindow *newParentWindow = newParent ? newParent->window() : 0; + if (newParentWindow != window() && m_platformMenuBar) + m_platformMenuBar->handleReparent(newParentWindow); +} + +void QtMenuBar::append_menu(QQmlListProperty<QtMenu> *list, QtMenu *menu) +{ + if (QtMenuBar *menuBar = qobject_cast<QtMenuBar *>(list->object)) { + menu->setParent(menuBar); + menuBar->m_menus.append(menu); + + if (menuBar->m_platformMenuBar) + menuBar->m_platformMenuBar->insertMenu(menu->platformMenu(), 0 /* append */); + + menuBar->menuChanged(); + } +} + +int QtMenuBar::count_menu(QQmlListProperty<QtMenu> *list) +{ + if (QtMenuBar *menuBar = qobject_cast<QtMenuBar *>(list->object)) + return menuBar->m_menus.size(); + return 0; +} + +QtMenu *QtMenuBar::at_menu(QQmlListProperty<QtMenu> *list, int index) +{ + QtMenuBar *menuBar = qobject_cast<QtMenuBar *>(list->object); + if (menuBar && 0 <= index && index < menuBar->m_menus.size()) + return menuBar->m_menus[index]; + return 0; +} + +QT_END_NAMESPACE diff --git a/src/controls/qtmenubar_p.h b/src/controls/qtmenubar_p.h new file mode 100644 index 00000000..eb9e8ea5 --- /dev/null +++ b/src/controls/qtmenubar_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTMENUBAR_P_H +#define QTMENUBAR_P_H + +#include <QtCore/qglobal.h> + +#include <QtQuick/QQuickItem> + +#include "qtmenu_p.h" + +QT_BEGIN_NAMESPACE + +class QPlatformMenuBar; + +class QtMenuBar: public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QQmlListProperty<QtMenu> menus READ menus NOTIFY menuChanged) + Q_CLASSINFO("DefaultProperty", "menus") + Q_PROPERTY(bool isNative READ isNative CONSTANT) + +public: + QtMenuBar(QQuickItem *parent = 0); + ~QtMenuBar(); + + QQmlListProperty<QtMenu> menus(); + + bool isNative(); + +signals: + void menuChanged(); + +protected Q_SLOTS: + void updateParent(QQuickItem *newParent); + +private: + static void append_menu(QQmlListProperty<QtMenu> *list, QtMenu *menu); + static int count_menu(QQmlListProperty<QtMenu> *list); + static QtMenu *at_menu(QQmlListProperty<QtMenu> *list, int index); + +private: + QList<QtMenu *> m_menus; + QPlatformMenuBar *m_platformMenuBar; + QQuickWindow *m_parentWindow; +}; + +QT_END_NAMESPACE + +#endif //QTMENUBAR_P_H diff --git a/src/controls/qtmenuitem.cpp b/src/controls/qtmenuitem.cpp new file mode 100644 index 00000000..6da06c4e --- /dev/null +++ b/src/controls/qtmenuitem.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtmenuitem_p.h" +#include "qtaction_p.h" +#include "qtmenu_p.h" + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformtheme.h> +#include <QtGui/qpa/qplatformmenu.h> +#include <QtQuick/QQuickItem> + +QT_BEGIN_NAMESPACE + +QtMenuBase::QtMenuBase(QObject *parent) + : QObject(parent), m_visualItem(0) +{ + m_platformItem = QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem(); +} + +QtMenuBase::~QtMenuBase() +{ + delete m_platformItem; +} + +void QtMenuBase::syncWithPlatformMenu() +{ + QtMenu *menu = qobject_cast<QtMenu *>(parent()); + if (menu && menu->platformMenu() && platformItem() + && menu->m_menuItems.contains(this)) // If not, it'll be added later and then sync'ed + menu->platformMenu()->syncMenuItem(platformItem()); +} + +QQuickItem *QtMenuBase::visualItem() const +{ + return m_visualItem; +} + +void QtMenuBase::setVisualItem(QQuickItem *item) +{ + m_visualItem = item; +} + + +/*! + \qmltype MenuSeparator + \instantiates QtMenuSeparator + \inqmlmodule QtQuick.Controls 1.0 + \inherits Item + \ingroup menus + \brief MenuSeparator provides a separator for your items inside a menu. + + \sa Menu, MenuItem +*/ + +QtMenuSeparator::QtMenuSeparator(QObject *parent) + : QtMenuBase(parent) +{ + if (platformItem()) + platformItem()->setIsSeparator(true); +} + + +/*! + \qmltype MenuItem + \instantiates QtMenuItem + \ingroup menus + \inqmlmodule QtQuick.Controls 1.0 + \brief MenuItem provides an item to add in a menu or a menu bar. + + \code + Menu { + text: "Edit" + + MenuItem { + text: "Cut" + shortcut: "Ctrl+X" + onTriggered: ... + } + + MenuItem { + text: "Copy" + shortcut: "Ctrl+C" + onTriggered: ... + } + + MenuItem { + text: "Paste" + shortcut: "Ctrl+V" + onTriggered: ... + } + } + \endcode + + \sa MenuBar, Menu, MenuSeparator, Action +*/ + +/*! + \qmlproperty string MenuItem::text + + Text for the menu item. +*/ + +/*! + \qmlproperty string MenuItem::shortcut + + Shorcut bound to the menu item. + + \sa Action::shortcut +*/ + +/*! + \qmlproperty bool MenuItem::checkable + + Whether the menu item can be toggled. + + \sa checked +*/ + +/*! + \qmlproperty bool MenuItem::checked + + If the menu item is checkable, this property reflects its checked state. + + \sa chekcable, Action::toggled() +*/ + +/*! + \qmlproperty url MenuItem::iconSource + + \sa iconName, Action::iconSource +*/ + +/*! + \qmlproperty string MenuItem::iconName + + \sa iconSource, Action::iconName +*/ + +/*! + \qmlproperty Action MenuItem::action + + The action bound to this menu item. + + \sa Action +*/ + +/*! \qmlsignal MenuItem::triggered() + + Emitted when either the menu item or its bound action have been activated. + + \sa trigger(), Action::triggered(), Action::toggled() +*/ + +/*! \qmlmethod MenuItem::trigger() + + Manually trigger a menu item. Will also trigger the item's bound action. + + \sa triggered(), Action::trigger() +*/ + +QtMenuItem::QtMenuItem(QObject *parent) + : QtMenuBase(parent), m_action(0) +{ } + +QtMenuItem::~QtMenuItem() +{ + unbindFromAction(m_action); +} + +void QtMenuItem::bindToAction(QtAction *action) +{ + m_action = action; + + if (platformItem()) { + connect(platformItem(), SIGNAL(activated()), m_action, SLOT(trigger())); + } + + connect(m_action, SIGNAL(destroyed(QObject*)), this, SLOT(unbindFromAction(QObject*))); + + connect(m_action, SIGNAL(triggered()), this, SIGNAL(triggered())); + connect(m_action, SIGNAL(toggled(bool)), this, SLOT(updateChecked())); + connect(m_action, SIGNAL(exclusiveGroupChanged()), this, SIGNAL(exclusiveGroupChanged())); + connect(m_action, SIGNAL(enabledChanged()), this, SLOT(updateEnabled())); + connect(m_action, SIGNAL(textChanged()), this, SLOT(updateText())); + connect(m_action, SIGNAL(shortcutChanged(QString)), this, SLOT(updateShortcut())); + connect(m_action, SIGNAL(checkableChanged()), this, SIGNAL(checkableChanged())); + connect(m_action, SIGNAL(iconNameChanged()), this, SLOT(updateIconName())); + connect(m_action, SIGNAL(iconSourceChanged()), this, SLOT(updateIconSource())); + + if (m_action->parent() != this) { + updateText(); + updateShortcut(); + updateEnabled(); + updateIconName(); + updateIconSource(); + if (checkable()) + updateChecked(); + } +} + +void QtMenuItem::unbindFromAction(QObject *o) +{ + if (!o) + return; + + if (o == m_action) + m_action = 0; + + QtAction *action = qobject_cast<QtAction *>(o); + if (!action) + return; + + if (platformItem()) { + disconnect(platformItem(), SIGNAL(activated()), action, SLOT(trigger())); + } + + disconnect(action, SIGNAL(destroyed(QObject*)), this, SLOT(unbindFromAction(QObject*))); + + disconnect(action, SIGNAL(triggered()), this, SIGNAL(triggered())); + disconnect(action, SIGNAL(toggled(bool)), this, SLOT(updateChecked())); + disconnect(action, SIGNAL(exclusiveGroupChanged()), this, SIGNAL(exclusiveGroupChanged())); + disconnect(action, SIGNAL(enabledChanged()), this, SLOT(updateEnabled())); + disconnect(action, SIGNAL(textChanged()), this, SLOT(updateText())); + disconnect(action, SIGNAL(shortcutChanged(QString)), this, SLOT(updateShortcut())); + disconnect(action, SIGNAL(checkableChanged()), this, SIGNAL(checkableChanged())); + disconnect(action, SIGNAL(iconNameChanged()), this, SLOT(updateIconName())); + disconnect(action, SIGNAL(iconSourceChanged()), this, SLOT(updateIconSource())); +} + +QtAction *QtMenuItem::action() +{ + if (!m_action) + bindToAction(new QtAction(this)); + return m_action; +} + +void QtMenuItem::setAction(QtAction *a) +{ + if (a == m_action) + return; + + if (m_action) { + if (m_action->parent() == this) + delete m_action; + else + unbindFromAction(m_action); + } + + bindToAction(a); + emit actionChanged(); +} + +QtMenu *QtMenuItem::parentMenu() const +{ + return qobject_cast<QtMenu *>(parent()); +} + +QString QtMenuItem::text() const +{ + return m_action ? m_action->text() : QString(); +} + +void QtMenuItem::setText(const QString &text) +{ + action()->setText(text); +} + +void QtMenuItem::updateText() +{ + if (platformItem()) { + platformItem()->setText(text()); + syncWithPlatformMenu(); + } + emit textChanged(); +} + +QString QtMenuItem::shortcut() const +{ + return m_action ? m_action->shortcut() : QString(); +} + +void QtMenuItem::setShortcut(const QString &shortcut) +{ + action()->setShortcut(shortcut); +} + +void QtMenuItem::updateShortcut() +{ + if (platformItem()) { + platformItem()->setShortcut(QKeySequence(shortcut())); + syncWithPlatformMenu(); + } + emit shortcutChanged(); +} + +bool QtMenuItem::checkable() const +{ + return m_action ? m_action->isCheckable() : false; +} + +void QtMenuItem::setCheckable(bool checkable) +{ + action()->setCheckable(checkable); +} + +bool QtMenuItem::checked() const +{ + return m_action ? m_action->isChecked() : false; +} + +void QtMenuItem::setChecked(bool checked) +{ + action()->setChecked(checked); +} + +void QtMenuItem::updateChecked() +{ + bool checked = this->checked(); + if (platformItem()) { + platformItem()->setChecked(checked); + syncWithPlatformMenu(); + } + emit toggled(checked); +} + +QtExclusiveGroup *QtMenuItem::exclusiveGroup() const +{ + return m_action ? m_action->exclusiveGroup() : 0; +} + +void QtMenuItem::setExclusiveGroup(QtExclusiveGroup *eg) +{ + action()->setExclusiveGroup(eg); +} + +bool QtMenuItem::enabled() const +{ + return m_action ? m_action->isEnabled() : false; +} + +void QtMenuItem::setEnabled(bool enabled) +{ + action()->setEnabled(enabled); +} + +void QtMenuItem::updateEnabled() +{ + if (platformItem()) { + platformItem()->setEnabled(enabled()); + syncWithPlatformMenu(); + } + emit enabledChanged(); +} + +QUrl QtMenuItem::iconSource() const +{ + return m_action ? m_action->iconSource() : QUrl(); +} + +void QtMenuItem::setIconSource(const QUrl &iconSource) +{ + action()->setIconSource(iconSource); +} + +void QtMenuItem::updateIconSource() +{ + if (platformItem()) { + platformItem()->setIcon(m_action->icon()); + syncWithPlatformMenu(); + } + emit iconSourceChanged(); +} + +QString QtMenuItem::iconName() const +{ + return m_action ? m_action->iconName() : QString(); +} + +void QtMenuItem::setIconName(const QString &iconName) +{ + action()->setIconName(iconName); +} + +void QtMenuItem::updateIconName() +{ + if (platformItem()) { + platformItem()->setIcon(m_action->icon()); + syncWithPlatformMenu(); + } + emit iconNameChanged(); +} + +void QtMenuItem::trigger() +{ + if (m_action) + m_action->trigger(); +} + +QT_END_NAMESPACE diff --git a/src/controls/qtmenuitem_p.h b/src/controls/qtmenuitem_p.h new file mode 100644 index 00000000..f9227f26 --- /dev/null +++ b/src/controls/qtmenuitem_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTMENUITEM_P_H +#define QTMENUITEM_P_H + +#include <QtCore/QObject> +#include <QtCore/QPointF> +#include <QtCore/QPointer> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +class QUrl; +class QPlatformMenuItem; +class QQuickItem; +class QtAction; +class QtExclusiveGroup; +class QtMenu; + +class QtMenuBase: public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isNative READ isNative CONSTANT) + Q_PROPERTY(QQuickItem * __visualItem WRITE setVisualItem) + +public: + QtMenuBase(QObject *parent = 0); + ~QtMenuBase(); + + inline QPlatformMenuItem *platformItem() { return m_platformItem; } + + void syncWithPlatformMenu(); + + QQuickItem *visualItem() const; + void setVisualItem(QQuickItem *item); + +protected: + virtual bool isNative() { return m_platformItem != 0; } + +private: + QPlatformMenuItem *m_platformItem; + QPointer<QQuickItem> m_visualItem; +}; + +class QtMenuSeparator : public QtMenuBase +{ + Q_OBJECT +public: + QtMenuSeparator(QObject *parent = 0); +}; + +class QtMenuItem: public QtMenuBase +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QString shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged) + Q_PROPERTY(bool checkable READ checkable WRITE setCheckable NOTIFY checkableChanged) + Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY toggled) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QUrl iconSource READ iconSource WRITE setIconSource NOTIFY iconSourceChanged) + Q_PROPERTY(QString iconName READ iconName WRITE setIconName NOTIFY iconNameChanged) + Q_PROPERTY(QtExclusiveGroup *exclusiveGroup READ exclusiveGroup WRITE setExclusiveGroup NOTIFY exclusiveGroupChanged) + + Q_PROPERTY(QtAction *action READ action WRITE setAction NOTIFY actionChanged) + Q_PROPERTY(QtMenu *parentMenu READ parentMenu) + +public: + QtMenuItem(QObject *parent = 0); + ~QtMenuItem(); + + QtAction *action(); + void setAction(QtAction *a); + + QtMenu *parentMenu() const; + + QString text() const; + void setText(const QString &text); + + QString shortcut() const; + void setShortcut(const QString &shortcut); + + bool checkable() const; + void setCheckable(bool checkable); + + bool checked() const; + void setChecked(bool checked); + + QtExclusiveGroup *exclusiveGroup() const; + void setExclusiveGroup(QtExclusiveGroup *); + + bool enabled() const; + void setEnabled(bool enabled); + + QUrl iconSource() const; + void setIconSource(const QUrl &icon); + QString iconName() const; + void setIconName(const QString &icon); + +Q_SIGNALS: + void triggered(); + void textChanged(); + void shortcutChanged(); + void checkableChanged(); + void exclusiveGroupChanged(); + void toggled(bool); + void enabledChanged(); + + void iconSourceChanged(); + void iconNameChanged(); + + void actionChanged(); + +public Q_SLOTS: + void trigger(); + +protected Q_SLOTS: + virtual void updateText(); + void updateShortcut(); + void updateChecked(); + void updateEnabled(); + void updateIconName(); + void updateIconSource(); + void bindToAction(QtAction *action); + void unbindFromAction(QObject *action); + +private: + QtAction *m_action; +}; + +QT_END_NAMESPACE + +#endif //QTMENUITEM_P_H diff --git a/src/controls/qtmenupopupwindow.cpp b/src/controls/qtmenupopupwindow.cpp new file mode 100644 index 00000000..4baec976 --- /dev/null +++ b/src/controls/qtmenupopupwindow.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#include "qtmenupopupwindow_p.h" +#include "qtmenu_p.h" +#include <QtGui/QGuiApplication> +#include <QtCore/QEvent> + +QT_BEGIN_NAMESPACE + +QtMenuPopupWindow::QtMenuPopupWindow(QWindow *parent) : + QQuickWindow(parent), m_pressedInside(true), m_itemAt(0) +{ + setFlags(Qt::Popup); + setModality(Qt::WindowModal); +} + +void QtMenuPopupWindow::setMenuContentItem(QQuickItem *contentItem) +{ + if (!contentItem) + return; + + contentItem->setParentItem(this->contentItem()); + connect(contentItem, SIGNAL(widthChanged()), this, SLOT(updateSize())); + connect(contentItem, SIGNAL(heightChanged()), this, SLOT(updateSize())); +} + +void QtMenuPopupWindow::setItemAt(const QQuickItem *menuItem) +{ + if (m_itemAt) { + disconnect(m_itemAt, SIGNAL(xChanged()), this, SLOT(updatePosition())); + disconnect(m_itemAt, SIGNAL(yChanged()), this, SLOT(updatePosition())); + } + + m_itemAt = menuItem; + if (menuItem) { + m_oldItemPos = menuItem->position().toPoint(); + connect(menuItem, SIGNAL(xChanged()), this, SLOT(updatePosition())); + connect(menuItem, SIGNAL(yChanged()), this, SLOT(updatePosition())); + } +} + +void QtMenuPopupWindow::setParentWindow(QQuickWindow *parentWindow) +{ + setTransientParent(parentWindow); + if (parentWindow) { + connect(parentWindow, SIGNAL(destroyed()), this, SLOT(dismissMenu())); + if (QtMenuPopupWindow *pw = qobject_cast<QtMenuPopupWindow *>(parentWindow)) + connect(this, SIGNAL(menuDismissed()), pw, SLOT(dismissMenu())); + } +} + +void QtMenuPopupWindow::dismissMenu() +{ + close(); + + emit menuDismissed(); +} + +void QtMenuPopupWindow::updateSize() +{ + QSize contentSize = contentItem()->childrenRect().size().toSize(); + setWidth(contentSize.width()); + setHeight(contentSize.height()); +} + +void QtMenuPopupWindow::updatePosition() +{ + QPointF newPos = position() + m_oldItemPos - m_itemAt->position(); + setPosition(newPos.toPoint()); +} + +void QtMenuPopupWindow::mouseMoveEvent(QMouseEvent *e) +{ + QRect rect = QRect(QPoint(), size()); + QWindow *parentMenuWindow = /*qobject_cast<QtMenuPopupWindow*>*/(transientParent()); + if (parentMenuWindow && !rect.contains(e->pos())) { + forwardEventToTransientParent(e); + } else { + QQuickWindow::mouseMoveEvent(e); + } +} + +void QtMenuPopupWindow::mousePressEvent(QMouseEvent *e) +{ + QRect rect = QRect(QPoint(), size()); + m_pressedInside = rect.contains(e->pos()); + if (m_pressedInside) + QQuickWindow::mousePressEvent(e); +} + +void QtMenuPopupWindow::mouseReleaseEvent(QMouseEvent *e) +{ + QRect rect = QRect(QPoint(), size()); + if (rect.contains(e->pos())) + QQuickWindow::mouseReleaseEvent(e); + else if (!m_pressedInside) + dismissMenu(); + else + forwardEventToTransientParent(e); +} + +void QtMenuPopupWindow::forwardEventToTransientParent(QMouseEvent *e) +{ + QWindow *parentMenuWindow = /*qobject_cast<QtMenuPopupWindow*>*/(transientParent()); + if (!parentMenuWindow) + return; + QPoint parentPos = parentMenuWindow->mapFromGlobal(mapToGlobal(e->pos())); + QMouseEvent pe = QMouseEvent(e->type(), parentPos, e->button(), e->buttons(), e->modifiers()); + QGuiApplication::sendEvent(parentMenuWindow, &pe); +} + +QT_END_NAMESPACE diff --git a/src/controls/qtmenupopupwindow_p.h b/src/controls/qtmenupopupwindow_p.h new file mode 100644 index 00000000..b1a9c051 --- /dev/null +++ b/src/controls/qtmenupopupwindow_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia Plc 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$ +** +****************************************************************************/ + +#ifndef QTMENUPOPUPWINDOW_H +#define QTMENUPOPUPWINDOW_H + +#include <QQuickWindow> + +QT_BEGIN_NAMESPACE + +class QEvent; +class QQuickItem; + +class QtMenuPopupWindow : public QQuickWindow +{ + Q_OBJECT +public: + QtMenuPopupWindow(QWindow *parent = 0); + void setMenuContentItem(QQuickItem *contentItem); + void setItemAt(const QQuickItem *menuItem); + void setParentWindow(QQuickWindow *parentWindow); + +public Q_SLOTS: + void dismissMenu(); + void updateSize(); + void updatePosition(); + +Q_SIGNALS: + void menuDismissed(); + +protected: + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + +private: + void forwardEventToTransientParent(QMouseEvent *); + + bool m_pressedInside; + const QQuickItem *m_itemAt; + QPointF m_oldItemPos; +}; + +QT_END_NAMESPACE + +#endif // QTMENUPOPUPWINDOW_H |