From 66faa149db5d7b0a31db98b792c5202b8f33bf8a Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sat, 3 Jun 2017 10:16:42 +0200 Subject: Introduce MenuBar MenuBar is an ordinary Item. It can be located basically anywhere, but the idea is to introduce a new ApplicationWindow::menuBar property in a follow-up commit. Currently the example snippets are using the header property. [ChangeLog][Controls][MenuBar] Introduced a MenuBar control. Task-number: QTBUG-60350 Change-Id: Ie66dc457a3d8edbe8362fab2a591dc49442c95e2 Reviewed-by: Qt CI Bot Reviewed-by: Mitch Curtis --- .gitignore | 1 + src/imports/controls/MenuBar.qml | 63 ++ src/imports/controls/MenuBarItem.qml | 78 +++ src/imports/controls/controls.pri | 2 + .../doc/images/qtquickcontrols2-menubar-custom.png | Bin 0 -> 3392 bytes .../doc/images/qtquickcontrols2-menubar.png | Bin 0 -> 10188 bytes .../snippets/qtquickcontrols2-menubar-custom.qml | 84 +++ .../doc/snippets/qtquickcontrols2-menubar.qml | 67 +++ .../doc/src/qtquickcontrols2-customize.qdoc | 15 + .../controls/doc/src/qtquickcontrols2-menus.qdoc | 13 + src/imports/controls/fusion/MenuBar.qml | 74 +++ src/imports/controls/fusion/MenuBarItem.qml | 79 +++ src/imports/controls/fusion/fusion.pri | 2 + src/imports/controls/material/MenuBar.qml | 65 ++ src/imports/controls/material/MenuBarItem.qml | 90 +++ src/imports/controls/material/material.pri | 2 + .../controls/material/qquickmaterialtheme.cpp | 1 + src/imports/controls/qtquickcontrols2plugin.cpp | 2 + src/imports/controls/universal/MenuBar.qml | 64 ++ src/imports/controls/universal/MenuBarItem.qml | 92 +++ src/imports/controls/universal/universal.pri | 2 + src/imports/templates/qtquicktemplates2plugin.cpp | 4 + src/quicktemplates2/qquickmenu.cpp | 40 +- src/quicktemplates2/qquickmenu_p_p.h | 6 +- src/quicktemplates2/qquickmenubar.cpp | 656 +++++++++++++++++++++ src/quicktemplates2/qquickmenubar_p.h | 126 ++++ src/quicktemplates2/qquickmenubar_p_p.h | 107 ++++ src/quicktemplates2/qquickmenubaritem.cpp | 185 ++++++ src/quicktemplates2/qquickmenubaritem_p.h | 102 ++++ src/quicktemplates2/qquickmenubaritem_p_p.h | 85 +++ src/quicktemplates2/qquickshortcutcontext.cpp | 9 +- src/quicktemplates2/quicktemplates2.pri | 6 + tests/auto/auto.pro | 3 +- tests/auto/qquickmenubar/data/empty.qml | 54 ++ tests/auto/qquickmenubar/data/menubar.qml | 106 ++++ tests/auto/qquickmenubar/qquickmenubar.pro | 14 + tests/auto/qquickmenubar/tst_qquickmenubar.cpp | 562 ++++++++++++++++++ 37 files changed, 2848 insertions(+), 13 deletions(-) create mode 100644 src/imports/controls/MenuBar.qml create mode 100644 src/imports/controls/MenuBarItem.qml create mode 100644 src/imports/controls/doc/images/qtquickcontrols2-menubar-custom.png create mode 100644 src/imports/controls/doc/images/qtquickcontrols2-menubar.png create mode 100644 src/imports/controls/doc/snippets/qtquickcontrols2-menubar-custom.qml create mode 100644 src/imports/controls/doc/snippets/qtquickcontrols2-menubar.qml create mode 100644 src/imports/controls/fusion/MenuBar.qml create mode 100644 src/imports/controls/fusion/MenuBarItem.qml create mode 100644 src/imports/controls/material/MenuBar.qml create mode 100644 src/imports/controls/material/MenuBarItem.qml create mode 100644 src/imports/controls/universal/MenuBar.qml create mode 100644 src/imports/controls/universal/MenuBarItem.qml create mode 100644 src/quicktemplates2/qquickmenubar.cpp create mode 100644 src/quicktemplates2/qquickmenubar_p.h create mode 100644 src/quicktemplates2/qquickmenubar_p_p.h create mode 100644 src/quicktemplates2/qquickmenubaritem.cpp create mode 100644 src/quicktemplates2/qquickmenubaritem_p.h create mode 100644 src/quicktemplates2/qquickmenubaritem_p_p.h create mode 100644 tests/auto/qquickmenubar/data/empty.qml create mode 100644 tests/auto/qquickmenubar/data/menubar.qml create mode 100644 tests/auto/qquickmenubar/qquickmenubar.pro create mode 100644 tests/auto/qquickmenubar/tst_qquickmenubar.cpp diff --git a/.gitignore b/.gitignore index 400fe075..d7febb1f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ /tests/auto/qquickiconlabel/tst_qquickiconlabel /tests/auto/qquickmaterialstyle/tst_qquickmaterialstyle /tests/auto/qquickmaterialstyleconf/tst_qquickmaterialstyleconf +/tests/auto/qquickmenubar/tst_qquickmenubar /tests/auto/qquickstyle/tst_qquickstyle /tests/auto/qquickstyleselector/tst_qquickstyleselector /tests/auto/qquickuniversalstyle/tst_qquickuniversalstyle diff --git a/src/imports/controls/MenuBar.qml b/src/imports/controls/MenuBar.qml new file mode 100644 index 00000000..ec5bb88f --- /dev/null +++ b/src/imports/controls/MenuBar.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 + +T.MenuBar { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentHeight + topPadding + bottomPadding) + + delegate: MenuBarItem { } + + contentItem: Row { + spacing: control.spacing + Repeater { + model: control.contentModel + } + } + + background: Rectangle { + implicitHeight: 40 + color: Default.delegateColor + } +} diff --git a/src/imports/controls/MenuBarItem.qml b/src/imports/controls/MenuBarItem.qml new file mode 100644 index 00000000..d2f2ebad --- /dev/null +++ b/src/imports/controls/MenuBarItem.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 + +T.MenuBarItem { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + Math.max(contentItem.implicitHeight, + indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + spacing: 6 + padding: 6 + leftPadding: 12 + rightPadding: 16 + + icon.width: 24 + icon.height: 24 + icon.color: enabled ? Default.textDarkColor : Default.textDisabledColor + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + alignment: Qt.AlignLeft + + icon: control.icon + text: control.text + font: control.font + color: control.enabled ? Default.textDarkColor : Default.textDisabledColor + } + + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + color: control.highlighted || control.down ? Default.delegatePressedColor : "transparent" + } +} diff --git a/src/imports/controls/controls.pri b/src/imports/controls/controls.pri index 8c127643..56c7a8ee 100644 --- a/src/imports/controls/controls.pri +++ b/src/imports/controls/controls.pri @@ -36,6 +36,8 @@ QML_CONTROLS = \ $$PWD/ItemDelegate.qml \ $$PWD/Label.qml \ $$PWD/Menu.qml \ + $$PWD/MenuBar.qml \ + $$PWD/MenuBarItem.qml \ $$PWD/MenuItem.qml \ $$PWD/MenuSeparator.qml \ $$PWD/Page.qml \ diff --git a/src/imports/controls/doc/images/qtquickcontrols2-menubar-custom.png b/src/imports/controls/doc/images/qtquickcontrols2-menubar-custom.png new file mode 100644 index 00000000..3903e31d Binary files /dev/null and b/src/imports/controls/doc/images/qtquickcontrols2-menubar-custom.png differ diff --git a/src/imports/controls/doc/images/qtquickcontrols2-menubar.png b/src/imports/controls/doc/images/qtquickcontrols2-menubar.png new file mode 100644 index 00000000..94e3676f Binary files /dev/null and b/src/imports/controls/doc/images/qtquickcontrols2-menubar.png differ diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-menubar-custom.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-menubar-custom.qml new file mode 100644 index 00000000..f328d0e5 --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-menubar-custom.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +ApplicationWindow { + id: window + visible: true + width: menuBar.implicitWidth + height: menuBar.height + + Component.onCompleted: menuBar.itemAt(1).highlighted = true + + header: + +// Indent it like this so that the indenting in the generated doc is normal. +MenuBar { + id: menuBar + + Menu { title: qsTr("File") } + Menu { title: qsTr("Edit") } + Menu { title: qsTr("View") } + Menu { title: qsTr("Help") } + + delegate: MenuBarItem { + id: menuBarItem + + contentItem: Text { + text: menuBarItem.text + font: menuBarItem.font + opacity: enabled ? 1.0 : 0.3 + color: menuBarItem.highlighted ? "#ffffff" : "#21be2b" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + opacity: enabled ? 1 : 0.3 + color: menuBarItem.highlighted ? "#21be2b" : "transparent" + } + } + + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + color: "#ffffff" + + Rectangle { + color: "#21be2b" + width: parent.width + height: 1 + anchors.bottom: parent.bottom + } + } +} +} //! [eof] diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-menubar.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-menubar.qml new file mode 100644 index 00000000..94d1dc7a --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-menubar.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +//! [begin] +ApplicationWindow { + id: window + width: 320 + height: 260 + visible: true + + //! [skipfrom] + Component.onCompleted: { + header.itemAt(0).triggered() + header.itemAt(0).menu.itemAt(2).highlighted = true + } + //! [skipto] + + header: MenuBar { + Menu { + title: qsTr("File") + Action { text: qsTr("New...") } + Action { text: qsTr("Open...") } + Action { text: qsTr("Save") } + Action { text: qsTr("Save As...") } + MenuSeparator { } + Action { text: qsTr("Quit") } + } + Menu { + title: qsTr("Edit") + Action { text: qsTr("Cut") } + Action { text: qsTr("Copy") } + Action { text: qsTr("Paste") } + } + Menu { + title: qsTr("Help") + Action { text: qsTr("About") } + } + } +} +//! [end] diff --git a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc index 46d8a0c6..a1d65396 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc @@ -560,6 +560,21 @@ \printto eof + \section2 Customizing MenuBar + + MenuBar can have a visual \l {Control::background}{background} item, + and MenuBarItem consists of two visual items: \l {Control::background} + {background} and \l {Control::contentItem}{content item}. + + \image qtquickcontrols2-menubar-custom.png + + \quotefromfile qtquickcontrols2-menubar-custom.qml + \skipto import QtQuick 2.9 + \printuntil import QtQuick.Controls 2.3 + \skipto MenuBar + \printto eof + + \section2 Customizing PageIndicator PageIndicator consists of a \l {Control::background}{background}, \l {Control::contentItem}{content item}, and \l {PageIndicator::delegate}{delegate}. diff --git a/src/imports/controls/doc/src/qtquickcontrols2-menus.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-menus.qdoc index 3c30ed38..43db2092 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-menus.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-menus.qdoc @@ -52,4 +52,17 @@ \li is highlighted (for example, on keyboard navigation) \li performs some action on activation \endlist + + \section1 MenuBar Control + + \image qtquickcontrols2-menubar.png + + \l MenuBar control can be used for window menu bars. + + \l MenuBarItem is an item in the MenuBar control. Each item in a menu bar: + \list + \li displays text to the user + \li is highlighted (for example, on keyboard navigation) + \li pops up the respective menu on activation + \endlist */ diff --git a/src/imports/controls/fusion/MenuBar.qml b/src/imports/controls/fusion/MenuBar.qml new file mode 100644 index 00000000..91c4e45c --- /dev/null +++ b/src/imports/controls/fusion/MenuBar.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Fusion 2.3 +import QtQuick.Controls.Fusion.impl 2.3 + +T.MenuBar { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentHeight + topPadding + bottomPadding) + + delegate: MenuBarItem { } + + contentItem: Row { + spacing: control.spacing + Repeater { + model: control.contentModel + } + } + + background: Rectangle { + implicitHeight: 20 + + color: control.palette.window + + Rectangle { + y: parent.height - height + width: parent.width + height: 1 + color: Fusion.mergedColors(Qt.darker(control.palette.window, 1.2), + Qt.lighter(Fusion.outline(control.palette), 1.4), 60) + } + } +} diff --git a/src/imports/controls/fusion/MenuBarItem.qml b/src/imports/controls/fusion/MenuBarItem.qml new file mode 100644 index 00000000..e3c19cd0 --- /dev/null +++ b/src/imports/controls/fusion/MenuBarItem.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Fusion 2.3 +import QtQuick.Controls.Fusion.impl 2.3 + +T.MenuBarItem { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + Math.max(contentItem.implicitHeight, + indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + padding: 6 + spacing: 6 + + icon.width: 16 + icon.height: 16 + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + alignment: Qt.AlignLeft + + icon: control.icon + text: control.text + font: control.font + color: control.down || control.highlighted ? Fusion.highlightedText(control.palette) : control.palette.text + } + + background: Rectangle { + implicitWidth: 20 + implicitHeight: 20 + + color: Fusion.highlight(control.palette) + visible: control.down || control.highlighted + } +} diff --git a/src/imports/controls/fusion/fusion.pri b/src/imports/controls/fusion/fusion.pri index 9902e971..4a6e8fb4 100644 --- a/src/imports/controls/fusion/fusion.pri +++ b/src/imports/controls/fusion/fusion.pri @@ -31,6 +31,8 @@ QML_FILES += \ $$PWD/ItemDelegate.qml \ $$PWD/Label.qml \ $$PWD/Menu.qml \ + $$PWD/MenuBar.qml \ + $$PWD/MenuBarItem.qml \ $$PWD/MenuItem.qml \ $$PWD/MenuSeparator.qml \ $$PWD/Page.qml \ diff --git a/src/imports/controls/material/MenuBar.qml b/src/imports/controls/material/MenuBar.qml new file mode 100644 index 00000000..bdf229a0 --- /dev/null +++ b/src/imports/controls/material/MenuBar.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Material 2.3 +import QtQuick.Controls.Material.impl 2.3 + +T.MenuBar { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentHeight + topPadding + bottomPadding) + + delegate: MenuBarItem { } + + contentItem: Row { + spacing: control.spacing + Repeater { + model: control.contentModel + } + } + + background: Rectangle { + implicitHeight: 40 + color: control.Material.dialogColor + } +} diff --git a/src/imports/controls/material/MenuBarItem.qml b/src/imports/controls/material/MenuBarItem.qml new file mode 100644 index 00000000..975d0daa --- /dev/null +++ b/src/imports/controls/material/MenuBarItem.qml @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Material 2.3 +import QtQuick.Controls.Material.impl 2.3 + +T.MenuBarItem { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentItem.implicitHeight + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + padding: 16 + topPadding: 12 + bottomPadding: 12 + spacing: 16 + + icon.width: 24 + icon.height: 24 + icon.color: enabled ? Material.foreground : Material.hintTextColor + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + alignment: Qt.AlignLeft + + icon: control.icon + text: control.text + font: control.font + color: control.enabled ? control.Material.foreground : control.Material.hintTextColor + } + + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + color: control.highlighted ? control.Material.listHighlightColor : "transparent" + + Ripple { + width: parent.width + height: parent.height + + clip: visible + pressed: control.pressed + anchor: control + active: control.down || control.highlighted + color: control.Material.rippleColor + } + } +} diff --git a/src/imports/controls/material/material.pri b/src/imports/controls/material/material.pri index 64d91442..ab925aa2 100644 --- a/src/imports/controls/material/material.pri +++ b/src/imports/controls/material/material.pri @@ -33,6 +33,8 @@ QML_FILES += \ $$PWD/ItemDelegate.qml \ $$PWD/Label.qml \ $$PWD/Menu.qml \ + $$PWD/MenuBar.qml \ + $$PWD/MenuBarItem.qml \ $$PWD/MenuItem.qml \ $$PWD/MenuSeparator.qml \ $$PWD/Page.qml \ diff --git a/src/imports/controls/material/qquickmaterialtheme.cpp b/src/imports/controls/material/qquickmaterialtheme.cpp index 7ef2f0e7..c697b2a5 100644 --- a/src/imports/controls/material/qquickmaterialtheme.cpp +++ b/src/imports/controls/material/qquickmaterialtheme.cpp @@ -103,6 +103,7 @@ const QFont *QQuickMaterialTheme::font(QPlatformTheme::Font type) const return &itemViewFont; case QPlatformTheme::ListViewFont: return &listViewFont; + case QPlatformTheme::MenuBarFont: case QPlatformTheme::MenuItemFont: case QPlatformTheme::ComboMenuItemFont: return &menuItemFont; diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp index 7a1651e4..dbb1e106 100644 --- a/src/imports/controls/qtquickcontrols2plugin.cpp +++ b/src/imports/controls/qtquickcontrols2plugin.cpp @@ -158,6 +158,8 @@ void QtQuickControls2Plugin::registerTypes(const char *uri) // QtQuick.Controls 2.3 (new types in Qt 5.10) qmlRegisterType(selector.select(QStringLiteral("Action.qml")), uri, 2, 3, "Action"); qmlRegisterType(selector.select(QStringLiteral("ActionGroup.qml")), uri, 2, 3, "ActionGroup"); + qmlRegisterType(selector.select(QStringLiteral("MenuBar.qml")), uri, 2, 3, "MenuBar"); + qmlRegisterType(selector.select(QStringLiteral("MenuBarItem.qml")), uri, 2, 3, "MenuBarItem"); qmlRegisterUncreatableType(uri, 2, 3, "Overlay", QStringLiteral("Overlay is only available as an attached property.")); } diff --git a/src/imports/controls/universal/MenuBar.qml b/src/imports/controls/universal/MenuBar.qml new file mode 100644 index 00000000..626f26fa --- /dev/null +++ b/src/imports/controls/universal/MenuBar.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Universal 2.3 + +T.MenuBar { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentHeight + topPadding + bottomPadding) + + delegate: MenuBarItem { } + + contentItem: Row { + spacing: control.spacing + Repeater { + model: control.contentModel + } + } + + background: Rectangle { + implicitHeight: 40 + color: control.Universal.chromeMediumColor + } +} diff --git a/src/imports/controls/universal/MenuBarItem.qml b/src/imports/controls/universal/MenuBarItem.qml new file mode 100644 index 00000000..a7354dee --- /dev/null +++ b/src/imports/controls/universal/MenuBarItem.qml @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Templates 2.3 as T +import QtQuick.Controls 2.3 +import QtQuick.Controls.impl 2.3 +import QtQuick.Controls.Universal 2.3 + +T.MenuBarItem { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + Math.max(contentItem.implicitHeight, + indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + padding: 12 + topPadding: padding - 1 + bottomPadding: padding + 1 + spacing: 12 + + icon.width: 20 + icon.height: 20 + icon.color: !enabled ? Universal.baseLowColor : Universal.baseHighColor + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + alignment: Qt.AlignLeft + + icon: control.icon + text: control.text + font: control.font + color: !control.enabled ? control.Universal.baseLowColor : control.Universal.baseHighColor + } + + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + + color: !control.enabled ? control.Universal.baseLowColor : + control.down ? control.Universal.listMediumColor : + control.highlighted ? control.Universal.listLowColor : "transparent" + + Rectangle { + x: 1; y: 1 + width: parent.width - 2 + height: parent.height - 2 + + visible: control.visualFocus + color: control.Universal.accent + opacity: control.Universal.theme === Universal.Light ? 0.4 : 0.6 + } + } +} diff --git a/src/imports/controls/universal/universal.pri b/src/imports/controls/universal/universal.pri index c80a2dfc..33d0dcb0 100644 --- a/src/imports/controls/universal/universal.pri +++ b/src/imports/controls/universal/universal.pri @@ -16,6 +16,8 @@ QML_FILES += \ $$PWD/ItemDelegate.qml \ $$PWD/Label.qml \ $$PWD/Menu.qml \ + $$PWD/MenuBar.qml \ + $$PWD/MenuBarItem.qml \ $$PWD/MenuItem.qml \ $$PWD/MenuSeparator.qml \ $$PWD/Page.qml \ diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp index 2ce655c3..a8aafa2c 100644 --- a/src/imports/templates/qtquicktemplates2plugin.cpp +++ b/src/imports/templates/qtquicktemplates2plugin.cpp @@ -60,6 +60,8 @@ #include #include #include +#include +#include #include #include #include @@ -300,6 +302,8 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri) qmlRegisterType(uri, 2, 3, "DialogButtonBox"); qRegisterMetaType(); qmlRegisterType(uri, 2, 3, "Menu"); + qmlRegisterType(uri, 2, 3, "MenuBar"); + qmlRegisterType(uri, 2, 3, "MenuBarItem"); qmlRegisterType(uri, 2, 3, "MenuItem"); qmlRegisterUncreatableType(uri, 2, 3, "Overlay", QStringLiteral("Overlay is only available as an attached property.")); qmlRegisterType(); diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp index e5af1592..9b196930 100644 --- a/src/quicktemplates2/qquickmenu.cpp +++ b/src/quicktemplates2/qquickmenu.cpp @@ -37,6 +37,8 @@ #include "qquickmenu_p.h" #include "qquickmenu_p_p.h" #include "qquickmenuitem_p_p.h" +#include "qquickmenubaritem_p.h" +#include "qquickmenubar_p.h" #include "qquickpopupitem_p_p.h" #include "qquickaction_p.h" @@ -522,6 +524,19 @@ void QQuickMenuPrivate::resolveParentItem() q->setParentItem(findParentMenuItem(q)); } +void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event) +{ + if (QQuickMenuItem *menuItem = qobject_cast(parentItem)) { + if (QQuickMenu *menu = menuItem->menu()) + QQuickMenuPrivate::get(menu)->propagateKeyEvent(event); + } else if (QQuickMenuBarItem *menuBarItem = qobject_cast(parentItem)) { + if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) { + event->accept(); + QCoreApplication::sendEvent(menuBar, event); + } + } +} + void QQuickMenuPrivate::startHoverTimer() { Q_Q(QQuickMenu); @@ -567,7 +582,7 @@ void QQuickMenuPrivate::setCurrentIndex(int index, Qt::FocusReason reason) emit q->currentIndexChanged(); } -void QQuickMenuPrivate::activateNextItem() +bool QQuickMenuPrivate::activateNextItem() { int index = currentIndex; int count = contentModel->count(); @@ -576,11 +591,12 @@ void QQuickMenuPrivate::activateNextItem() if (!item || !item->activeFocusOnTab()) continue; setCurrentIndex(index, Qt::TabFocusReason); - break; + return true; } + return false; } -void QQuickMenuPrivate::activatePreviousItem() +bool QQuickMenuPrivate::activatePreviousItem() { int index = currentIndex; while (--index >= 0) { @@ -588,8 +604,9 @@ void QQuickMenuPrivate::activatePreviousItem() if (!item || !item->activeFocusOnTab()) continue; setCurrentIndex(index, Qt::BacktabFocusReason); - break; + return true; } + return false; } void QQuickMenuPrivate::contentData_append(QQmlListProperty *prop, QObject *obj) @@ -1358,8 +1375,6 @@ void QQuickMenu::keyPressEvent(QKeyEvent *event) { Q_D(QQuickMenu); QQuickPopup::keyPressEvent(event); - if (d->contentModel->count() == 0) - return; // QTBUG-17051 // Work around the fact that ListView has no way of distinguishing between @@ -1369,7 +1384,8 @@ void QQuickMenu::keyPressEvent(QKeyEvent *event) // shown at once. switch (event->key()) { case Qt::Key_Up: - d->activatePreviousItem(); + if (!d->activatePreviousItem()) + d->propagateKeyEvent(event); break; case Qt::Key_Down: @@ -1378,17 +1394,23 @@ void QQuickMenu::keyPressEvent(QKeyEvent *event) case Qt::Key_Left: case Qt::Key_Right: + event->ignore(); if (d->popupItem->isMirrored() == (event->key() == Qt::Key_Right)) { if (d->parentMenu && d->currentItem) { if (!d->cascade) d->parentMenu->open(); close(); + event->accept(); } } else { - if (QQuickMenu *subMenu = d->currentSubMenu()) + if (QQuickMenu *subMenu = d->currentSubMenu()) { subMenu->popup(subMenu->itemAt(0)); + event->accept(); + } } - return; + if (!event->isAccepted()) + d->propagateKeyEvent(event); + break; default: break; diff --git a/src/quicktemplates2/qquickmenu_p_p.h b/src/quicktemplates2/qquickmenu_p_p.h index 9f3d66a2..0c8ffb4d 100644 --- a/src/quicktemplates2/qquickmenu_p_p.h +++ b/src/quicktemplates2/qquickmenu_p_p.h @@ -106,12 +106,14 @@ public: void setParentMenu(QQuickMenu *parent); void resolveParentItem(); + void propagateKeyEvent(QKeyEvent *event); + void startHoverTimer(); void stopHoverTimer(); void setCurrentIndex(int index, Qt::FocusReason reason); - void activateNextItem(); - void activatePreviousItem(); + bool activateNextItem(); + bool activatePreviousItem(); static void contentData_append(QQmlListProperty *prop, QObject *obj); static int contentData_count(QQmlListProperty *prop); diff --git a/src/quicktemplates2/qquickmenubar.cpp b/src/quicktemplates2/qquickmenubar.cpp new file mode 100644 index 00000000..f44edbd0 --- /dev/null +++ b/src/quicktemplates2/qquickmenubar.cpp @@ -0,0 +1,656 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmenubar_p.h" +#include "qquickmenubar_p_p.h" +#include "qquickmenubaritem_p_p.h" +#include "qquickmenu_p.h" +#include "qquickmenu_p_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MenuBar + \inherits Container + \instantiates QQuickMenuBar + \inqmlmodule QtQuick.Controls + \since 5.10 + \ingroup qtquickcontrols2-menus + \brief Provides a window menu bar. + + \image qtquickcontrols2-menubar.png + + MenuBar consists of drop-down menus, and is normally located at the top + edge of the window. + + \quotefromfile qtquickcontrols2-menubar.qml + \skipuntil begin + \printto skipfrom + \skipuntil skipto + \printto end + + Typically, menus are statically declared as children of the menu bar, but + MenuBar also provides API to \l {addMenu}{add}, \l {insertMenu}{insert}, + \l {removeMenu}{remove}, and \l {takeMenu}{take} menus dynamically. The + menus in a menu bar can be accessed using \l menuAt(). + + \sa {Customizing MenuBar}, Menu, MenuBarItem, {Menu Controls} +*/ + +QQuickMenuBarPrivate::QQuickMenuBarPrivate() + : popupMode(false), + triggering(false), + hasContentWidth(false), + hasContentHeight(false), + contentWidth(0), + contentHeight(0), + delegate(nullptr) +{ + changeTypes |= Geometry; +} + +QQuickItem *QQuickMenuBarPrivate::beginCreateItem() +{ + Q_Q(QQuickMenuBar); + if (!delegate) + return nullptr; + + QQmlContext *creationContext = delegate->creationContext(); + if (!creationContext) + creationContext = qmlContext(q); + QQmlContext *context = new QQmlContext(creationContext, q); + context->setContextObject(q); + + QObject *object = delegate->beginCreate(context); + QQuickItem *item = qobject_cast(object); + if (!item) + delete object; + + item->setParentItem(q); + QQml_setParent_noEvent(item, q); + + return item; +} + +void QQuickMenuBarPrivate::completeCreateItem() +{ + if (!delegate) + return; + + delegate->completeCreate(); +} + +QQuickItem *QQuickMenuBarPrivate::createItem(QQuickMenu *menu) +{ + QQuickItem *item = beginCreateItem(); + if (QQuickMenuBarItem *menuBarItem = qobject_cast(item)) + menuBarItem->setMenu(menu); + completeCreateItem(); + return item; +} + +void QQuickMenuBarPrivate::toggleCurrentMenu(bool visible, bool activate) +{ + if (!currentItem || visible == popupMode) + return; + + QQuickMenu *menu = currentItem->menu(); + + triggering = true; + popupMode = visible; + if (menu) + menu->setVisible(visible); + if (!visible) + currentItem->forceActiveFocus(); + else if (menu && activate) + menu->setCurrentIndex(0); + triggering = false; +} + +void QQuickMenuBarPrivate::activateItem(QQuickMenuBarItem *item) +{ + if (currentItem == item) + return; + + if (currentItem) { + currentItem->setHighlighted(false); + if (popupMode) { + if (QQuickMenu *menu = currentItem->menu()) + menu->dismiss(); + } + } + + if (item) { + item->setHighlighted(true); + if (popupMode) { + if (QQuickMenu *menu = item->menu()) + menu->open(); + } + } + + currentItem = item; +} + +void QQuickMenuBarPrivate::activateNextItem() +{ + int index = currentItem ? contentModel->indexOf(currentItem, nullptr) : -1; + if (index >= contentModel->count() - 1) + index = -1; + activateItem(qobject_cast(itemAt(++index))); +} + +void QQuickMenuBarPrivate::activatePreviousItem() +{ + int index = currentItem ? contentModel->indexOf(currentItem, nullptr) : contentModel->count(); + if (index <= 0) + index = contentModel->count(); + activateItem(qobject_cast(itemAt(--index))); +} + +void QQuickMenuBarPrivate::onItemHovered() +{ + Q_Q(QQuickMenuBar); + QQuickMenuBarItem *item = qobject_cast(q->sender()); + if (!item || item == currentItem || !item->isHovered() || QQuickMenuBarItemPrivate::get(item)->touchId != -1) + return; + + activateItem(item); +} + +void QQuickMenuBarPrivate::onItemTriggered() +{ + Q_Q(QQuickMenuBar); + QQuickMenuBarItem *item = qobject_cast(q->sender()); + if (!item) + return; + + if (item == currentItem) { + toggleCurrentMenu(!popupMode, false); + } else { + popupMode = true; + activateItem(item); + } +} + +void QQuickMenuBarPrivate::onMenuAboutToHide() +{ + if (triggering || !currentItem || currentItem->isHovered() || !currentItem->isHighlighted()) + return; + + popupMode = false; + activateItem(nullptr); +} + +void QQuickMenuBarPrivate::updateContentSize() +{ + Q_Q(QQuickMenuBar); + if (hasContentWidth && hasContentHeight) + return; + + const int count = contentModel->count(); + if (count <= 0 || !contentItem) + return; + + qreal maxHeight = 0; + qreal totalWidth = qMax(0, count - 1) * spacing; + + for (int i = 0; i < count; ++i) { + QQuickItem *item = q->itemAt(i); + if (item) { + totalWidth += item->width(); + maxHeight = qMax(maxHeight, item->implicitHeight()); + } + } + + bool contentWidthChange = false; + if (!hasContentWidth && !qFuzzyCompare(contentWidth, totalWidth)) { + contentWidth = totalWidth; + contentWidthChange = true; + } + + bool contentHeightChange = false; + if (!hasContentHeight && !qFuzzyCompare(contentHeight, maxHeight)) { + contentHeight = maxHeight; + contentHeightChange = true; + } + + if (contentWidthChange) + emit q->contentWidthChanged(); + if (contentHeightChange) + emit q->contentHeightChanged(); +} + +void QQuickMenuBarPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) +{ + if ((change.widthChange() && !hasContentWidth) || (change.heightChange() && !hasContentHeight)) + updateContentSize(); +} + +void QQuickMenuBarPrivate::contentData_append(QQmlListProperty *prop, QObject *obj) +{ + QQuickMenuBar *menuBar = static_cast(prop->object); + if (QQuickMenu *menu = qobject_cast(obj)) + obj = QQuickMenuBarPrivate::get(menuBar)->createItem(menu); + QQuickContainerPrivate::contentData_append(prop, obj); +} + +void QQuickMenuBarPrivate::menus_append(QQmlListProperty *prop, QQuickMenu *obj) +{ + QQuickMenuBar *menuBar = static_cast(prop->object); + menuBar->addMenu(obj); +} + +int QQuickMenuBarPrivate::menus_count(QQmlListProperty *prop) +{ + QQuickMenuBar *menuBar = static_cast(prop->object); + return menuBar->count(); +} + +QQuickMenu *QQuickMenuBarPrivate::menus_at(QQmlListProperty *prop, int index) +{ + QQuickMenuBar *menuBar = static_cast(prop->object); + return menuBar->menuAt(index); +} + +void QQuickMenuBarPrivate::menus_clear(QQmlListProperty *prop) +{ + QQuickMenuBar *menuBar = static_cast(prop->object); + QQuickMenuBarPrivate::get(menuBar)->contentModel->clear(); +} + +QQuickMenuBar::QQuickMenuBar(QQuickItem *parent) + : QQuickContainer(*(new QQuickMenuBarPrivate), parent) +{ + setFlag(ItemIsFocusScope); + setFocusPolicy(Qt::ClickFocus); +} + +/*! + \qmlproperty Component QtQuick.Controls::MenuBar::delegate + + This property holds the component that is used to create menu bar + items to present menus in the menu bar. + + \sa MenuBarItem +*/ +QQmlComponent *QQuickMenuBar::delegate() const +{ + Q_D(const QQuickMenuBar); + return d->delegate; +} + +void QQuickMenuBar::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQuickMenuBar); + if (d->delegate == delegate) + return; + + d->delegate = delegate; + emit delegateChanged(); +} + +/*! + \qmlmethod Menu QtQuick.Controls::MenuBar::menuAt(int index) + + Returns the menu at \a index, or \c null if it does not exist. +*/ +QQuickMenu *QQuickMenuBar::menuAt(int index) const +{ + Q_D(const QQuickMenuBar); + QQuickMenuBarItem *item = qobject_cast(d->itemAt(index)); + if (!item) + return nullptr; + return item->menu(); +} + +/*! + \qmlmethod void QtQuick.Controls::MenuBar::addMenu(Menu menu) + + Adds \a menu to the end of the list of menus. +*/ +void QQuickMenuBar::addMenu(QQuickMenu *menu) +{ + Q_D(QQuickMenuBar); + addItem(d->createItem(menu)); +} + +/*! + \qmlmethod void QtQuick.Controls::MenuBar::insertMenu(int index, Menu menu) + + Inserts \a menu at \a index. +*/ +void QQuickMenuBar::insertMenu(int index, QQuickMenu *menu) +{ + Q_D(QQuickMenuBar); + insertItem(index, d->createItem(menu)); +} + +/*! + \qmlmethod void QtQuick.Controls::MenuBar::removeMenu(Menu menu) + + Removes and destroys the specified \a menu. +*/ +void QQuickMenuBar::removeMenu(QQuickMenu *menu) +{ + Q_D(QQuickMenuBar); + if (!menu) + return; + + const int count = d->contentModel->count(); + for (int i = 0; i < count; ++i) { + QQuickMenuBarItem *item = qobject_cast(itemAt(i)); + if (!item || item->menu() != menu) + continue; + + removeItem(item); + break; + } + + menu->deleteLater(); +} + +/*! + \qmlmethod Menu QtQuick.Controls::MenuBar::takeMenu(int index) + + Removes and returns the menu at \a index. + + \note The ownership of the item is transferred to the caller. +*/ +QQuickMenu *QQuickMenuBar::takeMenu(int index) +{ + Q_D(QQuickMenuBar); + QQuickMenuBarItem *item = qobject_cast(itemAt(index)); + if (!item) + return nullptr; + + QQuickMenu *menu = item->menu(); + if (!menu) + return nullptr; + + d->removeItem(index, item); + item->deleteLater(); + return menu; +} + +/*! + \qmlproperty real QtQuick.Controls::MenuBar::contentWidth + + This property holds the content width. It is used for calculating the total + implicit width of the menu bar. + + Unless explicitly overridden, the content width is automatically calculated + based on the total implicit width of the items and the \l {Control::}{spacing} + of the menu bar. + + \sa contentHeight +*/ +qreal QQuickMenuBar::contentWidth() const +{ + Q_D(const QQuickMenuBar); + return d->contentWidth; +} + +void QQuickMenuBar::setContentWidth(qreal width) +{ + Q_D(QQuickMenuBar); + d->hasContentWidth = true; + if (qFuzzyCompare(d->contentWidth, width)) + return; + + d->contentWidth = width; + emit contentWidthChanged(); +} + +void QQuickMenuBar::resetContentWidth() +{ + Q_D(QQuickMenuBar); + if (!d->hasContentWidth) + return; + + d->hasContentWidth = false; + if (isComponentComplete()) + d->updateContentSize(); +} + +/*! + \qmlproperty real QtQuick.Controls::MenuBar::contentHeight + + This property holds the content height. It is used for calculating the total + implicit height of the menu bar. + + Unless explicitly overridden, the content height is automatically calculated + based on the maximum implicit height of the items. + + \sa contentWidth +*/ +qreal QQuickMenuBar::contentHeight() const +{ + Q_D(const QQuickMenuBar); + return d->contentHeight; +} + +void QQuickMenuBar::setContentHeight(qreal height) +{ + Q_D(QQuickMenuBar); + d->hasContentHeight = true; + if (qFuzzyCompare(d->contentHeight, height)) + return; + + d->contentHeight = height; + emit contentHeightChanged(); +} + +void QQuickMenuBar::resetContentHeight() +{ + Q_D(QQuickMenuBar); + if (!d->hasContentHeight) + return; + + d->hasContentHeight = false; + if (isComponentComplete()) + d->updateContentSize(); +} + +/*! + \qmlproperty list QtQuick.Controls::MenuBar::menus + + This property holds the list of menus. + + The list contains all menus that have been declared in QML as children + of the menu bar, and also menus that have been dynamically added or + inserted using the \l addMenu() and \l insertMenu() methods, respectively. +*/ +QQmlListProperty QQuickMenuBar::menus() +{ + return QQmlListProperty(this, nullptr, + QQuickMenuBarPrivate::menus_append, + QQuickMenuBarPrivate::menus_count, + QQuickMenuBarPrivate::menus_at, + QQuickMenuBarPrivate::menus_clear); +} + +QQmlListProperty QQuickMenuBar::contentData() +{ + return QQmlListProperty(this, nullptr, + QQuickMenuBarPrivate::contentData_append, + QQuickContainerPrivate::contentData_count, + QQuickContainerPrivate::contentData_at, + QQuickContainerPrivate::contentData_clear); +} + +void QQuickMenuBar::updatePolish() +{ + Q_D(QQuickMenuBar); + QQuickContainer::updatePolish(); + d->updateContentSize(); +} + +void QQuickMenuBar::componentComplete() +{ + Q_D(QQuickMenuBar); + QQuickContainer::componentComplete(); + d->updateContentSize(); +} + +bool QQuickMenuBar::eventFilter(QObject *object, QEvent *event) +{ + return QObject::eventFilter(object, event); +} + +void QQuickMenuBar::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickMenuBar); + QQuickContainer::keyReleaseEvent(event); + + switch (event->key()) { + case Qt::Key_Up: + d->toggleCurrentMenu(false, false); + break; + + case Qt::Key_Down: + d->toggleCurrentMenu(true, true); + break; + + case Qt::Key_Left: + case Qt::Key_Right: + if (isMirrored() == (event->key() == Qt::Key_Left)) + d->activateNextItem(); + else + d->activatePreviousItem(); + break; + case Qt::Key_Escape: + if (d->currentItem) { + d->activateItem(nullptr); + setFocus(false); + } + break; + default: + break; + } +} + +void QQuickMenuBar::keyReleaseEvent(QKeyEvent *event) +{ + QQuickContainer::keyReleaseEvent(event); + + switch (event->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Escape: + event->accept(); + break; + + default: + event->ignore(); + break; + } +} + +void QQuickMenuBar::hoverLeaveEvent(QHoverEvent *event) +{ + Q_D(QQuickMenuBar); + QQuickContainer::hoverLeaveEvent(event); + if (!d->popupMode && d->currentItem) + d->activateItem(nullptr); +} + +bool QQuickMenuBar::isContent(QQuickItem *item) const +{ + return qobject_cast(item); +} + +void QQuickMenuBar::itemAdded(int index, QQuickItem *item) +{ + Q_D(QQuickMenuBar); + QQuickContainer::itemAdded(index, item); + if (QQuickMenuBarItem *menuBarItem = qobject_cast(item)) { + QQuickMenuBarItemPrivate::get(menuBarItem)->setMenuBar(this); + QObjectPrivate::connect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered); + QObjectPrivate::connect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered); + if (QQuickMenu *menu = menuBarItem->menu()) + QObjectPrivate::connect(menu, &QQuickPopup::aboutToHide, d, &QQuickMenuBarPrivate::onMenuAboutToHide); + } + if (isComponentComplete()) + polish(); + if (isComponentComplete()) + polish(); + emit menusChanged(); +} + +void QQuickMenuBar::itemMoved(int index, QQuickItem *item) +{ + QQuickContainer::itemMoved(index, item); + emit menusChanged(); +} + +void QQuickMenuBar::itemRemoved(int index, QQuickItem *item) +{ + Q_D(QQuickMenuBar); + QQuickContainer::itemRemoved(index, item); + if (QQuickMenuBarItem *menuBarItem = qobject_cast(item)) { + QQuickMenuBarItemPrivate::get(menuBarItem)->setMenuBar(nullptr); + QObjectPrivate::disconnect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered); + QObjectPrivate::disconnect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered); + if (QQuickMenu *menu = menuBarItem->menu()) + QObjectPrivate::disconnect(menu, &QQuickPopup::aboutToHide, d, &QQuickMenuBarPrivate::onMenuAboutToHide); + } + emit menusChanged(); +} + +QFont QQuickMenuBar::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::MenuBarFont); +} + +QPalette QQuickMenuBar::defaultPalette() const +{ + return QQuickControlPrivate::themePalette(QPlatformTheme::MenuBarPalette); +} + +#if QT_CONFIG(accessibility) +QAccessible::Role QQuickMenuBar::accessibleRole() const +{ + return QAccessible::MenuBar; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickmenubar_p.h b/src/quicktemplates2/qquickmenubar_p.h new file mode 100644 index 00000000..8c703f25 --- /dev/null +++ b/src/quicktemplates2/qquickmenubar_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENUBAR_P_H +#define QQUICKMENUBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QQuickMenu; +class QQuickMenuBarPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBar : public QQuickContainer +{ + Q_OBJECT + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL) + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth RESET resetContentWidth NOTIFY contentWidthChanged FINAL) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight RESET resetContentHeight NOTIFY contentHeightChanged FINAL) + Q_PROPERTY(QQmlListProperty menus READ menus NOTIFY menusChanged FINAL) + Q_PROPERTY(QQmlListProperty contentData READ contentData FINAL) + +public: + explicit QQuickMenuBar(QQuickItem *parent = nullptr); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *delegate); + + Q_INVOKABLE QQuickMenu *menuAt(int index) const; + Q_INVOKABLE void addMenu(QQuickMenu *menu); + Q_INVOKABLE void insertMenu(int index, QQuickMenu *menu); + Q_INVOKABLE void removeMenu(QQuickMenu *menu); + Q_INVOKABLE QQuickMenu *takeMenu(int index); + + qreal contentWidth() const; + void setContentWidth(qreal width); + void resetContentWidth(); + + qreal contentHeight() const; + void setContentHeight(qreal height); + void resetContentHeight(); + + QQmlListProperty menus(); + QQmlListProperty contentData(); + +Q_SIGNALS: + void delegateChanged(); + void contentWidthChanged(); + void contentHeightChanged(); + void menusChanged(); + +protected: + void updatePolish() override; + void componentComplete() override; + + bool eventFilter(QObject *object, QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void hoverLeaveEvent(QHoverEvent *event) override; + + bool isContent(QQuickItem *item) const override; + void itemAdded(int index, QQuickItem *item) override; + void itemMoved(int index, QQuickItem *item) override; + void itemRemoved(int index, QQuickItem *item) override; + + QFont defaultFont() const override; + QPalette defaultPalette() const override; + +#if QT_CONFIG(accessibility) + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickMenuBar) + Q_DECLARE_PRIVATE(QQuickMenuBar) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMenuBar) + +#endif // QQUICKMENUBAR_P_H diff --git a/src/quicktemplates2/qquickmenubar_p_p.h b/src/quicktemplates2/qquickmenubar_p_p.h new file mode 100644 index 00000000..161806fb --- /dev/null +++ b/src/quicktemplates2/qquickmenubar_p_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENUBAR_P_P_H +#define QQUICKMENUBAR_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlComponent; +class QQuickMenuBarItem; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarPrivate : public QQuickContainerPrivate +{ + Q_DECLARE_PUBLIC(QQuickMenuBar) + +public: + QQuickMenuBarPrivate(); + + static QQuickMenuBarPrivate *get(QQuickMenuBar *menuBar) + { + return menuBar->d_func(); + } + + QQuickItem *beginCreateItem(); + void completeCreateItem(); + + QQuickItem *createItem(QQuickMenu *menu); + + void toggleCurrentMenu(bool visible, bool activate); + void activateItem(QQuickMenuBarItem *item); + void activateNextItem(); + void activatePreviousItem(); + + void onItemHovered(); + void onItemTriggered(); + void onMenuAboutToHide(); + + void updateContentSize(); + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override; + + static void contentData_append(QQmlListProperty *prop, QObject *obj); + + static void menus_append(QQmlListProperty *prop, QQuickMenu *obj); + static int menus_count(QQmlListProperty *prop); + static QQuickMenu *menus_at(QQmlListProperty *prop, int index); + static void menus_clear(QQmlListProperty *prop); + + bool popupMode; + bool triggering; + bool hasContentWidth; + bool hasContentHeight; + qreal contentWidth; + qreal contentHeight; + QQmlComponent *delegate; + QPointer currentItem; +}; + +QT_END_NAMESPACE + +#endif // QQUICKMENUBAR_P_P_H diff --git a/src/quicktemplates2/qquickmenubaritem.cpp b/src/quicktemplates2/qquickmenubaritem.cpp new file mode 100644 index 00000000..cbf490b8 --- /dev/null +++ b/src/quicktemplates2/qquickmenubaritem.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmenubaritem_p.h" +#include "qquickmenubaritem_p_p.h" +#include "qquickmenubar_p.h" +#include "qquickmenu_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MenuBarItem + \inherits AbstractButton + \instantiates QQuickMenuBarItem + \inqmlmodule QtQuick.Controls + \since 5.10 + \ingroup qtquickcontrols2-menus + \brief Presents a drop-down menu within a MenuBar. + + MenuBarItem presents a Menu within a MenuBar. The respective drop-down menu + is shown when a MenuBarItem is \l triggered via keyboard, mouse, or touch. + + \image qtquickcontrols2-menubar.png + + MenuBarItem is used as a default \l {MenuBar::}{delegate} type for MenuBar. + Notice that it is not necessary to declare MenuBarItem instances by hand when + using MenuBar. It is sufficient to declare Menu instances as children of the + MenuBar and the respective items are created automatically. + + \sa {Customizing MenuBar}, MenuBar, {Menu Controls} +*/ + +/*! + \qmlsignal void QtQuick.Controls::MenuBarItem::triggered() + + This signal is emitted when the menu bar item is triggered by the user. +*/ + +void QQuickMenuBarItemPrivate::setMenuBar(QQuickMenuBar *newMenuBar) +{ + Q_Q(QQuickMenuBarItem); + if (menuBar == newMenuBar) + return; + + menuBar = newMenuBar; + emit q->menuBarChanged(); +} + +QQuickMenuBarItem::QQuickMenuBarItem(QQuickItem *parent) + : QQuickAbstractButton(*(new QQuickMenuBarItemPrivate), parent) +{ + setFocusPolicy(Qt::NoFocus); + connect(this, &QQuickAbstractButton::clicked, this, &QQuickMenuBarItem::triggered); +} + +/*! + \qmlproperty Menu QtQuick.Controls::MenuBarItem::menuBar + \readonly + + This property holds the menu bar that contains this item, + or \c null if the item is not in a menu bar. +*/ +QQuickMenuBar *QQuickMenuBarItem::menuBar() const +{ + Q_D(const QQuickMenuBarItem); + return d->menuBar; +} + +/*! + \qmlproperty Menu QtQuick.Controls::MenuBarItem::menu + + This property holds the menu that this item presents in a + menu bar, or \c null if this item does not have a menu. +*/ +QQuickMenu *QQuickMenuBarItem::menu() const +{ + Q_D(const QQuickMenuBarItem); + return d->menu; +} + +void QQuickMenuBarItem::setMenu(QQuickMenu *menu) +{ + Q_D(QQuickMenuBarItem); + if (d->menu == menu) + return; + + if (d->menu) + disconnect(d->menu, &QQuickMenu::titleChanged, this, &QQuickAbstractButton::setText); + + if (menu) { + setText(menu->title()); + menu->setY(height()); + menu->setParentItem(this); + menu->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent | QQuickPopup::CloseOnReleaseOutsideParent); + connect(menu, &QQuickMenu::titleChanged, this, &QQuickAbstractButton::setText); + } + + d->menu = menu; + emit menuChanged(); +} + +/*! + \qmlproperty bool QtQuick.Controls::MenuBarItem::highlighted + + This property holds whether the menu bar item is highlighted by the user. + + A menu bar item can be highlighted by mouse hover or keyboard navigation. + + The default value is \c false. +*/ +bool QQuickMenuBarItem::isHighlighted() const +{ + Q_D(const QQuickMenuBarItem); + return d->highlighted; +} + +void QQuickMenuBarItem::setHighlighted(bool highlighted) +{ + Q_D(QQuickMenuBarItem); + if (highlighted == d->highlighted) + return; + + d->highlighted = highlighted; + emit highlightedChanged(); +} + +void QQuickMenuBarItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickMenuBarItem); + QQuickAbstractButton::geometryChanged(newGeometry, oldGeometry); + if (d->menu) + d->menu->setY(newGeometry.height()); +} + +QFont QQuickMenuBarItem::defaultFont() const +{ + return QQuickControlPrivate::themeFont(QPlatformTheme::MenuBarFont); +} + +QPalette QQuickMenuBarItem::defaultPalette() const +{ + return QQuickControlPrivate::themePalette(QPlatformTheme::MenuBarPalette); +} + +#if QT_CONFIG(accessibility) +QAccessible::Role QQuickMenuBarItem::accessibleRole() const +{ + return QAccessible::MenuBar; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickmenubaritem_p.h b/src/quicktemplates2/qquickmenubaritem_p.h new file mode 100644 index 00000000..86cd6a6a --- /dev/null +++ b/src/quicktemplates2/qquickmenubaritem_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENUBARITEM_P_H +#define QQUICKMENUBARITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QQuickMenu; +class QQuickMenuBar; +class QQuickMenuBarItemPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarItem : public QQuickAbstractButton +{ + Q_OBJECT + Q_PROPERTY(QQuickMenuBar *menuBar READ menuBar NOTIFY menuBarChanged FINAL) + Q_PROPERTY(QQuickMenu *menu READ menu WRITE setMenu NOTIFY menuChanged FINAL) + Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL) + +public: + explicit QQuickMenuBarItem(QQuickItem *parent = nullptr); + + QQuickMenuBar *menuBar() const; + + QQuickMenu *menu() const; + void setMenu(QQuickMenu *menu); + + bool isHighlighted() const; + void setHighlighted(bool highlighted); + +Q_SIGNALS: + void triggered(); + void menuBarChanged(); + void menuChanged(); + void highlightedChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + + QFont defaultFont() const override; + QPalette defaultPalette() const override; + +#if QT_CONFIG(accessibility) + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickMenuBarItem) + Q_DECLARE_PRIVATE(QQuickMenuBarItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMenuBarItem) + +#endif // QQUICKMENUBARITEM_P_H diff --git a/src/quicktemplates2/qquickmenubaritem_p_p.h b/src/quicktemplates2/qquickmenubaritem_p_p.h new file mode 100644 index 00000000..bd4c3a43 --- /dev/null +++ b/src/quicktemplates2/qquickmenubaritem_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMENUBARITEM_P_P_H +#define QQUICKMENUBARITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickMenu; +class QQuickMenuBar; + +class QQuickMenuBarItemPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickMenuBarItem) + +public: + QQuickMenuBarItemPrivate() + : highlighted(false), + menu(nullptr), + menuBar(nullptr) + { + } + + static QQuickMenuBarItemPrivate *get(QQuickMenuBarItem *item) + { + return item->d_func(); + } + + void setMenuBar(QQuickMenuBar *menuBar); + + bool highlighted; + QQuickMenu *menu; + QQuickMenuBar *menuBar; +}; + +QT_END_NAMESPACE + +#endif // QQUICKMENUBARITEM_P_P_H diff --git a/src/quicktemplates2/qquickshortcutcontext.cpp b/src/quicktemplates2/qquickshortcutcontext.cpp index 6553beb4..b44c723b 100644 --- a/src/quicktemplates2/qquickshortcutcontext.cpp +++ b/src/quicktemplates2/qquickshortcutcontext.cpp @@ -38,6 +38,8 @@ #include "qquickoverlay_p_p.h" #include "qquicktooltip_p.h" #include "qquickpopup_p.h" +#include "qquickmenu_p.h" +#include "qquickmenubaritem_p.h" #include @@ -53,8 +55,13 @@ static bool isBlockedByPopup(QQuickItem *item) for (QQuickPopup *popup : popups) { if (qobject_cast(popup)) continue; // ignore tooltips (QTBUG-60492) - if (popup->isModal() || popup->closePolicy() & QQuickPopup::CloseOnEscape) + if (popup->isModal() || popup->closePolicy() & QQuickPopup::CloseOnEscape) { + if (QQuickMenu *menu = qobject_cast(popup)) { + if (qobject_cast(menu->parentItem())) + continue; + } return item != popup->popupItem() && !popup->popupItem()->isAncestorOf(item); + } } return false; diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri index 47d8589d..a90b4f94 100644 --- a/src/quicktemplates2/quicktemplates2.pri +++ b/src/quicktemplates2/quicktemplates2.pri @@ -35,6 +35,10 @@ HEADERS += \ $$PWD/qquicklabel_p_p.h \ $$PWD/qquickmenu_p.h \ $$PWD/qquickmenu_p_p.h \ + $$PWD/qquickmenubar_p.h \ + $$PWD/qquickmenubar_p_p.h \ + $$PWD/qquickmenubaritem_p.h \ + $$PWD/qquickmenubaritem_p_p.h \ $$PWD/qquickmenuitem_p.h \ $$PWD/qquickmenuitem_p_p.h \ $$PWD/qquickmenuseparator_p.h \ @@ -109,6 +113,8 @@ SOURCES += \ $$PWD/qquickitemdelegate.cpp \ $$PWD/qquicklabel.cpp \ $$PWD/qquickmenu.cpp \ + $$PWD/qquickmenubar.cpp \ + $$PWD/qquickmenubaritem.cpp \ $$PWD/qquickmenuitem.cpp \ $$PWD/qquickmenuseparator.cpp \ $$PWD/qquickoverlay.cpp \ diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 9295fc21..935fa178 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -18,6 +18,7 @@ SUBDIRS += \ qquickiconlabel \ qquickmaterialstyle \ qquickmaterialstyleconf \ + qquickmenubar \ qquickstyle \ qquickstyleselector \ qquickuniversalstyle \ @@ -31,4 +32,4 @@ boot2qt: SUBDIRS -= applicationwindow calendar controls cursor \ drawer focus font menu platform palette popup \ qquickmaterialstyle qquickmaterialstyleconf \ qquickuniversalstyle qquickuniversalstyleconf \ - snippets + snippets qquickmenubar diff --git a/tests/auto/qquickmenubar/data/empty.qml b/tests/auto/qquickmenubar/data/empty.qml new file mode 100644 index 00000000..2ed46af0 --- /dev/null +++ b/tests/auto/qquickmenubar/data/empty.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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.9 +import QtQuick.Controls 2.3 + +MenuBar { } diff --git a/tests/auto/qquickmenubar/data/menubar.qml b/tests/auto/qquickmenubar/data/menubar.qml new file mode 100644 index 00000000..d0d6300a --- /dev/null +++ b/tests/auto/qquickmenubar/data/menubar.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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.9 +import QtQuick.Controls 2.3 + +ApplicationWindow { + width: 400 + height: 400 + visible: true + + header: MenuBar { + MenuBarItem { + menu: Menu { + title: "&File" + MenuItem { text: "&Open..." } + MenuItem { text: "&Save" } + MenuItem { text: "Save &As..." } + MenuSeparator { } + MenuItem { text: "&Quit" } + } + } + MenuBarItem { + menu: Menu { + title: "&Edit" + MenuItem { text: "&Cut" } + MenuItem { text: "&Copy" } + MenuItem { text: "&Paste" } + } + } + MenuBarItem { + menu: Menu { + title: "&View" + Menu { + title: "&Alignment" + Menu { + title: "&Horizontal" + MenuItem { text: "&Left" } + MenuItem { text: "&Center" } + MenuItem { text: "&Right" } + } + Menu { + title: "&Vertical" + MenuItem { text: "&Top" } + MenuItem { text: "&Center" } + MenuItem { text: "&Bottom" } + } + } + } + } + + MenuBarItem { + menu: Menu { + title: "&Help" + MenuItem { text: "&About" } + } + } + } +} diff --git a/tests/auto/qquickmenubar/qquickmenubar.pro b/tests/auto/qquickmenubar/qquickmenubar.pro new file mode 100644 index 00000000..b7d41f0f --- /dev/null +++ b/tests/auto/qquickmenubar/qquickmenubar.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qquickmenubar +SOURCES += tst_qquickmenubar.cpp + +macos:CONFIG -= app_bundle + +QT += core-private gui-private qml-private quick-private testlib quicktemplates2-private + +include (../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += \ + data/*.qml diff --git a/tests/auto/qquickmenubar/tst_qquickmenubar.cpp b/tests/auto/qquickmenubar/tst_qquickmenubar.cpp new file mode 100644 index 00000000..5e4d075e --- /dev/null +++ b/tests/auto/qquickmenubar/tst_qquickmenubar.cpp @@ -0,0 +1,562 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include "../shared/util.h" +#include "../shared/visualtestutil.h" +#include "../shared/qtest_quickcontrols.h" + +#include +#include +#include +#include +#include + +using namespace QQuickVisualTestUtil; + +class tst_qquickmenubar : public QQmlDataTest +{ + Q_OBJECT + +public: + +private slots: + void delegate(); + void mouse(); + void keys(); + void mnemonics(); + void addRemove(); +}; + +void tst_qquickmenubar::delegate() +{ + QQmlApplicationEngine engine(testFileUrl("empty.qml")); + QScopedPointer menuBar(qobject_cast(engine.rootObjects().value(0))); + QVERIFY(menuBar); + + QQmlComponent *delegate = menuBar->delegate(); + QVERIFY(delegate); + + QScopedPointer item(qobject_cast(delegate->create())); + QVERIFY(item); +} + +void tst_qquickmenubar::mouse() +{ + QQmlApplicationEngine engine(testFileUrl("menubar.qml")); + + QScopedPointer window(qobject_cast(engine.rootObjects().value(0))); + QVERIFY(window); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + moveMouseAway(window.data()); + + QQuickMenuBar *menuBar = window->property("header").value(); + QVERIFY(menuBar); + + QQuickMenu *fileMenuBarMenu = menuBar->menuAt(0); + QQuickMenu *editMenuBarMenu = menuBar->menuAt(1); + QQuickMenu *viewMenuBarMenu = menuBar->menuAt(2); + QQuickMenu *helpMenuBarMenu = menuBar->menuAt(3); + QVERIFY(fileMenuBarMenu && editMenuBarMenu && viewMenuBarMenu && helpMenuBarMenu); + + QQuickMenuBarItem *fileMenuBarItem = qobject_cast(fileMenuBarMenu->parentItem()); + QQuickMenuBarItem *editMenuBarItem = qobject_cast(editMenuBarMenu->parentItem()); + QQuickMenuBarItem *viewMenuBarItem = qobject_cast(viewMenuBarMenu->parentItem()); + QQuickMenuBarItem *helpMenuBarItem = qobject_cast(helpMenuBarMenu->parentItem()); + QVERIFY(fileMenuBarItem && editMenuBarItem && viewMenuBarItem && helpMenuBarItem); + + // highlight a menubar item + QTest::mouseMove(window.data(), fileMenuBarItem->mapToScene(QPointF(fileMenuBarItem->width() / 2, fileMenuBarItem->height() / 2)).toPoint()); + QVERIFY(fileMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarMenu->isVisible()); + + // highlight another menubar item + QTest::mouseMove(window.data(), editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint()); + QVERIFY(!fileMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarMenu->isVisible()); + QVERIFY(!editMenuBarMenu->isVisible()); + + // trigger a menubar item to open a menu + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + + // re-trigger a menubar item to hide the menu + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarItem->hasActiveFocus()); + QTRY_VERIFY(!editMenuBarMenu->isVisible()); + + // re-trigger a menubar item to show the menu again + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + + // highlight another menubar item to open another menu + QTest::mouseMove(window.data(), helpMenuBarItem->mapToScene(QPointF(helpMenuBarItem->width() / 2, helpMenuBarItem->height() / 2)).toPoint()); + QVERIFY(!fileMenuBarItem->isHighlighted()); + QVERIFY(!editMenuBarItem->isHighlighted()); + QVERIFY(!viewMenuBarItem->isHighlighted()); + QVERIFY(helpMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarMenu->isVisible()); + QVERIFY(!viewMenuBarMenu->isVisible()); + QVERIFY(helpMenuBarMenu->isVisible()); + QTRY_VERIFY(!editMenuBarMenu->isVisible()); + QTRY_VERIFY(helpMenuBarMenu->isOpened()); + + // trigger a menu item to close the menu + QQuickMenuItem *aboutMenuItem = qobject_cast(helpMenuBarMenu->itemAt(0)); + QVERIFY(aboutMenuItem); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, aboutMenuItem->mapToScene(QPointF(aboutMenuItem->width() / 2, aboutMenuItem->height() / 2)).toPoint()); + QVERIFY(!helpMenuBarItem->isHighlighted()); + QTRY_VERIFY(!helpMenuBarMenu->isVisible()); + + // highlight a menubar item + QTest::mouseMove(window.data(), editMenuBarItem->mapToScene(QPointF(editMenuBarItem->width() / 2, editMenuBarItem->height() / 2)).toPoint()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(!helpMenuBarItem->isHighlighted()); + QVERIFY(!editMenuBarMenu->isVisible()); + QVERIFY(!helpMenuBarMenu->isVisible()); + + // trigger a menubar item to open a menu + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, viewMenuBarItem->mapToScene(QPointF(viewMenuBarItem->width() / 2, viewMenuBarItem->height() / 2)).toPoint()); + QVERIFY(!editMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + QTRY_VERIFY(viewMenuBarMenu->isOpened()); + + // trigger a menu item to open a sub-menu + QQuickMenuItem *alignmentSubMenuItem = qobject_cast(viewMenuBarMenu->itemAt(0)); + QVERIFY(alignmentSubMenuItem); + QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu(); + QVERIFY(alignmentSubMenu); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint()); + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + QTRY_VERIFY(alignmentSubMenu->isOpened()); + + // trigger a menu item to open a sub-sub-menu + QQuickMenuItem *verticalSubMenuItem = qobject_cast(alignmentSubMenu->itemAt(1)); + QVERIFY(verticalSubMenuItem); + QQuickMenu *verticalSubMenu = verticalSubMenuItem->subMenu(); + QVERIFY(verticalSubMenu); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint()); + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + QVERIFY(verticalSubMenu->isVisible()); + QTRY_VERIFY(verticalSubMenu->isOpened()); + + // trigger a menu item to close the whole chain of menus + QQuickMenuItem *centerMenuItem = qobject_cast(verticalSubMenu->itemAt(1)); + QVERIFY(centerMenuItem); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, centerMenuItem->mapToScene(QPointF(centerMenuItem->width() / 2, centerMenuItem->height() / 2)).toPoint()); + QVERIFY(!viewMenuBarItem->isHighlighted()); + QTRY_VERIFY(!viewMenuBarMenu->isVisible()); + QTRY_VERIFY(!alignmentSubMenu->isVisible()); + QTRY_VERIFY(!verticalSubMenu->isVisible()); + + // re-highlight the same menubar item + QTest::mouseMove(window.data(), viewMenuBarItem->mapToScene(QPointF(viewMenuBarItem->width() / 2, viewMenuBarItem->height() / 2)).toPoint()); + QVERIFY(viewMenuBarItem->isHighlighted()); + + // re-open the chain of menus + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, viewMenuBarItem->mapToScene(QPointF(viewMenuBarItem->width() / 2, viewMenuBarItem->height() / 2)).toPoint()); + QTRY_VERIFY(viewMenuBarMenu->isOpened()); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, alignmentSubMenuItem->mapToScene(QPointF(alignmentSubMenuItem->width() / 2, alignmentSubMenuItem->height() / 2)).toPoint()); + QTRY_VERIFY(alignmentSubMenu->isOpened()); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, verticalSubMenuItem->mapToScene(QPointF(verticalSubMenuItem->width() / 2, verticalSubMenuItem->height() / 2)).toPoint()); + QTRY_VERIFY(verticalSubMenu->isOpened()); + + // click outside to close the whole chain of menus + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() - 1)); + QVERIFY(!viewMenuBarItem->isHighlighted()); + QTRY_VERIFY(!viewMenuBarMenu->isVisible()); + QTRY_VERIFY(!alignmentSubMenu->isVisible()); + QTRY_VERIFY(!verticalSubMenu->isVisible()); +} + +void tst_qquickmenubar::keys() +{ + QQmlApplicationEngine engine(testFileUrl("menubar.qml")); + + QScopedPointer window(qobject_cast(engine.rootObjects().value(0))); + QVERIFY(window); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + moveMouseAway(window.data()); + + QQuickMenuBar *menuBar = window->property("header").value(); + QVERIFY(menuBar); + + QQuickMenu *fileMenuBarMenu = menuBar->menuAt(0); + QQuickMenu *editMenuBarMenu = menuBar->menuAt(1); + QQuickMenu *viewMenuBarMenu = menuBar->menuAt(2); + QQuickMenu *helpMenuBarMenu = menuBar->menuAt(3); + QVERIFY(fileMenuBarMenu && editMenuBarMenu && viewMenuBarMenu && helpMenuBarMenu); + + QQuickMenuBarItem *fileMenuBarItem = qobject_cast(fileMenuBarMenu->parentItem()); + QQuickMenuBarItem *editMenuBarItem = qobject_cast(editMenuBarMenu->parentItem()); + QQuickMenuBarItem *viewMenuBarItem = qobject_cast(viewMenuBarMenu->parentItem()); + QQuickMenuBarItem *helpMenuBarItem = qobject_cast(helpMenuBarMenu->parentItem()); + QVERIFY(fileMenuBarItem && editMenuBarItem && viewMenuBarItem && helpMenuBarItem); + + // trigger a menubar item to open a menu + editMenuBarItem->forceActiveFocus(); + QTest::keyClick(window.data(), Qt::Key_Space); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + QVERIFY(editMenuBarMenu->hasActiveFocus()); + + // navigate down to the menu + QQuickMenuItem *cutMenuItem = qobject_cast(editMenuBarMenu->itemAt(0)); + QVERIFY(cutMenuItem); + QVERIFY(!cutMenuItem->isHighlighted()); + QVERIFY(!cutMenuItem->hasActiveFocus()); + QTest::keyClick(window.data(), Qt::Key_Down); + QVERIFY(cutMenuItem->isHighlighted()); + QVERIFY(cutMenuItem->hasActiveFocus()); + + // navigate up, back to the menubar + QTest::keyClick(window.data(), Qt::Key_Up); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarItem->hasActiveFocus()); + QTRY_VERIFY(!editMenuBarMenu->isVisible()); + QVERIFY(!cutMenuItem->isHighlighted()); + QVERIFY(!cutMenuItem->hasActiveFocus()); + + // navigate down to re-open the menu + QTest::keyClick(window.data(), Qt::Key_Down); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(!editMenuBarItem->hasActiveFocus()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + QVERIFY(editMenuBarMenu->hasActiveFocus()); + QVERIFY(cutMenuItem->isHighlighted()); + QVERIFY(cutMenuItem->hasActiveFocus()); + + // navigate left in popup mode (menu open) + QTest::keyClick(window.data(), Qt::Key_Left); + QVERIFY(fileMenuBarItem->isHighlighted()); + QVERIFY(!editMenuBarItem->isHighlighted()); + QVERIFY(fileMenuBarMenu->isVisible()); + QTRY_VERIFY(fileMenuBarMenu->isOpened()); + QTRY_VERIFY(!editMenuBarMenu->isVisible()); + + // navigate left in popup mode (wrap) + QTest::keyClick(window.data(), Qt::Key_Left); + QVERIFY(helpMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarItem->isHighlighted()); + QVERIFY(helpMenuBarMenu->isVisible()); + QTRY_VERIFY(helpMenuBarMenu->isOpened()); + QTRY_VERIFY(!fileMenuBarMenu->isVisible()); + + // navigate up to close the menu + QTest::keyClick(window.data(), Qt::Key_Up); + QVERIFY(helpMenuBarItem->isHighlighted()); + QTRY_VERIFY(!helpMenuBarMenu->isVisible()); + + // navigate right in non-popup mode (wrap) + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(fileMenuBarItem->isHighlighted()); + QVERIFY(!helpMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarMenu->isVisible()); + QVERIFY(!helpMenuBarMenu->isVisible()); + + // navigate right in non-popup mode (menu closed) + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(!fileMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(!fileMenuBarMenu->isVisible()); + QVERIFY(!editMenuBarMenu->isVisible()); + + // open a menu + viewMenuBarItem->forceActiveFocus(); + QTest::keyClick(window.data(), Qt::Key_Space); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + QTRY_VERIFY(viewMenuBarMenu->isOpened()); + QVERIFY(!viewMenuBarItem->hasActiveFocus()); + QVERIFY(viewMenuBarMenu->hasActiveFocus()); + + // open a sub-menu + QQuickMenuItem *alignmentSubMenuItem = qobject_cast(viewMenuBarMenu->itemAt(0)); + QVERIFY(alignmentSubMenuItem); + QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu(); + QVERIFY(alignmentSubMenu); + QTest::keyClick(window.data(), Qt::Key_Down); + QVERIFY(alignmentSubMenuItem->isHighlighted()); + QVERIFY(!alignmentSubMenu->isVisible()); + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(alignmentSubMenu->isVisible()); + QTRY_VERIFY(alignmentSubMenu->isOpened()); + + // open a sub-sub-menu + QQuickMenuItem *horizontalSubMenuItem = qobject_cast(alignmentSubMenu->itemAt(0)); + QVERIFY(horizontalSubMenuItem); + QVERIFY(horizontalSubMenuItem->isHighlighted()); + QQuickMenu *horizontalSubMenu = horizontalSubMenuItem->subMenu(); + QVERIFY(horizontalSubMenu); + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + QVERIFY(horizontalSubMenu->isVisible()); + QTRY_VERIFY(horizontalSubMenu->isOpened()); + + // navigate left to close a sub-menu + QTest::keyClick(window.data(), Qt::Key_Left); + QTRY_VERIFY(!horizontalSubMenu->isVisible()); + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + + // navigate right to re-open the sub-menu + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(horizontalSubMenuItem->isHighlighted()); + QVERIFY(horizontalSubMenu->isVisible()); + QTRY_VERIFY(horizontalSubMenu->isOpened()); + + // navigate right to the next menubar menu + QTest::keyClick(window.data(), Qt::Key_Right); + QVERIFY(!viewMenuBarItem->isHighlighted()); + QVERIFY(helpMenuBarItem->isHighlighted()); + QVERIFY(helpMenuBarMenu->isVisible()); + QTRY_VERIFY(!viewMenuBarMenu->isVisible()); + QTRY_VERIFY(!alignmentSubMenu->isVisible()); + QTRY_VERIFY(!horizontalSubMenu->isVisible()); + QTRY_VERIFY(helpMenuBarMenu->isOpened()); + + // navigate back + QTest::keyClick(window.data(), Qt::Key_Left); + QVERIFY(!helpMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + QTRY_VERIFY(!helpMenuBarMenu->isVisible()); + QTRY_VERIFY(viewMenuBarMenu->isOpened()); + + // re-open the chain of menus + QTest::keyClick(window.data(), Qt::Key_Down); + QVERIFY(alignmentSubMenuItem->isHighlighted()); + QTest::keyClick(window.data(), Qt::Key_Right); + QTRY_VERIFY(alignmentSubMenu->isOpened()); + QTest::keyClick(window.data(), Qt::Key_Right); + QTRY_VERIFY(horizontalSubMenu->isOpened()); + + // repeat escape to close the whole chain of menus one by one + QTest::keyClick(window.data(), Qt::Key_Escape); + QTRY_VERIFY(!horizontalSubMenu->isVisible()); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + + QTest::keyClick(window.data(), Qt::Key_Escape); + QTRY_VERIFY(!alignmentSubMenu->isVisible()); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + + QTest::keyClick(window.data(), Qt::Key_Escape); + QVERIFY(!viewMenuBarItem->isHighlighted()); + QTRY_VERIFY(!viewMenuBarMenu->isVisible()); +} + +void tst_qquickmenubar::mnemonics() +{ +#ifdef Q_OS_MACOS + QSKIP("Mnemonics are not used on macOS"); +#endif + + QQmlApplicationEngine engine(testFileUrl("menubar.qml")); + + QScopedPointer window(qobject_cast(engine.rootObjects().value(0))); + QVERIFY(window); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + moveMouseAway(window.data()); + + QQuickMenuBar *menuBar = window->property("header").value(); + QVERIFY(menuBar); + + QQuickMenu *fileMenuBarMenu = menuBar->menuAt(0); + QQuickMenu *editMenuBarMenu = menuBar->menuAt(1); + QQuickMenu *viewMenuBarMenu = menuBar->menuAt(2); + QQuickMenu *helpMenuBarMenu = menuBar->menuAt(3); + QVERIFY(fileMenuBarMenu && editMenuBarMenu && viewMenuBarMenu && helpMenuBarMenu); + + QQuickMenuBarItem *fileMenuBarItem = qobject_cast(fileMenuBarMenu->parentItem()); + QQuickMenuBarItem *editMenuBarItem = qobject_cast(editMenuBarMenu->parentItem()); + QQuickMenuBarItem *viewMenuBarItem = qobject_cast(viewMenuBarMenu->parentItem()); + QQuickMenuBarItem *helpMenuBarItem = qobject_cast(helpMenuBarMenu->parentItem()); + QVERIFY(fileMenuBarItem && editMenuBarItem && viewMenuBarItem && helpMenuBarItem); + + // trigger a menubar item to open a menu + QTest::keyClick(window.data(), Qt::Key_E, Qt::AltModifier); // "&Edit" + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(!editMenuBarItem->hasActiveFocus()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + QVERIFY(editMenuBarMenu->hasActiveFocus()); + + // re-trigger a menubar item to hide the menu + QTest::keyClick(window.data(), Qt::Key_E, Qt::AltModifier); // "&Edit" + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarItem->hasActiveFocus()); + QVERIFY(!editMenuBarMenu->hasActiveFocus()); + QTRY_VERIFY(!editMenuBarMenu->isVisible()); + + // re-trigger a menubar item to show the menu again + QTest::keyClick(window.data(), Qt::Key_E, Qt::AltModifier); // "&Edit" + QVERIFY(editMenuBarItem->isHighlighted()); + QVERIFY(editMenuBarMenu->isVisible()); + QTRY_VERIFY(editMenuBarMenu->isOpened()); + QVERIFY(editMenuBarMenu->hasActiveFocus()); + QVERIFY(!editMenuBarItem->hasActiveFocus()); + + // trigger another menubar item to open another menu + QTest::keyClick(window.data(), Qt::Key_H, Qt::AltModifier); // "&Help" + QVERIFY(!editMenuBarItem->isHighlighted()); + QVERIFY(helpMenuBarItem->isHighlighted()); + QVERIFY(!viewMenuBarMenu->isVisible()); + QVERIFY(helpMenuBarMenu->isVisible()); + QTRY_VERIFY(helpMenuBarMenu->isOpened()); + + // trigger a menu item to close the menu + QTest::keyClick(window.data(), Qt::Key_A, Qt::AltModifier); // "&About" + QVERIFY(!helpMenuBarItem->isHighlighted()); + QTRY_VERIFY(!helpMenuBarMenu->isVisible()); + + // trigger a menubar item to open a menu + QTest::keyClick(window.data(), Qt::Key_V, Qt::AltModifier); // "&View" + QVERIFY(!editMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarItem->isHighlighted()); + QVERIFY(viewMenuBarMenu->isVisible()); + QTRY_VERIFY(viewMenuBarMenu->isOpened()); + + // trigger a menu item to open a sub-menu + QQuickMenuItem *alignmentSubMenuItem = qobject_cast(viewMenuBarMenu->itemAt(0)); + QVERIFY(alignmentSubMenuItem); + QQuickMenu *alignmentSubMenu = alignmentSubMenuItem->subMenu(); + QVERIFY(alignmentSubMenu); + QTest::keyClick(window.data(), Qt::Key_A, Qt::AltModifier); // "&Alignment" + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + QTRY_VERIFY(alignmentSubMenu->isOpened()); + + // trigger a menu item to open a sub-sub-menu + QQuickMenuItem *verticalSubMenuItem = qobject_cast(alignmentSubMenu->itemAt(1)); + QVERIFY(verticalSubMenuItem); + QQuickMenu *verticalSubMenu = verticalSubMenuItem->subMenu(); + QVERIFY(verticalSubMenu); + QTest::keyClick(window.data(), Qt::Key_V, Qt::AltModifier); // "&Vertical" + QVERIFY(viewMenuBarMenu->isVisible()); + QVERIFY(alignmentSubMenu->isVisible()); + QVERIFY(verticalSubMenu->isVisible()); + QTRY_VERIFY(verticalSubMenu->isOpened()); + + // trigger a menu item to close the whole chain of menus + QTest::keyClick(window.data(), Qt::Key_C, Qt::AltModifier); // "&Center" + QVERIFY(!viewMenuBarItem->isHighlighted()); + QTRY_VERIFY(!viewMenuBarMenu->isVisible()); + QTRY_VERIFY(!alignmentSubMenu->isVisible()); + QTRY_VERIFY(!verticalSubMenu->isVisible()); +} + +void tst_qquickmenubar::addRemove() +{ + QQmlApplicationEngine engine(testFileUrl("empty.qml")); + + QScopedPointer menuBar(qobject_cast(engine.rootObjects().value(0))); + QVERIFY(menuBar); + + QQmlComponent component(&engine); + component.setData("import QtQuick.Controls 2.0; Menu { }", QUrl()); + + QPointer menu1(qobject_cast(component.create())); + QVERIFY(!menu1.isNull()); + menuBar->addMenu(menu1.data()); + QCOMPARE(menuBar->count(), 1); + QCOMPARE(menuBar->menuAt(0), menu1.data()); + + QPointer menuBarItem1(qobject_cast(menuBar->itemAt(0))); + QVERIFY(menuBarItem1); + QCOMPARE(menuBarItem1->menu(), menu1.data()); + QCOMPARE(menuBar->itemAt(0), menuBarItem1.data()); + + QScopedPointer menu2(qobject_cast(component.create())); + QVERIFY(!menu2.isNull()); + menuBar->insertMenu(0, menu2.data()); + QCOMPARE(menuBar->count(), 2); + QCOMPARE(menuBar->menuAt(0), menu2.data()); + QCOMPARE(menuBar->menuAt(1), menu1.data()); + + QPointer menuBarItem2(qobject_cast(menuBar->itemAt(0))); + QVERIFY(menuBarItem2); + QCOMPARE(menuBarItem2->menu(), menu2.data()); + QCOMPARE(menuBar->itemAt(0), menuBarItem2.data()); + QCOMPARE(menuBar->itemAt(1), menuBarItem1.data()); + + // takeMenu(int) does not destroy the menu, but does destroy the respective item in the menubar + QCOMPARE(menuBar->takeMenu(1), menu1.data()); + QCOMPARE(menuBar->count(), 1); + QVERIFY(!menuBar->menuAt(1)); + QVERIFY(!menuBar->itemAt(1)); + QCoreApplication::sendPostedEvents(menu1.data(), QEvent::DeferredDelete); + QVERIFY(!menu1.isNull()); + QCoreApplication::sendPostedEvents(menuBarItem1, QEvent::DeferredDelete); + QVERIFY(menuBarItem1.isNull()); + + // addMenu(Menu) re-creates the respective item in the menubar + menuBar->addMenu(menu1.data()); + QCOMPARE(menuBar->count(), 2); + menuBarItem1 = qobject_cast(menuBar->itemAt(1)); + QVERIFY(!menuBarItem1.isNull()); + + // removeMenu(Menu) destroys both the menu and the respective item in the menubar + menuBar->removeMenu(menu1.data()); + QCOMPARE(menuBar->count(), 1); + QVERIFY(!menuBar->itemAt(1)); + QCoreApplication::sendPostedEvents(menu1.data(), QEvent::DeferredDelete); + QVERIFY(menu1.isNull()); + QCoreApplication::sendPostedEvents(menuBarItem1, QEvent::DeferredDelete); + QVERIFY(menuBarItem1.isNull()); +} + +QTEST_QUICKCONTROLS_MAIN(tst_qquickmenubar) + +#include "tst_qquickmenubar.moc" -- cgit v1.2.3