aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates2/qquickmenubar.cpp
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2017-06-03 10:16:42 +0200
committerJ-P Nurmi <jpnurmi@qt.io>2017-07-26 14:11:39 +0000
commit66faa149db5d7b0a31db98b792c5202b8f33bf8a (patch)
tree4f757ec3ddfe6ab344269c43aa9ae44200df2a98 /src/quicktemplates2/qquickmenubar.cpp
parent9484e471324560ffd189ed7bd59ac182f0793b31 (diff)
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 <qt_ci_bot@qt-project.org> Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Diffstat (limited to 'src/quicktemplates2/qquickmenubar.cpp')
-rw-r--r--src/quicktemplates2/qquickmenubar.cpp656
1 files changed, 656 insertions, 0 deletions
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 <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlengine.h>
+
+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<QQuickItem *>(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<QQuickMenuBarItem *>(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<QQuickMenuBarItem *>(itemAt(++index)));
+}
+
+void QQuickMenuBarPrivate::activatePreviousItem()
+{
+ int index = currentItem ? contentModel->indexOf(currentItem, nullptr) : contentModel->count();
+ if (index <= 0)
+ index = contentModel->count();
+ activateItem(qobject_cast<QQuickMenuBarItem *>(itemAt(--index)));
+}
+
+void QQuickMenuBarPrivate::onItemHovered()
+{
+ Q_Q(QQuickMenuBar);
+ QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(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<QQuickMenuBarItem *>(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<QObject> *prop, QObject *obj)
+{
+ QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
+ if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
+ obj = QQuickMenuBarPrivate::get(menuBar)->createItem(menu);
+ QQuickContainerPrivate::contentData_append(prop, obj);
+}
+
+void QQuickMenuBarPrivate::menus_append(QQmlListProperty<QQuickMenu> *prop, QQuickMenu *obj)
+{
+ QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
+ menuBar->addMenu(obj);
+}
+
+int QQuickMenuBarPrivate::menus_count(QQmlListProperty<QQuickMenu> *prop)
+{
+ QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
+ return menuBar->count();
+}
+
+QQuickMenu *QQuickMenuBarPrivate::menus_at(QQmlListProperty<QQuickMenu> *prop, int index)
+{
+ QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
+ return menuBar->menuAt(index);
+}
+
+void QQuickMenuBarPrivate::menus_clear(QQmlListProperty<QQuickMenu> *prop)
+{
+ QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(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<QQuickMenuBarItem *>(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<QQuickMenuBarItem *>(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<QQuickMenuBarItem *>(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<Menu> 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<QQuickMenu> QQuickMenuBar::menus()
+{
+ return QQmlListProperty<QQuickMenu>(this, nullptr,
+ QQuickMenuBarPrivate::menus_append,
+ QQuickMenuBarPrivate::menus_count,
+ QQuickMenuBarPrivate::menus_at,
+ QQuickMenuBarPrivate::menus_clear);
+}
+
+QQmlListProperty<QObject> QQuickMenuBar::contentData()
+{
+ return QQmlListProperty<QObject>(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<QQuickMenuBarItem *>(item);
+}
+
+void QQuickMenuBar::itemAdded(int index, QQuickItem *item)
+{
+ Q_D(QQuickMenuBar);
+ QQuickContainer::itemAdded(index, item);
+ if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(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<QQuickMenuBarItem *>(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