summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dedietrich@digia.com>2013-11-07 14:11:26 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-14 11:05:10 +0100
commite88bdffe644e53912dfbce95117555cb6a87bfd2 (patch)
tree43b34dfad7bdcbfdf0ac2ea322b580933bbeb2a3
parent6cfba0c37dcdbe13681f8241d9d2989d1ac05db8 (diff)
downloadqtquickcontrols-e88bdffe644e53912dfbce95117555cb6a87bfd2.tar.gz
Introducing styling for Menu, MenuBar
MenuStyle We expose the frame and item properties together with some convenience properties (basically color related). The menu item data is exposed via a styleData object, similarly to what's done in TabViewStyle. In addition, we introduce MenuStyle.menuItem which brings some convenience when it comes to overriding subcontrols of the menu item. MenuBarStyle We expose background and menuBarItem. The menubar item's properties are accessible through the styleData property in scope. Style cascading Additionally, MenuBarStyle has a menuStyle property that will apply to all its menus and their submenus. Similarly, assigning a style to a Menu object, will apply it to its submenus. It's still possible to override the parent menu's style by declaring its own. [ChangeLog][QtQuickControls][Styles]Menu and MenuBar are now styleable Change-Id: Ib724b7a6426bdfce5da314620d51dfaa76f76500 Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
-rw-r--r--src/controls/Menu.qml14
-rw-r--r--src/controls/MenuBar.qml242
-rw-r--r--src/controls/Private/ColumnMenuContent.qml52
-rw-r--r--src/controls/Private/MenuContentItem.qml186
-rw-r--r--src/controls/Private/MenuContentScroller.qml27
-rw-r--r--src/controls/Private/MenuItemSubControls.qml49
-rw-r--r--src/controls/Private/private.pri1
-rw-r--r--src/controls/Private/qmldir1
-rw-r--r--src/controls/Private/qquickstyleitem.cpp17
-rw-r--r--src/controls/Styles/Base/ComboBoxStyle.qml19
-rw-r--r--src/controls/Styles/Base/MenuBarStyle.qml71
-rw-r--r--src/controls/Styles/Base/MenuStyle.qml462
-rw-r--r--src/controls/Styles/Desktop/ComboBoxStyle.qml17
-rw-r--r--src/controls/Styles/Desktop/MenuBarStyle.qml21
-rw-r--r--src/controls/Styles/Desktop/MenuStyle.qml76
-rw-r--r--src/controls/Styles/qmldir2
-rw-r--r--src/controls/qquickmenuitem_p.h3
17 files changed, 825 insertions, 435 deletions
diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml
index 9c766846..d7aa4a0a 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 42b3f39a..f294d54e 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 bb21dcb9..5a56b007 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 7056b4e1..44312fa5 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 30a8825a..e18132bb 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 00000000..af62bf5b
--- /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 7852edff..b12cabd7 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 5dc8061c..7536b7e6 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 105bb9f5..958adbdd 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 6eff488d..14f84c0d 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 3a3b552d..050367c3 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 c05c6a6d..bec8410b 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 d1b456b6..613bba04 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 a8b389c1..c032a7c0 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 ad04281d..b44dd70d 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 5cd368ac..4ffb08ee 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 3ba719f6..a2e74d7f 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
};
};