diff options
author | Jarkko Koivikko <jarkko.koivikko@code-q.fi> | 2017-01-06 11:14:07 +0200 |
---|---|---|
committer | Jarkko Koivikko <jarkko.koivikko@code-q.fi> | 2017-01-20 17:37:41 +0000 |
commit | 839a0afec5c39c92ac7221e2c5b6a866d6848382 (patch) | |
tree | 12a6bf83805af3cecc2887518d4ce12098dac817 | |
parent | e2c4fde1804654e449465aee1b9d217e05f06075 (diff) |
Automatically hide word candidate list
This change adds support for automatically hiding word candidate list
when inactive. This feature includes the following enhancements:
- Added new settings:
* VirtualKeyboardSettings.wordCandidateList.autoHideDelay
* VirtualKeyboardSettings.wordCandidateList.alwaysVisible
- Automatic hiding of word candidate list when inactive and when
autoHideDelay elapsed.
- alwaysVisible setting restores the old functionality.
- Added new signal selectionListsChanged() to input method, allowing
the input method to dynamically allocate or deallocate selection
lists.
- HunspellInputMethod does not allocate selection list when
dictionary cannot be loaded, or Qt::ImhNoPredictiveText is enabled.
Also, it will no longer use pre-edit text in this case.
- OpenWnnInputMethod does not allocate selection list if not needed.
[ChangeLog] Automatically hide word candidate list when inactive.
Change-Id: Ifa95ae8a7c47a96719ffdc2929601ff2ef9c0d2e
Reviewed-by: Gordan Markus <gordan.markus@pelagicore.com>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
24 files changed, 412 insertions, 72 deletions
diff --git a/src/virtualkeyboard/abstractinputmethod.cpp b/src/virtualkeyboard/abstractinputmethod.cpp index 05c60e79..0e53341a 100644 --- a/src/virtualkeyboard/abstractinputmethod.cpp +++ b/src/virtualkeyboard/abstractinputmethod.cpp @@ -274,8 +274,13 @@ bool AbstractInputMethod::reselect(int cursorPosition, const InputEngine::Resele Returns the list of selection lists used by this input method. This method is called by input engine when the input method is being - activated. The input method can reserve the selection lists for its use - by returning a list of selection list types required. + activated and every time the input method hints are updated. The input method + can reserve selection lists by returning the desired selection list types. + + The input method may request the input engine to update the selection lists + at any time by emitting selectionListsChanged() signal. This signal will + trigger a call to this method, allowing the input method to update the selection + list types. */ /*! @@ -314,4 +319,13 @@ bool AbstractInputMethod::reselect(int cursorPosition, const InputEngine::Resele in the selection list identified by \a type. */ +/*! + \fn void AbstractInputMethod::selectionListsChanged() + \since QtQuick.VirtualKeyboard 2.2 + + The input method emits this signal when the selection list types have + changed. This signal will trigger a call to selectionLists() method, + allowing the input method to update the selection list types. +*/ + } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/abstractinputmethod.h b/src/virtualkeyboard/abstractinputmethod.h index 168245de..f3157145 100644 --- a/src/virtualkeyboard/abstractinputmethod.h +++ b/src/virtualkeyboard/abstractinputmethod.h @@ -79,6 +79,7 @@ public: signals: void selectionListChanged(int type); void selectionListActiveItemChanged(int type, int index); + void selectionListsChanged(); public slots: virtual void reset(); diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml index a1430a21..27d66d2a 100644 --- a/src/virtualkeyboard/content/components/Keyboard.qml +++ b/src/virtualkeyboard/content/components/Keyboard.qml @@ -31,7 +31,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 import QtQuick.VirtualKeyboard 2.1 import QtQuick.VirtualKeyboard.Styles 2.1 -import QtQuick.VirtualKeyboard.Settings 2.1 +import QtQuick.VirtualKeyboard.Settings 2.2 import Qt.labs.folderlistmodel 2.0 Item { @@ -82,12 +82,13 @@ Item { } width: keyboardBackground.width - height: wordCandidateView.height + keyboardBackground.height + height: keyboardBackground.height + (VirtualKeyboardSettings.wordCandidateList.alwaysVisible ? wordCandidateView.height : 0) onActiveChanged: { hideLanguagePopup() if (active && symbolMode && !preferNumbers) symbolMode = false keyboardInputArea.reset() + wordCandidateViewAutoHideTimer.stop() } onActiveKeyChanged: { if (InputContext.inputEngine.activeKey !== Qt.Key_unknown) @@ -465,7 +466,7 @@ Item { Binding { target: InputContext property: "keyboardRectangle" - value: Qt.rect(keyboard.x, keyboard.y, keyboard.width, keyboard.height) + value: Qt.rect(keyboard.x, keyboard.y + wordCandidateView.currentYOffset, keyboard.width, keyboard.height - wordCandidateView.currentYOffset) when: keyboard.active && !InputContext.animating } Binding { @@ -544,9 +545,15 @@ Item { id: wordCandidateView objectName: "wordCandidateView" clip: true + z: -2 + property bool empty: true + readonly property bool visibleCondition: (((!wordCandidateView.empty || wordCandidateViewAutoHideTimer.running) && + InputContext.inputEngine.wordCandidateListVisibleHint) || VirtualKeyboardSettings.wordCandidateList.alwaysVisible) && + keyboard.active + readonly property real visibleYOffset: VirtualKeyboardSettings.wordCandidateList.alwaysVisible ? 0 : -height + readonly property real currentYOffset: visibleCondition || wordCandidateViewTransition.running ? visibleYOffset : 0 height: Math.round(style.selectionListHeight) anchors.left: parent.left - anchors.top: parent.top anchors.right: parent.right spacing: 0 orientation: ListView.Horizontal @@ -564,6 +571,24 @@ Item { target: wordCandidateView.model ? wordCandidateView.model : null onActiveItemChanged: wordCandidateView.currentIndex = index onItemSelected: if (wordCandidateView.currentItem) soundEffect.play(wordCandidateView.currentItem.soundEffect) + onCountChanged: { + var empty = wordCandidateView.model.count === 0 + if (empty) + wordCandidateViewAutoHideTimer.restart() + wordCandidateView.empty = empty + } + } + Connections { + target: InputContext + onInputItemChanged: wordCandidateViewAutoHideTimer.stop() + } + Connections { + target: InputContext.inputEngine + onWordCandidateListVisibleHintChanged: wordCandidateViewAutoHideTimer.stop() + } + Timer { + id: wordCandidateViewAutoHideTimer + interval: VirtualKeyboardSettings.wordCandidateList.autoHideDelay } Loader { sourceComponent: style.selectionListBackground @@ -574,6 +599,27 @@ Item { id: defaultHighlight Item {} } + states: State { + name: "visible" + when: wordCandidateView.visibleCondition + PropertyChanges { + target: wordCandidateView + y: wordCandidateView.visibleYOffset + } + } + transitions: Transition { + id: wordCandidateViewTransition + to: "visible" + enabled: !InputContext.animating && !VirtualKeyboardSettings.wordCandidateList.alwaysVisible + reversible: true + ParallelAnimation { + NumberAnimation { + properties: "y" + duration: 250 + easing.type: Easing.InOutQuad + } + } + } } Item { @@ -625,8 +671,8 @@ Item { id: keyboardBackground z: -1 anchors.left: parent.left - anchors.top: wordCandidateView.bottom anchors.right: parent.right + anchors.bottom: parent.bottom height: keyboardInnerContainer.height sourceComponent: style.keyboardBackground diff --git a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc index 94339df7..4a84c940 100644 --- a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc +++ b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc @@ -133,10 +133,10 @@ end. The QML types can be imported into your application using the following import statements in your .qml file: - \badcode + \code import QtQuick.VirtualKeyboard 2.0 import QtQuick.VirtualKeyboard.Styles 2.0 - import QtQuick.VirtualKeyboard.Settings 2.0 + import QtQuick.VirtualKeyboard.Settings 2.2 \endcode Styling: diff --git a/src/virtualkeyboard/hunspellinputmethod.cpp b/src/virtualkeyboard/hunspellinputmethod.cpp index 5e8be4d3..5cd88c2e 100644 --- a/src/virtualkeyboard/hunspellinputmethod.cpp +++ b/src/virtualkeyboard/hunspellinputmethod.cpp @@ -98,6 +98,10 @@ bool HunspellInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboar default: if (inputMethodHints.testFlag(Qt::ImhNoPredictiveText)) break; + if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded) { + update(); + break; + } if (text.length() > 0) { QChar c = text.at(0); bool addToWord = d->isValidInputChar(c) && (!d->word.isEmpty() || !d->isJoiner(c)); @@ -157,6 +161,10 @@ bool HunspellInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboar QList<SelectionListModel::Type> HunspellInputMethod::selectionLists() { + Q_D(const HunspellInputMethod); + Qt::InputMethodHints inputMethodHints = inputContext()->inputMethodHints(); + if (d->dictionaryState != HunspellInputMethodPrivate::DictionaryReady || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || inputMethodHints.testFlag(Qt::ImhHiddenText)) + return QList<SelectionListModel::Type>(); return QList<SelectionListModel::Type>() << SelectionListModel::WordCandidateList; } @@ -205,6 +213,9 @@ bool HunspellInputMethod::reselect(int cursorPosition, const InputEngine::Resele Q_D(HunspellInputMethod); Q_ASSERT(d->word.isEmpty()); + if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded) + return false; + InputContext *ic = inputContext(); if (!ic) return false; @@ -299,6 +310,10 @@ void HunspellInputMethod::update() void HunspellInputMethod::updateSuggestions(const QStringList &wordList, int activeWordIndex) { Q_D(HunspellInputMethod); + if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded) { + update(); + return; + } d->wordCandidates.clear(); d->wordCandidates.append(wordList); // Make sure the exact match is up-to-date @@ -309,4 +324,12 @@ void HunspellInputMethod::updateSuggestions(const QStringList &wordList, int act emit selectionListActiveItemChanged(SelectionListModel::WordCandidateList, d->activeWordIndex); } +void HunspellInputMethod::dictionaryLoadCompleted(bool success) +{ + Q_D(HunspellInputMethod); + d->dictionaryState = success ? HunspellInputMethodPrivate::DictionaryReady : + HunspellInputMethodPrivate::DictionaryNotLoaded; + emit selectionListsChanged(); +} + } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/hunspellinputmethod.h b/src/virtualkeyboard/hunspellinputmethod.h index 1aa12dce..9faeff5c 100644 --- a/src/virtualkeyboard/hunspellinputmethod.h +++ b/src/virtualkeyboard/hunspellinputmethod.h @@ -64,6 +64,7 @@ public: protected slots: void updateSuggestions(const QStringList &wordList, int activeWordIndex); + void dictionaryLoadCompleted(bool success); }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/hunspellinputmethod_p.cpp b/src/virtualkeyboard/hunspellinputmethod_p.cpp index 34f343d3..2512337d 100644 --- a/src/virtualkeyboard/hunspellinputmethod_p.cpp +++ b/src/virtualkeyboard/hunspellinputmethod_p.cpp @@ -53,7 +53,8 @@ HunspellInputMethodPrivate::HunspellInputMethodPrivate(HunspellInputMethod *q_pt activeWordIndex(-1), wordCompletionPoint(2), ignoreUpdate(false), - autoSpaceAllowed(false) + autoSpaceAllowed(false), + dictionaryState(DictionaryNotLoaded) { if (hunspellWorker) hunspellWorker->start(); @@ -65,6 +66,7 @@ HunspellInputMethodPrivate::~HunspellInputMethodPrivate() bool HunspellInputMethodPrivate::createHunspell(const QString &locale) { + Q_Q(HunspellInputMethod); if (!hunspellWorker) return false; if (this->locale != locale) { @@ -90,6 +92,8 @@ bool HunspellInputMethodPrivate::createHunspell(const QString &locale) searchPaths.append(defaultPath); } QSharedPointer<HunspellLoadDictionaryTask> loadDictionaryTask(new HunspellLoadDictionaryTask(locale, searchPaths)); + QObject::connect(loadDictionaryTask.data(), &HunspellLoadDictionaryTask::completed, q, &HunspellInputMethod::dictionaryLoadCompleted); + dictionaryState = HunspellInputMethodPrivate::DictionaryLoading; hunspellWorker->addTask(loadDictionaryTask); this->locale = locale; } @@ -110,7 +114,7 @@ void HunspellInputMethodPrivate::reset() bool HunspellInputMethodPrivate::updateSuggestions() { bool wordCandidateListChanged = false; - if (!word.isEmpty()) { + if (!word.isEmpty() && dictionaryState != HunspellInputMethodPrivate::DictionaryNotLoaded) { if (hunspellWorker) hunspellWorker->removeAllTasksExcept<HunspellLoadDictionaryTask>(); if (wordCandidates.isEmpty()) { diff --git a/src/virtualkeyboard/hunspellinputmethod_p.h b/src/virtualkeyboard/hunspellinputmethod_p.h index 1a56defa..a73273b2 100644 --- a/src/virtualkeyboard/hunspellinputmethod_p.h +++ b/src/virtualkeyboard/hunspellinputmethod_p.h @@ -43,6 +43,12 @@ public: HunspellInputMethodPrivate(HunspellInputMethod *q_ptr); ~HunspellInputMethodPrivate(); + enum DictionaryState { + DictionaryNotLoaded, + DictionaryLoading, + DictionaryReady + }; + bool createHunspell(const QString &locale); void reset(); bool updateSuggestions(); @@ -61,6 +67,7 @@ public: int wordCompletionPoint; bool ignoreUpdate; bool autoSpaceAllowed; + DictionaryState dictionaryState; }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/hunspellworker.cpp b/src/virtualkeyboard/hunspellworker.cpp index 7c325e05..1a6d18ef 100644 --- a/src/virtualkeyboard/hunspellworker.cpp +++ b/src/virtualkeyboard/hunspellworker.cpp @@ -88,26 +88,26 @@ void HunspellLoadDictionaryTask::run() affPath.clear(); } - if (affPath.isEmpty() || dicPath.isEmpty()) { + if (!affPath.isEmpty() && !dicPath.isEmpty()) { VIRTUALKEYBOARD_DEBUG() << "Hunspell dictionary is missing for the" << locale << "language. Search paths" << searchPaths; - return; - } - - *hunspellPtr = Hunspell_create(affPath.toUtf8().constData(), dicPath.toUtf8().constData()); - if (*hunspellPtr) { - /* Make sure the encoding used by the dictionary is supported - by the QTextCodec. - */ - if (!QTextCodec::codecForName(Hunspell_get_dic_encoding(*hunspellPtr))) { - qWarning() << "The Hunspell dictionary" << dicPath << "cannot be used because it uses an unknown text codec" << QString(Hunspell_get_dic_encoding(*hunspellPtr)); - Hunspell_destroy(*hunspellPtr); - *hunspellPtr = 0; + *hunspellPtr = Hunspell_create(affPath.toUtf8().constData(), dicPath.toUtf8().constData()); + if (*hunspellPtr) { + /* Make sure the encoding used by the dictionary is supported + by the QTextCodec. + */ + if (!QTextCodec::codecForName(Hunspell_get_dic_encoding(*hunspellPtr))) { + qWarning() << "The Hunspell dictionary" << dicPath << "cannot be used because it uses an unknown text codec" << QString(Hunspell_get_dic_encoding(*hunspellPtr)); + Hunspell_destroy(*hunspellPtr); + *hunspellPtr = 0; + } } - } #ifdef QT_VIRTUALKEYBOARD_DEBUG - VIRTUALKEYBOARD_DEBUG() << "HunspellLoadDictionaryTask::run(): time:" << perf.elapsed() << "ms"; + VIRTUALKEYBOARD_DEBUG() << "HunspellLoadDictionaryTask::run(): time:" << perf.elapsed() << "ms"; #endif + } + + emit completed(*hunspellPtr != 0); } /*! diff --git a/src/virtualkeyboard/hunspellworker.h b/src/virtualkeyboard/hunspellworker.h index 73e2c187..71025b91 100644 --- a/src/virtualkeyboard/hunspellworker.h +++ b/src/virtualkeyboard/hunspellworker.h @@ -65,6 +65,10 @@ public: void run(); +signals: + void completed(bool success); + +public: Hunhandle **hunspellPtr; const QString locale; const QStringList searchPaths; diff --git a/src/virtualkeyboard/inputengine.cpp b/src/virtualkeyboard/inputengine.cpp index f9c447c6..7965f06d 100644 --- a/src/virtualkeyboard/inputengine.cpp +++ b/src/virtualkeyboard/inputengine.cpp @@ -157,6 +157,7 @@ InputEngine::InputEngine(InputContext *parent) : if (d->inputContext) { connect(d->inputContext, SIGNAL(shiftChanged()), SLOT(shiftChanged())); connect(d->inputContext, SIGNAL(localeChanged()), SLOT(update())); + QObject::connect(d->inputContext, &InputContext::inputMethodHintsChanged, this, &InputEngine::updateSelectionListModels); } d->defaultInputMethod = new DefaultInputMethod(this); if (d->defaultInputMethod) @@ -364,43 +365,18 @@ void InputEngine::setInputMethod(AbstractInputMethod *inputMethod) if (d->inputMethod != inputMethod) { update(); if (d->inputMethod) { + QObject::disconnect(d->inputMethod.data(), &AbstractInputMethod::selectionListsChanged, this, &InputEngine::updateSelectionListModels); d->inputMethod->setInputEngine(0); } d->inputMethod = inputMethod; if (d->inputMethod) { d->inputMethod->setInputEngine(this); + QObject::connect(d->inputMethod.data(), &AbstractInputMethod::selectionListsChanged, this, &InputEngine::updateSelectionListModels); // Set current text case d->inputMethod->setTextCase(d->textCase); - // Allocate selection lists for the input method - const QList<SelectionListModel::Type> activeSelectionLists = d->inputMethod->selectionLists(); - QList<SelectionListModel::Type> inactiveSelectionLists = d->selectionListModels.keys(); - for (const SelectionListModel::Type &selectionListType : activeSelectionLists) { - auto it = d->selectionListModels.find(selectionListType); - if (it == d->selectionListModels.end()) { - it = d->selectionListModels.insert(selectionListType, new SelectionListModel(this)); - if (selectionListType == SelectionListModel::WordCandidateList) { - emit wordCandidateListModelChanged(); - } - } - it.value()->setDataSource(inputMethod, selectionListType); - if (selectionListType == SelectionListModel::WordCandidateList) { - emit wordCandidateListVisibleHintChanged(); - } - inactiveSelectionLists.removeAll(selectionListType); - } - - // Deallocate inactive selection lists - for (const SelectionListModel::Type &selectionListType : qAsConst(inactiveSelectionLists)) { - const auto it = d->selectionListModels.constFind(selectionListType); - if (it != d->selectionListModels.cend()) { - it.value()->setDataSource(0, selectionListType); - if (selectionListType == SelectionListModel::WordCandidateList) { - emit wordCandidateListVisibleHintChanged(); - } - } - } + updateSelectionListModels(); } emit inputMethodChanged(); emit inputModesChanged(); @@ -644,6 +620,41 @@ void InputEngine::shiftChanged() /*! \internal */ +void InputEngine::updateSelectionListModels() +{ + Q_D(InputEngine); + QList<SelectionListModel::Type> inactiveSelectionLists = d->selectionListModels.keys(); + if (d->inputMethod) { + // Allocate selection lists for the input method + const QList<SelectionListModel::Type> activeSelectionLists = d->inputMethod->selectionLists(); + for (const SelectionListModel::Type &selectionListType : activeSelectionLists) { + auto it = d->selectionListModels.find(selectionListType); + if (it == d->selectionListModels.end()) { + it = d->selectionListModels.insert(selectionListType, new SelectionListModel(this)); + if (selectionListType == SelectionListModel::WordCandidateList) + emit wordCandidateListModelChanged(); + } + it.value()->setDataSource(d->inputMethod, selectionListType); + if (selectionListType == SelectionListModel::WordCandidateList) + emit wordCandidateListVisibleHintChanged(); + inactiveSelectionLists.removeAll(selectionListType); + } + } + + // Deallocate inactive selection lists + for (const SelectionListModel::Type &selectionListType : qAsConst(inactiveSelectionLists)) { + const auto it = d->selectionListModels.constFind(selectionListType); + if (it != d->selectionListModels.cend()) { + it.value()->setDataSource(0, selectionListType); + if (selectionListType == SelectionListModel::WordCandidateList) + emit wordCandidateListVisibleHintChanged(); + } + } +} + +/*! + \internal +*/ void InputEngine::timerEvent(QTimerEvent *timerEvent) { Q_D(InputEngine); diff --git a/src/virtualkeyboard/inputengine.h b/src/virtualkeyboard/inputengine.h index 266541fb..7551d754 100644 --- a/src/virtualkeyboard/inputengine.h +++ b/src/virtualkeyboard/inputengine.h @@ -136,6 +136,7 @@ private slots: void reset(); void update(); void shiftChanged(); + void updateSelectionListModels(); protected: void timerEvent(QTimerEvent *timerEvent); diff --git a/src/virtualkeyboard/inputmethod.cpp b/src/virtualkeyboard/inputmethod.cpp index 7c91ab2f..b9e120ff 100644 --- a/src/virtualkeyboard/inputmethod.cpp +++ b/src/virtualkeyboard/inputmethod.cpp @@ -121,9 +121,14 @@ namespace QtVirtualKeyboard { Returns the list of selection types used for this input method. - This method is called by the input engine when the input method is being - activated. The input method can reserve the selection lists for its use - by returning a list of selection list types required. + This method is called by input engine when the input method is being + activated and every time the input method hints are updated. The input method + can reserve selection lists by returning the desired selection list types. + + The input method may request the input engine to update the selection lists + at any time by emitting selectionListsChanged() signal. This signal will + trigger a call to this method, allowing the input method to update the selection + list types. */ /*! @@ -163,6 +168,15 @@ namespace QtVirtualKeyboard { */ /*! + \qmlsignal InputMethod::selectionListsChanged() + \since QtQuick.VirtualKeyboard 2.2 + + The input method emits this signal when the selection list types have + changed. This signal will trigger a call to selectionLists() method, + allowing the input method to update the selection list types. +*/ + +/*! \qmlmethod list<int> InputMethod::patternRecognitionModes() \since QtQuick.VirtualKeyboard 2.0 diff --git a/src/virtualkeyboard/openwnninputmethod.cpp b/src/virtualkeyboard/openwnninputmethod.cpp index 8e5a1aaa..cb5a88f9 100644 --- a/src/virtualkeyboard/openwnninputmethod.cpp +++ b/src/virtualkeyboard/openwnninputmethod.cpp @@ -356,7 +356,7 @@ public: void fitInputType() { - enablePrediction = true; + Q_Q(OpenWnnInputMethod); enableConverter = true; Qt::InputMethodHints inputMethodHints = inputEngine->inputContext()->inputMethodHints(); @@ -370,13 +370,17 @@ public: enableConverter = false; } - if (inputMethodHints.testFlag(Qt::ImhHiddenText) || - inputMethodHints.testFlag(Qt::ImhSensitiveData)) { - enablePrediction = false; - } - - if (inputMethodHints.testFlag(Qt::ImhNoPredictiveText)) { - enablePrediction = false; + if (inputMode != InputEngine::Hiragana || + inputMethodHints.testFlag(Qt::ImhHiddenText) || + inputMethodHints.testFlag(Qt::ImhSensitiveData) || + inputMethodHints.testFlag(Qt::ImhNoPredictiveText)) { + if (enablePrediction) { + enablePrediction = false; + emit q->selectionListsChanged(); + } + } else if (inputMode == InputEngine::Hiragana && !enablePrediction) { + enablePrediction = true; + emit q->selectionListsChanged(); } activeConvertType = CONVERT_TYPE_NONE; @@ -640,6 +644,7 @@ bool OpenWnnInputMethod::setInputMode(const QString &locale, InputEngine::InputM break; } d->inputMode = inputMode; + d->fitInputType(); return true; } @@ -758,6 +763,9 @@ bool OpenWnnInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard QList<SelectionListModel::Type> OpenWnnInputMethod::selectionLists() { + Q_D(OpenWnnInputMethod); + if (!d->enablePrediction) + return QList<SelectionListModel::Type>(); return QList<SelectionListModel::Type>() << SelectionListModel::WordCandidateList; } diff --git a/src/virtualkeyboard/plugin.cpp b/src/virtualkeyboard/plugin.cpp index 9b3bd3b7..5349e735 100644 --- a/src/virtualkeyboard/plugin.cpp +++ b/src/virtualkeyboard/plugin.cpp @@ -187,6 +187,7 @@ QPlatformInputContext *QVirtualKeyboardPlugin::create(const QString &system, con qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 1, 2, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 2, 0, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 2, 1, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); + qmlRegisterUncreatableType<WordCandidateListSettings>(pluginSettingsUri, 2, 2, "WordCandidateListSettings", QLatin1String("Cannot create word candidate list settings")); const QString path(QStringLiteral("qrc:///QtQuick/VirtualKeyboard/content/")); qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 1, 0, "InputPanel"); diff --git a/src/virtualkeyboard/selectionlistmodel.cpp b/src/virtualkeyboard/selectionlistmodel.cpp index 88f79ff7..5d3ac375 100644 --- a/src/virtualkeyboard/selectionlistmodel.cpp +++ b/src/virtualkeyboard/selectionlistmodel.cpp @@ -192,6 +192,15 @@ QHash<int,QByteArray> SelectionListModel::roleNames() const return d->roles; } +/*! + \internal +*/ +int SelectionListModel::count() const +{ + Q_D(const SelectionListModel); + return d->rowCount; +} + /*! \qmlmethod void SelectionListModel::selectItem(int index) This method should be called when the user selects an item at position @@ -249,6 +258,8 @@ void SelectionListModel::selectionListChanged(int type) d->rowCount = 0; endResetModel(); } + if (d->rowCount != oldCount) + emit countChanged(); } } diff --git a/src/virtualkeyboard/selectionlistmodel.h b/src/virtualkeyboard/selectionlistmodel.h index e78e64d4..47153558 100644 --- a/src/virtualkeyboard/selectionlistmodel.h +++ b/src/virtualkeyboard/selectionlistmodel.h @@ -44,6 +44,7 @@ class SelectionListModel : public QAbstractListModel Q_ENUMS(Type) Q_ENUMS(Role) Q_DECLARE_PRIVATE(SelectionListModel) + Q_PROPERTY(int count READ count NOTIFY countChanged) explicit SelectionListModel(QObject *parent = 0); @@ -65,10 +66,13 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QHash<int,QByteArray> roleNames() const; + int count() const; + Q_INVOKABLE void selectItem(int index); Q_INVOKABLE QVariant dataAt(int index, int role = Qt::DisplayRole) const; signals: + void countChanged(); void activeItemChanged(int index); void itemSelected(int index); diff --git a/src/virtualkeyboard/settings.cpp b/src/virtualkeyboard/settings.cpp index f01c506d..55faca8c 100644 --- a/src/virtualkeyboard/settings.cpp +++ b/src/virtualkeyboard/settings.cpp @@ -42,7 +42,9 @@ public: locale(), availableLocales(), activeLocales(), - layoutPath() + layoutPath(), + wclAutoHideDelay(5000), + wclAlwaysVisible(false) {} QString style; @@ -51,6 +53,8 @@ public: QStringList availableLocales; QStringList activeLocales; QUrl layoutPath; + int wclAutoHideDelay; + bool wclAlwaysVisible; }; static QScopedPointer<Settings> s_settingsInstance; @@ -162,4 +166,34 @@ void Settings::setLayoutPath(const QUrl &layoutPath) } } +int Settings::wclAutoHideDelay() const +{ + Q_D(const Settings); + return d->wclAutoHideDelay; +} + +void Settings::setWclAutoHideDelay(int wclAutoHideDelay) +{ + Q_D(Settings); + if (d->wclAutoHideDelay != wclAutoHideDelay) { + d->wclAutoHideDelay = wclAutoHideDelay; + emit wclAutoHideDelayChanged(); + } +} + +bool Settings::wclAlwaysVisible() const +{ + Q_D(const Settings); + return d->wclAlwaysVisible; +} + +void Settings::setWclAlwaysVisible(bool wclAlwaysVisible) +{ + Q_D(Settings); + if (d->wclAlwaysVisible != wclAlwaysVisible) { + d->wclAlwaysVisible = wclAlwaysVisible; + emit wclAlwaysVisibleChanged(); + } +} + } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/settings.h b/src/virtualkeyboard/settings.h index eeeff7e4..3bc65fbb 100644 --- a/src/virtualkeyboard/settings.h +++ b/src/virtualkeyboard/settings.h @@ -66,6 +66,12 @@ public: QUrl layoutPath() const; void setLayoutPath(const QUrl &layoutPath); + int wclAutoHideDelay() const; + void setWclAutoHideDelay(int wclAutoHideDelay); + + bool wclAlwaysVisible() const; + void setWclAlwaysVisible(bool wclAlwaysVisible); + signals: void styleChanged(); void styleNameChanged(); @@ -73,6 +79,8 @@ signals: void availableLocalesChanged(); void activeLocalesChanged(); void layoutPathChanged(); + void wclAutoHideDelayChanged(); + void wclAlwaysVisibleChanged(); }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/styles/styles_plugin.cpp b/src/virtualkeyboard/styles/styles_plugin.cpp index 975ab401..d6e17f5c 100644 --- a/src/virtualkeyboard/styles/styles_plugin.cpp +++ b/src/virtualkeyboard/styles/styles_plugin.cpp @@ -43,7 +43,7 @@ The QML types can be imported into your application using the following import statements in your .qml file: - \badcode + \code import QtQuick.VirtualKeyboard.Styles 2.0 \endcode */ diff --git a/src/virtualkeyboard/virtualkeyboardsettings.cpp b/src/virtualkeyboard/virtualkeyboardsettings.cpp index 97554001..bb832a2e 100644 --- a/src/virtualkeyboard/virtualkeyboardsettings.cpp +++ b/src/virtualkeyboard/virtualkeyboardsettings.cpp @@ -42,7 +42,8 @@ class VirtualKeyboardSettingsPrivate : public QObjectPrivate public: VirtualKeyboardSettingsPrivate() : QObjectPrivate(), - engine() {} + engine() + {} QString buildStyleImportPath(const QString &path, const QString &name) const { @@ -90,10 +91,11 @@ public: } QPointer<QQmlEngine> engine; + WordCandidateListSettings wordCandidateListSettings; }; /*! - \qmlmodule QtQuick.VirtualKeyboard.Settings 2.0 + \qmlmodule QtQuick.VirtualKeyboard.Settings 2.2 \title Qt Quick Virtual Keyboard Settings QML Types \ingroup qmlmodules @@ -102,8 +104,8 @@ public: The QML types can be imported into your application using the following import statements in your .qml file: - \badcode - import QtQuick.VirtualKeyboard.Settings 2.0 + \code + import QtQuick.VirtualKeyboard.Settings 2.2 \endcode */ @@ -161,6 +163,8 @@ VirtualKeyboardSettings::VirtualKeyboardSettings(QQmlEngine *engine) : connect(settings, SIGNAL(availableLocalesChanged()), SIGNAL(availableLocalesChanged())); connect(settings, SIGNAL(activeLocalesChanged()), SIGNAL(activeLocalesChanged())); connect(settings, SIGNAL(layoutPathChanged()), SIGNAL(layoutPathChanged())); + connect(settings, SIGNAL(wclAutoHideDelayChanged()), &d->wordCandidateListSettings, SIGNAL(autoHideDelayChanged())); + connect(settings, SIGNAL(wclAlwaysVisibleChanged()), &d->wordCandidateListSettings, SIGNAL(alwaysVisibleChanged())); } /*! @@ -267,6 +271,12 @@ QStringList VirtualKeyboardSettings::activeLocales() const return Settings::instance()->activeLocales(); } +WordCandidateListSettings *VirtualKeyboardSettings::wordCandidateList() const +{ + Q_D(const VirtualKeyboardSettings); + return const_cast<WordCandidateListSettings *>(&d->wordCandidateListSettings); +} + void VirtualKeyboardSettings::resetStyle() { Q_D(VirtualKeyboardSettings); @@ -346,4 +356,59 @@ void VirtualKeyboardSettings::resetStyle() used to limit the list of available languages in the application lifetime. */ +/*! + \since QtQuick.VirtualKeyboard.Settings 2.2 + \qmlpropertygroup QtQuick.VirtualKeyboard::VirtualKeyboardSettings::wordCandidateList + \qmlproperty int QtQuick.VirtualKeyboard::VirtualKeyboardSettings::wordCandidateList.autoHideDelay + \qmlproperty bool QtQuick.VirtualKeyboard::VirtualKeyboardSettings::wordCandidateList.alwaysVisible + + \table + \header + \li Name + \li Description + \row + \li autoHideDelay + \li This property defines the delay, in milliseconds, after which the + word candidate list is hidden if empty. + + If the value is \c 0, the list is immediately hidden when cleared. + + If the value is \c -1, the list is visible until input focus + changes, or the input panel is hidden. + + The default value is \c 5000 milliseconds. + \row + \li alwaysVisible + \li This property defines whether the word candidate list should always + remain visible. + + The default value is \c false. + \endtable +*/ + +WordCandidateListSettings::WordCandidateListSettings(QObject *parent) : + QObject(parent) +{ +} + +int WordCandidateListSettings::autoHideDelay() const +{ + return Settings::instance()->wclAutoHideDelay(); +} + +void WordCandidateListSettings::setAutoHideDelay(int autoHideDelay) +{ + Settings::instance()->setWclAutoHideDelay(autoHideDelay); +} + +bool WordCandidateListSettings::alwaysVisible() const +{ + return Settings::instance()->wclAlwaysVisible(); +} + +void WordCandidateListSettings::setAlwaysVisible(bool alwaysVisible) +{ + Settings::instance()->setWclAlwaysVisible(alwaysVisible); +} + } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/virtualkeyboardsettings.h b/src/virtualkeyboard/virtualkeyboardsettings.h index b4eae869..397746e0 100644 --- a/src/virtualkeyboard/virtualkeyboardsettings.h +++ b/src/virtualkeyboard/virtualkeyboardsettings.h @@ -34,6 +34,7 @@ namespace QtVirtualKeyboard { +class WordCandidateListSettings; class VirtualKeyboardSettingsPrivate; class VirtualKeyboardSettings : public QObject @@ -46,6 +47,7 @@ class VirtualKeyboardSettings : public QObject Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged) Q_PROPERTY(QStringList availableLocales READ availableLocales NOTIFY availableLocalesChanged) Q_PROPERTY(QStringList activeLocales READ activeLocales WRITE setActiveLocales NOTIFY activeLocalesChanged) + Q_PROPERTY(WordCandidateListSettings *wordCandidateList READ wordCandidateList CONSTANT) public: static QObject *registerSettingsModule(QQmlEngine *engine, QJSEngine *jsEngine); @@ -68,6 +70,8 @@ public: void setActiveLocales(const QStringList &activeLocales); QStringList activeLocales() const; + WordCandidateListSettings *wordCandidateList() const; + signals: void styleChanged(); void styleNameChanged(); @@ -81,6 +85,27 @@ private: void resetLayoutPath(); }; +class WordCandidateListSettings : public QObject +{ + Q_OBJECT + Q_PROPERTY(int autoHideDelay READ autoHideDelay WRITE setAutoHideDelay NOTIFY autoHideDelayChanged) + Q_PROPERTY(bool alwaysVisible READ alwaysVisible WRITE setAlwaysVisible NOTIFY alwaysVisibleChanged) + + explicit WordCandidateListSettings(QObject *parent = 0); + friend class VirtualKeyboardSettingsPrivate; + +public: + int autoHideDelay() const; + void setAutoHideDelay(int autoHideDelay); + + bool alwaysVisible() const; + void setAlwaysVisible(bool alwaysVisible); + +signals: + void autoHideDelayChanged(); + void alwaysVisibleChanged(); +}; + } #endif // VIRTUALKEYBOARDSETTINGS_H diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml index 1fb37d74..034150be 100644 --- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml +++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml @@ -30,7 +30,7 @@ import QtTest 1.0 import QtQuick 2.0 import QtQuick.VirtualKeyboard 2.1 -import QtQuick.VirtualKeyboard.Settings 2.1 +import QtQuick.VirtualKeyboard.Settings 2.2 import "handwriting.js" as Handwriting import "utils.js" as Utils @@ -83,6 +83,7 @@ InputPanel { property alias soundEffectSpy: soundEffectSpy property alias inputMethodResultSpy: inputMethodResultSpy property alias wordCandidateListChangedSpy: wordCandidateListChangedSpy + property alias wordCandidateListVisibleSpy: wordCandidateListVisibleSpy property alias shiftStateSpy: shiftStateSpy signal inputMethodResult(var text) @@ -164,6 +165,12 @@ InputPanel { } SignalSpy { + id: wordCandidateListVisibleSpy + target: wordCandidateView + signalName: "onVisibleConditionChanged" + } + + SignalSpy { id: shiftStateSpy target: InputContext signalName: "onShiftChanged" @@ -207,6 +214,14 @@ InputPanel { VirtualKeyboardSettings.activeLocales = activeLocales } + function setWclAutoHideDelay(wclAutoHideDelay) { + VirtualKeyboardSettings.wordCandidateList.autoHideDelay = wclAutoHideDelay + } + + function setWclAlwaysVisible(wclAlwaysVisible) { + VirtualKeyboardSettings.wordCandidateList.alwaysVisible = wclAlwaysVisible + } + function mapInputMode(inputModeName) { if (inputModeName === "Latin") return InputEngine.Latin diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml index 9cc1fedb..ffe88cc8 100644 --- a/tests/auto/inputpanel/data/tst_inputpanel.qml +++ b/tests/auto/inputpanel/data/tst_inputpanel.qml @@ -78,6 +78,8 @@ Rectangle { } function prepareTest(data) { + inputPanel.setWclAutoHideDelay(data !== undefined && data.hasOwnProperty("wclAutoHideDelay") ? data.wclAutoHideDelay : 5000) + inputPanel.setWclAlwaysVisible(data !== undefined && data.hasOwnProperty("wclAlwaysVisible") && data.wclAlwaysVisible) container.forceActiveFocus() if (data !== undefined && data.hasOwnProperty("initText")) { textInput.text = data.initText @@ -172,6 +174,14 @@ Rectangle { property var locale: VirtualKeyboardSettings.locale; \ property var availableLocales: VirtualKeyboardSettings.availableLocales; \ property var activeLocales: VirtualKeyboardSettings.activeLocales }" }, + { qml: "import QtQuick 2.7; \ + import QtQuick.VirtualKeyboard.Settings 2.2; \ + Item { property var styleName: VirtualKeyboardSettings.styleName; \ + property var locale: VirtualKeyboardSettings.locale; \ + property var availableLocales: VirtualKeyboardSettings.availableLocales; \ + property var activeLocales: VirtualKeyboardSettings.activeLocales; \ + property var wclAutoHideDelay: VirtualKeyboardSettings.wordCandidateList.autoHideDelay; \ + property var wclAlwaysVisible: VirtualKeyboardSettings.wordCandidateList.alwaysVisible; }" }, ] } @@ -1569,5 +1579,38 @@ Rectangle { compare(languagePopupList.visible, false) } + + function test_wclAutoHide_data() { + return [ + { wclAutoHideDelay: 100, wclAlwaysVisible: false }, + { wclAutoHideDelay: 0, wclAlwaysVisible: true }, + { wclAutoHideDelay: 0, wclAlwaysVisible: false }, + ] + } + + function test_wclAutoHide(data) { + prepareTest(data) + inputPanel.wordCandidateListChangedSpy.clear() + Qt.inputMethod.show() + waitForRendering(inputPanel) + compare(inputPanel.wordCandidateView.visibleCondition, data.wclAlwaysVisible) + inputPanel.virtualKeyClick("a") + inputPanel.virtualKeyClick("u") + inputPanel.virtualKeyClick("t") + inputPanel.virtualKeyClick("o") + waitForRendering(inputPanel) + if (!inputPanel.wordCandidateListVisibleHint) + skip("Prediction/spell correction not enabled") + inputPanel.wordCandidateListChangedSpy.wait(1000) + compare(inputPanel.wordCandidateView.visibleCondition, true) + inputPanel.wordCandidateListVisibleSpy.clear() + inputPanel.selectionListSelectCurrentItem() + if (data.wclAlwaysVisible) + wait(data.wclAutoHideDelay + 250) + else + inputPanel.wordCandidateListVisibleSpy.wait(data.wclAutoHideDelay + 500) + waitForRendering(inputPanel) + compare(inputPanel.wordCandidateView.visibleCondition, data.wclAlwaysVisible) + } } } |