diff options
Diffstat (limited to 'src/plugins/example/hwr')
-rw-r--r-- | src/plugins/example/hwr/CMakeLists.txt | 284 | ||||
-rw-r--r-- | src/plugins/example/hwr/examplehwrinputmethod.cpp | 632 | ||||
-rw-r--r-- | src/plugins/example/hwr/examplehwrinputmethod_p.h | 65 |
3 files changed, 981 insertions, 0 deletions
diff --git a/src/plugins/example/hwr/CMakeLists.txt b/src/plugins/example/hwr/CMakeLists.txt new file mode 100644 index 00000000..61f64101 --- /dev/null +++ b/src/plugins/example/hwr/CMakeLists.txt @@ -0,0 +1,284 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_qml_module(qtvkbexamplehwrplugin + URI "QtQuick.VirtualKeyboard.Plugins.Example.HWR" + VERSION "${PROJECT_VERSION}" + PAST_MAJOR_VERSIONS 2 + PLUGIN_TARGET qtvkbexamplehwrplugin + NO_PLUGIN_OPTIONAL + DEPENDENCIES + QtQuick.VirtualKeyboard/auto + SOURCES + examplehwrinputmethod.cpp examplehwrinputmethod_p.h + DEFINES + QT_ASCII_CAST_WARNINGS + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_FROM_BYTEARRAY + QT_NO_CAST_TO_ASCII + LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::VirtualKeyboardPrivate + NO_GENERATE_CPP_EXPORTS +) + +set(qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/fallback/handwriting.qml" +) + +if (QT_FEATURE_vkb_lang_en_GB) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/en_GB/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_en_US) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/en_US/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_ar_AR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ar_AR/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_bg_BG) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/bg_BG/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_cs_CZ) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/cs_CZ/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_da_DK) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/da_DK/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_de_DE) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/de_DE/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_el_GR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/el_GR/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_es_ES) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/es_ES/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_es_MX) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/es_MX/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_et_EE) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/et_EE/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_fa_FA) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/fa_FA/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_fi_FI) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/fi_FI/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_fr_FR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/fr_FR/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_fr_CA) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/fr_CA/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_he_IL) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/he_IL/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_hr_HR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/hr_HR/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_hu_HU) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/hu_HU/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_id_ID) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/id_ID/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_it_IT) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/it_IT/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_ms_MY) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ms_MY/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_nb_NO) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/nb_NO/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_nl_NL) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/nl_NL/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_pl_PL) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/pl_PL/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_pt_BR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/pt_BR/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_pt_PT) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/pt_PT/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_ro_RO) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ro_RO/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_ru_RU) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ru_RU/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_sk_SK) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/sk_SK/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_sl_SI) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/sl_SI/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_sq_AL) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/sq_AL/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_sr_SP) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/sr_SP/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_sv_SE) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/sv_SE/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_th_TH) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/th_TH/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_tr_TR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/tr_TR/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_uk_UA) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/uk_UA/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_vi_VN) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/vi_VN/handwriting.fallback" + ) +endif() + +if (QT_FEATURE_vkb_lang_ja_JP) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ja_JP/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_ko_KR) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/ko_KR/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_zh_CN) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/zh_CN/handwriting.qml" + ) +endif() + +if (QT_FEATURE_vkb_lang_zh_TW) + list(APPEND qt_virtualkeyboard_example_hwr_layouts_resource_files + "${VKB_LAYOUTS_BASE}/zh_TW/handwriting.qml" + ) +endif() + +qt_internal_add_resource(qtvkbexamplehwrplugin "qt_virtualkeyboard_example_hwr_layouts" + PREFIX + "${VKB_LAYOUTS_PREFIX}" + BASE + "${VKB_LAYOUTS_BASE}" + FILES + ${qt_virtualkeyboard_example_hwr_layouts_resource_files} +) diff --git a/src/plugins/example/hwr/examplehwrinputmethod.cpp b/src/plugins/example/hwr/examplehwrinputmethod.cpp new file mode 100644 index 00000000..58d1d2c8 --- /dev/null +++ b/src/plugins/example/hwr/examplehwrinputmethod.cpp @@ -0,0 +1,632 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "examplehwrinputmethod_p.h" +#include <QtVirtualKeyboard/qvirtualkeyboardinputengine.h> +#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h> +#include <QtVirtualKeyboard/qvirtualkeyboardtrace.h> +#include <QtVirtualKeyboard/private/handwritinggesturerecognizer_p.h> +#include <QtVirtualKeyboard/private/settings_p.h> +#include <QtVirtualKeyboard/private/qvirtualkeyboardabstractinputmethod_p.h> +#include <QCryptographicHash> +#include <QRandomGenerator> +#include <QLoggingCategory> +#include <QLocale> + +QT_BEGIN_NAMESPACE +namespace QtVirtualKeyboard { + +Q_LOGGING_CATEGORY(lcExampleHwr, "qt.virtualkeyboard.example.hwr") + +class ExampleHwrInputMethodPrivate : public QVirtualKeyboardAbstractInputMethodPrivate +{ +public: + Q_DECLARE_PUBLIC(ExampleHwrInputMethod) + + ExampleHwrInputMethodPrivate(ExampleHwrInputMethod *q_ptr) : + QVirtualKeyboardAbstractInputMethodPrivate(), + q_ptr(q_ptr) + { + } + + bool setInputMode(const QLocale &locale, QVirtualKeyboardInputEngine::InputMode inputMode) + { + Q_UNUSED(locale); + finishRecognition(); + this->inputMode = inputMode; + return true; + } + + QByteArray getContext(QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, + const QVariantMap &traceCaptureDeviceInfo, + const QVariantMap &traceScreenInfo) const + { + QCryptographicHash hash(QCryptographicHash::Md5); + + hash.addData(QByteArrayView(reinterpret_cast<const char *>(&patternRecognitionMode), sizeof(patternRecognitionMode))); + + QByteArray mapData; + QDataStream ds(&mapData, QIODevice::WriteOnly); + ds << traceCaptureDeviceInfo; + ds << traceScreenInfo; + hash.addData(mapData); + + return hash.result(); + } + + void setContext(QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, + const QVariantMap &traceCaptureDeviceInfo, + const QVariantMap &traceScreenInfo, + const QByteArray &context) + { + Q_UNUSED(patternRecognitionMode); + Q_UNUSED(traceScreenInfo); + if (context == currentContext) + return; + currentContext = context; + + qCDebug(lcExampleHwr) << "setContext:" << QLatin1String((context.toHex())); + + // Finish recognition, but preserve current input + const int dpi = traceCaptureDeviceInfo.value(QLatin1String("dpi"), 96).toInt(); + static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25; + gestureWidthThreshold = qRound(INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi); + + gestureRecognizer.setDpi(dpi); + } + + QVirtualKeyboardTrace *traceBegin( + int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, + const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) + { + // The result id follows the trace id so that the (previous) + // results completed during the handwriting can be rejected. + resultId = traceId; + + QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); + if (context != currentContext) { + setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo, context); + } + + Q_Q(ExampleHwrInputMethod); + QVirtualKeyboardTrace *trace = new QVirtualKeyboardTrace(q); + traceList.append(trace); + + return trace; + } + + void traceEnd(QVirtualKeyboardTrace *trace) + { + if (trace->isCanceled()) { + traceList.removeOne(trace); + delete trace; + } else if (handleGesture()) { + finishRecognition(); + return; + } + if (!traceList.isEmpty()) { + if (countActiveTraces() == 0) + restartRecognition(); + } + } + + int countActiveTraces() const + { + int count = 0; + for (QVirtualKeyboardTrace *trace : std::as_const(traceList)) { + if (!trace->isFinal()) + count++; + } + return count; + } + + void clearTraces() + { + qDeleteAll(traceList); + traceList.clear(); + } + + bool applyGesture(const QChar &gesture) + { + Q_Q(ExampleHwrInputMethod); + QVirtualKeyboardInputContext *ic = q->inputContext(); + switch (gesture.unicode()) { + case '\b': + return ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier); + case '\r': + return ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QLatin1String("\n"), Qt::NoModifier); + case ' ': + return ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier); + default: + return false; + } + } + + bool handleGesture() + { + if (countActiveTraces() > 0) + return false; + + QVariantMap gesture(gestureRecognizer.recognize(traceList.mid(traceList.size() - 1, 1))); + if (gesture.isEmpty()) + return false; + + qCDebug(lcExampleHwr) << "handleGesture:" << gesture; + + if (gesture[QLatin1String("type")].toString() == QLatin1String("swipe")) { + + static const int SWIPE_ANGLE_THRESHOLD = 15; // degrees +- + + qreal swipeLength = gesture[QLatin1String("length")].toReal(); + if (swipeLength >= gestureWidthThreshold) { + + Q_Q(ExampleHwrInputMethod); + QVirtualKeyboardInputContext *ic = q->inputContext(); + if (!ic) + return false; + + qreal swipeAngle = gesture[QLatin1String("angle_degrees")].toReal(); + int swipeTouchCount = gesture[QLatin1String("touch_count")].toInt(); + + // Swipe left + if (swipeAngle <= 180 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 180 - SWIPE_ANGLE_THRESHOLD) { + if (swipeTouchCount == 1) { + // Single swipe: backspace + ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier); + return true; + } + return false; + } + + // Swipe right + if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) { + if (swipeTouchCount == 1) { + // Single swipe: space + ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier); + return true; + } + return false; + } + + // Swipe up + if (swipeAngle <= 270 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 270 - SWIPE_ANGLE_THRESHOLD) { + if (swipeTouchCount == 1) { + // Single swipe: toggle input mode + select(); + if (!(ic->inputMethodHints() & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly))) { + QList<int> inputModes = ic->inputEngine()->inputModes(); + // Filter out duplicate numeric mode (in favor of Numeric) + int indexOfNumericInputMode = inputModes.indexOf(static_cast<int>(QVirtualKeyboardInputEngine::InputMode::Numeric)); + int indexOfDialableInputMode = inputModes.indexOf(static_cast<int>(QVirtualKeyboardInputEngine::InputMode::Dialable)); + if (indexOfNumericInputMode != -1 && indexOfDialableInputMode != -1) + inputModes.removeAt(inputMode != QVirtualKeyboardInputEngine::InputMode::Dialable ? + indexOfDialableInputMode : + indexOfNumericInputMode); + if (inputModes.size() > 1) { + int inputModeIndex = inputModes.indexOf(static_cast<int>(inputMode)) + 1; + if (inputModeIndex >= inputModes.size()) + inputModeIndex = 0; + ic->inputEngine()->setInputMode(static_cast<QVirtualKeyboardInputEngine::InputMode>(inputModes.at(inputModeIndex))); + } + } + return true; + } + } + } + } + + return false; + } + + bool isValidInputChar(const QChar &c) const + { + if (c.isLetterOrNumber()) + return true; + if (isJoiner(c)) + return true; + return false; + } + + bool isJoiner(const QChar &c) const + { + if (c.isPunct() || c.isSymbol()) { + Q_Q(const ExampleHwrInputMethod); + QVirtualKeyboardInputContext *ic = q->inputContext(); + if (ic) { + Qt::InputMethodHints inputMethodHints = ic->inputMethodHints(); + if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly) || inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) + return QString(QStringLiteral(":/?#[]@!$&'()*+,;=-_.%")).contains(c); + } + ushort unicode = c.unicode(); + if (unicode == Qt::Key_Apostrophe || unicode == Qt::Key_Minus) + return true; + } + return false; + } + + void restartRecognition() + { + qCDebug(lcExampleHwr) << "restartRecognition"; + + resetResultTimer(Settings::instance()->hwrTimeoutForAlphabetic()); + } + + bool finishRecognition(bool emitSelectionListChanged = true) + { + qCDebug(lcExampleHwr) << "finishRecognition"; + bool result = !traceList.isEmpty(); + + stopResultTimer(); + clearTraces(); + + if (!wordCandidates.isEmpty()) { + wordCandidates.clear(); + activeWordIndex = -1; + if (emitSelectionListChanged) { + Q_Q(ExampleHwrInputMethod); + emit q->selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); + emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, activeWordIndex); + } + result = true; + } + + return result; + } + + void select(int index = -1) + { + Q_Q(ExampleHwrInputMethod); + QVirtualKeyboardInputContext *ic = q->inputContext(); + if (!ic) + return; + + if (!wordCandidates.isEmpty()) + ic->commit(wordCandidates.at(index != -1 ? index : 0)); + + finishRecognition(); + } + + void processResult() + { + qCDebug(lcExampleHwr) << "processResult"; + + Q_Q(ExampleHwrInputMethod); + QVirtualKeyboardInputContext *ic = q->inputContext(); + if (!ic) + return; + + QStringList newWordCandidates; + QString word = !wordCandidates.isEmpty() ? wordCandidates.at(0) : QString(); + switch (inputMode) { + case QVirtualKeyboardInputEngine::InputMode::Latin: + appendRandomChar(word); + break; + case QVirtualKeyboardInputEngine::InputMode::Numeric: + case QVirtualKeyboardInputEngine::InputMode::Dialable: + appendRandomDigit(word); + break; + default: + break; + } + newWordCandidates.append(word); + activeWordIndex = 0; + wordCandidates = newWordCandidates; + qCDebug(lcExampleHwr) << "wordCandidates" << wordCandidates; + ic->setPreeditText(word); + + emit q->selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); + emit q->selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, activeWordIndex); + } + + static void appendRandomChar(QString& word) + { + word.append(QChar('a' + QRandomGenerator::global()->bounded(26))); + } + + static void appendRandomDigit(QString& word) + { + word.append(QChar('0' + QRandomGenerator::global()->bounded(10))); + } + + void resetResultTimer(int interval = 500) + { + qCDebug(lcExampleHwr) << "resetResultTimer:" << interval; + Q_Q(ExampleHwrInputMethod); + stopResultTimer(); + resultTimer = q->startTimer(interval); + } + + void stopResultTimer() + { + if (resultTimer) { + qCDebug(lcExampleHwr) << "stopResultTimer"; + Q_Q(ExampleHwrInputMethod); + q->killTimer(resultTimer); + resultTimer = 0; + } + } + + ExampleHwrInputMethod *q_ptr = nullptr; + QVirtualKeyboardInputEngine::InputMode inputMode = QVirtualKeyboardInputEngine::InputMode::Latin; + QByteArray currentContext; + int gestureWidthThreshold = 0; + int resultId = 0; + int lastResultId = 0; + int resultTimer = 0; + QList<QVirtualKeyboardTrace *> traceList; + HandwritingGestureRecognizer gestureRecognizer; + QStringList wordCandidates; + int activeWordIndex = -1; +}; + +/*! + \class QtVirtualKeyboard::ExampleHwrInputMethod + \internal +*/ + +ExampleHwrInputMethod::ExampleHwrInputMethod(QObject *parent) : + QVirtualKeyboardAbstractInputMethod(*new ExampleHwrInputMethodPrivate(this), parent) +{ +} + +ExampleHwrInputMethod::~ExampleHwrInputMethod() +{ +} + +QList<QVirtualKeyboardInputEngine::InputMode> ExampleHwrInputMethod::inputModes(const QString &locale) +{ + Q_UNUSED(locale); + QList<QVirtualKeyboardInputEngine::InputMode> availableInputModes = { + QVirtualKeyboardInputEngine::InputMode::Latin, + QVirtualKeyboardInputEngine::InputMode::Numeric, + }; + return availableInputModes; +} + +bool ExampleHwrInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode) +{ + Q_D(ExampleHwrInputMethod); + d->select(); + return d->setInputMode(QLocale(locale), inputMode); +} + +bool ExampleHwrInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase) +{ + Q_UNUSED(textCase); + return true; +} + +bool ExampleHwrInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) +{ + Q_UNUSED(modifiers); + Q_D(ExampleHwrInputMethod); + switch (key) { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Tab: + case Qt::Key_Space: + d->select(); + update(); + break; + + case Qt::Key_Backspace: + { + QVirtualKeyboardInputContext *ic = inputContext(); + QString preeditText = ic->preeditText(); + if (preeditText.length() > 1) { + preeditText.chop(1); + ic->setPreeditText(preeditText); + emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); + emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->activeWordIndex); + return true; + } else { + bool result = !preeditText.isEmpty(); + if (result) + ic->clear(); + d->finishRecognition(); + return result; + } + } + + default: + if (text.length() > 0) { + QVirtualKeyboardInputContext *ic = inputContext(); + QString preeditText = ic->preeditText(); + QChar c = text.at(0); + bool addToWord = d->isValidInputChar(c) && (!preeditText.isEmpty() || !d->isJoiner(c)); + if (addToWord) { + preeditText.append(text); + ic->setPreeditText(preeditText); + d->finishRecognition(false); + d->wordCandidates.append(preeditText); + d->activeWordIndex = 0; + emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); + emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->activeWordIndex); + return true; + } else { + ic->commit(); + d->finishRecognition(); + } + break; + } + } + return false; +} + +void ExampleHwrInputMethod::reset() +{ + Q_D(ExampleHwrInputMethod); + qCDebug(lcExampleHwr) << "reset"; + d->finishRecognition(); +} + +void ExampleHwrInputMethod::update() +{ + Q_D(ExampleHwrInputMethod); + qCDebug(lcExampleHwr) << "update"; + d->select(); +} + +QList<QVirtualKeyboardSelectionListModel::Type> ExampleHwrInputMethod::selectionLists() +{ + return QList<QVirtualKeyboardSelectionListModel::Type>() << QVirtualKeyboardSelectionListModel::Type::WordCandidateList; +} + +int ExampleHwrInputMethod::selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type) +{ + Q_UNUSED(type); + Q_D(ExampleHwrInputMethod); + return d->wordCandidates.size(); +} + +QVariant ExampleHwrInputMethod::selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role) +{ + QVariant result; + Q_D(ExampleHwrInputMethod); + switch (role) { + case QVirtualKeyboardSelectionListModel::Role::Display: + result = QVariant(d->wordCandidates.at(index)); + break; + case QVirtualKeyboardSelectionListModel::Role::WordCompletionLength: + result.setValue(0); + break; + case QVirtualKeyboardSelectionListModel::Role::Dictionary: + { + QVirtualKeyboardSelectionListModel::DictionaryType dictionaryType = + QVirtualKeyboardSelectionListModel::DictionaryType::Default; + result = QVariant(static_cast<int>(dictionaryType)); + break; + } + case QVirtualKeyboardSelectionListModel::Role::CanRemoveSuggestion: + result = QVariant(false); + break; + default: + result = QVirtualKeyboardAbstractInputMethod::selectionListData(type, index, role); + break; + } + return result; +} + +void ExampleHwrInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index) +{ + Q_UNUSED(type); + Q_D(ExampleHwrInputMethod); + d->select(index); +} + +QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> ExampleHwrInputMethod::patternRecognitionModes() const +{ + return QList<QVirtualKeyboardInputEngine::PatternRecognitionMode>() + << QVirtualKeyboardInputEngine::PatternRecognitionMode::Handwriting; +} + +QVirtualKeyboardTrace *ExampleHwrInputMethod::traceBegin( + int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, + const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) +{ + Q_D(ExampleHwrInputMethod); + return d->traceBegin(traceId, patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); +} + +bool ExampleHwrInputMethod::traceEnd(QVirtualKeyboardTrace *trace) +{ + Q_D(ExampleHwrInputMethod); + d->traceEnd(trace); + return true; +} + +bool ExampleHwrInputMethod::reselect(int cursorPosition, const QVirtualKeyboardInputEngine::ReselectFlags &reselectFlags) +{ + Q_D(ExampleHwrInputMethod); + + QVirtualKeyboardInputContext *ic = inputContext(); + if (!ic) + return false; + + const int maxLength = 32; + const QString surroundingText = ic->surroundingText(); + int replaceFrom = 0; + QString stringStart; + + if (cursorPosition > surroundingText.length()) + return false; + + if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordBeforeCursor)) { + for (int i = cursorPosition - 1; i >= 0 && stringStart.length() < maxLength; --i) { + QChar c = surroundingText.at(i); + if (!d->isValidInputChar(c)) + break; + stringStart.insert(0, c); + --replaceFrom; + } + + while (replaceFrom < 0 && d->isJoiner(stringStart.at(0))) { + stringStart.remove(0, 1); + ++replaceFrom; + } + } + + if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor) && replaceFrom == 0) { + stringStart.clear(); + return false; + } + + if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAfterCursor)) { + for (int i = cursorPosition; i < surroundingText.length() && stringStart.length() < maxLength; ++i) { + QChar c = surroundingText.at(i); + if (!d->isValidInputChar(c)) + break; + stringStart.append(c); + } + + while (replaceFrom > -stringStart.length()) { + int lastPos = stringStart.length() - 1; + if (!d->isJoiner(stringStart.at(lastPos))) + break; + stringStart.remove(lastPos, 1); + } + } + + if (stringStart.isEmpty()) + return false; + + if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor) && replaceFrom == -stringStart.length() && stringStart.length() < maxLength) { + stringStart.clear(); + return false; + } + + if (d->isJoiner(stringStart.at(0))) { + stringStart.clear(); + return false; + } + + if (d->isJoiner(stringStart.at(stringStart.length() - 1))) { + stringStart.clear(); + return false; + } + + ic->setPreeditText(stringStart, QList<QInputMethodEvent::Attribute>(), replaceFrom, stringStart.length()); + d->activeWordIndex = 0; + d->wordCandidates = {stringStart}; + emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList); + emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->activeWordIndex); + + return true; +} + +void ExampleHwrInputMethod::timerEvent(QTimerEvent *timerEvent) +{ + Q_D(ExampleHwrInputMethod); + int timerId = timerEvent->timerId(); + qCDebug(lcExampleHwr) << "timerEvent():" << timerId; + if (timerId == d->resultTimer) { + if (!d->countActiveTraces()) { + d->stopResultTimer(); + d->processResult(); + d->clearTraces(); + } + } +} + +} // namespace QtVirtualKeyboard +QT_END_NAMESPACE diff --git a/src/plugins/example/hwr/examplehwrinputmethod_p.h b/src/plugins/example/hwr/examplehwrinputmethod_p.h new file mode 100644 index 00000000..3bb47c3f --- /dev/null +++ b/src/plugins/example/hwr/examplehwrinputmethod_p.h @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef EXAMPLEHWRINPUTMETHOD_P_H +#define EXAMPLEHWRINPUTMETHOD_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 <QtVirtualKeyboard/qvirtualkeyboardabstractinputmethod.h> + +QT_BEGIN_NAMESPACE +namespace QtVirtualKeyboard { + +class ExampleHwrInputMethodPrivate; + +class ExampleHwrInputMethod : public QVirtualKeyboardAbstractInputMethod +{ + Q_OBJECT + Q_DECLARE_PRIVATE(ExampleHwrInputMethod) + QML_NAMED_ELEMENT(HandwritingInputMethod) + QML_ADDED_IN_VERSION(2, 0) + +public: + explicit ExampleHwrInputMethod(QObject *parent = nullptr); + ~ExampleHwrInputMethod(); + + QList<QVirtualKeyboardInputEngine::InputMode> inputModes(const QString &locale) override; + bool setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode) override; + bool setTextCase(QVirtualKeyboardInputEngine::TextCase textCase) override; + + bool keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) override; + + void reset() override; + void update() override; + + QList<QVirtualKeyboardSelectionListModel::Type> selectionLists() override; + int selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type) override; + QVariant selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role) override; + void selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index) override; + + QList<QVirtualKeyboardInputEngine::PatternRecognitionMode> patternRecognitionModes() const override; + QVirtualKeyboardTrace *traceBegin( + int traceId, QVirtualKeyboardInputEngine::PatternRecognitionMode patternRecognitionMode, + const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) override; + bool traceEnd(QVirtualKeyboardTrace *trace) override; + + bool reselect(int cursorPosition, const QVirtualKeyboardInputEngine::ReselectFlags &reselectFlags) override; + +protected: + void timerEvent(QTimerEvent *timerEvent) override; +}; + +} // namespace QtVirtualKeyboard +QT_END_NAMESPACE + +#endif |