diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml index 9c76684605fa74ddaa68de85dc42e14a0ca28b00..d7aa4a0a1b628835039dfaa76ab62af6aaeaf725 100644 --- a/src/controls/Menu.qml +++ b/src/controls/Menu.qml @@ -129,9 +129,17 @@ MenuPrivate { /*! \internal */ property Component __selfComponent: null - /*! \internal */ - property Component style: Qt.createComponent(Settings.style + "/MenuStyle.qml", root) + property Component style + Component.onCompleted: { + if (!style) { + __usingDefaultStyle = true + style = Qt.binding(function() { return Qt.createComponent(Settings.style + "/MenuStyle.qml", root) }) + } + } + + /*! \internal */ + property bool __usingDefaultStyle: false /*! \internal */ property var __parentContentItem: __parentMenu.__contentItem /*! \internal */ @@ -142,7 +150,7 @@ MenuPrivate { /*! \internal */ __contentItem: Loader { sourceComponent: MenuContentItem { - menu: root + __menu: root } active: !root.__isNative && root.__popupVisible focus: true diff --git a/src/controls/MenuBar.qml b/src/controls/MenuBar.qml index 42b3f39aad6dbb297adbc25ae1197a7b06aa95fb..f294d54eb1e80babbb3724ca5c9b8e4688057872 100644 --- a/src/controls/MenuBar.qml +++ b/src/controls/MenuBar.qml @@ -73,7 +73,6 @@ import QtQuick.Controls.Private 1.0 MenuBarPrivate { id: root - /*! \internal */ property Component style: Qt.createComponent(Settings.style + "/MenuBarStyle.qml", root) /*! \internal */ @@ -83,21 +82,18 @@ MenuBarPrivate { active: !root.__isNative focus: true Keys.forwardTo: [item] - property bool altPressed: item ? item.altPressed : false + property bool altPressed: item ? item.__altPressed : false } /*! \internal */ property Component __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 - sourceComponent: __style ? __style.frame : undefined + sourceComponent: d.style ? d.style.background : undefined Loader { id: styleLoader @@ -109,9 +105,8 @@ MenuBarPrivate { } } - property int openedMenuIndex: -1 - property bool preselectMenuItem: false - property alias contentHeight: row.height + width: root.__contentItem.width + height: Math.max(row.height + d.heightPadding, item ? item.implicitHeight : 0) Binding { // Make sure the styled menu bar is in the background @@ -120,63 +115,145 @@ MenuBarPrivate { value: menuMouseArea.z - 1 } - focus: true + QtObject { + id: d + + property Style style: styleLoader.item + + property int openedMenuIndex: -1 + property bool preselectMenuItem: false + property real heightPadding: style ? style.padding.top + style.padding.bottom : 0 + + property bool altPressed: false + property bool altPressedAgain: false + property var mnemonicsMap: ({}) + + function dismissActiveFocus(event, reason) { + if (reason) { + altPressedAgain = false + altPressed = false + openedMenuIndex = -1 + root.__contentItem.parent.forceActiveFocus() + } else { + event.accepted = false + } + } + + function maybeOpenFirstMenu(event) { + if (altPressed && openedMenuIndex === -1) { + preselectMenuItem = true + openedMenuIndex = 0 + } else { + event.accepted = false + } + } + } + property alias __altPressed: d.altPressed // Needed for the menu contents - property bool altPressed: false - property bool altPressedAgain: false - property var mnemonicsMap: ({}) + focus: true Keys.onPressed: { var action = null if (event.key === Qt.Key_Alt) { - if (!altPressed) - altPressed = true + if (!d.altPressed) + d.altPressed = true else - altPressedAgain = true - } else if (altPressed && (action = mnemonicsMap[event.text.toUpperCase()])) { - preselectMenuItem = true + d.altPressedAgain = true + } else if (d.altPressed && (action = d.mnemonicsMap[event.text.toUpperCase()])) { + d.preselectMenuItem = true action.trigger() event.accepted = true } } - function dismissActiveFocus(event, reason) { - if (reason) { - altPressedAgain = false - altPressed = false - openedMenuIndex = -1 - root.__contentItem.parent.forceActiveFocus() - } else { - event.accepted = false - } - } + Keys.onReleased: d.dismissActiveFocus(event, d.altPressedAgain && d.openedMenuIndex === -1) + Keys.onEscapePressed: d.dismissActiveFocus(event, d.openedMenuIndex === -1) - Keys.onReleased: dismissActiveFocus(event, altPressedAgain && openedMenuIndex === -1) - Keys.onEscapePressed: dismissActiveFocus(event, openedMenuIndex === -1) + Keys.onUpPressed: d.maybeOpenFirstMenu(event) + Keys.onDownPressed: d.maybeOpenFirstMenu(event) - function maybeOpenFirstMenu(event) { - if (altPressed && openedMenuIndex === -1) { - preselectMenuItem = true - openedMenuIndex = 0 - } else { - event.accepted = false + Keys.onLeftPressed: { + if (d.openedMenuIndex > 0) { + d.preselectMenuItem = true + d.openedMenuIndex-- } } - Keys.onUpPressed: maybeOpenFirstMenu(event) - Keys.onDownPressed: maybeOpenFirstMenu(event) - - Keys.onLeftPressed: { - if (openedMenuIndex > 0) { - preselectMenuItem = true - openedMenuIndex-- + Keys.onRightPressed: { + if (d.openedMenuIndex !== -1 && d.openedMenuIndex < root.menus.length - 1) { + d.preselectMenuItem = true + d.openedMenuIndex++ } } - Keys.onRightPressed: { - if (openedMenuIndex !== -1 && openedMenuIndex < root.menus.length - 1) { - preselectMenuItem = true - openedMenuIndex++ + Row { + id: row + x: d.style ? d.style.padding.left : 0 + y: d.style ? d.style.padding.top : 0 + width: parent.width - (d.style ? d.style.padding.left + d.style.padding.right : 0) + LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft + + Repeater { + id: itemsRepeater + model: root.menus + Loader { + id: menuItemLoader + + property var styleData: QtObject { + readonly property int index: __menuItemIndex + readonly property string text: !!__menuItem && __menuItem.title + readonly property bool enabled: !!__menuItem && __menuItem.enabled + readonly property bool selected: menuMouseArea.hoveredItem === menuItemLoader + readonly property bool open: !!__menuItem && __menuItem.__popupVisible || d.openedMenuIndex === index + readonly property bool underlineMnemonic: d.altPressed + } + + height: Math.max(menuBarLoader.height - d.heightPadding, + menuItemLoader.item ? menuItemLoader.item.implicitHeight : 0) + + readonly property var __menuItem: modelData + readonly property int __menuItemIndex: index + sourceComponent: d.style ? d.style.itemDelegate : null + visible: __menuItem.visible + + Connections { + target: d + onOpenedMenuIndexChanged: { + if (d.openedMenuIndex === index) { + if (__menuItem.__usingDefaultStyle) + __menuItem.style = d.style.menuStyle + __menuItem.__popup(row.LayoutMirroring.enabled ? menuItemLoader.width : 0, + menuBarLoader.height - d.heightPadding, 0) + if (d.preselectMenuItem) + __menuItem.__currentIndex = 0 + } else { + __menuItem.__closeMenu() + } + } + } + + Connections { + target: __menuItem + onPopupVisibleChanged: { + if (!__menuItem.__popupVisible && d.openedMenuIndex === index) + d.openedMenuIndex = -1 + } + } + + Connections { + target: __menuItem.__action + onTriggered: d.openedMenuIndex = __menuItemIndex + } + + Component.onCompleted: { + __menuItem.__visualItem = menuItemLoader + + var title = __menuItem.title + var ampersandPos = title.indexOf("&") + if (ampersandPos !== -1) + d.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = __menuItem.__action + } + } } } @@ -188,8 +265,8 @@ MenuBarPrivate { onPositionChanged: updateCurrentItem(mouse, false) onPressed: { if (updateCurrentItem(mouse)) { - menuBarLoader.preselectMenuItem = false - menuBarLoader.openedMenuIndex = currentItem.menuItemIndex + d.preselectMenuItem = false + d.openedMenuIndex = currentItem.__menuItemIndex } } onExited: hoveredItem = null @@ -203,74 +280,13 @@ MenuBarPrivate { if (!hoveredItem) return false; currentItem = hoveredItem - if (menuBarLoader.openedMenuIndex !== -1) { - menuBarLoader.preselectMenuItem = false - menuBarLoader.openedMenuIndex = currentItem.menuItemIndex + if (d.openedMenuIndex !== -1) { + d.preselectMenuItem = false + d.openedMenuIndex = currentItem.__menuItemIndex } } return true; } - - Row { - id: row - width: parent.width - LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft - - Repeater { - id: itemsRepeater - model: root.menus - Loader { - id: menuItemLoader - - property var menuItem: modelData - property bool selected: menuMouseArea.hoveredItem === menuItemLoader - property bool sunken: menuItem.__popupVisible || menuBarLoader.openedMenuIndex === index - property bool showUnderlined: menuBarLoader.altPressed - - sourceComponent: menuBarLoader.menuItemStyle - property int menuItemIndex: index - visible: menuItem.visible - - Connections { - target: menuBarLoader - onOpenedMenuIndexChanged: { - if (menuBarLoader.openedMenuIndex === index) { - if (row.LayoutMirroring.enabled) - menuItem.__popup(menuItemLoader.width, menuBarLoader.height, 0) - else - menuItem.__popup(0, menuBarLoader.height, 0) - if (menuBarLoader.preselectMenuItem) - menuItem.__currentIndex = 0 - } else { - menuItem.__closeMenu() - } - } - } - - Connections { - target: menuItem - onPopupVisibleChanged: { - if (!menuItem.__popupVisible && menuBarLoader.openedMenuIndex === index) - menuBarLoader.openedMenuIndex = -1 - } - } - - Connections { - target: menuItem.__action - onTriggered: menuBarLoader.openedMenuIndex = menuItemIndex - } - - Component.onCompleted: { - menuItem.__visualItem = menuItemLoader - - var title = menuItem.title - var ampersandPos = title.indexOf("&") - if (ampersandPos !== -1) - menuBarLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItem.__action - } - } - } - } } } } diff --git a/src/controls/Private/ColumnMenuContent.qml b/src/controls/Private/ColumnMenuContent.qml index bb21dcb9ce9b57187562a512fb6856d0eba9c459..5a56b007c167154bdcee9e992d595a4c22df0d27 100644 --- a/src/controls/Private/ColumnMenuContent.qml +++ b/src/controls/Private/ColumnMenuContent.qml @@ -45,11 +45,11 @@ Item { id: content property Component menuItemDelegate + property Component scrollIndicatorStyle property Component scrollerStyle property var itemsModel property int minWidth: 100 property real maxHeight: 800 - property int margin: 1 signal triggered(var item) @@ -59,43 +59,55 @@ Item { } width: Math.max(list.contentWidth, minWidth) - height: Math.min(list.contentHeight, fittedMaxHeight) + 2 * margin + height: Math.min(list.contentHeight, fittedMaxHeight) - readonly property int currentIndex: menu.__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) readonly property real fittedMaxHeight: itemHeight * fittingItems + downScroller.height - readonly property bool shouldUseScrollers: scrollView.__style.useScrollers && itemsModel.length > fittingItems + readonly property bool shouldUseScrollers: scrollView.style === emptyScrollerStyle && 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() + if (currentItem && !hoverArea.pressed + && currentItem.styleData.type === MenuItemType.Menu) + currentItem.__closeSubMenu() currentItem = list.itemAt(pos.x, pos.y) if (currentItem) { - menu.__currentIndex = currentItem.menuItemIndex - if (currentItem.isSubmenu && !currentItem.menuItem.__popupVisible) - currentItem.showSubMenu(false) + __menu.__currentIndex = currentItem.__menuItemIndex + if (currentItem.styleData.type === MenuItemType.Menu + && !currentItem.__menuItem.__popupVisible) + currentItem.__showSubMenu(false) } else { - menu.__currentIndex = -1 + __menu.__currentIndex = -1 } } } + Component { + id: emptyScrollerStyle + 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 } + } + } + ScrollView { id: scrollView anchors { fill: parent - topMargin: content.margin + upScrollerHeight - bottomMargin: downScrollerHeight - content.margin - 1 - rightMargin: -1 + topMargin: upScrollerHeight + bottomMargin: downScrollerHeight } - style: scrollerStyle + style: scrollerStyle || emptyScrollerStyle __wheelAreaScrollSpeed: itemHeight ListView { @@ -121,25 +133,23 @@ Item { onPositionChanged: updateCurrentItem(mouse) onReleased: content.triggered(currentItem) onExited: { - if (currentItem && !currentItem.menuItem.__popupVisible) { + if (currentItem && !currentItem.__menuItem.__popupVisible) { currentItem = null - menu.__currentIndex = -1 + __menu.__currentIndex = -1 } } MenuContentScroller { id: upScroller - direction: "up" + direction: Qt.UpArrow visible: shouldUseScrollers && !list.atYBeginning - x: margin function scrollABit() { list.contentY -= itemHeight } } MenuContentScroller { id: downScroller - direction: "down" + direction: Qt.DownArrow visible: shouldUseScrollers && !list.atYEnd - x: margin function scrollABit() { list.contentY += itemHeight } } } @@ -148,7 +158,7 @@ Item { interval: 1 running: true repeat: false - onTriggered: list.positionViewAtIndex(currentIndex, scrollView.__style.useScrollers + onTriggered: list.positionViewAtIndex(currentIndex, !scrollView.__style ? ListView.Center : ListView.Beginning) } diff --git a/src/controls/Private/MenuContentItem.qml b/src/controls/Private/MenuContentItem.qml index 7056b4e1f08c763f9f41cc4984c0b6256c004277..44312fa5389c50e08d568c24da3f3a782818cf10 100644 --- a/src/controls/Private/MenuContentItem.qml +++ b/src/controls/Private/MenuContentItem.qml @@ -45,42 +45,66 @@ 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) + property var __menu: root visible: status === Loader.Ready - sourceComponent: __style ? __style.frame : undefined + width: content.width + (d.style ? d.style.padding.left + d.style.padding.right : 0) + height: content.height + (d.style ? d.style.padding.top + d.style.padding.bottom : 0) Loader { id: styleLoader - active: !menu.isNative - sourceComponent: menu.style + active: !__menu.isNative + sourceComponent: __menu.style property alias __control: menuFrameLoader onStatusChanged: { if (status === Loader.Error) - console.error("Failed to load Style for", menu) + console.error("Failed to load Style for", __menu) + } + } + sourceComponent: d.style ? d.style.frame : undefined + + QtObject { + id: d + property var mnemonicsMap: ({}) + readonly property Style style: styleLoader.item + readonly property Component menuItemPanel: style ? style.menuItemPanel : null + + function canBeHovered(index) { + var item = content.menuItemAt(index) + if (item && item.styleData.type !== MenuItemType.Separator && item.styleData.enabled) { + __menu.__currentIndex = index + return true + } + return false + } + + function triggerCurrent() { + var item = content.menuItemAt(__menu.__currentIndex) + if (item) + content.triggered(item) + } + + function triggerAndDismiss(item) { + if (item && item.styleData.type !== MenuItemType.Separator) { + __menu.__dismissMenu() + if (item.styleData.type !== MenuItemType.Menu) + item.__menuItem.trigger() + } } } 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 + && (item = d.mnemonicsMap[event.text.toUpperCase()])) { + if (item.styleData.type === MenuItemType.Menu) { + __menu.__currentIndex = item.__menuItemIndex + item.__showSubMenu(true) + item.__menuItem.__currentIndex = 0 } else { - triggerAndDismiss(item) + d.triggerAndDismiss(item) } event.accepted = true } else { @@ -88,64 +112,41 @@ Loader { } } - Keys.onEscapePressed: menu.__dismissMenu() + Keys.onEscapePressed: __menu.__dismissMenu() Keys.onDownPressed: { - if (menu.__currentIndex < 0) - menu.__currentIndex = -1 + if (__menu.__currentIndex < 0) + __menu.__currentIndex = -1 - for (var i = menu.__currentIndex + 1; - i < menu.items.length && !canBeHovered(i); i++) + for (var i = __menu.__currentIndex + 1; + i < __menu.items.length && !d.canBeHovered(i); i++) ; event.accepted = true } Keys.onUpPressed: { - for (var i = menu.__currentIndex - 1; - i >= 0 && !canBeHovered(i); i--) + for (var i = __menu.__currentIndex - 1; + i >= 0 && !d.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"))) + 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 + var item = content.menuItemAt(__menu.__currentIndex) + if ((event.accepted = (item && item.styleData.type === MenuItemType.Menu))) { + 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() - } - } + Keys.onSpacePressed: d.triggerCurrent() + Keys.onReturnPressed: d.triggerCurrent() + Keys.onEnterPressed: d.triggerCurrent() Binding { // Make sure the styled frame is in the background @@ -156,13 +157,15 @@ Loader { ColumnMenuContent { id: content + x: d.style ? d.style.padding.left : 0 + y: d.style ? d.style.padding.top : 0 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) + scrollIndicatorStyle: d.style && d.style.scrollIndicator + scrollerStyle: d.style && d.style.__scrollerStyle + itemsModel: __menu.items + minWidth: __menu.__minimumWidth + maxHeight: d.style ? d.style.__maxPopupHeight : 0 + onTriggered: d.triggerAndDismiss(item) } Component { @@ -170,25 +173,38 @@ Loader { 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 QtObject styleData: QtObject { + id: opts + readonly property int index: __menuItemIndex + readonly property int type: __menuItem ? __menuItem.type : -1 + readonly property bool selected: type !== MenuItemType.Separator && __menu.__currentIndex === index + readonly property string text: type === MenuItemType.Menu ? __menuItem.title : + type !== MenuItemType.Separator ? __menuItem.text : "" + readonly property bool underlineMnemonic: __menu.__contentItem.altPressed + readonly property string shortcut: !!__menuItem && __menuItem["shortcut"] || "" + readonly property var iconSource: !!__menuItem && __menuItem["iconSource"] || undefined + readonly property bool enabled: type !== MenuItemType.Separator && !!__menuItem && __menuItem.enabled + readonly property bool checked: !!__menuItem && !!__menuItem["checked"] + readonly property bool checkable: !!__menuItem && !!__menuItem["checkable"] + readonly property bool exclusive: !!__menuItem && !!__menuItem["exclusiveGroup"] + readonly property int scrollerDirection: Qt.NoArrow + } - property int menuItemIndex: index + readonly property var __menuItem: modelData + readonly property int __menuItemIndex: index - sourceComponent: menuFrameLoader.menuItemStyle - enabled: visible && !isSeparator && !!menuItem && menuItem.enabled - visible: !!menuItem && menuItem.visible + sourceComponent: d.menuItemPanel + enabled: visible && opts.enabled + visible: !!__menuItem && __menuItem.visible active: visible - function showSubMenu(immediately) { + function __showSubMenu(immediately) { if (immediately) { - if (menu.__currentIndex === menuItemIndex) - menuItem.__popup(menuFrameLoader.subMenuXPos, 0, -1) + if (__menu.__currentIndex === __menuItemIndex) { + if (__menuItem.__usingDefaultStyle) + __menuItem.style = __menu.style + __menuItem.__popup(menuFrameLoader.width - (d.style.submenuOverlap + d.style.padding.right), -d.style.padding.top, -1) + } } else { openMenuTimer.start() } @@ -197,37 +213,37 @@ Loader { Timer { id: openMenuTimer interval: 50 - onTriggered: menuItemLoader.showSubMenu(true) + onTriggered: menuItemLoader.__showSubMenu(true) } - function closeSubMenu() { closeMenuTimer.start() } + function __closeSubMenu() { closeMenuTimer.start() } Timer { id: closeMenuTimer interval: 1 onTriggered: { - if (menu.__currentIndex !== menuItemIndex) - menuItem.__closeMenu() + if (__menu.__currentIndex !== __menuItemIndex) + __menuItem.__closeMenu() } } onLoaded: { - menuItem.__visualItem = menuItemLoader + __menuItem.__visualItem = menuItemLoader if (content.width < item.implicitWidth) content.width = item.implicitWidth - var title = text + var title = opts.text var ampersandPos = title.indexOf("&") if (ampersandPos !== -1) - menuFrameLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader + d.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader } Binding { target: menuItemLoader.item property: "width" property alias menuItem: menuItemLoader.item - value: menuItem ? Math.max(menu.__minimumWidth, content.width) - 2 * menuItem.x : 0 + value: menuItem ? Math.max(__menu.__minimumWidth, content.width) - 2 * menuItem.x : 0 } } } diff --git a/src/controls/Private/MenuContentScroller.qml b/src/controls/Private/MenuContentScroller.qml index 30a8825af2ade1fdd94a4c94b787f4554a4b5674..e18132bb0312acb704ca291292b41deae4d2c51e 100644 --- a/src/controls/Private/MenuContentScroller.qml +++ b/src/controls/Private/MenuContentScroller.qml @@ -39,27 +39,36 @@ ****************************************************************************/ import QtQuick 2.1 +import QtQuick.Controls 1.1 MouseArea { - property string direction + id: scrollIndicator + property int direction: 0 anchors { - top: direction === "up" ? parent.top : undefined - bottom: direction === "down" ? parent.bottom : undefined + top: direction === Qt.UpArrow ? parent.top : undefined + bottom: direction === Qt.DownArrow ? parent.bottom : undefined } hoverEnabled: visible - height: scrollerLoader.item.height + height: scrollerLoader.height width: parent.width Loader { id: scrollerLoader - sourceComponent: menuItemDelegate - property int index: -1 - property var modelData: { - "visible": true, - "scrollerDirection": direction, + width: parent.width + sourceComponent: scrollIndicatorStyle + // Extra property values for desktop style + property var __menuItem: null + property var styleData: { + "index": -1, + "type": MenuItemType.ScrollIndicator, + "text": "", + "selected": scrollIndicator.containsMouse, + "scrollerDirection": scrollIndicator.direction, + "checkable": false, + "checked": false, "enabled": true } } diff --git a/src/controls/Private/MenuItemSubControls.qml b/src/controls/Private/MenuItemSubControls.qml new file mode 100644 index 0000000000000000000000000000000000000000..af62bf5be63880f49b812a1193a2f927e3a690fc --- /dev/null +++ b/src/controls/Private/MenuItemSubControls.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 + +QtObject { + property Component background: null + property Component label: null + property Component submenuIndicator: null + property Component shortcut: null + property Component checkmarkIndicator: null +} diff --git a/src/controls/Private/private.pri b/src/controls/Private/private.pri index 7852edffbfd6f69455e27e9d1a0cfded079875c5..b12cabd771784dd577c650825091e9279b289492 100644 --- a/src/controls/Private/private.pri +++ b/src/controls/Private/private.pri @@ -34,6 +34,7 @@ PRIVATE_QML_FILES += \ $$PWD/SourceProxy.qml\ $$PWD/Style.qml \ $$PWD/style.js \ + $$PWD/MenuItemSubControls.qml \ $$PWD/ModalPopupBehavior.qml \ $$PWD/StackViewSlideDelegate.qml \ $$PWD/StackView.js \ diff --git a/src/controls/Private/qmldir b/src/controls/Private/qmldir index 5dc8061cde841b2f6ec0d883a4cb125041a02c06..7536b7e63f95b1bde0c24108101828ae72fbaf04 100644 --- a/src/controls/Private/qmldir +++ b/src/controls/Private/qmldir @@ -7,6 +7,7 @@ BasicButton 1.0 BasicButton.qml ScrollBar 1.0 ScrollBar.qml ScrollViewHelper 1.0 ScrollViewHelper.qml Style 1.0 Style.qml +MenuItemSubControls 1.0 MenuItemSubControls.qml TabBar 1.0 TabBar.qml StackViewSlideDelegate 1.0 StackViewSlideDelegate.qml StyleHelpers 1.0 style.js diff --git a/src/controls/Private/qquickstyleitem.cpp b/src/controls/Private/qquickstyleitem.cpp index 105bb9f5470eb8b9e2fcf9d24bcaebcf17bfbd59..958adbdd9c52ea59617f993572feba335b9987b2 100644 --- a/src/controls/Private/qquickstyleitem.cpp +++ b/src/controls/Private/qquickstyleitem.cpp @@ -51,6 +51,7 @@ #include <qquickwindow.h> #include "private/qguiapplication_p.h" #include <QtGui/qpa/qplatformtheme.h> +#include "../qquickmenuitem_p.h" QT_BEGIN_NAMESPACE @@ -503,19 +504,23 @@ void QQuickStyleItem::initStyleOption() // For GTK style. See below, in setElementType() setProperty("_q_isComboBoxPopupItem", m_itemType == ComboBoxItem); - QString scrollerDirection = m_properties["scrollerDirection"].toString(); - if (!scrollerDirection.isEmpty()) { + QQuickMenuItemType::MenuItemType type = + static_cast<QQuickMenuItemType::MenuItemType>(m_properties["type"].toInt()); + if (type == QQuickMenuItemType::ScrollIndicator) { + int scrollerDirection = m_properties["scrollerDirection"].toInt(); opt->menuItemType = QStyleOptionMenuItem::Scroller; - opt->state |= scrollerDirection == "up" ? + opt->state |= scrollerDirection == Qt::UpArrow ? QStyle::State_UpArrow : QStyle::State_DownArrow; - } else if (text().isEmpty()) { + } else if (type == QQuickMenuItemType::Separator) { opt->menuItemType = QStyleOptionMenuItem::Separator; } else { opt->text = text(); - if (m_properties["isSubmenu"].toBool()) { + if (type == QQuickMenuItemType::Menu) { opt->menuItemType = QStyleOptionMenuItem::SubMenu; } else { + opt->menuItemType = QStyleOptionMenuItem::Normal; + QString shortcut = m_properties["shortcut"].toString(); if (!shortcut.isEmpty()) { opt->text += QLatin1Char('\t') + shortcut; @@ -527,8 +532,6 @@ void QQuickStyleItem::initStyleOption() QVariant exclusive = m_properties["exclusive"]; opt->checkType = exclusive.toBool() ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive; - } else { - opt->menuItemType = QStyleOptionMenuItem::Normal; } } if (m_properties["icon"].canConvert<QIcon>()) diff --git a/src/controls/Styles/Base/ComboBoxStyle.qml b/src/controls/Styles/Base/ComboBoxStyle.qml index 6eff488dea03ddc9bdddfc2daf80631ba8194b83..14f84c0d14a9c2ddbf29857aed7efae3a089ebab 100644 --- a/src/controls/Styles/Base/ComboBoxStyle.qml +++ b/src/controls/Styles/Base/ComboBoxStyle.qml @@ -213,15 +213,15 @@ Style { /*! \internal */ property Component __dropDownStyle: MenuStyle { - maxPopupHeight: 600 + __maxPopupHeight: 600 __menuItemType: "comboboxitem" - scrollerStyle: ScrollViewStyle { - property bool useScrollers: false - } + __scrollerStyle: ScrollViewStyle { } } /*! \internal */ property Component __popupStyle: Style { + property int __maxPopupHeight: 400 + property int submenuOverlap: 0 property Component frame: Rectangle { width: (parent ? parent.contentWidth : 0) @@ -231,7 +231,7 @@ Style { property int margin: 1 } - property Component menuItem: Text { + property Component menuItemPanel: Text { text: "NOT IMPLEMENTED" color: "red" font { @@ -240,13 +240,6 @@ Style { } } - 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 - } + property Component __scrollerStyle: null } } diff --git a/src/controls/Styles/Base/MenuBarStyle.qml b/src/controls/Styles/Base/MenuBarStyle.qml index 3a3b552dd66e9abf1e7b87747aa485a98a3f06f6..050367c39782d66bd61f4b27f394b3139c029f30 100644 --- a/src/controls/Styles/Base/MenuBarStyle.qml +++ b/src/controls/Styles/Base/MenuBarStyle.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 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. @@ -44,33 +44,76 @@ import QtQuick.Controls.Private 1.0 /*! \qmltype MenuBarStyle - \internal - \ingroup applicationwindowstyling \inqmlmodule QtQuick.Controls.Styles + \since 5.3 + \ingroup controlsstyling + \brief Provides custom styling for MenuBar + + \note Styling menu bars may not be supported on platforms using native menu bars + through their QPA plugin. */ Style { - readonly property color __backgroundColor: "#dcdcdc" - property Component frame: Rectangle { - width: control.__contentItem.width - height: contentHeight - color: __backgroundColor + /*! Returns a formatted string to render mnemonics for a given menu item. + + The mnemonic character is prefixed by an ampersand in the original string. + + Passing \c true for \c underline will underline the mnemonic character (e.g., + \c formatMnemonic("&File", true) will return \c "<u>F</u>ile"). Passing \c false + for \c underline will return the plain text form (e.g., \c formatMnemonic("&File", false) + will return \c "File"). + + \sa label + */ + function formatMnemonic(text, underline) { + return underline ? StyleHelpers.stylizeMnemonics(text) : StyleHelpers.removeMnemonics(text) } - property Component menuItem: Rectangle { - width: text.width + 12 - height: text.height + 4 - color: sunken ? "#49d" :__backgroundColor + /*! The background for the full menu bar. + + The background will be extended to the full containing window width. + Its height will always fit all of the menu bar items. The final size + will include the paddings. + */ + property Component background: Rectangle { + color: "#dcdcdc" + implicitHeight: 20 + } + + /*! The menu bar item. + + \target styleData properties + This item has to be configured using the \b styleData object which is in scope, + and contains the following read-only properties: + \table + \row \li \b {styleData.index} : int \li The index of the menu item in its menu. + \row \li \b {styleData.selected} : bool \li \c true if the menu item is selected. + \row \li \b {styleData.open} : bool \li \c true when the pull down menu is open. + \row \li \b {styleData.text} : string \li The menu bar item's text. + \row \li \b {styleData.underlineMnemonic} : bool \li When \c true, the style should underline the menu item's label mnemonic. + \endtable + + */ + property Component itemDelegate: Rectangle { + implicitWidth: text.width + 12 + implicitHeight: text.height + 4 + color: styleData.open ? "#49d" : "transparent" SystemPalette { id: syspal } Text { id: text - text: StyleHelpers.stylizeMnemonics(menuItem.title) + text: formatMnemonic(styleData.text, styleData.underlineMnemonic) anchors.centerIn: parent renderType: Text.NativeRendering - color: sunken ? "white" : syspal.windowText + color: styleData.open ? "white" : syspal.windowText } } + + /*! The style component for the menubar's own menus and their submenus. + + \sa MenuStyle + */ + property Component menuStyle: MenuStyle { } } diff --git a/src/controls/Styles/Base/MenuStyle.qml b/src/controls/Styles/Base/MenuStyle.qml index c05c6a6d100a4a9a20361e0811b4e9d770b3e922..bec8410b7e3b06a871b234d594b431f10c7e7e18 100644 --- a/src/controls/Styles/Base/MenuStyle.qml +++ b/src/controls/Styles/Base/MenuStyle.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 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. @@ -45,67 +45,190 @@ import QtQuick.Controls.Private 1.0 /*! \qmltype MenuStyle - \internal - \ingroup menusstyling \inqmlmodule QtQuick.Controls.Styles + \since 5.3 + \ingroup controlsstyling + \brief Provides custom styling for Menu + + \target styleData properties + The \b styleData object contains the following read-only properties: + \table + \row \li \b {styleData.index} : int \li The index of the menu item in its menu. + \row \li \b {styleData.type} : enumeration \li The type of menu item. See below for possible values. + \row \li \b {styleData.selected} : bool \li \c true if the menu item is selected. + \row \li \b {styleData.text} : string \li The menu item's text, or title if it's a submenu. + \row \li \b {styleData.underlineMnemonic} : bool \li Whether the style should underline the menu item's label mnemonic. + \row \li \b {styleData.shortcut} : string \li The text for the menu item's shortcut. + \row \li \b {styleData.iconSource} : url \li The source URL to the menu item's icon. Undefined if it has no icon. + \row \li \b {styleData.enabled} : bool \li \c true if the menu item is enabled. + \row \li \b {styleData.checkable} : bool \li \c true if the menu item is checkable. + \row \li \b {styleData.exclusive} : bool \li \c true if the menu item is checkable, and it's part of an \l ExclusiveGroup. + \row \li \b {styleData.checked} : bool \li \c true if the menu item is checkable and currently checked. + \row \li \b {styleData.scrollerDirection} : enumeration \li If the menu item is a scroller, its pointing direction. + Valid values are \c Qt.UpArrow, \c Qt.DownArrow, and \c Qt.NoArrow. + \endtable + + The valid values for \b {styleData.type} are: + \list + \li MenuItemType.Item + \li MenuItemType.Menu + \li MenuItemType.Separator + \li MenuItemType.ScrollIndicator + \endlist + + \note Styling menus may not be supported on platforms using native menus + through their QPA plugin. */ Style { id: styleRoot - property string __menuItemType: "menuitem" - property real maxPopupHeight: 600 // ### FIXME Screen.desktopAvailableHeight * 0.99 + padding { + top: 1 + bottom: 1 + left: 1 + right: 1 + } - property Component frame: Rectangle { - width: (parent ? parent.contentWidth : 0) + 2 - height: (parent ? parent.contentHeight : 0) + 2 + /*! The amount of pixels by which a submenu popup overlaps horizontally its parent menu. */ + property int submenuOverlap: 1 + + /*! Returns a rich-text string to render mnemonics for a given menu item. + + The mnemonic character is prefixed by an ampersand in the original string. - color: "lightgray" - border { width: 1; color: "darkgray" } + Passing \c true for \c underline will underline the mnemonic character (e.g., + \c formatMnemonic("&Open...", true) will return \c "<u>O</u>pen..."). Passing \c false + for \c underline will return the plain text form (e.g., \c formatMnemonic("&Open...", false) + will return \c "Open..."). - property int subMenuOverlap: -1 - property real maxHeight: maxPopupHeight - property int margin: 1 + \sa label + */ + function formatMnemonic(text, underline) { + return underline ? StyleHelpers.stylizeMnemonics(text) : StyleHelpers.removeMnemonics(text) } - property Component menuItem: Rectangle { - x: 1 - y: 1 - implicitWidth: Math.max((parent ? parent.width : 0), - 18 + text.paintedWidth + (rightDecoration.visible ? rightDecoration.width + 40 : 12)) - 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 - border.color: selected && enabled ? Qt.darker(selectedColor, 1) : color - readonly property int leftMargin: __menuItemType === "menuitem" ? 18 : 0 - - readonly property color backgroundColor: "#dcdcdc" - readonly property color selectedColor: "#49d" - Gradient { - id: selectedGradient - GradientStop {color: Qt.lighter(selectedColor, 1.3) ; position: -0.2} - GradientStop {color: selectedColor; position: 1.4} + /*! The background frame for the menu popup. + + The \l Menu will resize the frame to its contents plus the padding. + */ + property Component frame: Rectangle { + color: styleRoot.__backgroundColor + border { width: 1; color: styleRoot.__borderColor } + } + + /*! \qmlproperty Object MenuStyle::itemDelegate + + The object containing the menu item subcontrol components. These subcontrols are used + for normal menu items only, i.e. not for separators or scroll indicators. + + The subcontrols are: + + \list + \li \b {itemDelegate.background} : Component + + The menu item background component. + + Its appearance generally changes with \l {styleData properties} {styleData.selected} + and \l {styleData properties} {styleData.enabled}. + + The default implementation shows only when the item is enabled and selected. It remains + invisible otherwise. + + \li \b {itemDelegate.label} : Component + + Component for the actual text label. + + The text itself is fetched from \l {styleData properties} {styleData.text}, and its appearance should depend + on \l {styleData properties} {styleData.enabled} and \l {styleData properties} {styleData.selected}. + + If \l {styleData properties} {styleData.underlineMnemonic} is true, the label should underline its mnemonic + character. \l formatMnemonic provides the default formatting. + + \li \b {itemDelegate.submenuIndicator} : Component + + It indicates that the current menu item is a submenu. + + Only used when \l {styleData properties} {styleData.type} equals \c MenuItemType.Menu. + + \li \b {itemDelegate.shortcut} : Component + + Displays the shortcut attached to the menu item. + + Only used when \l {styleData properties} {styleData.shortcut} is not empty. + + \li \b {itemDelegate.checkmarkIndicator} : Component + + Will be used when \l {styleData properties} {styleData.checkable} is \c true and its appearance + may depend on \l {styleData properties} {styleData.exclusive}, i.e., whether it will behave like a + checkbox or a radio button. Use \l {styleData properties} {styleData.checked} for the checked state. + \endlist + + \note This property cannot be overwritten although all of the subcontrol properties can. + */ + property alias itemDelegate: internalMenuItem + + MenuItemSubControls { + id: internalMenuItem + + background: Rectangle { + visible: styleData.selected && styleData.enabled + gradient: Gradient { + id: selectedGradient + GradientStop { color: Qt.lighter(__selectedBackgroundColor, 1.3); position: -0.2 } + GradientStop { color: __selectedBackgroundColor; position: 1.4 } + } + + border.width: 1 + border.color: Qt.darker(__selectedBackgroundColor, 1) + antialiasing: true } - antialiasing: true - SystemPalette { - id: syspal - colorGroup: enabled ? SystemPalette.Active : SystemPalette.Disabled + label: Text { + text: formatMnemonic(styleData.text, styleData.underlineMnemonic) + color: __currentTextColor + font.pixelSize: __labelFontPixelSize } - readonly property string itemText: parent ? parent.text : "" - readonly property bool mirrored: Qt.application.layoutDirection === Qt.RightToLeft + submenuIndicator: Text { + text: __mirrored ? "\u25c2" : "\u25b8" // BLACK LEFT/RIGHT-POINTING SMALL TRIANGLE + font.pixelSize: __labelFontPixelSize + color: __currentTextColor + style: styleData.selected ? Text.Normal : Text.Raised + styleColor: Qt.lighter(color, 4) + } - Loader { - id: checkMark - x: mirrored ? parent.width - width - 4 : 4 - y: 6 - active: __menuItemType === "menuitem" && !!menuItem && !!menuItem["checkable"] - sourceComponent: exclusive ? exclusiveCheckMark : nonExclusiveCheckMark + shortcut: Text { + text: styleData.shortcut + font.pixelSize: __labelFontPixelSize * 0.9 + color: __currentTextColor + } - readonly property bool checked: !!menuItem && !!menuItem.checked - readonly property bool exclusive: !!menuItem && !!menuItem["exclusiveGroup"] + checkmarkIndicator: Loader { + sourceComponent: styleData.exclusive ? exclusiveCheckMark : nonExclusiveCheckMark + Component { + id: exclusiveCheckMark + Rectangle { + x: 1 + width: 10 + height: 10 + color: "white" + border.color: "gray" + antialiasing: true + radius: height/2 + + Rectangle { + anchors.centerIn: parent + visible: styleData.checked + width: 4 + height: 4 + color: "#666" + border.color: "#222" + antialiasing: true + radius: height/2 + } + } + } Component { id: nonExclusiveCheckMark @@ -120,13 +243,11 @@ Style { Rectangle { antialiasing: true - visible: checkMark.checked + visible: styleData.checked color: "#666" radius: 1 anchors.margins: 4 anchors.fill: parent - anchors.topMargin: 3 - anchors.bottomMargin: 5 border.color: "#222" Rectangle { anchors.fill: parent @@ -137,70 +258,15 @@ Style { } } } - - Component { - id: exclusiveCheckMark - Rectangle { - x: 1 - width: 10 - height: 10 - color: "white" - border.color: "gray" - antialiasing: true - radius: height/2 - - Rectangle { - anchors.centerIn: parent - visible: checkMark.checked - width: 4 - height: 4 - color: "#666" - border.color: "#222" - antialiasing: true - radius: height/2 - } - } - } - } - - Text { - id: text - visible: !isSeparator - text: StyleHelpers.stylizeMnemonics(itemText) - readonly property real offset: __menuItemType === "menuitem" ? 24 : 6 - x: mirrored ? parent.width - width - offset : offset - anchors.verticalCenter: parent.verticalCenter - renderType: Text.NativeRendering - color: selected && enabled ? "white" : syspal.text } + } - Text { - id: rightDecoration - readonly property string shortcut: !!menuItem && menuItem["shortcut"] || "" - visible: isSubmenu || shortcut !== "" - text: isSubmenu ? mirrored ? "\u25c2" : "\u25b8" // BLACK LEFT/RIGHT-POINTING SMALL TRIANGLE - : shortcut - LayoutMirroring.enabled: mirrored - anchors { - right: parent.right - rightMargin: 6 - baseline: isSubmenu ? undefined : text.baseline - } - font.pixelSize: isSubmenu ? text.font.pixelSize : text.font.pixelSize * 0.9 - color: text.color - renderType: Text.NativeRendering - 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" - } + /*! Component for the separator menu item. + Will be used when \l {styleData properties} {styleData.type} equals \c MenuItemType.Separator. + */ + property Component separator: Item { Rectangle { - visible: isSeparator width: parent.width - 2 height: 1 x: 1 @@ -209,12 +275,178 @@ Style { } } - 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 + /*! Component for the scroll indicator menu item. + + Will be used when \l {styleData properties} {styleData.type} equals \c MenuItemType.ScrollIndicator. + Its appearance should follow \l {styleData properties} {styleData.scrollerDirection}. + + This is the item added at the top and bottom of the menu popup when its contents won't fit the screen + to indicate more content is available in the direction of the arrow. + */ + property Component scrollIndicator: Image { + anchors.centerIn: parent + source: styleData.scrollerDirection === Qt.UpArrow ? "images/arrow-up.png" : "images/arrow-down.png" + } + + /*! \internal */ + property string __menuItemType: "menuitem" + + /*! \internal + The menu popup frame background color. + + This is set to be a uniform background. If you want a gradient or a pixmap, + you should override \l frame. + + \sa frame, borderColor + */ + property color __backgroundColor: "#dcdcdc" + + /*! \internal + The menu popup frame border color. + + The border width is set to 1 pixel. Override \l frame if you want a larger border. + + \sa frame, backgroundColor + */ + property color __borderColor: "darkgray" + + /*! \internal + The maximum height for a popup before it will show scrollers. + */ + property int __maxPopupHeight: 600 + + /*! \internal + The menu item background color when selected. + + This property is provided for convenience and only sets the color. + It does not change the style in any other way. + */ + property color __selectedBackgroundColor: "#49d" + + /*! \internal + The menu item label color. + + When set, keyboard shorcuts get the same color as the item's text. + + \sa selectedLabelColor, disabledLabelColor + */ + property color __labelColor: "#444" + + /*! \internal + The menu item label color when selected. + + \sa labelColor, selectedLabelColor + */ + property color __selectedLabelColor: "white" + + /*! \internal + The menu item label color when disabled. + + \sa labelColor, disabledLabelColor + */ + property color __disabledLabelColor: "gray" + + + /*! \internal */ + readonly property bool __mirrored: Qt.application.layoutDirection === Qt.RightToLeft + + /*! \internal */ + readonly property real __labelFontPixelSize: TextSingleton.font.pixelSize + + /*! \internal + The margin between the frame and the menu item label's left side. + + Generally, this should be large enough to fit optional checkmarks on + the label's left side. + */ + property int __leftLabelMargin: 18 + + /*! \internal + The margin between the menu item label's right side and the frame. */ + property int __rightLabelMargin: 12 + + /*! \internal + The minimum spacing between the menu item label's text right side and any + element located on its right (submenu indicator or shortcut). + */ + property int __minRightLabelSpacing: 28 + + /*! \internal */ + property Component __scrollerStyle: null + + /*! \internal + The menu item contents itself. + + The default implementation uses \l MenuItemStyle. + */ + property Component menuItemPanel: Item { + id: panel + + property QtObject __styleData: styleData + /*! \internal + The current color of the text label. + + Use this if you're overriding e.g. \l shortcutIndicator to keep the color matched + with \l label, or to derive new colors from it. + */ + property color currentTextColor: !styleData.enabled ? __disabledLabelColor : + styleData.selected ? __selectedLabelColor : __labelColor + + implicitWidth: Math.max((parent ? parent.width : 0), + Math.round(__leftLabelMargin + labelLoader.width + __rightLabelMargin + + (rightIndicatorLoader.active ? __minRightLabelSpacing + rightIndicatorLoader.width : 0))) + implicitHeight: Math.round(styleData.isSeparator ? __labelFontPixelSize / 2 : + !!styleData.scrollerDirection ? __labelFontPixelSize * 0.75 : labelLoader.height + 4) + + Loader { + property alias styleData: panel.__styleData + property alias __currentTextColor: panel.currentTextColor + anchors.fill: parent + sourceComponent: itemDelegate.background + } + + Loader { + property alias styleData: panel.__styleData + property alias __currentTextColor: panel.currentTextColor + anchors.fill: parent + sourceComponent: separator + active: styleData.type === MenuItemType.Separator + } + + Loader { + property alias styleData: panel.__styleData + property alias __currentTextColor: panel.currentTextColor + x: __mirrored ? parent.width - width - 4 : 4 + y: styleData.exclusive ? 5 : 4 + active: __menuItemType === "menuitem" && styleData.checkable + sourceComponent: itemDelegate.checkmarkIndicator + } + + Loader { + id: labelLoader + readonly property real offset: __menuItemType === "menuitem" ? __leftLabelMargin : 6 + property alias styleData: panel.__styleData + property alias __currentTextColor: panel.currentTextColor + x: __mirrored ? parent.width - width - offset : offset + y: 1 + active: styleData.type !== MenuItemType.Separator + sourceComponent: itemDelegate.label + baselineOffset: item ? item.baselineOffset : 0.0 + } + + Loader { + id: rightIndicatorLoader + property alias styleData: panel.__styleData + property alias __currentTextColor: panel.currentTextColor + active: styleData.type === MenuItemType.Menu || styleData.shortcut !== "" + sourceComponent: styleData.type === MenuItemType.Menu ? itemDelegate.submenuIndicator : itemDelegate.shortcut + LayoutMirroring.enabled: __mirrored + baselineOffset: item ? item.baselineOffset : 0.0 + anchors { + right: parent.right + rightMargin: 6 + baseline: !styleData.isSubmenu ? labelLoader.baseline : undefined + } + } } } diff --git a/src/controls/Styles/Desktop/ComboBoxStyle.qml b/src/controls/Styles/Desktop/ComboBoxStyle.qml index d1b456b677ad7b55b1702a4fcb7f338802dff055..613bba04e620f8d81849527377d31ed7653c9ea1 100644 --- a/src/controls/Styles/Desktop/ComboBoxStyle.qml +++ b/src/controls/Styles/Desktop/ComboBoxStyle.qml @@ -40,7 +40,6 @@ 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 @@ -86,18 +85,18 @@ Style { } property Component __dropDownStyle: Style { + property int __maxPopupHeight: 600 + property int submenuOverlap: 0 + property Component frame: StyleItem { elementType: "frame" - 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 { + property Component menuItemPanel: StyleItem { elementType: "itemrow" - selected: parent ? parent.selected : false + selected: styleData.selected x: pixelMetric("defaultframewidth") y: pixelMetric("defaultframewidth") @@ -110,13 +109,11 @@ Style { elementType: "item" contentWidth: textWidth(text) contentHeight: textHeight(text) - text: parent && parent.parent ? parent.parent.text : "" + text: styleData.text selected: parent ? parent.selected : false } } - property Component scrollerStyle: Desktop.ScrollViewStyle { - property bool useScrollers: false - } + property Component __scrollerStyle: Desktop.ScrollViewStyle { } } } diff --git a/src/controls/Styles/Desktop/MenuBarStyle.qml b/src/controls/Styles/Desktop/MenuBarStyle.qml index a8b389c1a70da9eaf8f37aabfb18cdde56a46d4f..c032a7c0b095c746c1e0496e1255a60745e5e0d2 100644 --- a/src/controls/Styles/Desktop/MenuBarStyle.qml +++ b/src/controls/Styles/Desktop/MenuBarStyle.qml @@ -41,13 +41,11 @@ import QtQuick 2.1 import QtQuick.Controls 1.1 import QtQuick.Controls.Private 1.0 - +import "." as Desktop Style { - property Component frame: StyleItem { + property Component background: StyleItem { elementType: "menubar" - contentWidth: control.__contentItem.width - contentHeight: parent ? parent.contentHeight : 0 width: implicitWidth + 2 * (pixelMetric("menubarhmargin") + pixelMetric("menubarpanelwidth")) height: implicitHeight + 2 * (pixelMetric("menubarvmargin") + pixelMetric("menubarpanelwidth")) + pixelMetric("spacebelowmenubar") @@ -55,23 +53,22 @@ Style { Accessible.role: Accessible.MenuBar } - property Component menuItem: StyleItem { + property Component itemDelegate: StyleItem { elementType: "menubaritem" - x: pixelMetric("menubarhmargin") + pixelMetric("menubarpanelwidth") - y: pixelMetric("menubarvmargin") + pixelMetric("menubarpanelwidth") - text: menuItem.title + text: styleData.text contentWidth: textWidth(text) contentHeight: textHeight(text) width: implicitWidth + pixelMetric("menubaritemspacing") - enabled: menuItem.enabled - selected: (parent && parent.selected) || sunken - sunken: parent && parent.sunken + enabled: styleData.enabled + sunken: styleData.open - hints: { "showUnderlined": showUnderlined } + hints: { "showUnderlined": styleData.underlineMnemonic } Accessible.role: Accessible.MenuItem Accessible.name: StyleHelpers.removeMnemonics(text) } + + property Component menuStyle: Desktop.MenuStyle { } } diff --git a/src/controls/Styles/Desktop/MenuStyle.qml b/src/controls/Styles/Desktop/MenuStyle.qml index ad04281de85f469d4ed295d19c0f008671bd9df6..b44dd70d7fe2ff968017a3c55f100f91cdc712d7 100644 --- a/src/controls/Styles/Desktop/MenuStyle.qml +++ b/src/controls/Styles/Desktop/MenuStyle.qml @@ -48,18 +48,16 @@ Style { property string __menuItemType: "menuitem" + property int submenuOverlap: 0 + property int __maxPopupHeight: 0 + property Component frame: StyleItem { elementType: "menu" - contentWidth: parent ? parent.contentWidth : 0 - contentHeight: parent ? parent.contentHeight : 0 - width: implicitWidth - height: implicitHeight - - property int subMenuOverlap: -2 * pixelMetric("menupanelwidth") - property real maxHeight: Screen.desktopAvailableHeight * 0.99 - property int margin: pixelMetric("menuvmargin") + pixelMetric("menupanelwidth") - + contentWidth: parent ? Math.round(parent.contentWidth) : 0 + contentHeight: parent ? Math.round(parent.contentHeight) : 0 + width: implicitWidth + 2 * (pixelMetric("menuhmargin") + pixelMetric("menupanelwidth")) + height: implicitHeight + 2 * (pixelMetric("menuvmargin") + pixelMetric("menupanelwidth")) Rectangle { visible: anchors.margins > 0 anchors { @@ -70,43 +68,57 @@ Style { } Accessible.role: Accessible.PopupMenu + + Binding { + target: styleRoot + property: "submenuOverlap" + value: 2 * pixelMetric("menupanelwidth") + } + + Binding { + target: styleRoot + property: "margin" + value: pixelMetric("menuvmargin") + pixelMetric("menupanelwidth") + } + + // ### The Screen attached property can only be set on an Item, + // ### and will get its values only when put on a Window. + readonly property int desktopAvailableHeight: Screen.desktopAvailableHeight + Binding { + target: styleRoot + property: "__maxPopupHeight" + value: desktopAvailableHeight * 0.99 + } } - property Component menuItem: StyleItem { + property Component menuItemPanel: StyleItem { elementType: __menuItemType - x: pixelMetric("menuhmargin") + pixelMetric("menupanelwidth") - y: pixelMetric("menuvmargin") - text: !!parent && parent.text - property string textAndShorcut: text + (properties.shortcut ? "\t" + properties.shortcut : "") + text: styleData.text + property string textAndShorcut: text + (styleData.shortcut ? "\t" + styleData.shortcut : "") contentWidth: textWidth(textAndShorcut) contentHeight: textHeight(textAndShorcut) - enabled: !!parent && parent.enabled - selected: !!parent && parent.selected - on: !!menuItem && !!menuItem["checkable"] && menuItem.checked + enabled: styleData.enabled + selected: styleData.selected + on: styleData.checkable && styleData.checked - hints: { "showUnderlined": showUnderlined } + hints: { "showUnderlined": styleData.underlineMnemonics } properties: { - "checkable": !!menuItem && !!menuItem["checkable"], - "exclusive": !!menuItem && !!menuItem["exclusiveGroup"], - "shortcut": !!menuItem && menuItem["shortcut"] || "", - "isSubmenu": isSubmenu, - "scrollerDirection": scrollerDirection, - "icon": !!menuItem && menuItem.__icon + "checkable": styleData.checkable, + "exclusive": styleData.exclusive, + "shortcut": styleData.shortcut, + "type": styleData.type, + "scrollerDirection": styleData.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 - } + property Component scrollIndicator: menuItemPanel + + property Component __scrollerStyle: null } diff --git a/src/controls/Styles/qmldir b/src/controls/Styles/qmldir index 5cd368ac881ba1074b360f8c00e02f0f884eb1c5..4ffb08ee0eb46eccb3577ef22100b78f3601ad11 100644 --- a/src/controls/Styles/qmldir +++ b/src/controls/Styles/qmldir @@ -3,6 +3,8 @@ ButtonStyle 1.0 Base/ButtonStyle.qml BusyIndicatorStyle 1.1 Base/BusyIndicatorStyle.qml CheckBoxStyle 1.0 Base/CheckBoxStyle.qml ComboBoxStyle 1.0 Base/ComboBoxStyle.qml +MenuStyle 1.2 Base/MenuStyle.qml +MenuBarStyle 1.2 Base/MenuBarStyle.qml ProgressBarStyle 1.0 Base/ProgressBarStyle.qml RadioButtonStyle 1.0 Base/RadioButtonStyle.qml ScrollViewStyle 1.0 Base/ScrollViewStyle.qml diff --git a/src/controls/qquickmenuitem_p.h b/src/controls/qquickmenuitem_p.h index 3ba719f6459e03bce074d1176210238f5fc1a7a7..a2e74d7f5497520562bc453a7db73d88210c3de9 100644 --- a/src/controls/qquickmenuitem_p.h +++ b/src/controls/qquickmenuitem_p.h @@ -68,7 +68,8 @@ public: enum MenuItemType { Separator = 0, Item, - Menu + Menu, + ScrollIndicator }; };