diff options
author | Gabriel de Dietrich <gabriel.dedietrich@digia.com> | 2013-09-05 17:29:10 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-13 14:56:38 +0200 |
commit | 4eb3400843cafcedffa47fb8a272704bca4e9473 (patch) | |
tree | 6541694bca4cca47ea8fa8b4cc8db38fb3ead11a /src/controls | |
parent | 82081e737038262c82c2e408d412fc80a607bf9f (diff) | |
download | qtquickcontrols-4eb3400843cafcedffa47fb8a272704bca4e9473.tar.gz |
Menu: Enable mnemonic menu navigation
We also added a new mnemonic specific shortcut context matcher. This
prevents two menu items with the same mnemonic but within different
menus to be reported as ambiguous.
Task-number: QTBUG-33030
ChangeLog: Added mnemonic navigation for menus
Change-Id: I192c9aacba4d15851fe65bf9201251962fe976d5
Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
Diffstat (limited to 'src/controls')
-rw-r--r-- | src/controls/ApplicationWindow.qml | 2 | ||||
-rw-r--r-- | src/controls/Menu.qml | 51 | ||||
-rw-r--r-- | src/controls/MenuBar.qml | 62 | ||||
-rw-r--r-- | src/controls/qquickaction.cpp | 37 | ||||
-rw-r--r-- | src/controls/qquickmenu.cpp | 4 | ||||
-rw-r--r-- | src/controls/qquickmenu_p.h | 4 | ||||
-rw-r--r-- | src/controls/qquickmenuitem.cpp | 7 | ||||
-rw-r--r-- | src/controls/qquickmenuitem_p.h | 3 |
8 files changed, 151 insertions, 19 deletions
diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index 364b2b11..8967a8a1 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -133,6 +133,8 @@ Window { id: backgroundItem anchors.fill: parent + Keys.forwardTo: [menuBar.__contentItem] + Item { id: contentArea anchors.top: toolBarArea.bottom diff --git a/src/controls/Menu.qml b/src/controls/Menu.qml index 6c9f8fd2..525b0737 100644 --- a/src/controls/Menu.qml +++ b/src/controls/Menu.qml @@ -133,7 +133,7 @@ MenuPrivate { property Component style: Qt.createComponent(Settings.style + "/MenuStyle.qml", root) /*! \internal */ - property var __menuBar: null + property var __parentContentItem: __parentMenu.__contentItem /*! \internal */ property int __currentIndex: -1 /*! \internal */ @@ -144,6 +144,8 @@ MenuPrivate { sourceComponent: __menuComponent active: !root.__isNative && root.__popupVisible focus: true + Keys.forwardTo: item ? [item, root.__parentContentItem] : [] + property bool altPressed: root.__parentContentItem ? root.__parentContentItem.altPressed : false } /*! \internal */ @@ -174,14 +176,30 @@ MenuPrivate { } focus: true - Keys.forwardTo: __menuBar ? [__menuBar] : [] + property var mnemonicsMap: ({}) + + Keys.onPressed: { + var item = null + if (!(event.modifiers & Qt.AltModifier) + && (item = mnemonicsMap[event.text.toUpperCase()])) { + if (item.isSubmenu) { + root.__currentIndex = item.menuItemIndex + item.showSubMenu(true) + item.menuItem.__currentIndex = 0 + } else { + triggerAndDismiss(item) + } + event.accepted = true + } else { + event.accepted = false + } + } + Keys.onEscapePressed: root.__dismissMenu() Keys.onDownPressed: { - if (root.__currentIndex < 0) { - root.__currentIndex = 0 - return - } + if (root.__currentIndex < 0) + root.__currentIndex = -1 for (var i = root.__currentIndex + 1; i < root.items.length && !canBeHovered(i); i++) @@ -204,13 +222,13 @@ MenuPrivate { } Keys.onLeftPressed: { - if (root.__parentMenu) + if ((event.accepted = root.__parentMenu.hasOwnProperty("title"))) __closeMenu() } Keys.onRightPressed: { var item = itemsRepeater.itemAt(root.__currentIndex) - if (item && item.isSubmenu) { + if ((event.accepted = (item && item.isSubmenu))) { item.showSubMenu(true) item.menuItem.__currentIndex = 0 } @@ -220,8 +238,9 @@ MenuPrivate { Keys.onReturnPressed: menuFrameLoader.triggerAndDismiss() Keys.onEnterPressed: menuFrameLoader.triggerAndDismiss() - function triggerAndDismiss() { - var item = itemsRepeater.itemAt(root.__currentIndex) + function triggerAndDismiss(item) { + if (!item) + item = itemsRepeater.itemAt(root.__currentIndex) if (item && !item.isSeparator) { root.__dismissMenu() if (!item.isSubmenu) @@ -261,7 +280,7 @@ MenuPrivate { var itemUnderMouse = column.childAt(pos.x, pos.y) if (itemUnderMouse) { currentItem = itemUnderMouse - } else { + } else if (currentItem) { var itemItem = currentItem.item if (!itemItem.contains(itemItem.mapFromItem(column, pos))) currentItem = null @@ -294,6 +313,7 @@ MenuPrivate { readonly property bool isSubmenu: !!menuItem && menuItem.type === MenuItemType.Menu property bool selected: !isSeparator && root.__currentIndex === index property string text: isSubmenu ? menuItem.title : !isSeparator ? menuItem.text : "" + property bool showUnderlined: __contentItem.altPressed property int menuItemIndex: index @@ -328,7 +348,14 @@ MenuPrivate { } } - Component.onCompleted: menuItem.__visualItem = menuItemLoader + Component.onCompleted: { + menuItem.__visualItem = menuItemLoader + + var title = text + var ampersandPos = title.indexOf("&") + if (ampersandPos !== -1) + menuFrameLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItemLoader + } } } diff --git a/src/controls/MenuBar.qml b/src/controls/MenuBar.qml index eabb4d48..a126c7c6 100644 --- a/src/controls/MenuBar.qml +++ b/src/controls/MenuBar.qml @@ -82,6 +82,8 @@ MenuBarPrivate { sourceComponent: __menuBarComponent active: !root.__isNative focus: true + Keys.forwardTo: [item] + property bool altPressed: item ? item.altPressed : false } /*! \internal */ @@ -118,7 +120,51 @@ MenuBarPrivate { value: menuMouseArea.z - 1 } - focus: openedMenuIndex !== -1 + focus: true + + property bool altPressed: false + property bool altPressedAgain: false + property var mnemonicsMap: ({}) + + Keys.onPressed: { + var action = null + if (event.key === Qt.Key_Alt) { + if (!altPressed) + altPressed = true + else + altPressedAgain = true + } else if (altPressed && (action = mnemonicsMap[event.text.toUpperCase()])) { + preselectMenuItem = true + action.trigger() + event.accepted = true + } + } + + function dismissActiveFocus(event, reason) { + if (reason) { + altPressedAgain = false + altPressed = false + openedMenuIndex = -1 + root.__contentItem.parent.forceActiveFocus() + } else { + event.accepted = false + } + } + + Keys.onReleased: dismissActiveFocus(event, altPressedAgain && openedMenuIndex === -1) + Keys.onEscapePressed: dismissActiveFocus(event, openedMenuIndex === -1) + + function maybeOpenFirstMenu(event) { + if (altPressed && openedMenuIndex === -1) { + preselectMenuItem = true + openedMenuIndex = 0 + } else { + event.accepted = false + } + } + + Keys.onUpPressed: maybeOpenFirstMenu(event) + Keys.onDownPressed: maybeOpenFirstMenu(event) Keys.onLeftPressed: { if (openedMenuIndex > 0) { @@ -128,7 +174,7 @@ MenuBarPrivate { } Keys.onRightPressed: { - if (openedMenuIndex < root.menus.length - 1) { + if (openedMenuIndex !== -1 && openedMenuIndex < root.menus.length - 1) { preselectMenuItem = true openedMenuIndex++ } @@ -177,6 +223,7 @@ MenuBarPrivate { property var menuItem: modelData property bool selected: menuMouseArea.hoveredItem === menuItemLoader property bool sunken: menuItem.__popupVisible || menuBarLoader.openedMenuIndex === index + property bool showUnderlined: menuBarLoader.altPressed sourceComponent: menuBarLoader.menuItemStyle property int menuItemIndex: index @@ -203,9 +250,18 @@ MenuBarPrivate { } } + Connections { + target: menuItem.__action + onTriggered: menuBarLoader.openedMenuIndex = menuItemIndex + } + Component.onCompleted: { menuItem.__visualItem = menuItemLoader - menuItem.__menuBar = menuBarLoader + + var title = menuItem.title + var ampersandPos = title.indexOf("&") + if (ampersandPos !== -1) + menuBarLoader.mnemonicsMap[title[ampersandPos + 1].toUpperCase()] = menuItem.__action } } } diff --git a/src/controls/qquickaction.cpp b/src/controls/qquickaction.cpp index e240d111..6a031404 100644 --- a/src/controls/qquickaction.cpp +++ b/src/controls/qquickaction.cpp @@ -41,6 +41,7 @@ #include "qquickaction_p.h" #include "qquickexclusivegroup_p.h" +#include "qquickmenuitem_p.h" #include <QtGui/qguiapplication.h> #include <QtQuick/qquickitem.h> @@ -203,6 +204,8 @@ void QQuickAction::setText(const QString &text) emit textChanged(); } +namespace { + bool qShortcutContextMatcher(QObject *o, Qt::ShortcutContext context) { switch (context) { @@ -226,6 +229,38 @@ bool qShortcutContextMatcher(QObject *o, Qt::ShortcutContext context) return false; } +bool qMnemonicContextMatcher(QObject *o, Qt::ShortcutContext context) +{ + switch (context) { + case Qt::ApplicationShortcut: + return true; + case Qt::WindowShortcut: { + QObject *w = o; + while (w && !w->isWindowType()) { + w = w->parent(); + if (QQuickItem * item = qobject_cast<QQuickItem*>(w)) + w = item->window(); + else if (QQuickMenuBase *mb = qobject_cast<QQuickMenuBase *>(w)) { + QQuickItem *vi = mb->visualItem(); + if (vi && vi->isVisible()) + w = vi->window(); + else + break; // Non visible menu objects don't get mnemonic match + } + } + if (w && w == QGuiApplication::focusWindow()) + return true; + } + case Qt::WidgetShortcut: + case Qt::WidgetWithChildrenShortcut: + break; + } + + return false; +} + +} // namespace + QString QQuickAction::shortcut() const { return m_shortcut.toString(QKeySequence::NativeText); @@ -262,7 +297,7 @@ void QQuickAction::setMnemonicFromText(const QString &text) if (!m_mnemonic.isEmpty()) { Qt::ShortcutContext context = Qt::WindowShortcut; - QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_mnemonic, context, qShortcutContextMatcher); + QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_mnemonic, context, qMnemonicContextMatcher); } } diff --git a/src/controls/qquickmenu.cpp b/src/controls/qquickmenu.cpp index 2dbaf952..f65d7044 100644 --- a/src/controls/qquickmenu.cpp +++ b/src/controls/qquickmenu.cpp @@ -401,8 +401,10 @@ void QQuickMenu::__popup(qreal x, qreal y, int atItemIndex) void QQuickMenu::setMenuContentItem(QQuickItem *item) { - if (m_menuContentItem != item) + if (m_menuContentItem != item) { m_menuContentItem = item; + emit menuContentItemChanged(); + } } void QQuickMenu::setPopupVisible(bool v) diff --git a/src/controls/qquickmenu_p.h b/src/controls/qquickmenu_p.h index 54f7451b..f15e6caa 100644 --- a/src/controls/qquickmenu_p.h +++ b/src/controls/qquickmenu_p.h @@ -68,11 +68,12 @@ class QQuickMenu : public QQuickMenuText Q_PROPERTY(int __selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY __selectedIndexChanged) Q_PROPERTY(bool __popupVisible READ popupVisible NOTIFY popupVisibleChanged) - Q_PROPERTY(QQuickItem *__contentItem READ menuContentItem WRITE setMenuContentItem) + Q_PROPERTY(QQuickItem *__contentItem READ menuContentItem WRITE setMenuContentItem NOTIFY menuContentItemChanged) Q_PROPERTY(int __minimumWidth READ minimumWidth WRITE setMinimumWidth) Q_PROPERTY(QFont __font READ font WRITE setFont) Q_PROPERTY(qreal __xOffset READ xOffset WRITE setXOffset) Q_PROPERTY(qreal __yOffset READ yOffset WRITE setYOffset) + Q_PROPERTY(QQuickAction *__action READ action CONSTANT) public: Q_INVOKABLE void popup(); @@ -98,6 +99,7 @@ Q_SIGNALS: void __selectedIndexChanged(); void __menuClosed(); void popupVisibleChanged(); + void menuContentItemChanged(); public: QQuickMenu(QObject *parent = 0); diff --git a/src/controls/qquickmenuitem.cpp b/src/controls/qquickmenuitem.cpp index 2fda7e00..4d276ac1 100644 --- a/src/controls/qquickmenuitem.cpp +++ b/src/controls/qquickmenuitem.cpp @@ -84,6 +84,13 @@ void QQuickMenuBase::setVisible(bool v) } } +QObject *QQuickMenuBase::parentMenuOrMenuBar() const +{ + if (!m_parentMenu) + return parent(); + return m_parentMenu; +} + QQuickMenu *QQuickMenuBase::parentMenu() const { return m_parentMenu; diff --git a/src/controls/qquickmenuitem_p.h b/src/controls/qquickmenuitem_p.h index 02e7f5e3..791a0edb 100644 --- a/src/controls/qquickmenuitem_p.h +++ b/src/controls/qquickmenuitem_p.h @@ -78,7 +78,7 @@ class QQuickMenuBase: public QObject Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(QQuickMenuItemType::MenuItemType type READ type CONSTANT) - Q_PROPERTY(QQuickMenu *__parentMenu READ parentMenu CONSTANT) + Q_PROPERTY(QObject *__parentMenu READ parentMenuOrMenuBar CONSTANT) Q_PROPERTY(bool __isNative READ isNative CONSTANT) Q_PROPERTY(QQuickItem *__visualItem READ visualItem WRITE setVisualItem) @@ -93,6 +93,7 @@ public: virtual void setVisible(bool); QQuickMenu *parentMenu() const; + QObject *parentMenuOrMenuBar() const; virtual void setParentMenu(QQuickMenu *parentMenu); QQuickMenuItemContainer *container() const; |