diff options
author | Gabriel de Dietrich <gabriel.dedietrich@digia.com> | 2013-06-05 21:45:04 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 16:35:08 +0200 |
commit | 1f8ee02fbc06f822ee131fdd395c8e8cd244601d (patch) | |
tree | 8846593196d96701741a5a5fba046d13e851ca43 | |
parent | 7adeff65148a3967af7b2ec44764a2c275dcd3fe (diff) | |
download | qtquickcontrols-1f8ee02fbc06f822ee131fdd395c8e8cd244601d.tar.gz |
ComboBox: Make popup scrollable
We factor out part the menu item container logic from Menu into
ColumnMenuContent, which takes care of scrolling and mouse hovering,
and selection. This makes possible to extend the menu items layout.
The pop-over and pull-down look is specified by the menu style
component by overriding the ScrollView style. The popup's maximum
height is also specified by the menu style.
The gallery example can finally use a font families combo box.
Task-number: QTBUG-31568
Change-Id: I34a7278f476471c0eb51ef51dde3dd83e13002fe
Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
-rw-r--r-- | examples/quick/controls/gallery/content/Controls.qml | 5 | ||||
-rw-r--r-- | src/controls/Menu.qml | 209 | ||||
-rw-r--r-- | src/controls/Private/ColumnMenuContent.qml | 160 | ||||
-rw-r--r-- | src/controls/Private/MenuContentScroller.qml | 74 | ||||
-rw-r--r-- | src/controls/Private/ScrollBar.qml | 2 | ||||
-rw-r--r-- | src/controls/Private/private.pri | 2 | ||||
-rw-r--r-- | src/controls/Private/qmldir | 2 | ||||
-rw-r--r-- | src/controls/Private/qquickstyleitem.cpp | 23 | ||||
-rw-r--r-- | src/controls/ScrollView.qml | 3 | ||||
-rw-r--r-- | src/controls/Styles/Base/ComboBoxStyle.qml | 16 | ||||
-rw-r--r-- | src/controls/Styles/Base/MenuStyle.qml | 22 | ||||
-rw-r--r-- | src/controls/Styles/Desktop/ComboBoxStyle.qml | 8 | ||||
-rw-r--r-- | src/controls/Styles/Desktop/MenuStyle.qml | 13 | ||||
-rw-r--r-- | src/controls/Styles/Desktop/ScrollViewStyle.qml | 2 | ||||
-rw-r--r-- | src/controls/qquickmenu.cpp | 2 | ||||
-rw-r--r-- | src/controls/qquickmenu_p.h | 3 | ||||
-rw-r--r-- | src/controls/qquickmenupopupwindow.cpp | 10 | ||||
-rw-r--r-- | src/controls/qquickmenupopupwindow_p.h | 1 |
18 files changed, 423 insertions, 134 deletions
diff --git a/examples/quick/controls/gallery/content/Controls.qml b/examples/quick/controls/gallery/content/Controls.qml index 98945675..7f6ea9aa 100644 --- a/examples/quick/controls/gallery/content/Controls.qml +++ b/examples/quick/controls/gallery/content/Controls.qml @@ -87,6 +87,11 @@ Item { currentIndex: 2 } ComboBox { + model: Qt.fontFamilies() + width: parent.width + currentIndex: 47 + } + ComboBox { id: editableCombo editable: true model: choices diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml index 4c3bc1be..949b6a91 100644 --- a/src/controls/Menu.qml +++ b/src/controls/Menu.qml @@ -152,14 +152,14 @@ MenuPrivate { property Component __menuComponent: Loader { id: menuFrameLoader - property Style __style: styleLoader.item - property Component menuItemStyle: __style ? __style.menuItem : null + readonly property Style __style: styleLoader.item + readonly property Component menuItemStyle: __style ? __style.menuItem : null - property var control: root - property alias contentWidth: column.width - property alias contentHeight: column.height + readonly property var control: root + property alias contentWidth: content.width + property alias contentHeight: content.height - property int subMenuXPos: width + (item && item["subMenuOverlap"] || 0) + readonly property int subMenuXPos: width + (item && item["subMenuOverlap"] || 0) visible: status === Loader.Ready sourceComponent: __style ? __style.frame : undefined @@ -204,17 +204,19 @@ MenuPrivate { 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 = itemsRepeater.itemAt(index) - if (!item["isSeparator"] && item.enabled) { + var item = content.menuItemAt(index) + if (item && !item["isSeparator"] && item.enabled) { root.__currentIndex = index return true } @@ -227,20 +229,24 @@ MenuPrivate { } Keys.onRightPressed: { - var item = itemsRepeater.itemAt(root.__currentIndex) + var item = content.menuItemAt(root.__currentIndex) if ((event.accepted = (item && item.isSubmenu))) { item.showSubMenu(true) item.menuItem.__currentIndex = 0 } } - Keys.onSpacePressed: menuFrameLoader.triggerAndDismiss() - Keys.onReturnPressed: menuFrameLoader.triggerAndDismiss() - Keys.onEnterPressed: menuFrameLoader.triggerAndDismiss() + 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 = itemsRepeater.itemAt(root.__currentIndex) if (item && !item.isSeparator) { root.__dismissMenu() if (!item.isSubmenu) @@ -250,126 +256,85 @@ MenuPrivate { Binding { // Make sure the styled frame is in the background - target: menuFrameLoader.item + target: item property: "z" - value: menuMouseArea.z - 1 + value: content.z - 1 } - MouseArea { - id: menuMouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.AllButtons - - onPositionChanged: updateCurrentItem(mouse) - onReleased: menuFrameLoader.triggerAndDismiss() - onExited: { - if (currentItem && !currentItem.menuItem.__popupVisible) { - currentItem = null - root.__currentIndex = -1 - } - } - - property Item currentItem: null - - function updateCurrentItem(mouse) { - var pos = mapToItem(column, mouse.x, mouse.y) - if (!currentItem || !currentItem.contains(Qt.point(pos.x - currentItem.x, pos.y - currentItem.y))) { - if (currentItem && !pressed && currentItem.isSubmenu) - currentItem.closeSubMenu() - var itemUnderMouse = column.childAt(pos.x, pos.y) - if (itemUnderMouse) { - currentItem = itemUnderMouse - } else if (currentItem) { - var itemItem = currentItem.item - if (!itemItem.contains(itemItem.mapFromItem(column, pos))) - currentItem = null - } + 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) + } - if (currentItem) { - root.__currentIndex = currentItem.menuItemIndex - if (currentItem.isSubmenu && !currentItem.menuItem.__popupVisible) - currentItem.showSubMenu(false) + 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.visible + active: visible + + function showSubMenu(immediately) { + if (immediately) { + if (root.__currentIndex === menuItemIndex) + menuItem.__popup(menuFrameLoader.subMenuXPos, 0, -1) } else { - root.__currentIndex = -1 + openMenuTimer.start() } } - } - // 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.items - - 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 && root.__currentIndex === index - property string text: isSubmenu ? menuItem.title : !isSeparator ? menuItem.text : "" - property bool showUnderlined: __contentItem.altPressed - - property int menuItemIndex: index - - sourceComponent: menuFrameLoader.menuItemStyle - enabled: visible && !isSeparator && !!menuItem && menuItem.enabled - visible: menuItem.visible - active: visible - - function showSubMenu(immediately) { - if (immediately) { - if (root.__currentIndex === menuItemIndex) { - if (Qt.application.layoutDirection === Qt.RightToLeft) - menuItem.__popup(0, 0, -1) - else - 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() - } - } - - Component.onCompleted: { - menuItem.__visualItem = menuItemLoader - - var title = text - var ampersandPos = title.indexOf("&") - if (ampersandPos !== -1) - menuFrameLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader - } - } + Timer { + id: openMenuTimer + interval: 50 + onTriggered: menuItemLoader.showSubMenu(true) } - onWidthChanged: { - for (var i = 0; i < children.length; i++) { - var item = children[i]["item"] - if (item) - item.implicitWidth = Math.max(root.__minimumWidth, implicitWidth) + 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 new file mode 100644 index 00000000..505756d1 --- /dev/null +++ b/src/controls/Private/ColumnMenuContent.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 + +Item { + id: content + + property Component menuItemDelegate + property Component scrollerStyle + property var itemsModel + property int minWidth: 100 + property real maxHeight: 800 + property int margin: 1 + + signal triggered(var item) + + function menuItemAt(index) { + list.currentIndex = index + return list.currentItem + } + + width: Math.max(list.contentWidth, minWidth) + height: Math.min(list.contentHeight, fittedMaxHeight) + 2 * margin + + readonly property int currentIndex: root.__currentIndex + property Item currentItem: null + readonly property int itemHeight: list.count > 0 ? list.contentItem.children[0].height : 23 + readonly property int fittingItems: Math.floor((maxHeight - downScroller.height) / itemHeight) + readonly property real fittedMaxHeight: itemHeight * fittingItems + downScroller.height + readonly property bool shouldUseScrollers: scrollView.__style.useScrollers && itemsModel.length > fittingItems + readonly property real upScrollerHeight: upScroller.visible ? upScroller.height : 0 + readonly property real downScrollerHeight: downScroller.visible ? downScroller.height : 0 + + function updateCurrentItem(mouse) { + var pos = mapToItem(list.contentItem, mouse.x, mouse.y) + if (!currentItem || !currentItem.contains(Qt.point(pos.x - currentItem.x, pos.y - currentItem.y))) { + if (currentItem && !hoverArea.pressed && currentItem.isSubmenu) + currentItem.closeSubMenu() + currentItem = list.itemAt(pos.x, pos.y) + if (currentItem) { + root.__currentIndex = currentItem.menuItemIndex + if (currentItem.isSubmenu && !currentItem.menuItem.__popupVisible) + currentItem.showSubMenu(false) + } else { + root.__currentIndex = -1 + } + } + } + + MouseArea { + id: hoverArea + anchors.left: scrollView.left + width: scrollView.width - scrollView.__verticalScrollBar.width + height: parent.height + + hoverEnabled: true + acceptedButtons: Qt.AllButtons + + onPositionChanged: updateCurrentItem(mouse) + onReleased: content.triggered(currentItem) + onExited: { + if (currentItem && !currentItem.menuItem.__popupVisible) { + currentItem = null + root.__currentIndex = -1 + } + } + + MenuContentScroller { + id: upScroller + direction: "up" + visible: shouldUseScrollers && !list.atYBeginning + x: margin + function scrollABit() { list.contentY -= itemHeight } + } + + MenuContentScroller { + id: downScroller + direction: "down" + visible: shouldUseScrollers && !list.atYEnd + x: margin + function scrollABit() { list.contentY += itemHeight } + } + } + + ScrollView { + id: scrollView + anchors { + fill: parent + topMargin: content.margin + upScrollerHeight + bottomMargin: downScrollerHeight - content.margin - 1 + rightMargin: -1 + } + + style: scrollerStyle + __wheelAreaScrollSpeed: itemHeight + + ListView { + id: list + model: itemsModel + delegate: menuItemDelegate + snapMode: ListView.SnapToItem + boundsBehavior: Flickable.StopAtBounds + highlightFollowsCurrentItem: true + highlightMoveDuration: 0 + } + } + + Timer { + interval: 1 + running: true + repeat: false + onTriggered: list.positionViewAtIndex(currentIndex, scrollView.__style.useScrollers + ? ListView.Center : ListView.Beginning) + } + + Binding { + target: scrollView.__verticalScrollBar + property: "singleStep" + value: itemHeight + } +} diff --git a/src/controls/Private/MenuContentScroller.qml b/src/controls/Private/MenuContentScroller.qml new file mode 100644 index 00000000..30a8825a --- /dev/null +++ b/src/controls/Private/MenuContentScroller.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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 + +MouseArea { + property string direction + + anchors { + top: direction === "up" ? parent.top : undefined + bottom: direction === "down" ? parent.bottom : undefined + } + + hoverEnabled: visible + height: scrollerLoader.item.height + width: parent.width + + Loader { + id: scrollerLoader + + sourceComponent: menuItemDelegate + property int index: -1 + property var modelData: { + "visible": true, + "scrollerDirection": direction, + "enabled": true + } + } + + Timer { + interval: 100 + repeat: true + triggeredOnStart: true + running: parent.containsMouse + onTriggered: scrollABit() + } +} diff --git a/src/controls/Private/ScrollBar.qml b/src/controls/Private/ScrollBar.qml index 9ee330d3..49dd20a4 100644 --- a/src/controls/Private/ScrollBar.qml +++ b/src/controls/Private/ScrollBar.qml @@ -56,6 +56,7 @@ Item { property alias minimumValue: slider.minimumValue property alias maximumValue: slider.maximumValue property alias value: slider.value + property int singleStep: 20 activeFocusOnTab: false @@ -89,7 +90,6 @@ Item { id: internal property bool horizontal: orientation === Qt.Horizontal property int pageStep: internal.horizontal ? width : height - property int singleStep: 20 property bool scrollToClickposition: internal.scrollToClickPosition anchors.fill: parent cursorShape: __panel.visible ? Qt.ArrowCursor : Qt.IBeamCursor // forces a cursor change diff --git a/src/controls/Private/private.pri b/src/controls/Private/private.pri index bd001c0a..58cc7f11 100644 --- a/src/controls/Private/private.pri +++ b/src/controls/Private/private.pri @@ -37,6 +37,8 @@ PRIVATE_QML_FILES += \ $$PWD/ScrollViewHelper.qml \ $$PWD/ScrollBar.qml \ $$PWD/FocusFrame.qml \ + $$PWD/ColumnMenuContent.qml \ + $$PWD/MenuContentScroller.qml \ $$PWD/qmldir QML_FILES += $$PRIVATE_QML_FILES diff --git a/src/controls/Private/qmldir b/src/controls/Private/qmldir index 1ee66461..a49401dc 100644 --- a/src/controls/Private/qmldir +++ b/src/controls/Private/qmldir @@ -16,3 +16,5 @@ SpinBoxStyle 1.0 ../Styles/Base/SpinBoxStyle.qml ToolBarStyle 1.0 ../Styles/Base/ToolBarStyle.qml StatusBarStyle 1.0 ../Styles/Base/StatusBarStyle.qml ToolButtonStyle 1.0 ../Styles/Base/ToolButtonStyle.qml +MenuContentScroller 1.0 MenuContentScroller.qml +ColumnMenuContent 1.0 ColumnMenuContent.qml diff --git a/src/controls/Private/qquickstyleitem.cpp b/src/controls/Private/qquickstyleitem.cpp index 4101f90f..82e503dd 100644 --- a/src/controls/Private/qquickstyleitem.cpp +++ b/src/controls/Private/qquickstyleitem.cpp @@ -418,7 +418,13 @@ void QQuickStyleItem::initStyleOption() QStyleOptionMenuItem *opt = qstyleoption_cast<QStyleOptionMenuItem*>(m_styleoption); // For GTK style. See below, in setElementType() setProperty("_q_isComboBoxPopupItem", m_itemType == ComboBoxItem); - if (text().isEmpty()) { + + QString scrollerDirection = m_properties["scrollerDirection"].toString(); + if (!scrollerDirection.isEmpty()) { + opt->menuItemType = QStyleOptionMenuItem::Scroller; + opt->state |= scrollerDirection == "up" ? + QStyle::State_UpArrow : QStyle::State_DownArrow; + } else if (text().isEmpty()) { opt->menuItemType = QStyleOptionMenuItem::Separator; } else { opt->text = text(); @@ -861,7 +867,12 @@ QSize QQuickStyleItem::sizeFromContents(int width, int height) break; case MenuItem: case ComboBoxItem: - size = qApp->style()->sizeFromContents(QStyle::CT_MenuItem, m_styleoption, QSize(width,height)); + if (static_cast<QStyleOptionMenuItem *>(m_styleoption)->menuItemType == QStyleOptionMenuItem::Scroller) { + size.setHeight(qMax(QApplication::globalStrut().height(), + qApp->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, 0))); + } else { + size = qApp->style()->sizeFromContents(QStyle::CT_MenuItem, m_styleoption, QSize(width,height)); + } break; default: break; @@ -1286,8 +1297,12 @@ void QQuickStyleItem::paint(QPainter *painter) qApp->style()->drawControl(QStyle::CE_MenuBarItem, m_styleoption, painter); break; case MenuItem: - case ComboBoxItem: // fall through - qApp->style()->drawControl(QStyle::CE_MenuItem, m_styleoption, painter); + case ComboBoxItem: { // fall through + QStyle::ControlElement menuElement = + static_cast<QStyleOptionMenuItem *>(m_styleoption)->menuItemType == QStyleOptionMenuItem::Scroller ? + QStyle::CE_MenuScroller : QStyle::CE_MenuItem; + qApp->style()->drawControl(menuElement, m_styleoption, painter); + } break; case CheckBox: qApp->style()->drawControl(QStyle::CE_CheckBox, m_styleoption, painter); diff --git a/src/controls/ScrollView.qml b/src/controls/ScrollView.qml index 5717d70f..06343b74 100644 --- a/src/controls/ScrollView.qml +++ b/src/controls/ScrollView.qml @@ -137,6 +137,8 @@ FocusScope { /*! \internal */ property Item __scroller: scroller /*! \internal */ + property alias __wheelAreaScrollSpeed: wheelArea.scrollSpeed + /*! \internal */ property int __scrollBarTopMargin: 0 /*! \internal */ property int __viewTopMargin: 0 @@ -287,7 +289,6 @@ FocusScope { verticalScrollBar.width + scrollBarSpacing : 0 property int horizontalScrollbarOffset: horizontalScrollBar.visible && !horizontalScrollBar.isTransient ? horizontalScrollBar.height + scrollBarSpacing : 0 - Loader { id: frameLoader sourceComponent: __style ? __style.frame : null diff --git a/src/controls/Styles/Base/ComboBoxStyle.qml b/src/controls/Styles/Base/ComboBoxStyle.qml index 2011b219..b49d8f97 100644 --- a/src/controls/Styles/Base/ComboBoxStyle.qml +++ b/src/controls/Styles/Base/ComboBoxStyle.qml @@ -38,6 +38,7 @@ ** ****************************************************************************/ import QtQuick 2.1 +import QtQuick.Window 2.1 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Private 1.0 @@ -188,7 +189,11 @@ Style { /*! \internal */ property Component __dropDownStyle: MenuStyle { + maxPopupHeight: 600 __menuItemType: "comboboxitem" + scrollerStyle: ScrollViewStyle { + property bool useScrollers: false + } } /*! \internal */ @@ -198,6 +203,8 @@ Style { width: (parent ? parent.contentWidth : 0) height: (parent ? parent.contentHeight : 0) + 2 border.color: "#777" + property real maxHeight: Math.min(500, Screen.desktopAvailableHeight) + property int margin: 1 } property Component menuItem: Rectangle { @@ -211,5 +218,14 @@ Style { text: textRef } } + + property Component scrollerStyle: Style { + padding { left: 0; right: 0; top: 0; bottom: 0 } + property bool scrollToClickedPosition: false + property Component frame: Item { visible: false } + property Component corner: Item { visible: false } + property Component __scrollbar: Item { visible: false } + property bool useScrollers: true + } } } diff --git a/src/controls/Styles/Base/MenuStyle.qml b/src/controls/Styles/Base/MenuStyle.qml index 9be32459..c05c6a6d 100644 --- a/src/controls/Styles/Base/MenuStyle.qml +++ b/src/controls/Styles/Base/MenuStyle.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.1 +import QtQuick.Window 2.1 import QtQuick.Controls 1.1 import QtQuick.Controls.Private 1.0 @@ -53,6 +54,7 @@ Style { id: styleRoot property string __menuItemType: "menuitem" + property real maxPopupHeight: 600 // ### FIXME Screen.desktopAvailableHeight * 0.99 property Component frame: Rectangle { width: (parent ? parent.contentWidth : 0) + 2 @@ -62,6 +64,8 @@ Style { border { width: 1; color: "darkgray" } property int subMenuOverlap: -1 + property real maxHeight: maxPopupHeight + property int margin: 1 } property Component menuItem: Rectangle { @@ -69,7 +73,7 @@ Style { y: 1 implicitWidth: Math.max((parent ? parent.width : 0), 18 + text.paintedWidth + (rightDecoration.visible ? rightDecoration.width + 40 : 12)) - implicitHeight: isSeparator ? text.font.pixelSize / 2 : text.paintedHeight + 4 + implicitHeight: isSeparator ? text.font.pixelSize / 2 : !!scrollerDirection ? text.font.pixelSize * 0.75 : text.paintedHeight + 4 color: selected && enabled ? "" : backgroundColor gradient: selected && enabled ? selectedGradient : undefined border.width: 1 @@ -188,6 +192,13 @@ Style { style: selected || !isSubmenu ? Text.Normal : Text.Raised; styleColor: Qt.lighter(color, 4) } + Image { + id: scrollerDecoration + visible: !!scrollerDirection + anchors.centerIn: parent + source: scrollerDirection === "up" ? "images/arrow-up.png" : "images/arrow-down.png" + } + Rectangle { visible: isSeparator width: parent.width - 2 @@ -197,4 +208,13 @@ Style { color: "darkgray" } } + + property Component scrollerStyle: Style { + padding { left: 0; right: 0; top: 0; bottom: 0 } + property bool scrollToClickedPosition: false + property Component frame: Item { visible: false } + property Component corner: Item { visible: false } + property Component __scrollbar: Item { visible: false } + property bool useScrollers: true + } } diff --git a/src/controls/Styles/Desktop/ComboBoxStyle.qml b/src/controls/Styles/Desktop/ComboBoxStyle.qml index e6495ceb..2c1cd7c2 100644 --- a/src/controls/Styles/Desktop/ComboBoxStyle.qml +++ b/src/controls/Styles/Desktop/ComboBoxStyle.qml @@ -38,9 +38,11 @@ ** ****************************************************************************/ import QtQuick 2.1 +import QtQuick.Window 2.1 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Private 1.0 +import "." as Desktop Style { readonly property ComboBox control: __control @@ -86,6 +88,8 @@ Style { width: (parent ? parent.contentWidth : 0) height: (parent ? parent.contentHeight : 0) + 2 * pixelMetric("defaultframewidth") + property real maxHeight: 600 + property int margin: pixelMetric("menuvmargin") + pixelMetric("menupanelwidth") } property Component menuItem: StyleItem { @@ -107,5 +111,9 @@ Style { selected: parent ? parent.selected : false } } + + property Component scrollerStyle: Desktop.ScrollViewStyle { + property bool useScrollers: false + } } } diff --git a/src/controls/Styles/Desktop/MenuStyle.qml b/src/controls/Styles/Desktop/MenuStyle.qml index b1f5e9cf..a3f3e8fa 100644 --- a/src/controls/Styles/Desktop/MenuStyle.qml +++ b/src/controls/Styles/Desktop/MenuStyle.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.1 +import QtQuick.Window 2.1 import QtQuick.Controls 1.1 import QtQuick.Controls.Private 1.0 @@ -56,6 +57,8 @@ Style { height: implicitHeight + 2 * (pixelMetric("menuvmargin") + pixelMetric("menupanelwidth")) property int subMenuOverlap: pixelMetric("submenuoverlap") + property real maxHeight: Screen.desktopAvailableHeight * 0.99 + property int margin: pixelMetric("menuvmargin") + pixelMetric("menupanelwidth") Rectangle { visible: anchors.margins > 0 @@ -90,10 +93,20 @@ Style { "exclusive": !!menuItem && !!menuItem["exclusiveGroup"], "shortcut": !!menuItem && menuItem["shortcut"] || "", "isSubmenu": isSubmenu, + "scrollerDirection": scrollerDirection, "icon": !!menuItem && menuItem.__icon } Accessible.role: Accessible.MenuItem Accessible.name: StyleHelpers.removeMnemonics(text) } + + property Component scrollerStyle: Style { + padding { left: 0; right: 0; top: 0; bottom: 0 } + property bool scrollToClickedPosition: false + property Component frame: Item { visible: false } + property Component corner: Item { visible: false } + property Component __scrollbar: Item { visible: false } + property bool useScrollers: true + } } diff --git a/src/controls/Styles/Desktop/ScrollViewStyle.qml b/src/controls/Styles/Desktop/ScrollViewStyle.qml index 9342d524..cd8a2a04 100644 --- a/src/controls/Styles/Desktop/ScrollViewStyle.qml +++ b/src/controls/Styles/Desktop/ScrollViewStyle.qml @@ -58,7 +58,7 @@ Style { id: styleitem elementType: "frame" sunken: true - visible: frameVisible + visible: control.frameVisible } property Component corner: StyleItem { elementType: "scrollareacorner" } diff --git a/src/controls/qquickmenu.cpp b/src/controls/qquickmenu.cpp index 62e7a7bd..600dbe79 100644 --- a/src/controls/qquickmenu.cpp +++ b/src/controls/qquickmenu.cpp @@ -298,6 +298,8 @@ void QQuickMenu::setMinimumWidth(int w) m_minimumWidth = w; if (m_platformMenu) m_platformMenu->setMinimumWidth(w); + + emit minimumWidthChanged(); } void QQuickMenu::setFont(const QFont &arg) diff --git a/src/controls/qquickmenu_p.h b/src/controls/qquickmenu_p.h index f15e6caa..c1fab0b5 100644 --- a/src/controls/qquickmenu_p.h +++ b/src/controls/qquickmenu_p.h @@ -69,7 +69,7 @@ class QQuickMenu : public QQuickMenuText Q_PROPERTY(int __selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY __selectedIndexChanged) Q_PROPERTY(bool __popupVisible READ popupVisible NOTIFY popupVisibleChanged) Q_PROPERTY(QQuickItem *__contentItem READ menuContentItem WRITE setMenuContentItem NOTIFY menuContentItemChanged) - Q_PROPERTY(int __minimumWidth READ minimumWidth WRITE setMinimumWidth) + Q_PROPERTY(int __minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) Q_PROPERTY(QFont __font READ font WRITE setFont) Q_PROPERTY(qreal __xOffset READ xOffset WRITE setXOffset) Q_PROPERTY(qreal __yOffset READ yOffset WRITE setYOffset) @@ -100,6 +100,7 @@ Q_SIGNALS: void __menuClosed(); void popupVisibleChanged(); void menuContentItemChanged(); + void minimumWidthChanged(); public: QQuickMenu(QObject *parent = 0); diff --git a/src/controls/qquickmenupopupwindow.cpp b/src/controls/qquickmenupopupwindow.cpp index d21bd0d5..e06b5f03 100644 --- a/src/controls/qquickmenupopupwindow.cpp +++ b/src/controls/qquickmenupopupwindow.cpp @@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE QQuickMenuPopupWindow::QQuickMenuPopupWindow(QWindow *parent) : QQuickWindow(parent), m_mouseMoved(false), m_needsActivatedEvent(true), - m_itemAt(0), m_parentItem(0), m_menuContentItem(0) + m_dismissed(false), m_itemAt(0), m_parentItem(0), m_menuContentItem(0) { setFlags(Qt::Popup); setModality(Qt::WindowModal); @@ -176,6 +176,7 @@ void QQuickMenuPopupWindow::setGeometry(int posx, int posy, int w, int h) void QQuickMenuPopupWindow::dismissMenu() { + m_dismissed = true; emit menuDismissed(); close(); } @@ -210,7 +211,9 @@ void QQuickMenuPopupWindow::mouseMoveEvent(QMouseEvent *e) void QQuickMenuPopupWindow::mousePressEvent(QMouseEvent *e) { QRect rect = QRect(QPoint(), size()); - if (!rect.contains(e->pos())) + if (rect.contains(e->pos())) + QQuickWindow::mousePressEvent(e); + else forwardEventToTransientParent(e); } @@ -221,7 +224,8 @@ void QQuickMenuPopupWindow::mouseReleaseEvent(QMouseEvent *e) if (m_mouseMoved) { QMouseEvent pe = QMouseEvent(QEvent::MouseButtonPress, e->pos(), e->button(), e->buttons(), e->modifiers()); QQuickWindow::mousePressEvent(&pe); - QQuickWindow::mouseReleaseEvent(e); + if (!m_dismissed) + QQuickWindow::mouseReleaseEvent(e); } m_mouseMoved = true; // Initial mouse release counts as move. } else { diff --git a/src/controls/qquickmenupopupwindow_p.h b/src/controls/qquickmenupopupwindow_p.h index 4407fdec..4c629219 100644 --- a/src/controls/qquickmenupopupwindow_p.h +++ b/src/controls/qquickmenupopupwindow_p.h @@ -89,6 +89,7 @@ private: bool m_mouseMoved; bool m_needsActivatedEvent; + bool m_dismissed; QQuickItem *m_itemAt; QPointF m_oldItemPos; QQuickItem *m_parentItem; |