Refactor QWasmEventTranslator for added readability

Change listing:
- change names of abbreviated structures/variables (e.g. KeyTbl
-> WebToQtKeyCodeMappings)
- add constants for commonly used magic strings ("Dead", StringTerminator)
- use common idioms for common tasks (find_if, std::optional)
- use binary search as facilitated by the sorted array instead of a full
search - this optimizes the code at no cost
- remove dead code (double translateEmscriptKey in
QWasmEventTranslator::getKey, remove sticky dead key support as it was
write-only, remove the dead setIsMac and g_usePlatformMacSpecifics,
remove the dead getWindowAt).
- cull the public interface on QWasmEventTranslator as some functions are
only used internally (translateEmscriptKey)
- simplify / shorten functions by short-circuiting
- modernize definitions (= default)
- auto-format the changes using clang-format

The file is now much easier to read and understand.

Change-Id: I5ea2bdafd9b9abc009feeb5516ddd87fb0ca212e
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Mikolaj Boc 2022-07-29 08:57:16 +02:00
parent 595526e446
commit 784555cc2f
3 changed files with 82 additions and 126 deletions

View File

@ -1145,7 +1145,6 @@ bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEve
break;
case EMSCRIPTEN_EVENT_KEYUP: // up
keyType = QEvent::KeyRelease;
m_eventTranslator->setStickyDeadKey(keyEvent);
break;
default:
break;

View File

@ -28,41 +28,47 @@ QT_BEGIN_NAMESPACE
using namespace emscripten;
typedef struct emkb2qt {
namespace {
constexpr std::string_view WebDeadKeyValue = "Dead";
struct Emkb2QtData
{
static constexpr char StringTerminator = '\0';
const char *em;
unsigned int qt;
constexpr bool operator <=(const emkb2qt &that) const noexcept
constexpr bool operator<=(const Emkb2QtData &that) const noexcept
{
return !(strcmp(that) > 0);
}
bool operator <(const emkb2qt &that) const noexcept
bool operator<(const Emkb2QtData &that) const noexcept { return ::strcmp(em, that.em) < 0; }
constexpr bool operator==(const Emkb2QtData &that) const noexcept { return strcmp(that) == 0; }
constexpr int strcmp(const Emkb2QtData &that, const int i = 0) const
{
return ::strcmp(em, that.em) < 0;
return em[i] == StringTerminator && that.em[i] == StringTerminator ? 0
: em[i] == StringTerminator ? -1
: that.em[i] == StringTerminator ? 1
: em[i] < that.em[i] ? -1
: em[i] > that.em[i] ? 1
: strcmp(that, i + 1);
}
constexpr int strcmp(const emkb2qt &that, const int i = 0) const
{
return em[i] == 0 && that.em[i] == 0 ? 0
: em[i] == 0 ? -1
: that.em[i] == 0 ? 1
: em[i] < that.em[i] ? -1
: em[i] > that.em[i] ? 1
: strcmp(that, i + 1);
}
} emkb2qt_t;
};
template<unsigned int Qt, char ... EmChar>
struct Emkb2Qt
{
static constexpr const char storage[sizeof ... (EmChar) + 1] = {EmChar..., '\0'};
using Type = emkb2qt_t;
using Type = Emkb2QtData;
static constexpr Type data() noexcept { return Type{storage, Qt}; }
};
template<unsigned int Qt, char ... EmChar> constexpr char Emkb2Qt<Qt, EmChar...>::storage[];
static constexpr const auto KeyTbl = qMakeArray(
static constexpr const auto WebToQtKeyCodeMappings = qMakeArray(
QSortedData<
Emkb2Qt< Qt::Key_Escape, 'E','s','c','a','p','e' >,
Emkb2Qt< Qt::Key_Tab, 'T','a','b' >,
@ -120,7 +126,7 @@ static constexpr const auto KeyTbl = qMakeArray(
>::Data{}
);
static constexpr const auto DeadKeyShiftTbl = qMakeArray(
static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray(
QSortedData<
// shifted
Emkb2Qt< Qt::Key_Agrave, '\xc3','\x80' >,
@ -155,42 +161,33 @@ static constexpr const auto DeadKeyShiftTbl = qMakeArray(
>::Data{}
);
QWasmEventTranslator::QWasmEventTranslator() : QObject()
std::optional<Qt::Key> findMappingByBisection(const char *toFind)
{
const Emkb2QtData searchKey{ toFind, 0 };
const auto it = std::lower_bound(WebToQtKeyCodeMappings.cbegin(), WebToQtKeyCodeMappings.cend(),
searchKey);
return it != WebToQtKeyCodeMappings.cend() && searchKey == *it ? static_cast<Qt::Key>(it->qt)
: std::optional<Qt::Key>();
}
QWasmEventTranslator::~QWasmEventTranslator()
bool isDeadKeyEvent(const EmscriptenKeyboardEvent *emKeyEvent)
{
return qstrncmp(emKeyEvent->key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0;
}
Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
{
Qt::Key qtKey = Qt::Key_unknown;
if (qstrncmp(emscriptKey->key, "Dead", 4) == 0 ) {
emkb2qt_t searchKey1{emscriptKey->code, 0};
for (auto it1 = KeyTbl.cbegin(); it1 != KeyTbl.end(); ++it1)
if (it1 != KeyTbl.end() && (qstrcmp(searchKey1.em, it1->em) == 0)) {
qtKey = static_cast<Qt::Key>(it1->qt);
}
}
if (qtKey == Qt::Key_unknown) {
emkb2qt_t searchKey{emscriptKey->key, 0};
// search key
auto it1 = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
if (it1 != KeyTbl.end() && !(searchKey < *it1)) {
qtKey = static_cast<Qt::Key>(it1->qt);
}
if (isDeadKeyEvent(emscriptKey)) {
if (auto mapping = findMappingByBisection(emscriptKey->code))
return *mapping;
}
if (auto mapping = findMappingByBisection(emscriptKey->key))
return *mapping;
if (qtKey == Qt::Key_unknown) {
// cast to unicode key
QString str = QString::fromUtf8(emscriptKey->key).toUpper();
QStringIterator i(str);
qtKey = static_cast<Qt::Key>(i.next(0));
}
return qtKey;
// cast to unicode key
QString str = QString::fromUtf8(emscriptKey->key).toUpper();
QStringIterator i(str);
return static_cast<Qt::Key>(i.next(0));
}
struct KeyMapping { Qt::Key from, to; };
@ -247,46 +244,45 @@ static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept
return find_impl(map, map + N, key);
}
Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey)
Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey)
{
Qt::Key wasmKey = Qt::Key_unknown;
if (deadKey == Qt::Key_QuoteLeft ) {
if (platform() == Platform::MacOS) { // ` macOS: Key_Dead_Grave
wasmKey = find(graveKeyTable, accentBaseKey);
} else {
wasmKey = find(diaeresisKeyTable, accentBaseKey);
}
return wasmKey;
}
switch (deadKey) {
// case Qt::Key_QuoteLeft:
case Qt::Key_QuoteLeft: {
// ` macOS: Key_Dead_Grave
return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey)
: find(diaeresisKeyTable, accentBaseKey);
}
case Qt::Key_O: // ´ Key_Dead_Grave
wasmKey = find(graveKeyTable, accentBaseKey);
break;
return find(graveKeyTable, accentBaseKey);
case Qt::Key_E: // ´ Key_Dead_Acute
wasmKey = find(acuteKeyTable, accentBaseKey);
break;
return find(acuteKeyTable, accentBaseKey);
case Qt::Key_AsciiTilde:
case Qt::Key_N:// Key_Dead_Tilde
wasmKey = find(tildeKeyTable, accentBaseKey);
break;
case Qt::Key_U:// ¨ Key_Dead_Diaeresis
wasmKey = find(diaeresisKeyTable, accentBaseKey);
break;
case Qt::Key_I:// macOS Key_Dead_Circumflex
case Qt::Key_6:// linux
case Qt::Key_Apostrophe:// linux
wasmKey = find(circumflexKeyTable, accentBaseKey);
break;
case Qt::Key_N: // Key_Dead_Tilde
return find(tildeKeyTable, accentBaseKey);
case Qt::Key_U: // ¨ Key_Dead_Diaeresis
return find(diaeresisKeyTable, accentBaseKey);
case Qt::Key_I: // macOS Key_Dead_Circumflex
case Qt::Key_6: // linux
case Qt::Key_Apostrophe: // linux
return find(circumflexKeyTable, accentBaseKey);
default:
break;
return Qt::Key_unknown;
};
return wasmKey;
}
template<class T>
std::optional<QString> findKeyTextByKeyId(const T &mappingArray, Qt::Key qtKey)
{
const auto it = std::find_if(mappingArray.cbegin(), mappingArray.cend(),
[qtKey](const Emkb2QtData &data) { return data.qt == qtKey; });
return it != mappingArray.cend() ? it->em : std::optional<QString>();
}
} // namespace
QWasmEventTranslator::QWasmEventTranslator() = default;
QWasmEventTranslator::~QWasmEventTranslator() = default;
QCursor QWasmEventTranslator::cursorForMode(QWasmCompositor::ResizeMode m)
{
switch (m) {
@ -310,45 +306,27 @@ QCursor QWasmEventTranslator::cursorForMode(QWasmCompositor::ResizeMode m)
QString QWasmEventTranslator::getKeyText(const EmscriptenKeyboardEvent *keyEvent, Qt::Key qtKey)
{
QString keyText;
if (m_emDeadKey != Qt::Key_unknown) {
Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey);
const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(qtKey, m_emDeadKey);
if (translatedKey != Qt::Key_unknown)
qtKey = translatedKey;
if (transformedKey != Qt::Key_unknown)
qtKey = transformedKey;
if (keyEvent->shiftKey == 0) {
for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) {
if (it != KeyTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
keyText = it->em;
m_emDeadKey = Qt::Key_unknown;
break;
}
}
} else {
for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) {
if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
keyText = it->em;
m_emDeadKey = Qt::Key_unknown;
break;
}
}
if (auto text = keyEvent->shiftKey
? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, qtKey)
: findKeyTextByKeyId(WebToQtKeyCodeMappings, qtKey)) {
m_emDeadKey = Qt::Key_unknown;
return *text;
}
}
if (keyText.isEmpty())
keyText = QString::fromUtf8(keyEvent->key);
return keyText;
return QString::fromUtf8(keyEvent->key);
}
Qt::Key QWasmEventTranslator::getKey(const EmscriptenKeyboardEvent *keyEvent)
{
Qt::Key qtKey = translateEmscriptKey(keyEvent);
if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) {
qtKey = translateEmscriptKey(keyEvent);
m_emStickyDeadKey = true;
if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft)
if (isDeadKeyEvent(keyEvent) || qtKey == Qt::Key_AltGr) {
if (keyEvent->shiftKey && qtKey == Qt::Key_QuoteLeft)
qtKey = Qt::Key_AsciiTilde;
m_emDeadKey = qtKey;
}
@ -356,13 +334,4 @@ Qt::Key QWasmEventTranslator::getKey(const EmscriptenKeyboardEvent *keyEvent)
return qtKey;
}
void QWasmEventTranslator::setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent)
{
Qt::Key qtKey = translateEmscriptKey(keyEvent);
if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) {
m_emStickyDeadKey = false;
}
}
QT_END_NAMESPACE

View File

@ -24,30 +24,18 @@ class QWasmEventTranslator : public QObject
Q_OBJECT
public:
explicit QWasmEventTranslator();
~QWasmEventTranslator();
static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey);
static QCursor cursorForMode(QWasmCompositor::ResizeMode mode);
QString getKeyText(const EmscriptenKeyboardEvent *keyEvent, Qt::Key key);
Qt::Key getKey(const EmscriptenKeyboardEvent *keyEvent);
void setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent);
void setIsMac(bool is_mac) {g_usePlatformMacSpecifics = is_mac;};
bool g_usePlatformMacSpecifics = false;
Q_SIGNALS:
void getWindowAt(const QPoint &point, QWindow **window);
private:
static Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey);
private:
static quint64 getTimestamp();
Qt::Key m_emDeadKey = Qt::Key_unknown;
bool m_emStickyDeadKey = false;
};