diff options
21 files changed, 596 insertions, 212 deletions
@@ -1,7 +1,7 @@ # This file is used to ignore files that should not be committed # -------------------------------------------------------------- -examples/virtualkeyboard/basic +examples/virtualkeyboard/basic/basic !src/virtualkeyboard/3rdparty/hunspell/hunspell.pro src/virtualkeyboard/3rdparty/hunspell/* src/virtualkeyboard/3rdparty/hunspell/doc/html diff --git a/.qmake.conf b/.qmake.conf index 0338efe0..dc68d388 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.10.1 +MODULE_VERSION = 5.11.0 diff --git a/src/virtualkeyboard/3rdparty/t9write/unpack.py b/src/virtualkeyboard/3rdparty/t9write/unpack.py index e6a3e7a1..d3a67923 100644 --- a/src/virtualkeyboard/3rdparty/t9write/unpack.py +++ b/src/virtualkeyboard/3rdparty/t9write/unpack.py @@ -87,6 +87,7 @@ UNPACK_RULES = [ '*/decumaFunctionalSupportCheck.h', '*/decumaLanguages.h', '*/decumaLiteFunctionalSupport.h', + '*/decumaPlusFunctionalSupport.h', '*/decumaRuntimeMallocData.h', '*/decumaStatus.h', '*/decumaStorageSpecifiers.h', diff --git a/src/virtualkeyboard/content/InputPanel.qml b/src/virtualkeyboard/content/InputPanel.qml index f9d25a02..3f88bb30 100644 --- a/src/virtualkeyboard/content/InputPanel.qml +++ b/src/virtualkeyboard/content/InputPanel.qml @@ -61,6 +61,52 @@ Item { */ property alias active: keyboard.active + /*! + \qmlproperty bool InputPanel::externalLanguageSwitchEnabled + \since QtQuick.VirtualKeyboard 2.4 + + This property enables the external language switch mechanism. + When this property is \c true, the virtual keyboard will not show + the built-in language popup, but will emit the \l externalLanguageSwitch + signal instead. The application can handle this signal and show a + custom language selection dialog instead. + */ + property bool externalLanguageSwitchEnabled + + /*! + \qmlsignal InputPanel::externalLanguageSwitch(var localeList, int currentIndex) + \since QtQuick.VirtualKeyboard 2.4 + + This signal is emitted when \l externalLanguageSwitchEnabled is \c true + and the \l {user-guide-language}{language switch key} is pressed by the user. + + It serves as a hook to display a custom language dialog instead of + the built-in language popup in the virtual keyboard. + + The \a localeList parameter contains a list of locale names to choose + from. To get more information about a particular language, use the \l Qt.locale() + function. The \a currentIndex is the index of current locale in the + \a localeList. This item should be highlighted as the current item in the UI. + + To select a new language, use the \l {VirtualKeyboardSettings::locale} + {VirtualKeyboardSettings.locale} property. + + Below is an example that demonstrates a custom language dialog implementation: + + \snippet qtvirtualkeyboard-custom-language-popup.qml popup + + The dialog would then be declared: + + \snippet qtvirtualkeyboard-custom-language-popup.qml declaring + + In the application's InputPanel, add the following code: + + \snippet qtvirtualkeyboard-custom-language-popup.qml using + + The custom dialog will now be shown when the language switch key is pressed. + */ + signal externalLanguageSwitch(var localeList, int currentIndex) + /*! \internal */ property alias keyboard: keyboard diff --git a/src/virtualkeyboard/content/components/ChangeLanguageKey.qml b/src/virtualkeyboard/content/components/ChangeLanguageKey.qml index b14e70f4..70e12fa4 100644 --- a/src/virtualkeyboard/content/components/ChangeLanguageKey.qml +++ b/src/virtualkeyboard/content/components/ChangeLanguageKey.qml @@ -71,7 +71,7 @@ BaseKey { keyPanelDelegate: keyboard.style ? keyboard.style.languageKeyPanel : undefined onClicked: { if (keyboard.style.languagePopupListEnabled) - keyboard.showLanguagePopup(changeLanguageKey, customLayoutsOnly) + keyboard.showLanguagePopup(changeLanguageKey, false) else keyboard.changeInputLanguage(customLayoutsOnly) } diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml index 22378b44..5a2e76eb 100644 --- a/src/virtualkeyboard/content/components/Keyboard.qml +++ b/src/virtualkeyboard/content/components/Keyboard.qml @@ -1069,9 +1069,20 @@ Item { function showLanguagePopup(parentItem, customLayoutsOnly) { if (!languagePopupList.enabled) { - var locales = keyboard.listLocales(customLayoutsOnly) + var locales = keyboard.listLocales(customLayoutsOnly, parent.externalLanguageSwitchEnabled) + if (parent.externalLanguageSwitchEnabled) { + var currentIndex = 0 + for (var i = 0; i < locales.length; i++) { + if (locales[i] === keyboard.locale) { + currentIndex = i + break + } + } + parent.externalLanguageSwitch(locales, currentIndex) + return + } languageListModel.clear() - for (var i = 0; i < locales.length; i++) { + for (i = 0; i < locales.length; i++) { languageListModel.append({localeName: locales[i].name, displayName: locales[i].locale.nativeLanguageName, localeIndex: locales[i].index}) if (locales[i].index === keyboard.localeIndex) languagePopupList.currentIndex = i @@ -1269,12 +1280,15 @@ Item { availableCustomLocaleIndices = newIndices } - function listLocales(customLayoutsOnly) { + function listLocales(customLayoutsOnly, localeNameOnly) { var locales = [] var localeIndices = customLayoutsOnly ? availableCustomLocaleIndices : availableLocaleIndices for (var i = 0; i < localeIndices.length; i++) { var layoutFolder = layoutsModel.get(localeIndices[i], "fileName") - locales.push({locale:Qt.locale(layoutFolder), index:localeIndices[i], name:layoutFolder}) + if (localeNameOnly) + locales.push(layoutFolder) + else + locales.push({locale:Qt.locale(layoutFolder), index:localeIndices[i], name:layoutFolder}) } return locales } diff --git a/src/virtualkeyboard/doc/snippets/qtvirtualkeyboard-custom-language-popup.qml b/src/virtualkeyboard/doc/snippets/qtvirtualkeyboard-custom-language-popup.qml new file mode 100644 index 00000000..261ad9be --- /dev/null +++ b/src/virtualkeyboard/doc/snippets/qtvirtualkeyboard-custom-language-popup.qml @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// ![popup] +Dialog { + id: languageDialog + title: "Select Input Language" + modality: Qt.ApplicationModal + + function show(localeList, currentIndex) { + languageListModel.clear() + for (var i = 0; i < localeList.length; i++) { + languageListModel.append({localeName: localeList[i], displayName: Qt.locale(localeList[i]).nativeLanguageName}) + } + languageListView.currentIndex = currentIndex + languageListView.positionViewAtIndex(currentIndex, ListView.Center) + languageDialog.visible = true + } + + contentItem: ListView { + id: languageListView + model: ListModel { + id: languageListModel + function selectItem(index) { + VirtualKeyboardSettings.locale = languageListModel.get(index).localeName + languageDialog.visible = false + } + } + delegate: Item { + id: languageListItem + width: languageNameTextMetrics.width * 17 + height: languageNameTextMetrics.height + languageListLabel.anchors.topMargin + languageListLabel.anchors.bottomMargin + Text { + id: languageListLabel + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: languageNameTextMetrics.height / 2 + anchors.rightMargin: anchors.leftMargin + anchors.topMargin: languageNameTextMetrics.height / 3 + anchors.bottomMargin: anchors.topMargin + text: languageNameFormatter.elidedText + color: "#5CAA15" + font { + weight: Font.Normal + pixelSize: 28 + } + } + TextMetrics { + id: languageNameTextMetrics + font { + weight: Font.Normal + pixelSize: 28 + } + text: "X" + } + TextMetrics { + id: languageNameFormatter + font { + weight: Font.Normal + pixelSize: 28 + } + elide: Text.ElideRight + elideWidth: languageListItem.width - languageListLabel.anchors.leftMargin - languageListLabel.anchors.rightMargin + text: displayName + } + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (index === -1) + return + parent.ListView.view.currentIndex = index + parent.ListView.view.model.selectItem(index) + } + } + states: State { + name: "current" + when: languageListItem.ListView.isCurrentItem + PropertyChanges { + target: languageListLabel + color: "black" + } + } + } + } +} +// ![popup] + +// ![declaring] +LanguageDialog { + id: languageDialog + width: 400 + height: 400 +} +// ![declaring] + +// ![using] +InputPanel { + id: inputPanel + externalLanguageSwitchEnabled: true + onExternalLanguageSwitch: languageDialog.show(localeList, currentIndex) + // ... +} +// ![using] diff --git a/src/virtualkeyboard/doc/src/user-guide.qdoc b/src/virtualkeyboard/doc/src/user-guide.qdoc index c2d9d9f0..67ea6747 100644 --- a/src/virtualkeyboard/doc/src/user-guide.qdoc +++ b/src/virtualkeyboard/doc/src/user-guide.qdoc @@ -44,6 +44,7 @@ Once \l {Deployment Guide}{properly installed}, the virtual keyboard can be opened by clicking on a text input field. \section1 Language +\target user-guide-language The language can be changed by pressing the language key, which is illustrated with a "globe" icon: diff --git a/src/virtualkeyboard/hunspellinputmethod.cpp b/src/virtualkeyboard/hunspellinputmethod.cpp index 76b5f654..87134162 100644 --- a/src/virtualkeyboard/hunspellinputmethod.cpp +++ b/src/virtualkeyboard/hunspellinputmethod.cpp @@ -90,7 +90,6 @@ bool HunspellInputMethod::setTextCase(InputEngine::TextCase textCase) bool HunspellInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) { - Q_UNUSED(modifiers) Q_D(HunspellInputMethod); InputContext *ic = inputContext(); Qt::InputMethodHints inputMethodHints = ic->inputMethodHints(); @@ -196,7 +195,6 @@ int HunspellInputMethod::selectionListItemCount(SelectionListModel::Type type) QVariant HunspellInputMethod::selectionListData(SelectionListModel::Type type, int index, int role) { QVariant result; - Q_UNUSED(type) Q_D(HunspellInputMethod); switch (role) { case SelectionListModel::DisplayRole: diff --git a/src/virtualkeyboard/inputcontext.cpp b/src/virtualkeyboard/inputcontext.cpp index e23f6e46..2c2c8fcf 100644 --- a/src/virtualkeyboard/inputcontext.cpp +++ b/src/virtualkeyboard/inputcontext.cpp @@ -719,7 +719,6 @@ void InputContext::externalCommit() void InputContext::update(Qt::InputMethodQueries queries) { Q_D(InputContext); - Q_UNUSED(queries); // No need to fetch input clip rectangle during animation if (!(queries & ~Qt::ImInputItemClipRectangle) && d->animating) diff --git a/src/virtualkeyboard/inputselectionhandle.h b/src/virtualkeyboard/inputselectionhandle.h index e4c3e910..5d70df2b 100644 --- a/src/virtualkeyboard/inputselectionhandle.h +++ b/src/virtualkeyboard/inputselectionhandle.h @@ -49,8 +49,8 @@ public: void applyImage(const QSize &windowSize); protected: - void paintEvent(QPaintEvent *pe) Q_DECL_OVERRIDE; - bool event(QEvent *event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *pe) override; + bool event(QEvent *event) override; private: DesktopInputSelectionControl *m_control; diff --git a/src/virtualkeyboard/lipiinputmethod.cpp b/src/virtualkeyboard/lipiinputmethod.cpp index 10dcb4ae..5bb1d46c 100644 --- a/src/virtualkeyboard/lipiinputmethod.cpp +++ b/src/virtualkeyboard/lipiinputmethod.cpp @@ -500,10 +500,11 @@ QList<InputEngine::InputMode> LipiInputMethod::inputModes(const QString &locale) bool LipiInputMethod::setInputMode(const QString &locale, InputEngine::InputMode inputMode) { - Q_UNUSED(locale) Q_D(LipiInputMethod); #ifdef HAVE_HUNSPELL HunspellInputMethod::setInputMode(locale, inputMode); +#else + Q_UNUSED(locale) #endif bool result = d->recognizer.setModel(QStringLiteral("SHAPEREC_ALPHANUM")); if (!result) diff --git a/src/virtualkeyboard/openwnninputmethod.cpp b/src/virtualkeyboard/openwnninputmethod.cpp index edba99c0..d94f212a 100644 --- a/src/virtualkeyboard/openwnninputmethod.cpp +++ b/src/virtualkeyboard/openwnninputmethod.cpp @@ -784,7 +784,6 @@ int OpenWnnInputMethod::selectionListItemCount(SelectionListModel::Type type) QVariant OpenWnnInputMethod::selectionListData(SelectionListModel::Type type, int index, int role) { QVariant result; - Q_UNUSED(type) Q_D(OpenWnnInputMethod); switch (role) { case SelectionListModel::DisplayRole: diff --git a/src/virtualkeyboard/plugin.cpp b/src/virtualkeyboard/plugin.cpp index 136bba80..b0571116 100644 --- a/src/virtualkeyboard/plugin.cpp +++ b/src/virtualkeyboard/plugin.cpp @@ -85,7 +85,6 @@ static QPointer<PlatformInputContext> platformInputContext; static QObject *createInputContextModule(QQmlEngine *engine, QJSEngine *scriptEngine) { - Q_UNUSED(engine); Q_UNUSED(scriptEngine); QQmlContext *rootContext = engine->rootContext(); QStringList inputMethodList = QStringList() diff --git a/src/virtualkeyboard/t9write.h b/src/virtualkeyboard/t9write.h index 621d2312..beebbaa2 100644 --- a/src/virtualkeyboard/t9write.h +++ b/src/virtualkeyboard/t9write.h @@ -37,6 +37,7 @@ #ifdef HAVE_T9WRITE_CJK #include "decuma_hwr_cjk.h" #endif +#include "decumaFunctionalSupport.h" #if defined(HAVE_T9WRITE_CJK) && defined(HAVE_T9WRITE_ALPHABETIC) #define DECUMA_API(FUNC_NAME) (cjk ? decumaCJK ## FUNC_NAME : decuma ## FUNC_NAME) diff --git a/src/virtualkeyboard/t9writeinputmethod.cpp b/src/virtualkeyboard/t9writeinputmethod.cpp index 14b6508b..329bdbd3 100644 --- a/src/virtualkeyboard/t9writeinputmethod.cpp +++ b/src/virtualkeyboard/t9writeinputmethod.cpp @@ -130,6 +130,7 @@ public: attachedDictionary(0), traceListHardLimit(32), resultId(0), + lastResultId(0), resultTimer(0), decumaSession(0), activeWordIndex(-1), @@ -166,7 +167,6 @@ public: static QMutex s_logMutex; static QByteArray s_logString; Q_UNUSED(pUserData) - Q_UNUSED(nLogStringLength) QMutexLocker guard(&s_logMutex); s_logString.append(pLogString, nLogStringLength); if (s_logString.endsWith('\n')) { @@ -231,7 +231,6 @@ public: languageCategories.append(DECUMA_LANG_EN); sessionSettings.recognitionMode = mcrMode; - sessionSettings.bMinimizeAddArcPreProcessing = 1; sessionSettings.writingDirection = unknownWriting; sessionSettings.charSet.pSymbolCategories = symbolCategories.data(); sessionSettings.charSet.nSymbolCategories = symbolCategories.size(); @@ -256,6 +255,9 @@ public: worker.reset(new T9WriteWorker(decumaSession, cjk)); worker->start(); + Q_Q(T9WriteInputMethod); + processResultConnection = QObject::connect(q, &T9WriteInputMethod::resultListChanged, q, &T9WriteInputMethod::processResult, Qt::QueuedConnection); + return true; } @@ -263,6 +265,9 @@ public: { VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::exitEngine()"; + if (processResultConnection) + QObject::disconnect(processResultConnection); + worker.reset(); if (sessionSettings.pStaticDB) { @@ -449,7 +454,7 @@ public: updateDictionary(language, locale, languageChanged); static const QList<DECUMA_UINT32> rtlLanguages = QList<DECUMA_UINT32>() << DECUMA_LANG_AR << DECUMA_LANG_IW << DECUMA_LANG_FA << DECUMA_LANG_UR; - sessionSettings.writingDirection = rtlLanguages.contains(language) ? rightToLeft : unknownWriting; + sessionSettings.writingDirection = rtlLanguages.contains(language) ? rightToLeft : leftToRight; // Enable multi-threaded recognition if available. #ifdef DECUMA_USE_MULTI_THREAD @@ -686,13 +691,38 @@ public: Q_Q(T9WriteInputMethod); Q_UNUSED(language) Q_UNUSED(locale) - Q_UNUSED(inputMode) // Select recognition mode // Note: MCR mode is preferred, as it does not require recognition // timer and provides better user experience. sessionSettings.recognitionMode = mcrMode; + // T9 Write Alphabetic v8.0.0 supports UCR mode for specific languages +#if T9WRITEAPIMAJORVERNUM >= 21 + if (!cjk) { + switch (inputMode) { + case InputEngine::Latin: + switch (language) { + case DECUMA_LANG_EN: + case DECUMA_LANG_FR: + case DECUMA_LANG_IT: + case DECUMA_LANG_DE: + case DECUMA_LANG_ES: + sessionSettings.recognitionMode = ucrMode; + break; + default: + break; + } + break; + case InputEngine::Arabic: + sessionSettings.recognitionMode = ucrMode; + break; + default: + break; + } + } +#endif + // Use scrMode with hidden text or with no predictive mode if (inputMode != InputEngine::ChineseHandwriting && inputMode != InputEngine::JapaneseHandwriting && @@ -800,7 +830,6 @@ public: bool updateSymbolCategoriesCjk(DECUMA_UINT32 language, const QLocale &locale, InputEngine::InputMode inputMode) { - Q_UNUSED(language) Q_ASSERT(cjk); symbolCategories.clear(); @@ -944,7 +973,7 @@ public: } // Attach existing dictionary, if available - if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && + if (sessionSettings.recognitionMode != scrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && loadedDictionary && !attachedDictionary) { if (attachDictionary(loadedDictionary)) attachedDictionary = loadedDictionary; @@ -970,9 +999,10 @@ public: void setContext(InputEngine::PatternRecognitionMode patternRecognitionMode, const QVariantMap &traceCaptureDeviceInfo, - const QVariantMap &traceScreenInfo) + const QVariantMap &traceScreenInfo, + const QByteArray &context) { - QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); + Q_UNUSED(patternRecognitionMode) if (context == currentContext) return; currentContext = context; @@ -1025,13 +1055,13 @@ public: Trace *traceBegin(int traceId, InputEngine::PatternRecognitionMode patternRecognitionMode, const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo) { - Q_UNUSED(traceId) - Q_UNUSED(patternRecognitionMode) - Q_UNUSED(traceScreenInfo) - if (!worker) return 0; + // The result id follows the trace id so that the (previous) + // results completed during the handwriting can be rejected. + resultId = traceId; + stopResultTimer(); // Dictionary must be completed before the arc addition can begin @@ -1042,10 +1072,9 @@ public: // Cancel the current recognition task worker->removeAllTasks<T9WriteRecognitionResultsTask>(); + worker->removeAllTasks<T9WriteRecognitionTask>(); if (recognitionTask) { recognitionTask->cancelRecognition(); - recognitionTask->wait(); - worker->removeTask(recognitionTask); recognitionTask.reset(); } @@ -1054,25 +1083,21 @@ public: unipenTrace.reset(new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo)); #endif - setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); + QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo); + if (context != currentContext) { + worker->waitForAllTasks(); + setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo, context); + } DECUMA_STATUS status; if (!arcAdditionStarted) { + worker->waitForAllTasks(); status = DECUMA_API(BeginArcAddition)(decumaSession); Q_ASSERT(status == decumaNoError); arcAdditionStarted = true; } - DECUMA_UINT32 arcID = (DECUMA_UINT32)traceId; - status = DECUMA_API(StartNewArc)(decumaSession, arcID); - Q_ASSERT(status == decumaNoError); - if (status != decumaNoError) { - DECUMA_API(EndArcAddition)(decumaSession); - arcAdditionStarted = false; - return NULL; - } - Trace *trace = new Trace(); #ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT trace->setChannels(QStringList("t")); @@ -1085,25 +1110,21 @@ public: void traceEnd(Trace *trace) { if (trace->isCanceled()) { - DECUMA_API(CancelArc)(decumaSession, trace->traceId()); traceList.removeOne(trace); delete trace; } else { - addPointsToTraceGroup(trace); + if (cjk && countActiveTraces() == 0) { + // For some reason gestures don't seem to work in CJK mode + // Using our own gesture recognizer as fallback + if (handleGesture()) + return; + } + worker->addTask(QSharedPointer<T9WriteAddArcTask>(new T9WriteAddArcTask(trace))); } if (!traceList.isEmpty()) { Q_ASSERT(arcAdditionStarted); - if (countActiveTraces() == 0) { + if (countActiveTraces() == 0) restartRecognition(); - if (cjk) { - // For some reason gestures don't seem to work in CJK mode - // Using our own gesture recognizer as fallback - handleGesture(); - } - } - } else if (arcAdditionStarted) { - DECUMA_API(EndArcAddition)(decumaSession); - arcAdditionStarted = false; } } @@ -1119,37 +1140,11 @@ public: void clearTraces() { + worker->waitForAllTasks(); qDeleteAll(traceList); traceList.clear(); } - void addPointsToTraceGroup(Trace *trace) - { - Q_ASSERT(decumaSession != 0); - - const QVariantList points = trace->points(); - Q_ASSERT(!points.isEmpty()); - DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId(); - DECUMA_STATUS status; - - for (const QVariant &p : points) { - const QPoint pt(p.toPointF().toPoint()); - status = DECUMA_API(AddPoint)(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID); - if (status != decumaNoError) { - VIRTUALKEYBOARD_DEBUG() << "decumaAddPoint failed" << status; - finishRecognition(); - return; - } - } - - status = DECUMA_API(CommitArc)(decumaSession, arcID); - if (status != decumaNoError) { - VIRTUALKEYBOARD_DEBUG() << "decumaCommitArc failed" << status; - finishRecognition(); - return; - } - } - void noteSelected(int index) { if (wordCandidatesHwrResultIndex.isEmpty()) @@ -1169,10 +1164,24 @@ public: Q_Q(T9WriteInputMethod); - QSharedPointer<T9WriteRecognitionResult> recognitionResult(new T9WriteRecognitionResult(++resultId, 9, 64)); + worker->removeAllTasks<T9WriteRecognitionResultsTask>(); + if (recognitionTask) { + recognitionTask->cancelRecognition(); + recognitionTask.reset(); + } + + // Boost dictionary words by default + BOOST_LEVEL boostLevel = attachedDictionary ? boostDictWords : noBoost; + + // Disable dictionary boost in UCR mode for URL and E-mail input + // Otherwise it will completely mess input + const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints(); + if (sessionSettings.recognitionMode == ucrMode && (inputMethodHints & (Qt::ImhUrlCharactersOnly | Qt::ImhEmailCharactersOnly))) + boostLevel = noBoost; + + QSharedPointer<T9WriteRecognitionResult> recognitionResult(new T9WriteRecognitionResult(resultId, 9, 64)); recognitionTask.reset(new T9WriteRecognitionTask(recognitionResult, instantGestureSettings, - attachedDictionary ? boostDictWords : noBoost, - stringStart)); + boostLevel, stringStart)); worker->addTask(recognitionTask); QSharedPointer<T9WriteRecognitionResultsTask> resultsTask(new T9WriteRecognitionResultsTask(recognitionResult)); @@ -1182,6 +1191,16 @@ public: resetResultTimer(); } + void waitForRecognitionResults() + { + if (!worker) + return; + + VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::waitForRecognitionResults()"; + worker->waitForAllTasks(); + processResult(); + } + bool finishRecognition(bool emitSelectionListChanged = true) { VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::finishRecognition()"; @@ -1194,17 +1213,16 @@ public: stopResultTimer(); - clearTraces(); - + worker->removeAllTasks<T9WriteAddArcTask>(); worker->removeAllTasks<T9WriteRecognitionResultsTask>(); if (recognitionTask) { recognitionTask->cancelRecognition(); - recognitionTask->wait(); - worker->removeTask(recognitionTask); recognitionTask.reset(); result = true; } + clearTraces(); + if (arcAdditionStarted) { DECUMA_API(EndArcAddition)(decumaSession); arcAdditionStarted = false; @@ -1238,7 +1256,7 @@ public: if (!worker) return false; - if (sessionSettings.recognitionMode == mcrMode && wordCandidates.isEmpty()) { + if (sessionSettings.recognitionMode != scrMode && wordCandidates.isEmpty()) { finishRecognition(); return false; } @@ -1250,7 +1268,7 @@ public: VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::select():" << index; Q_Q(T9WriteInputMethod); - if (sessionSettings.recognitionMode == mcrMode) { + if (sessionSettings.recognitionMode != scrMode) { index = index >= 0 ? index : activeWordIndex; noteSelected(index); QString finalWord = wordCandidates.at(index); @@ -1274,7 +1292,11 @@ public: #endif finishRecognition(); + QChar gesture = T9WriteInputMethodPrivate::mapSymbolToGesture(finalWord.right(1).at(0)); + if (!gesture.isNull()) + finalWord.chop(1); q->inputContext()->commit(finalWord); + applyGesture(gesture); } else if (sessionSettings.recognitionMode == scrMode) { QString finalWord = scrResult; finishRecognition(); @@ -1284,12 +1306,12 @@ public: return true; } - void resetResultTimer() + void resetResultTimer(int interval = 500) { - VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::resetResultTimer()"; + VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::resetResultTimer():" << interval; Q_Q(T9WriteInputMethod); stopResultTimer(); - resultTimer = q->startTimer(500); + resultTimer = q->startTimer(interval); } void stopResultTimer() @@ -1302,24 +1324,9 @@ public: } } - void resultsAvailable(const QVariantList &resultList) + void processResult() { - if (!resultList.isEmpty()) { - if (recognitionTask && recognitionTask->resultId() == resultList.first().toMap()["resultId"].toInt()) - processResult(resultList); - } - } - - void processResult(const QVariantList &resultList) - { - if (resultList.isEmpty()) - return; - - if (resultList.first().toMap()["resultId"] != resultId) - return; - VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult()"; - Q_Q(T9WriteInputMethod); InputContext *ic = q->inputContext(); if (!ic) @@ -1330,49 +1337,100 @@ public: QString resultString; QString gesture; QVariantList symbolStrokes; - for (int i = 0; i < resultList.size(); i++) { - QVariantMap result = resultList.at(i).toMap(); - QString resultChars = result["chars"].toString(); - if (i == 0) - caseFormatter.ensureLength(resultChars.length(), textCase); - if (!resultChars.isEmpty()) { - resultChars = caseFormatter.formatString(resultChars); - if (sessionSettings.recognitionMode == mcrMode) { - newWordCandidates.append(resultChars); - newWordCandidatesHwrResultIndex.append(i); - } + { + QMutexLocker resultListGuard(&resultListLock); + if (resultList.isEmpty()) + return; + + if (resultList.first().toMap()["resultId"] != resultId) { + VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): resultId mismatch" << resultList.first().toMap()["resultId"] << "(" << resultId << ")"; + resultList.clear(); + return; } - if (i == 0) { - resultString = resultChars; - if (result.contains("gesture")) - gesture = result["gesture"].toString(); - if (sessionSettings.recognitionMode == mcrMode && result.contains("symbolStrokes")) - symbolStrokes = result["symbolStrokes"].toList(); - if (sessionSettings.recognitionMode == scrMode) - break; + lastResultId = resultId; + + for (int i = 0; i < resultList.size(); i++) { + QVariantMap result = resultList.at(i).toMap(); + QString resultChars = result["chars"].toString(); + if (i == 0) { + if (ic->shift()) { + caseFormatter.ensureLength(1, textCase); + caseFormatter.ensureLength(resultChars.length(), InputEngine::Lower); + } else { + caseFormatter.ensureLength(resultChars.length(), textCase); + } + } + if (!resultChars.isEmpty()) { + resultChars = caseFormatter.formatString(resultChars); + if (sessionSettings.recognitionMode != scrMode) { + newWordCandidates.append(resultChars); + newWordCandidatesHwrResultIndex.append(i); + } + } + if (i == 0) { + resultString = resultChars; + if (result.contains("gesture")) + gesture = result["gesture"].toString(); + if (sessionSettings.recognitionMode != scrMode && result.contains("symbolStrokes")) + symbolStrokes = result["symbolStrokes"].toList(); + if (sessionSettings.recognitionMode == scrMode) + break; + } else { + // Add a gesture symbol to the secondary candidate + if (sessionSettings.recognitionMode != scrMode && result.contains("gesture")) { + QString gesture2 = result["gesture"].toString(); + if (gesture2.length() == 1) { + QChar symbol = T9WriteInputMethodPrivate::mapGestureToSymbol(gesture2.at(0).unicode()); + if (!symbol.isNull()) { + // Check for duplicates + bool duplicateFound = false; + for (const QString &wordCandidate : newWordCandidates) { + duplicateFound = wordCandidate.size() == 1 && wordCandidate.at(0) == symbol; + if (duplicateFound) + break; + } + if (!duplicateFound) { + if (!resultChars.isEmpty()) { + newWordCandidates.last().append(symbol); + } else { + newWordCandidates.append(symbol); + newWordCandidatesHwrResultIndex.append(i); + } + } + } + } + } + } } + + resultList.clear(); } bool wordCandidatesChanged = wordCandidates != newWordCandidates; #ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT // Delete trace history - const InputEngine::InputMode inputMode = q->inputEngine()->inputMode(); - if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() && - inputMode != InputEngine::ChineseHandwriting && - inputMode != InputEngine::JapaneseHandwriting && - inputMode != InputEngine::KoreanHandwriting) { - int activeTraces = symbolStrokes.at(symbolStrokes.count() - 1).toInt(); - if (symbolStrokes.count() > 1) - activeTraces += symbolStrokes.at(symbolStrokes.count() - 2).toInt(); - while (activeTraces < traceList.count()) - delete traceList.takeFirst(); - } + // Note: We have to be sure there are no background tasks + // running since the Trace objects consumed there. + if (worker->numberOfPendingTasks() == 0) { + + const InputEngine::InputMode inputMode = q->inputEngine()->inputMode(); + if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() && + inputMode != InputEngine::ChineseHandwriting && + inputMode != InputEngine::JapaneseHandwriting && + inputMode != InputEngine::KoreanHandwriting) { + int activeTraces = symbolStrokes.at(symbolStrokes.count() - 1).toInt(); + if (symbolStrokes.count() > 1) + activeTraces += symbolStrokes.at(symbolStrokes.count() - 2).toInt(); + while (activeTraces < traceList.count()) + delete traceList.takeFirst(); + } - // Enforce hard limit for number of traces - if (traceList.count() >= traceListHardLimit) { - VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): Clearing traces (hard limit):" << traceList.count(); - clearTraces(); + // Enforce hard limit for number of traces + if (traceList.count() >= traceListHardLimit) { + VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): Clearing traces (hard limit):" << traceList.count(); + clearTraces(); + } } #endif @@ -1380,37 +1438,20 @@ public: if (!gesture.isEmpty()) { DECUMA_UNICODE gestureSymbol = gesture.at(0).unicode(); - switch (gestureSymbol) { - case '\b': - ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier); - break; - - case '\r': - ic->inputEngine()->virtualKeyClick(Qt::Key_Return, QLatin1String("\n"), Qt::NoModifier); - break; - - case ' ': - ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QLatin1String(" "), Qt::NoModifier); - break; - - default: + if (!applyGesture(gestureSymbol)) { ic->commit(ic->preeditText()); finishRecognition(); - break; } return; } - if (sessionSettings.recognitionMode == mcrMode) { + if (sessionSettings.recognitionMode != scrMode) { ignoreUpdate = true; ic->setPreeditText(resultString); ignoreUpdate = false; - } else if (sessionSettings.recognitionMode == scrMode) { - if (resultTimer == 0 && !resultString.isEmpty()) - ic->inputEngine()->virtualKeyClick((Qt::Key)resultString.at(0).unicode(), resultString, Qt::NoModifier); - else - scrResult = resultString; + } else { + scrResult = resultString; } if (wordCandidatesChanged) { @@ -1420,6 +1461,51 @@ public: emit q->selectionListChanged(SelectionListModel::WordCandidateList); emit q->selectionListActiveItemChanged(SelectionListModel::WordCandidateList, activeWordIndex); } + + if (arcAdditionStarted && traceList.isEmpty() && worker->numberOfPendingTasks() == 0) { + DECUMA_API(EndArcAddition)(decumaSession); + arcAdditionStarted = false; + } + } + + static QChar mapGestureToSymbol(const QChar &gesture) + { + switch (gesture.unicode()) { + case '\r': + return QChar(0x23CE); + case ' ': + return QChar(0x2423); + default: + return QChar(); + } + } + + static QChar mapSymbolToGesture(const QChar &symbol) + { + switch (symbol.unicode()) { + case 0x23CE: + return QChar('\r'); + case 0x2423: + return QChar(' '); + default: + return QChar(); + } + } + + bool applyGesture(const QChar &gesture) + { + Q_Q(T9WriteInputMethod); + InputContext *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() @@ -1550,8 +1636,12 @@ public: QSharedPointer<T9WriteDictionary> attachedDictionary; QSharedPointer<T9WriteDictionaryTask> dictionaryTask; QSharedPointer<T9WriteRecognitionTask> recognitionTask; + QMutex resultListLock; + QVariantList resultList; int resultId; + int lastResultId; int resultTimer; + QMetaObject::Connection processResultConnection; QByteArray session; DECUMA_SESSION *decumaSession; QStringList wordCandidates; @@ -1693,7 +1783,6 @@ bool T9WriteInputMethod::setTextCase(InputEngine::TextCase textCase) bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) { - Q_UNUSED(text) Q_UNUSED(modifiers) Q_D(T9WriteInputMethod); switch (key) { @@ -1715,6 +1804,7 @@ bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard // WA: T9Write CJK may crash in some cases with long stringStart. // Therefore we commit the current input and finish the recognition. if (d->cjk) { + d->waitForRecognitionResults(); ic->commit(); d->finishRecognition(); return true; @@ -1742,7 +1832,8 @@ bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard } default: - if (d->sessionSettings.recognitionMode == mcrMode && text.length() > 0) { + if (d->sessionSettings.recognitionMode != scrMode && text.length() > 0) { + d->waitForRecognitionResults(); InputContext *ic = inputContext(); QString preeditText = ic->preeditText(); QChar c = text.at(0); @@ -1761,7 +1852,7 @@ bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard emit selectionListActiveItemChanged(SelectionListModel::WordCandidateList, d->activeWordIndex); return true; } else { - ic->clear(); + ic->commit(); d->finishRecognition(); } break; @@ -1802,7 +1893,6 @@ int T9WriteInputMethod::selectionListItemCount(SelectionListModel::Type type) QVariant T9WriteInputMethod::selectionListData(SelectionListModel::Type type, int index, int role) { QVariant result; - Q_UNUSED(type) Q_D(T9WriteInputMethod); switch (role) { case SelectionListModel::DisplayRole: @@ -1934,9 +2024,25 @@ void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent) int timerId = timerEvent->timerId(); VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::timerEvent():" << timerId; if (timerId == d->resultTimer) { - if (d->sessionSettings.recognitionMode == mcrMode) { - d->stopResultTimer(); + d->stopResultTimer(); + + // Ignore if the result is not yet available + if (d->resultId != d->lastResultId) { + VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::timerEvent(): Result not yet available"; + return; + } + + if (d->sessionSettings.recognitionMode != scrMode) { #ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT + // Don't clear traces in UCR mode if dictionary is loaded. + // In UCR mode the whole purpose is to write the word with + // one or few strokes. + if (d->sessionSettings.recognitionMode == ucrMode) { + QMutexLocker dictionaryGuard(&d->dictionaryLock); + if (d->attachedDictionary) + return; + } + const InputEngine::InputMode inputMode = inputEngine()->inputMode(); if (inputMode != InputEngine::ChineseHandwriting && inputMode != InputEngine::JapaneseHandwriting && @@ -1944,7 +2050,7 @@ void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent) d->clearTraces(); } #endif - } else if (d->sessionSettings.recognitionMode == scrMode) { + } else { d->select(); } } @@ -1965,7 +2071,7 @@ void T9WriteInputMethod::dictionaryLoadCompleted(QSharedPointer<T9WriteDictionar InputContext *ic = inputContext(); if (ic && dictionary->fileName() == d->dictionaryFileName) { d->loadedDictionary = dictionary; - if (d->sessionSettings.recognitionMode == mcrMode && + if (d->sessionSettings.recognitionMode != scrMode && !ic->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) && !d->attachedDictionary) { if (d->attachDictionary(d->loadedDictionary)) @@ -1996,7 +2102,22 @@ void T9WriteInputMethod::resultsAvailable(const QVariantList &resultList) } #endif Q_D(T9WriteInputMethod); - d->resultsAvailable(resultList); + QMutexLocker resultListGuard(&d->resultListLock); + d->resultList = resultList; + emit resultListChanged(); +} + +void T9WriteInputMethod::processResult() +{ + Q_D(T9WriteInputMethod); + bool resultTimerWasRunning = d->resultTimer != 0; + + d->processResult(); + + // Restart the result timer now if it stopped before the results were completed + if (!resultTimerWasRunning && (!d->scrResult.isEmpty() || !d->wordCandidates.isEmpty())) + d->resetResultTimer(0); + } void T9WriteInputMethod::recognitionError(int status) diff --git a/src/virtualkeyboard/t9writeinputmethod.h b/src/virtualkeyboard/t9writeinputmethod.h index 173abd99..9d922537 100644 --- a/src/virtualkeyboard/t9writeinputmethod.h +++ b/src/virtualkeyboard/t9writeinputmethod.h @@ -68,12 +68,16 @@ public: bool reselect(int cursorPosition, const InputEngine::ReselectFlags &reselectFlags); +signals: + void resultListChanged(); + protected: void timerEvent(QTimerEvent *timerEvent); protected slots: void dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary); void resultsAvailable(const QVariantList &resultList); + void processResult(); void recognitionError(int status); }; diff --git a/src/virtualkeyboard/t9writeworker.cpp b/src/virtualkeyboard/t9writeworker.cpp index adbdcedb..cc4564dd 100644 --- a/src/virtualkeyboard/t9writeworker.cpp +++ b/src/virtualkeyboard/t9writeworker.cpp @@ -93,6 +93,47 @@ void T9WriteDictionaryTask::run() emit completed(dictionary); } +T9WriteAddArcTask::T9WriteAddArcTask(Trace *trace) : + trace(trace) +{ +} + +void T9WriteAddArcTask::run() +{ +#ifdef QT_VIRTUALKEYBOARD_DEBUG + QTime perf; + perf.start(); +#endif + DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId(); + DECUMA_STATUS status = DECUMA_API(StartNewArc)(decumaSession, arcID); + Q_ASSERT(status == decumaNoError); + if (status != decumaNoError) { + qWarning() << "T9WriteAddArcTask::run(): Failed to start new arc, status:" << status; + return; + } + + const QVariantList points = trace->points(); + Q_ASSERT(!points.isEmpty()); + + for (const QVariant &p : points) { + const QPoint pt(p.toPointF().toPoint()); + status = DECUMA_API(AddPoint)(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID); + if (status != decumaNoError) { + qWarning() << "T9WriteAddArcTask::run(): Failed to add point, status:" << status; + DECUMA_API(CancelArc)(decumaSession, arcID); + return; + } + } + + status = DECUMA_API(CommitArc)(decumaSession, arcID); + if (status != decumaNoError) + qWarning() << "T9WriteAddArcTask::run(): Failed to commit arc, status:" << status; +#ifdef QT_VIRTUALKEYBOARD_DEBUG + else + VIRTUALKEYBOARD_DEBUG() << "T9WriteAddArcTask::run(): time:" << perf.elapsed() << "ms"; +#endif +} + /*! \class QtVirtualKeyboard::T9WriteRecognitionResult \internal @@ -142,8 +183,6 @@ T9WriteRecognitionTask::T9WriteRecognitionTask(QSharedPointer<T9WriteRecognition void T9WriteRecognitionTask::run() { - VIRTUALKEYBOARD_DEBUG() << "T9WriteRecognitionTask::run()"; - if (!decumaSession) return; @@ -167,40 +206,17 @@ void T9WriteRecognitionTask::run() perf.start(); #endif - DECUMA_STATUS status; - if (!cjk) { - status = DECUMA_API(IndicateInstantGesture)(decumaSession, &result->instantGesture, &instantGestureSettings); - Q_ASSERT(status == decumaNoError); - } - +#if SUPPORTS_ABORTRECOGNITION DECUMA_INTERRUPT_FUNCTIONS interruptFunctions; interruptFunctions.pShouldAbortRecognize = shouldAbortRecognize; interruptFunctions.pUserData = (void *)this; - result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, &interruptFunctions); - if (result->status == decumaAbortRecognitionUnsupported) - result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, NULL); - - QStringList resultList; - QString gesture; - for (int i = 0; i < result->numResults; i++) - { - QString resultString; - resultString.reserve(result->results[i].nChars); - int charPos = 0; - for (int symbolIndex = 0; symbolIndex < result->results[i].nSymbols; symbolIndex++) { - int symbolLength = result->results[i].pSymbolChars[symbolIndex]; - QString symbol(QString::fromUtf16(&result->results[i].pChars[charPos], symbolLength)); - // Do not append gesture symbol to result string - if (result->results[i].bGesture && symbolIndex == (result->results[i].nSymbols - 1)) { - if (i == 0 && (result->instantGesture || (symbol != QLatin1String(" ") && symbol != QLatin1String("\b")))) - gesture = symbol; - } else { - resultString.append(symbol); - } - charPos += symbolLength; - } - resultList.append(resultString); - } + DECUMA_INTERRUPT_FUNCTIONS *pInterruptFunctions = &interruptFunctions; +#else + DECUMA_INTERRUPT_FUNCTIONS *pInterruptFunctions = NULL; +#endif + result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, pInterruptFunctions); + if (result->status != decumaNoError) + qWarning() << "T9WriteRecognitionTask::run(): Recognition failed, status:" << result->status; #ifdef QT_VIRTUALKEYBOARD_DEBUG int perfElapsed = perf.elapsed(); @@ -273,12 +289,11 @@ void T9WriteRecognitionResultsTask::run() int symbolLength = hwrResult.pSymbolChars[symbolIndex]; QString symbol(QString::fromUtf16(&hwrResult.pChars[charPos], symbolLength)); // Do not append gesture symbol to result string - if (hwrResult.bGesture && symbolIndex == (hwrResult.nSymbols - 1)) { - if (result->instantGesture || (symbol != QLatin1String(" ") && symbol != QLatin1String("\b"))) - gesture = symbol; - } else { - resultString.append(symbol); + if (hwrResult.bGesture) { + gesture = symbol.right(1); + symbol.chop(1); } + resultString.append(symbol); charPos += symbolLength; if (hwrResult.pSymbolStrokes) symbolStrokes.append(QVariant((int)hwrResult.pSymbolStrokes[symbolIndex])); @@ -352,12 +367,33 @@ int T9WriteWorker::removeAllTasks() return count; } +void T9WriteWorker::waitForAllTasks() +{ + while (isRunning()) { + idleSema.acquire(); + QMutexLocker guard(&taskLock); + if (taskList.isEmpty()) { + idleSema.release(); + break; + } + idleSema.release(); + } +} + +int T9WriteWorker::numberOfPendingTasks() +{ + QMutexLocker guard(&taskLock); + return taskList.count() + !idleSema.available() ? 1 : 0; +} + void T9WriteWorker::run() { while (!abort) { + idleSema.release(); taskSema.acquire(); if (abort) break; + idleSema.acquire(); QSharedPointer<T9WriteTask> currentTask; { QMutexLocker guard(&taskLock); diff --git a/src/virtualkeyboard/t9writeworker.h b/src/virtualkeyboard/t9writeworker.h index 437a84f5..f34eef67 100644 --- a/src/virtualkeyboard/t9writeworker.h +++ b/src/virtualkeyboard/t9writeworker.h @@ -86,6 +86,18 @@ signals: void completed(QSharedPointer<T9WriteDictionary> dictionary); }; +class T9WriteAddArcTask : public T9WriteTask +{ + Q_OBJECT +public: + explicit T9WriteAddArcTask(Trace *trace); + + void run(); + +private: + Trace *trace; +}; + class T9WriteRecognitionResult { Q_DISABLE_COPY(T9WriteRecognitionResult) @@ -159,6 +171,8 @@ public: void addTask(QSharedPointer<T9WriteTask> task); int removeTask(QSharedPointer<T9WriteTask> task); int removeAllTasks(); + void waitForAllTasks(); + int numberOfPendingTasks(); template <class X> int removeAllTasks() { @@ -181,6 +195,7 @@ protected: private: QList<QSharedPointer<T9WriteTask> > taskList; + QSemaphore idleSema; QSemaphore taskSema; QMutex taskLock; DECUMA_SESSION *decumaSession; diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml index dfdb941b..ded8aee3 100644 --- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml +++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml @@ -91,6 +91,7 @@ InputPanel { property alias wordCandidateListVisibleSpy: wordCandidateListVisibleSpy property alias shiftStateSpy: shiftStateSpy property alias shadowInputControlVisibleSpy: shadowInputControlVisibleSpy + property alias externalLanguageSwitchSpy: externalLanguageSwitchSpy signal inputMethodResult(var text) @@ -188,6 +189,12 @@ InputPanel { signalName: "onVisibleChanged" } + SignalSpy { + id: externalLanguageSwitchSpy + target: inputPanel + signalName: "onExternalLanguageSwitch" + } + function findChildByProperty(parent, propertyName, propertyValue, compareCb) { var obj = null if (parent === null) @@ -293,6 +300,10 @@ InputPanel { return true } + function setExternalLanguageSwitchEnabled(enabled) { + externalLanguageSwitchEnabled = enabled + } + function findVirtualKey(key) { return Utils.findChild(keyboardLayoutLoader, key, function(obj, param) { if (!obj.hasOwnProperty("key") || !obj.hasOwnProperty("text")) diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml index e505d36c..0b9f11ff 100644 --- a/tests/auto/inputpanel/data/tst_inputpanel.qml +++ b/tests/auto/inputpanel/data/tst_inputpanel.qml @@ -82,6 +82,7 @@ Rectangle { inputPanel.setWclAlwaysVisible(data !== undefined && data.hasOwnProperty("wclAlwaysVisible") && data.wclAlwaysVisible) inputPanel.setWclAutoCommitWord(data !== undefined && data.hasOwnProperty("wclAutoCommitWord") && data.wclAutoCommitWord) inputPanel.setFullScreenMode(data !== undefined && data.hasOwnProperty("fullScreenMode") && data.fullScreenMode) + inputPanel.setExternalLanguageSwitchEnabled(data !== undefined && data.hasOwnProperty("externalLanguageSwitchEnabled") && data.externalLanguageSwitchEnabled) container.forceActiveFocus() if (data !== undefined && data.hasOwnProperty("initText")) { textInput.text = data.initText @@ -1048,7 +1049,7 @@ Rectangle { // Remove Jamos one by one. // The number of removed characters must match to the number of Jamos entered. - for (; inputIndex >= 0; inputIndex--) { + for (inputIndex = data.inputSequence.length - 1; inputIndex >= 0; inputIndex--) { compare(textInput.text, intermediateResult.pop()) inputPanel.virtualKeyClick(Qt.Key_Backspace) } @@ -1531,14 +1532,23 @@ Rectangle { } } - function test_languagePopupListToggle() { - prepareTest() + function test_languagePopupListToggle_data() { + return [ + { externalLanguageSwitchEnabled: true }, + { externalLanguageSwitchEnabled: false }, + ] + } + + function test_languagePopupListToggle(data) { + prepareTest(data) if (inputPanel.availableLocales.length < 2) skip("Input language can not be changed") var changeLanguageKey = inputPanel.findObjectByName("changeLanguageKey") var languagePopupList = inputPanel.findObjectByName("languagePopupList") + inputPanel.externalLanguageSwitchSpy.clear() inputPanel.virtualKeyClick(changeLanguageKey) - compare(languagePopupList.visible, true) + compare(languagePopupList.visible, !data.externalLanguageSwitchEnabled) + compare(inputPanel.externalLanguageSwitchSpy.count, data.externalLanguageSwitchEnabled ? 1 : 0) inputPanel.virtualKeyClick(changeLanguageKey) compare(languagePopupList.visible, false) } |