summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 9c7668460..d7aa4a0a1 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 42b3f39aa..f294d54eb 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 bb21dcb9c..5a56b007c 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 7056b4e1f..44312fa53 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 30a8825af..e18132bb0 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 000000000..af62bf5be
--- /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 7852edffb..b12cabd77 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 5dc8061cd..7536b7e63 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 105bb9f54..958adbdd9 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 6eff488de..14f84c0d1 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 3a3b552dd..050367c39 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 c05c6a6d1..bec8410b7 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 d1b456b67..613bba04e 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 a8b389c1a..c032a7c0b 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 ad04281de..b44dd70d7 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 5cd368ac8..4ffb08ee0 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 3ba719f64..a2e74d7f5 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
};
};