Align QKeySequence behavior between macOS and iOS

External keyboards for iOS/iPad devices have the same Macintosh based
keyboard as Macs have, with Command (or Cmd) ⌘; Option (or Alt) ⌥;
and Control (or Ctrl) ⌃ keys.

We were already declaring the QPlatformTheme::KeyboardScheme as
MacKeyboardScheme on iOS.

[ChangeLog][iOS] Keyboard shortcuts now follow the same scheme as on
macOS, with their native representation expressed via the ⌘, ⌥, and ⌃
modifiers. Use Qt::AA_MacDontSwapCtrlAndMeta to override this.

Pick-to: 6.6
Fixes: QTBUG-113165
Change-Id: Ia1856ee1718dab9f2f2512ffffc8b4d3cc5adecc
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Tor Arne Vestbø 2023-06-07 12:04:41 +02:00
parent 58ea69aaab
commit f341f75c8c
2 changed files with 46 additions and 46 deletions

View File

@ -120,15 +120,15 @@
set to true won't be used as a native menubar (e.g, the menubar at
the top of the main screen on \macos).
\value AA_MacDontSwapCtrlAndMeta Keyboard shortcuts on \macos are typically
\value AA_MacDontSwapCtrlAndMeta Keyboard shortcuts on Apple platforms are typically
based on the Command (or Cmd) keyboard modifier, represented by
the ⌘ symbol. For example, the 'Copy' action is Command+C (⌘+C).
To ease cross platform development Qt will by default remap Command
to the Qt::ControlModifier, to align with other platforms. This
allows creating keyboard shortcuts such as "Ctrl+J", which on
\macos will then map to Command+J, as expected by \macos users. The
actual Control (or Ctrl) modifier on \macos, represented by ⌃, is
mapped to Qt::MetaModifier.
actual Control (or Ctrl) modifier on Apple platforms, represented by ⌃,
is mapped to Qt::MetaModifier.
When this attribute is true Qt will not do the remapping, and pressing
the Command modifier will result in Qt::MetaModifier, while pressing

View File

@ -13,7 +13,7 @@
#endif
#include "qvariant.h"
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
#include <QtCore/private/qcore_mac_p.h>
#endif
@ -24,11 +24,11 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
#if defined(Q_OS_MACOS) || defined(Q_QDOC)
#if defined(Q_OS_APPLE) || defined(Q_QDOC)
Q_CONSTINIT static bool qt_sequence_no_mnemonics = true;
struct MacSpecialKey {
struct AppleSpecialKey {
int key;
ushort macSymbol;
ushort appleSymbol;
};
// Unicode code points for the glyphs associated with these keys
@ -38,7 +38,7 @@ static constexpr int kControlUnicode = 0x2303;
static constexpr int kOptionUnicode = 0x2325;
static constexpr int kCommandUnicode = 0x2318;
static constexpr MacSpecialKey entries[] = {
static constexpr AppleSpecialKey entries[] = {
{ Qt::Key_Escape, 0x238B },
{ Qt::Key_Tab, 0x21E5 },
{ Qt::Key_Backtab, 0x21E4 },
@ -63,45 +63,45 @@ static constexpr MacSpecialKey entries[] = {
{ Qt::Key_Eject, 0x23CF },
};
static constexpr bool operator<(const MacSpecialKey &lhs, const MacSpecialKey &rhs)
static constexpr bool operator<(const AppleSpecialKey &lhs, const AppleSpecialKey &rhs)
{
return lhs.key < rhs.key;
}
static constexpr bool operator<(const MacSpecialKey &lhs, int rhs)
static constexpr bool operator<(const AppleSpecialKey &lhs, int rhs)
{
return lhs.key < rhs;
}
static constexpr bool operator<(int lhs, const MacSpecialKey &rhs)
static constexpr bool operator<(int lhs, const AppleSpecialKey &rhs)
{
return lhs < rhs.key;
}
static_assert(q20::is_sorted(std::begin(entries), std::end(entries)));
QChar qt_macSymbolForQtKey(int key)
static QChar appleSymbolForQtKey(int key)
{
const auto i = std::lower_bound(std::begin(entries), std::end(entries), key);
if (i == std::end(entries) || key < *i)
return QChar();
ushort macSymbol = i->macSymbol;
ushort appleSymbol = i->appleSymbol;
if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)
&& (macSymbol == kControlUnicode || macSymbol == kCommandUnicode)) {
if (macSymbol == kControlUnicode)
macSymbol = kCommandUnicode;
&& (appleSymbol == kControlUnicode || appleSymbol == kCommandUnicode)) {
if (appleSymbol == kControlUnicode)
appleSymbol = kCommandUnicode;
else
macSymbol = kControlUnicode;
appleSymbol = kControlUnicode;
}
return QChar(macSymbol);
return QChar(appleSymbol);
}
static int qtkeyForMacSymbol(const QChar ch)
static int qtkeyForAppleSymbol(const QChar ch)
{
const ushort unicode = ch.unicode();
for (const MacSpecialKey &entry : entries) {
if (entry.macSymbol == unicode) {
for (const AppleSpecialKey &entry : entries) {
if (entry.appleSymbol == unicode) {
int key = entry.key;
if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)
&& (unicode == kControlUnicode || unicode == kCommandUnicode)) {
@ -191,7 +191,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
QKeySequence objects can be cast to a QString to obtain a human-readable
translated version of the sequence. Similarly, the toString() function
produces human-readable strings for use in menus. On \macos, the
produces human-readable strings for use in menus. On Apple platforms, the
appropriate symbols are used to describe keyboard shortcuts using special
keys on the Macintosh keyboard.
@ -199,12 +199,12 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
code point of the character; for example, 'A' gives the same key sequence
as Qt::Key_A.
\note On \macos, references to "Ctrl", Qt::CTRL, Qt::Key_Control
\note On Apple platforms, references to "Ctrl", Qt::CTRL, Qt::Key_Control
and Qt::ControlModifier correspond to the \uicontrol Command keys on the
Macintosh keyboard, and references to "Meta", Qt::META, Qt::Key_Meta and
Qt::MetaModifier correspond to the \uicontrol Control keys. Developers on
\macos can use the same shortcut descriptions across all platforms,
and their applications will automatically work as expected on \macos.
Qt::MetaModifier correspond to the \uicontrol Control keys. In effect,
developers can use the same shortcut descriptions across all platforms,
and their applications will automatically work as expected on Apple platforms.
\section1 Standard Shortcuts
@ -213,12 +213,12 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
setting up actions in a typical application. The table below shows
some common key sequences that are often used for these standard
shortcuts by applications on four widely-used platforms. Note
that on \macos, the \uicontrol Ctrl value corresponds to the \uicontrol
that on Apple platforms, the \uicontrol Ctrl value corresponds to the \uicontrol
Command keys on the Macintosh keyboard, and the \uicontrol Meta value
corresponds to the \uicontrol Control keys.
\table
\header \li StandardKey \li Windows \li \macos \li KDE Plasma \li GNOME
\header \li StandardKey \li Windows \li Apple platforms \li KDE Plasma \li GNOME
\row \li HelpContents \li F1 \li Ctrl+? \li F1 \li F1
\row \li WhatsThis \li Shift+F1 \li Shift+F1 \li Shift+F1 \li Shift+F1
\row \li Open \li Ctrl+O \li Ctrl+O \li Ctrl+O \li Ctrl+O
@ -374,7 +374,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni
\enum QKeySequence::SequenceFormat
\value NativeText The key sequence as a platform specific string.
This means that it will be shown translated and on the Mac it will
This means that it will be shown translated and on Apple platforms it will
resemble a key sequence from the menu bar. This enum is best used when you
want to display the string to the user.
@ -710,7 +710,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value InsertLineSeparator Insert a new line.
\value InsertParagraphSeparator Insert a new paragraph.
\value Italic Italic text.
\value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on the \macos.
\value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on Apple platforms.
\value MoveToEndOfDocument Move cursor to end of document.
\value MoveToEndOfLine Move cursor to end of line.
\value MoveToNextChar Move cursor to next character.
@ -721,7 +721,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value MoveToPreviousLine Move cursor to previous line.
\value MoveToPreviousPage Move cursor to previous page.
\value MoveToPreviousWord Move cursor to previous word.
\value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on \macos.
\value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on Apple platforms.
\value MoveToStartOfDocument Move cursor to start of document.
\value MoveToStartOfLine Move cursor to start of line.
\value New Create new document.
@ -739,7 +739,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value Save Save document.
\value SelectAll Select all text.
\value Deselect Deselect text. Since 5.1
\value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on \macos.
\value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on Apple platforms.
\value SelectEndOfDocument Extend selection to end of document.
\value SelectEndOfLine Extend selection to end of line.
\value SelectNextChar Extend selection to next character.
@ -750,7 +750,7 @@ static constexpr int numKeyNames = sizeof keyname / sizeof *keyname;
\value SelectPreviousLine Extend selection to previous line.
\value SelectPreviousPage Extend selection to previous page.
\value SelectPreviousWord Extend selection to previous word.
\value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on \macos.
\value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on Apple platforms.
\value SelectStartOfDocument Extend selection to start of document.
\value SelectStartOfLine Extend selection to start of line.
\value Underline Underline text.
@ -1056,7 +1056,7 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
if (nativeText) {
gmodifs = globalModifs();
if (gmodifs->isEmpty()) {
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
if (dontSwap)
*gmodifs << QModifKeyName(Qt::META, QChar(kCommandUnicode));
@ -1098,7 +1098,7 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
modifs += *gmodifs; // Test non-translated ones last
QString sl = accel;
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
for (int i = 0; i < modifs.size(); ++i) {
const QModifKeyName &mkf = modifs.at(i);
if (sl.contains(mkf.name)) {
@ -1153,8 +1153,8 @@ int QKeySequencePrivate::decodeString(QString accel, QKeySequence::SequenceForma
int fnum = 0;
if (accelRef.size() == 1) {
#if defined(Q_OS_MACOS)
int qtKey = qtkeyForMacSymbol(accelRef.at(0));
#if defined(Q_OS_APPLE)
int qtKey = qtkeyForAppleSymbol(accelRef.at(0));
if (qtKey != -1) {
ret |= qtKey;
} else
@ -1225,11 +1225,11 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat
if (key == -1 || key == Qt::Key_unknown)
return s;
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
if (nativeText) {
// On OS X the order (by default) is Meta, Alt, Shift, Control.
// On Apple platforms the order (by default) is Meta, Alt, Shift, Control.
// If the AA_MacDontSwapCtrlAndMeta is enabled, then the order
// is Ctrl, Alt, Shift, Meta. The macSymbolForQtKey does this swap
// is Ctrl, Alt, Shift, Meta. The appleSymbolForQtKey helper does this swap
// for us, which means that we have to adjust our order here.
// The upshot is a lot more infrastructure to keep the number of
// if tests down and the code relatively clean.
@ -1249,7 +1249,7 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat
for (int i = 0; modifierOrder[i] != 0; ++i) {
if (key & modifierOrder[i])
s += qt_macSymbolForQtKey(qtkeyOrder[i]);
s += appleSymbolForQtKey(qtkeyOrder[i]);
}
} else
#endif
@ -1269,7 +1269,7 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat
QString p = keyName(key, format);
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
if (nativeText)
s += p;
else
@ -1304,9 +1304,9 @@ QString QKeySequencePrivate::keyName(int key, QKeySequence::SequenceFormat forma
: QString::fromLatin1("F%1").arg(key - Qt::Key_F1 + 1);
} else if (key) {
int i=0;
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
if (nativeText) {
QChar ch = qt_macSymbolForQtKey(key);
QChar ch = appleSymbolForQtKey(key);
if (!ch.isNull())
p = ch;
else
@ -1314,7 +1314,7 @@ QString QKeySequencePrivate::keyName(int key, QKeySequence::SequenceFormat forma
} else
#endif
{
#if defined(Q_OS_MACOS)
#if defined(Q_OS_APPLE)
NonSymbol:
#endif
while (i < numKeyNames) {
@ -1504,7 +1504,7 @@ bool QKeySequence::isDetached() const
If the key sequence has no keys, an empty string is returned.
On \macos, the string returned resembles the sequence that is
On Apple platforms, the string returned resembles the sequence that is
shown in the menu bar if \a format is
QKeySequence::NativeText; otherwise, the string uses the
"portable" format, suitable for writing to a file.