summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@digia.com>2013-10-16 16:14:52 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-18 13:43:41 +0200
commit12a49a3c0c38b7ac5599c7b421573721750d62f5 (patch)
tree46b43d4cc97b89adf11be22cb0586c0cea86d937
parent771e2babf6d201f7f6a11c93a59c152896dad8db (diff)
downloadqtquickcontrols-12a49a3c0c38b7ac5599c7b421573721750d62f5.tar.gz
Menu: Factor MenuContentItem out
Preliminary patch to get non-windowed menus on Android, iOS, etc. Change-Id: Idc17009baa3a647ec2fbc353f1360fb9ca9e051a Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
-rw-r--r--src/controls/Menu.qml195
-rw-r--r--src/controls/Private/ColumnMenuContent.qml8
-rw-r--r--src/controls/Private/MenuContentItem.qml233
-rw-r--r--src/controls/Private/private.pri1
-rw-r--r--src/controls/Private/qmldir1
5 files changed, 242 insertions, 196 deletions
diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml
index 689505ac..9c766846 100644
--- a/src/controls/Menu.qml
+++ b/src/controls/Menu.qml
@@ -141,201 +141,12 @@ MenuPrivate {
/*! \internal */
__contentItem: Loader {
- sourceComponent: __menuComponent
+ sourceComponent: MenuContentItem {
+ menu: root
+ }
active: !root.__isNative && root.__popupVisible
focus: true
Keys.forwardTo: item ? [item, root.__parentContentItem] : []
property bool altPressed: root.__parentContentItem ? root.__parentContentItem.altPressed : false
}
-
- /*! \internal */
- property Component __menuComponent: Loader {
- id: menuFrameLoader
-
- readonly property Style __style: styleLoader.item
- readonly property Component menuItemStyle: __style ? __style.menuItem : null
-
- readonly property var control: root
- property alias contentWidth: content.width
- property alias contentHeight: content.height
-
- readonly 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
- property alias __control: menuFrameLoader
- onStatusChanged: {
- if (status === Loader.Error)
- console.error("Failed to load Style for", root)
- }
- }
-
- focus: true
- property var mnemonicsMap: ({})
-
- Keys.onPressed: {
- var item = null
- if (!(event.modifiers & Qt.AltModifier)
- && (item = mnemonicsMap[event.text.toUpperCase()])) {
- if (item.isSubmenu) {
- root.__currentIndex = item.menuItemIndex
- item.showSubMenu(true)
- item.menuItem.__currentIndex = 0
- } else {
- triggerAndDismiss(item)
- }
- event.accepted = true
- } else {
- event.accepted = false
- }
- }
-
- Keys.onEscapePressed: root.__dismissMenu()
-
- Keys.onDownPressed: {
- if (root.__currentIndex < 0)
- root.__currentIndex = -1
-
- for (var i = root.__currentIndex + 1;
- i < root.items.length && !canBeHovered(i); i++)
- ;
- event.accepted = true
- }
-
- Keys.onUpPressed: {
- for (var i = root.__currentIndex - 1;
- i >= 0 && !canBeHovered(i); i--)
- ;
- event.accepted = true
- }
-
- function canBeHovered(index) {
- var item = content.menuItemAt(index)
- if (item && !item["isSeparator"] && item.enabled) {
- root.__currentIndex = index
- return true
- }
- return false
- }
-
- Keys.onLeftPressed: {
- if ((event.accepted = root.__parentMenu.hasOwnProperty("title")))
- __closeMenu()
- }
-
- Keys.onRightPressed: {
- var item = content.menuItemAt(root.__currentIndex)
- if ((event.accepted = (item && item.isSubmenu))) {
- item.showSubMenu(true)
- item.menuItem.__currentIndex = 0
- }
- }
-
- Keys.onSpacePressed: triggerCurrent()
- Keys.onReturnPressed: triggerCurrent()
- Keys.onEnterPressed: triggerCurrent()
-
- function triggerCurrent() {
- var item = content.menuItemAt(root.__currentIndex)
- if (item)
- content.triggered(item)
- }
-
- function triggerAndDismiss(item) {
- if (item && !item.isSeparator) {
- root.__dismissMenu()
- if (!item.isSubmenu)
- item.menuItem.trigger()
- }
- }
-
- Binding {
- // Make sure the styled frame is in the background
- target: item
- property: "z"
- value: content.z - 1
- }
-
- ColumnMenuContent {
- id: content
- menuItemDelegate: menuItemComponent
- scrollerStyle: __style ? __style.scrollerStyle : undefined
- itemsModel: root.items
- margin: menuFrameLoader.item ? menuFrameLoader.item.margin : 0
- minWidth: root.__minimumWidth
- maxHeight: menuFrameLoader.item ? menuFrameLoader.item.maxHeight : 0
- onTriggered: triggerAndDismiss(item)
- }
-
- Component {
- id: menuItemComponent
- Loader {
- id: menuItemLoader
-
- property var menuItem: modelData
- readonly property bool isSeparator: !!menuItem && menuItem.type === MenuItemType.Separator
- readonly property bool isSubmenu: !!menuItem && menuItem.type === MenuItemType.Menu
- property bool selected: !(isSeparator || !!scrollerDirection) && root.__currentIndex === index
- property string text: isSubmenu ? menuItem.title : !(isSeparator || !!scrollerDirection) ? menuItem.text : ""
- property bool showUnderlined: __contentItem.altPressed
- readonly property var scrollerDirection: menuItem["scrollerDirection"]
-
- property int menuItemIndex: index
-
- sourceComponent: menuFrameLoader.menuItemStyle
- enabled: visible && !isSeparator && !!menuItem && menuItem.enabled
- visible: !!menuItem && menuItem.visible
- active: visible
-
- function showSubMenu(immediately) {
- if (immediately) {
- if (root.__currentIndex === menuItemIndex)
- menuItem.__popup(menuFrameLoader.subMenuXPos, 0, -1)
- } else {
- openMenuTimer.start()
- }
- }
-
- Timer {
- id: openMenuTimer
- interval: 50
- onTriggered: menuItemLoader.showSubMenu(true)
- }
-
- function closeSubMenu() { closeMenuTimer.start() }
-
- Timer {
- id: closeMenuTimer
- interval: 1
- onTriggered: {
- if (root.__currentIndex !== menuItemIndex)
- menuItem.__closeMenu()
- }
- }
-
- onLoaded: {
- menuItem.__visualItem = menuItemLoader
-
- if (content.width < item.implicitWidth)
- content.width = item.implicitWidth
-
- var title = text
- var ampersandPos = title.indexOf("&")
- if (ampersandPos !== -1)
- menuFrameLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader
- }
-
- Binding {
- target: menuItemLoader.item
- property: "width"
- value: Math.max(root.__minimumWidth, content.width)
- }
- }
- }
- }
}
diff --git a/src/controls/Private/ColumnMenuContent.qml b/src/controls/Private/ColumnMenuContent.qml
index 9e29361d..bb21dcb9 100644
--- a/src/controls/Private/ColumnMenuContent.qml
+++ b/src/controls/Private/ColumnMenuContent.qml
@@ -61,7 +61,7 @@ Item {
width: Math.max(list.contentWidth, minWidth)
height: Math.min(list.contentHeight, fittedMaxHeight) + 2 * margin
- readonly property int currentIndex: root.__currentIndex
+ readonly property int currentIndex: menu.__currentIndex
property Item currentItem: null
readonly property int itemHeight: (list.count > 0 && list.contentItem.children[0]) ? list.contentItem.children[0].height : 23
readonly property int fittingItems: Math.floor((maxHeight - downScroller.height) / itemHeight)
@@ -77,11 +77,11 @@ Item {
currentItem.closeSubMenu()
currentItem = list.itemAt(pos.x, pos.y)
if (currentItem) {
- root.__currentIndex = currentItem.menuItemIndex
+ menu.__currentIndex = currentItem.menuItemIndex
if (currentItem.isSubmenu && !currentItem.menuItem.__popupVisible)
currentItem.showSubMenu(false)
} else {
- root.__currentIndex = -1
+ menu.__currentIndex = -1
}
}
}
@@ -123,7 +123,7 @@ Item {
onExited: {
if (currentItem && !currentItem.menuItem.__popupVisible) {
currentItem = null
- root.__currentIndex = -1
+ menu.__currentIndex = -1
}
}
diff --git a/src/controls/Private/MenuContentItem.qml b/src/controls/Private/MenuContentItem.qml
new file mode 100644
index 00000000..a7b7233d
--- /dev/null
+++ b/src/controls/Private/MenuContentItem.qml
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Quick Controls module 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$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.1
+import QtQuick.Controls.Styles 1.1
+
+Loader {
+ id: menuFrameLoader
+
+ readonly property Style __style: styleLoader.item
+ readonly property Component menuItemStyle: __style ? __style.menuItem : null
+
+ property var menu: root
+ property alias contentWidth: content.width
+ property alias contentHeight: content.height
+
+ readonly property int subMenuXPos: width + (item && item["subMenuOverlap"] || 0)
+
+ visible: status === Loader.Ready
+ sourceComponent: __style ? __style.frame : undefined
+
+ Loader {
+ id: styleLoader
+ active: !menu.isNative
+ sourceComponent: menu.style
+ property alias __control: menuFrameLoader
+ onStatusChanged: {
+ if (status === Loader.Error)
+ console.error("Failed to load Style for", menu)
+ }
+ }
+
+ focus: true
+ property var mnemonicsMap: ({})
+
+ Keys.onPressed: {
+ var item = null
+ if (!(event.modifiers & Qt.AltModifier)
+ && (item = mnemonicsMap[event.text.toUpperCase()])) {
+ if (item.isSubmenu) {
+ menu.__currentIndex = item.menuItemIndex
+ item.showSubMenu(true)
+ item.menuItem.__currentIndex = 0
+ } else {
+ triggerAndDismiss(item)
+ }
+ event.accepted = true
+ } else {
+ event.accepted = false
+ }
+ }
+
+ Keys.onEscapePressed: menu.__dismissMenu()
+
+ Keys.onDownPressed: {
+ if (menu.__currentIndex < 0)
+ menu.__currentIndex = -1
+
+ for (var i = menu.__currentIndex + 1;
+ i < menu.items.length && !canBeHovered(i); i++)
+ ;
+ event.accepted = true
+ }
+
+ Keys.onUpPressed: {
+ for (var i = menu.__currentIndex - 1;
+ i >= 0 && !canBeHovered(i); i--)
+ ;
+ event.accepted = true
+ }
+
+ function canBeHovered(index) {
+ var item = content.menuItemAt(index)
+ if (item && !item["isSeparator"] && item.enabled) {
+ menu.__currentIndex = index
+ return true
+ }
+ return false
+ }
+
+ Keys.onLeftPressed: {
+ if ((event.accepted = menu.__parentMenu.hasOwnProperty("title")))
+ __closeMenu()
+ }
+
+ Keys.onRightPressed: {
+ var item = content.menuItemAt(menu.__currentIndex)
+ if ((event.accepted = (item && item.isSubmenu))) {
+ item.showSubMenu(true)
+ item.menuItem.__currentIndex = 0
+ }
+ }
+
+ Keys.onSpacePressed: triggerCurrent()
+ Keys.onReturnPressed: triggerCurrent()
+ Keys.onEnterPressed: triggerCurrent()
+
+ function triggerCurrent() {
+ var item = content.menuItemAt(menu.__currentIndex)
+ if (item)
+ content.triggered(item)
+ }
+
+ function triggerAndDismiss(item) {
+ if (item && !item.isSeparator) {
+ menu.__dismissMenu()
+ if (!item.isSubmenu)
+ item.menuItem.trigger()
+ }
+ }
+
+ Binding {
+ // Make sure the styled frame is in the background
+ target: item
+ property: "z"
+ value: content.z - 1
+ }
+
+ ColumnMenuContent {
+ id: content
+ menuItemDelegate: menuItemComponent
+ scrollerStyle: __style ? __style.scrollerStyle : undefined
+ itemsModel: menu.items
+ margin: menuFrameLoader.item ? menuFrameLoader.item.margin : 0
+ minWidth: menu.__minimumWidth
+ maxHeight: menuFrameLoader.item ? menuFrameLoader.item.maxHeight : 0
+ onTriggered: triggerAndDismiss(item)
+ }
+
+ Component {
+ id: menuItemComponent
+ Loader {
+ id: menuItemLoader
+
+ property var menuItem: modelData
+ readonly property bool isSeparator: !!menuItem && menuItem.type === MenuItemType.Separator
+ readonly property bool isSubmenu: !!menuItem && menuItem.type === MenuItemType.Menu
+ property bool selected: !(isSeparator || !!scrollerDirection) && menu.__currentIndex === index
+ property string text: isSubmenu ? menuItem.title : !(isSeparator || !!scrollerDirection) ? menuItem.text : ""
+ property bool showUnderlined: menu.__contentItem.altPressed
+ readonly property var scrollerDirection: menuItem["scrollerDirection"]
+
+ property int menuItemIndex: index
+
+ sourceComponent: menuFrameLoader.menuItemStyle
+ enabled: visible && !isSeparator && !!menuItem && menuItem.enabled
+ visible: !!menuItem && menuItem.visible
+ active: visible
+
+ function showSubMenu(immediately) {
+ if (immediately) {
+ if (menu.__currentIndex === menuItemIndex)
+ menuItem.__popup(menuFrameLoader.subMenuXPos, 0, -1)
+ } else {
+ openMenuTimer.start()
+ }
+ }
+
+ Timer {
+ id: openMenuTimer
+ interval: 50
+ onTriggered: menuItemLoader.showSubMenu(true)
+ }
+
+ function closeSubMenu() { closeMenuTimer.start() }
+
+ Timer {
+ id: closeMenuTimer
+ interval: 1
+ onTriggered: {
+ if (menu.__currentIndex !== menuItemIndex)
+ menuItem.__closeMenu()
+ }
+ }
+
+ onLoaded: {
+ menuItem.__visualItem = menuItemLoader
+
+ if (content.width < item.implicitWidth)
+ content.width = item.implicitWidth
+
+ var title = text
+ var ampersandPos = title.indexOf("&")
+ if (ampersandPos !== -1)
+ menuFrameLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader
+ }
+
+ Binding {
+ target: menuItemLoader.item
+ property: "width"
+ value: Math.max(menu.__minimumWidth, content.width)
+ }
+ }
+ }
+}
diff --git a/src/controls/Private/private.pri b/src/controls/Private/private.pri
index 6cd9f523..f51223cd 100644
--- a/src/controls/Private/private.pri
+++ b/src/controls/Private/private.pri
@@ -41,6 +41,7 @@ PRIVATE_QML_FILES += \
$$PWD/TextSingleton.qml \
$$PWD/FocusFrame.qml \
$$PWD/ColumnMenuContent.qml \
+ $$PWD/MenuContentItem.qml \
$$PWD/MenuContentScroller.qml \
$$PWD/qmldir
diff --git a/src/controls/Private/qmldir b/src/controls/Private/qmldir
index 93a71e4d..05d52b19 100644
--- a/src/controls/Private/qmldir
+++ b/src/controls/Private/qmldir
@@ -16,6 +16,7 @@ GroupBoxStyle 1.0 ../Styles/Base/GroupBoxStyle.qml
ToolBarStyle 1.0 ../Styles/Base/ToolBarStyle.qml
StatusBarStyle 1.0 ../Styles/Base/StatusBarStyle.qml
ToolButtonStyle 1.0 ../Styles/Base/ToolButtonStyle.qml
+MenuContentItem 1.0 MenuContentItem.qml
MenuContentScroller 1.0 MenuContentScroller.qml
ColumnMenuContent 1.0 ColumnMenuContent.qml
singleton TextSingleton 1.0 TextSingleton.qml