diff options
24 files changed, 935 insertions, 886 deletions
diff --git a/examples/virtualkeyboard/basic/content/AutoScroller.qml b/examples/virtualkeyboard/basic/content/AutoScroller.qml index 2ea8efb0..b6b7924f 100644 --- a/examples/virtualkeyboard/basic/content/AutoScroller.qml +++ b/examples/virtualkeyboard/basic/content/AutoScroller.qml @@ -34,7 +34,7 @@ Item { property var innerFlickable property var outerFlickable - property var inputItem: InputContext.inputItem + property var inputItem: InputContext.priv.inputItem onInputItemChanged: { innerFlickable = null diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp index 956bafbd..b07ba0ec 100644 --- a/src/plugin/plugin.cpp +++ b/src/plugin/plugin.cpp @@ -31,6 +31,7 @@ #include "extensionloader.h" #include <QtVirtualKeyboard/inputcontext.h> #include <QtVirtualKeyboard/inputengine.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/private/shifthandler_p.h> #include <QtVirtualKeyboard/private/plaininputmethod_p.h> #include <QtVirtualKeyboard/private/inputmethod_p.h> @@ -102,6 +103,8 @@ QPlatformInputContext *QVirtualKeyboardPlugin::create(const QString &system, con qmlRegisterSingletonType<InputContext>(pluginUri, 1, 0, "InputContext", createInputContextModule); qmlRegisterSingletonType<InputContext>(pluginUri, 2, 0, "InputContext", createInputContextModule); + qRegisterMetaType<InputContextPrivate *>("InputContextPrivate*"); + qmlRegisterUncreatableType<InputContextPrivate>(pluginUri, 1, 0, "InputContextPrivate", QLatin1String("Cannot create input context private")); qRegisterMetaType<InputEngine *>("InputEngine*"); qmlRegisterUncreatableType<InputEngine>(pluginUri, 1, 0, "InputEngine", QLatin1String("Cannot create input method engine")); qmlRegisterUncreatableType<InputEngine>(pluginUri, 2, 0, "InputEngine", QLatin1String("Cannot create input method engine")); diff --git a/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp b/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp index 86877623..c6eb2157 100644 --- a/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp +++ b/src/plugins/lipi-toolkit/plugin/lipiinputmethod.cpp @@ -31,6 +31,7 @@ #include "lipisharedrecognizer_p.h" #include <QtVirtualKeyboard/inputengine.h> #include <QtVirtualKeyboard/inputcontext.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/private/shifthandler_p.h> #include <QLoggingCategory> #include <QtVirtualKeyboard/trace.h> @@ -303,7 +304,7 @@ public: } else if (swipeTouchCount == 2) { // Double swipe: toggle text case cancelRecognition(); - ic->shiftHandler()->toggleShift(); + ic->priv()->shiftHandler()->toggleShift(); } return; } diff --git a/src/virtualkeyboard/content/HandwritingInputPanel.qml b/src/virtualkeyboard/content/HandwritingInputPanel.qml index 8582f721..67bf0706 100644 --- a/src/virtualkeyboard/content/HandwritingInputPanel.qml +++ b/src/virtualkeyboard/content/HandwritingInputPanel.qml @@ -110,7 +110,7 @@ Item { } Binding { - target: InputContext + target: InputContext.priv property: "keyboardRectangle" value: Qt.rect(hwrInputArea.x, hwrInputArea.y, hwrInputArea.width, hwrInputArea.height) when: handwritingInputPanel.enabled && handwritingInputPanel.available && handwritingInputPanel.active diff --git a/src/virtualkeyboard/content/InputPanel.qml b/src/virtualkeyboard/content/InputPanel.qml index e41756a7..ef900af4 100644 --- a/src/virtualkeyboard/content/InputPanel.qml +++ b/src/virtualkeyboard/content/InputPanel.qml @@ -135,7 +135,7 @@ Item { } Binding { - target: InputContext + target: InputContext.priv property: "keyboardRectangle" value: mapToItem(null, __isRootItem ? keyboard.x : x, diff --git a/src/virtualkeyboard/content/components/EnterKey.qml b/src/virtualkeyboard/content/components/EnterKey.qml index 74b3984c..9f7c04da 100644 --- a/src/virtualkeyboard/content/components/EnterKey.qml +++ b/src/virtualkeyboard/content/components/EnterKey.qml @@ -45,13 +45,13 @@ BaseKey { /*! This property holds the action id for the enter key. */ - readonly property int actionId: InputContext.hasEnterKeyAction(InputContext.inputItem) ? InputContext.inputItem.EnterKeyAction.actionId : EnterKeyAction.None + readonly property int actionId: InputContext.priv.hasEnterKeyAction(InputContext.priv.inputItem) ? InputContext.priv.inputItem.EnterKeyAction.actionId : EnterKeyAction.None text: "\n" - displayText: InputContext.hasEnterKeyAction(InputContext.inputItem) ? InputContext.inputItem.EnterKeyAction.label : "" + displayText: InputContext.priv.hasEnterKeyAction(InputContext.priv.inputItem) ? InputContext.priv.inputItem.EnterKeyAction.label : "" key: Qt.Key_Return showPreview: false highlighted: enabled && displayText.length > 0 - enabled: InputContext.hasEnterKeyAction(InputContext.inputItem) ? InputContext.inputItem.EnterKeyAction.enabled : true + enabled: InputContext.priv.hasEnterKeyAction(InputContext.priv.inputItem) ? InputContext.priv.inputItem.EnterKeyAction.enabled : true keyPanelDelegate: keyboard.style ? keyboard.style.enterKeyPanel : undefined } diff --git a/src/virtualkeyboard/content/components/HideKeyboardKey.qml b/src/virtualkeyboard/content/components/HideKeyboardKey.qml index e9de1002..ab38fbc2 100644 --- a/src/virtualkeyboard/content/components/HideKeyboardKey.qml +++ b/src/virtualkeyboard/content/components/HideKeyboardKey.qml @@ -43,6 +43,6 @@ import QtQuick.VirtualKeyboard 2.1 BaseKey { functionKey: true - onClicked: InputContext.hideInputPanel() + onClicked: InputContext.priv.hideInputPanel() keyPanelDelegate: keyboard.style ? keyboard.style.hideKeyPanel : undefined } diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml index 47b096e7..1290502e 100644 --- a/src/virtualkeyboard/content/components/Keyboard.qml +++ b/src/virtualkeyboard/content/components/Keyboard.qml @@ -126,7 +126,7 @@ Item { } onInputLocaleChanged: { if (Qt.locale(inputLocale).name !== "C") - InputContext.locale = inputLocale + InputContext.priv.locale = inputLocale } onLayoutChanged: hideLanguagePopup() onLayoutTypeChanged: { @@ -150,17 +150,20 @@ Item { Connections { target: InputContext + onInputMethodHintsChanged: { + if (InputContext.priv.focus) + updateInputMethod() + } + } + Connections { + target: InputContext.priv onInputItemChanged: { keyboard.hideLanguagePopup() if (active && symbolMode && !preferNumbers) symbolMode = false } onFocusChanged: { - if (InputContext.focus) - updateInputMethod() - } - onInputMethodHintsChanged: { - if (InputContext.focus) + if (InputContext.priv.focus) updateInputMethod() } onNavigationKeyPressed: { @@ -424,7 +427,7 @@ Item { keyboard.symbolMode = false } else if (key === Qt.Key_Space) { var surroundingText = InputContext.surroundingText.trim() - if (InputContext.shiftHandler.sentenceEndingCharacters.indexOf(surroundingText.charAt(surroundingText.length-1)) >= 0) + if (InputContext.priv.shiftHandler.sentenceEndingCharacters.indexOf(surroundingText.charAt(surroundingText.length-1)) >= 0) keyboard.symbolMode = false } } @@ -453,8 +456,8 @@ Item { alternativeKeys.listView.height + verticalMargin * 2) onVisibleChanged: { if (visible) - InputContext.previewRectangle = Qt.binding(function() {return previewRect}) - InputContext.previewVisible = visible + InputContext.priv.previewRectangle = Qt.binding(function() {return previewRect}) + InputContext.priv.previewVisible = visible } } Timer { @@ -513,19 +516,19 @@ Item { characterPreview.height) } Binding { - target: InputContext + target: InputContext.priv property: "previewRectangle" value: characterPreview.previewRect when: characterPreview.visible } Binding { - target: InputContext + target: InputContext.priv property: "previewRectangle" value: languagePopupList.previewRect when: languagePopupListActive } Binding { - target: InputContext + target: InputContext.priv property: "previewVisible" value: characterPreview.visible || languagePopupListActive } @@ -622,7 +625,7 @@ Item { SelectionControl { objectName: "fullScreenModeSelectionControl" - inputContext: InputContext.shadow + inputContext: InputContext.priv.shadow anchors.top: shadowInputControl.top anchors.left: shadowInputControl.left enabled: keyboard.enabled && fullScreenMode @@ -668,7 +671,7 @@ Item { } } Connections { - target: InputContext + target: InputContext.priv onInputItemChanged: wordCandidateViewAutoHideTimer.stop() } Connections { @@ -1278,7 +1281,7 @@ Item { function updateInputMethod() { if (!keyboardLayoutLoader.item) return - if (!InputContext.focus) + if (!InputContext.priv.focus) return // Reset the custom input method if it is not included in the list of shared layouts @@ -1364,7 +1367,7 @@ Item { } // Clear the toggle shift timer - InputContext.shiftHandler.clearToggleShiftTimer() + InputContext.priv.shiftHandler.clearToggleShiftTimer() } function updateLayout() { @@ -1437,7 +1440,7 @@ Item { newIndices.sort(function(a, b) { return a - b }) availableLocaleIndices = newIndices newAvailableLocales.sort() - InputContext.updateAvailableLocales(newAvailableLocales) + InputContext.priv.updateAvailableLocales(newAvailableLocales) // Update list of custom locale indices newIndices = [] @@ -1544,20 +1547,20 @@ Item { } function layoutExists(localeName, layoutType) { - var result = InputContext.fileExists(getLayoutFile(localeName, layoutType)) + var result = InputContext.priv.fileExists(getLayoutFile(localeName, layoutType)) if (!result && layoutType === "handwriting") - result = InputContext.fileExists(getFallbackFile(localeName, layoutType)) + result = InputContext.priv.fileExists(getFallbackFile(localeName, layoutType)) return result } function findLayout(localeName, layoutType) { var layoutFile = getLayoutFile(localeName, layoutType) - if (InputContext.fileExists(layoutFile)) + if (InputContext.priv.fileExists(layoutFile)) return layoutFile var fallbackFile = getFallbackFile(localeName, layoutType) - if (InputContext.fileExists(fallbackFile)) { + if (InputContext.priv.fileExists(fallbackFile)) { layoutFile = getLayoutFile("fallback", layoutType) - if (InputContext.fileExists(layoutFile)) + if (InputContext.priv.fileExists(layoutFile)) return layoutFile } return "" diff --git a/src/virtualkeyboard/content/components/ShadowInputControl.qml b/src/virtualkeyboard/content/components/ShadowInputControl.qml index b935d5c6..a059c40e 100644 --- a/src/virtualkeyboard/content/components/ShadowInputControl.qml +++ b/src/virtualkeyboard/content/components/ShadowInputControl.qml @@ -40,8 +40,8 @@ Item { anchors.fill: parent } - onXChanged: InputContext.shadow.updateSelectionProperties() - onYChanged: InputContext.shadow.updateSelectionProperties() + onXChanged: InputContext.priv.shadow.updateSelectionProperties() + onYChanged: InputContext.priv.shadow.updateSelectionProperties() Loader { sourceComponent: keyboard.style.fullScreenInputContainerBackground @@ -61,7 +61,7 @@ Item { flickableDirection: Flickable.HorizontalFlick interactive: contentWidth > width contentWidth: shadowInput.width - onContentXChanged: InputContext.shadow.updateSelectionProperties() + onContentXChanged: InputContext.priv.shadow.updateSelectionProperties() function ensureVisible(rectangle) { if (contentX >= rectangle.x) @@ -113,7 +113,7 @@ Item { onTriggered: { var anchorPosition = shadowInput.getAnchorPosition() if (anchorPosition !== InputContext.anchorPosition || shadowInput.cursorPosition !== InputContext.cursorPosition) - InputContext.forceCursorPosition(anchorPosition, shadowInput.cursorPosition) + InputContext.priv.forceCursorPosition(anchorPosition, shadowInput.cursorPosition) } } @@ -130,7 +130,7 @@ Item { } Binding { - target: InputContext.shadow + target: InputContext.priv.shadow property: "inputItem" value: shadowInput when: VirtualKeyboardSettings.fullScreenMode diff --git a/src/virtualkeyboard/content/components/ShiftKey.qml b/src/virtualkeyboard/content/components/ShiftKey.qml index d7705d9f..93a1ae58 100644 --- a/src/virtualkeyboard/content/components/ShiftKey.qml +++ b/src/virtualkeyboard/content/components/ShiftKey.qml @@ -44,9 +44,9 @@ import QtQuick.VirtualKeyboard 2.1 BaseKey { id: shiftKey key: Qt.Key_Shift - enabled: InputContext.shiftHandler.toggleShiftEnabled + enabled: InputContext.priv.shiftHandler.toggleShiftEnabled highlighted: InputContext.capsLock functionKey: true keyPanelDelegate: keyboard.style ? keyboard.style.shiftKeyPanel : undefined - onClicked: InputContext.shiftHandler.toggleShift() + onClicked: InputContext.priv.shiftHandler.toggleShift() } diff --git a/src/virtualkeyboard/desktopinputpanel.cpp b/src/virtualkeyboard/desktopinputpanel.cpp index e299b59d..907e4e62 100644 --- a/src/virtualkeyboard/desktopinputpanel.cpp +++ b/src/virtualkeyboard/desktopinputpanel.cpp @@ -31,6 +31,7 @@ #include <QtVirtualKeyboard/private/appinputpanel_p_p.h> #include <QtVirtualKeyboard/private/inputview_p.h> #include <QtVirtualKeyboard/private/platforminputcontext_p.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/inputcontext.h> #include <QGuiApplication> #include <QQmlEngine> @@ -175,8 +176,9 @@ void DesktopInputPanel::repositionView(const QRect &rect) if (inputContext) { inputContext->setAnimating(true); if (!d->previewBindingActive) { - connect(inputContext, SIGNAL(previewRectangleChanged()), SLOT(previewRectangleChanged())); - connect(inputContext, SIGNAL(previewVisibleChanged()), SLOT(previewVisibleChanged())); + InputContextPrivate *inputContextPrivate = inputContext->priv(); + QObject::connect(inputContextPrivate, &InputContextPrivate::previewRectangleChanged, this, &DesktopInputPanel::previewRectangleChanged); + QObject::connect(inputContextPrivate, &InputContextPrivate::previewVisibleChanged, this, &DesktopInputPanel::previewVisibleChanged); d->previewBindingActive = true; } } @@ -201,7 +203,7 @@ void DesktopInputPanel::focusWindowVisibleChanged(bool visible) if (!visible) { InputContext *inputContext = qobject_cast<PlatformInputContext *>(parent())->inputContext(); if (inputContext) - inputContext->hideInputPanel(); + inputContext->priv()->hideInputPanel(); } } @@ -209,7 +211,7 @@ void DesktopInputPanel::previewRectangleChanged() { Q_D(DesktopInputPanel); InputContext *inputContext = qobject_cast<PlatformInputContext *>(parent())->inputContext(); - d->previewRect = inputContext->previewRectangle(); + d->previewRect = inputContext->priv()->previewRectangle(); if (d->previewVisible) updateInputRegion(); } @@ -218,7 +220,7 @@ void DesktopInputPanel::previewVisibleChanged() { Q_D(DesktopInputPanel); InputContext *inputContext = qobject_cast<PlatformInputContext *>(parent())->inputContext(); - d->previewVisible = inputContext->previewVisible(); + d->previewVisible = inputContext->priv()->previewVisible(); if (d->view->isVisible()) updateInputRegion(); } diff --git a/src/virtualkeyboard/desktopinputselectioncontrol.cpp b/src/virtualkeyboard/desktopinputselectioncontrol.cpp index f17b62b6..98dee491 100644 --- a/src/virtualkeyboard/desktopinputselectioncontrol.cpp +++ b/src/virtualkeyboard/desktopinputselectioncontrol.cpp @@ -29,6 +29,7 @@ #include <QtVirtualKeyboard/private/desktopinputselectioncontrol_p.h> #include <QtVirtualKeyboard/inputcontext.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/private/inputselectionhandle_p.h> #include <QtVirtualKeyboard/private/settings_p.h> #include <QtVirtualKeyboard/private/platforminputcontext_p.h> @@ -123,7 +124,7 @@ void DesktopInputSelectionControl::updateVisibility() globalAnchorRectangle.moveTopLeft(tl); m_anchorHandleVisible = m_anchorHandleVisible && m_inputContext->anchorRectIntersectsClipRect() - && !(m_inputContext->keyboardRectangle().intersects(globalAnchorRectangle)); + && !(m_inputContext->priv()->keyboardRectangle().intersects(globalAnchorRectangle)); } if (wasAnchorVisible != m_anchorHandleVisible) { @@ -142,7 +143,7 @@ void DesktopInputSelectionControl::updateVisibility() globalCursorRectangle.moveTopLeft(tl); m_cursorHandleVisible = m_cursorHandleVisible && m_inputContext->cursorRectIntersectsClipRect() - && !(m_inputContext->keyboardRectangle().intersects(globalCursorRectangle)); + && !(m_inputContext->priv()->keyboardRectangle().intersects(globalCursorRectangle)); } diff --git a/src/virtualkeyboard/inputcontext.cpp b/src/virtualkeyboard/inputcontext.cpp index 493f68e6..6c7774b0 100644 --- a/src/virtualkeyboard/inputcontext.cpp +++ b/src/virtualkeyboard/inputcontext.cpp @@ -28,27 +28,13 @@ ****************************************************************************/ #include <QtVirtualKeyboard/inputcontext.h> -#include <QtVirtualKeyboard/inputengine.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/private/shifthandler_p.h> #include <QtVirtualKeyboard/private/platforminputcontext_p.h> -#include <QtVirtualKeyboard/private/shadowinputcontext_p.h> #include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h> -#include <QtVirtualKeyboard/private/enterkeyaction_p.h> -#include <QtVirtualKeyboard/private/settings_p.h> #include <QTextFormat> #include <QGuiApplication> -#include <QtCore/private/qobject_p.h> - -QT_BEGIN_NAMESPACE - -bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2) -{ - return attribute1.start == attribute2.start && - attribute1.length == attribute2.length && - attribute1.type == attribute2.type && - attribute1.value == attribute2.value; -} /*! \namespace QtVirtualKeyboard @@ -57,86 +43,9 @@ bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMeth \brief Namespace for the Qt Virtual Keyboard C++ API. */ +QT_BEGIN_NAMESPACE namespace QtVirtualKeyboard { -class InputContextPrivate : public QObjectPrivate -{ -public: - enum StateFlag { - ReselectEventState = 0x1, - InputMethodEventState = 0x2, - KeyEventState = 0x4, - InputMethodClickState = 0x8, - SyncShadowInputState = 0x10 - }; - Q_DECLARE_FLAGS(StateFlags, StateFlag) - - InputContextPrivate() : - QObjectPrivate(), - inputContext(nullptr), - inputEngine(nullptr), - shiftHandler(nullptr), - keyboardRect(), - previewRect(), - previewVisible(false), - animating(false), - focus(false), - shift(false), - capsLock(false), - cursorPosition(0), - anchorPosition(0), - forceAnchorPosition(-1), - forceCursorPosition(-1), - inputMethodHints(Qt::ImhNone), - preeditText(), - preeditTextAttributes(), - surroundingText(), - selectedText(), - anchorRectangle(), - cursorRectangle(), - selectionControlVisible(false), - anchorRectIntersectsClipRect(false), - cursorRectIntersectsClipRect(false) -#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION - , activeNavigationKeys() -#endif - { - } - - PlatformInputContext *inputContext; - InputEngine *inputEngine; - ShiftHandler *shiftHandler; - QRectF keyboardRect; - QRectF previewRect; - bool previewVisible; - bool animating; - bool focus; - bool shift; - bool capsLock; - StateFlags stateFlags; - int cursorPosition; - int anchorPosition; - int forceAnchorPosition; - int forceCursorPosition; - Qt::InputMethodHints inputMethodHints; - QString preeditText; - QList<QInputMethodEvent::Attribute> preeditTextAttributes; - QString surroundingText; - QString selectedText; - QRectF anchorRectangle; - QRectF cursorRectangle; - bool selectionControlVisible; - bool anchorRectIntersectsClipRect; - bool cursorRectIntersectsClipRect; -#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION - QSet<int> activeNavigationKeys; -#endif - QSet<quint32> activeKeys; - ShadowInputContext shadow; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(InputContextPrivate::StateFlags) - /*! \qmltype InputContext \instantiates QtVirtualKeyboard::InputContext @@ -159,17 +68,14 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(InputContextPrivate::StateFlags) context. */ InputContext::InputContext(PlatformInputContext *parent) : - QObject(*new InputContextPrivate(), parent) + d_ptr(new InputContextPrivate(this, parent)) { Q_D(InputContext); - d->inputContext = parent; - d->shadow.setInputContext(this); - if (d->inputContext) { - d->inputContext->setInputContext(this); - connect(d->inputContext, SIGNAL(focusObjectChanged()), SLOT(onInputItemChanged())); - } - d->inputEngine = new InputEngine(this); - d->shiftHandler = new ShiftHandler(this); + d->init(); + QObject::connect(d->_shiftHandler, &ShiftHandler::shiftChanged, this, &InputContext::shiftChanged); + QObject::connect(d->_shiftHandler, &ShiftHandler::capsLockChanged, this, &InputContext::capsLockChanged); + QObject::connect(d->_shiftHandler, &ShiftHandler::uppercaseChanged, this, &InputContext::uppercaseChanged); + QObject::connect(d, &InputContextPrivate::localeChanged, this, &InputContext::localeChanged); } /*! @@ -180,50 +86,22 @@ InputContext::~InputContext() { } -bool InputContext::focus() const -{ - Q_D(const InputContext); - return d->focus; -} - bool InputContext::shift() const { Q_D(const InputContext); - return d->shift; -} - -void InputContext::setShift(bool enable) -{ - Q_D(InputContext); - if (d->shift != enable) { - d->shift = enable; - emit shiftChanged(); - if (!d->capsLock) - emit uppercaseChanged(); - } + return d->_shiftHandler->shift(); } bool InputContext::capsLock() const { Q_D(const InputContext); - return d->capsLock; -} - -void InputContext::setCapsLock(bool enable) -{ - Q_D(InputContext); - if (d->capsLock != enable) { - d->capsLock = enable; - emit capsLockChanged(); - if (!d->shift) - emit uppercaseChanged(); - } + return d->_shiftHandler->capsLock(); } bool InputContext::uppercase() const { Q_D(const InputContext); - return d->shift || d->capsLock; + return d->_shiftHandler->uppercase(); } int InputContext::anchorPosition() const @@ -255,16 +133,16 @@ void InputContext::setPreeditText(const QString &text, QList<QInputMethodEvent:: Q_D(InputContext); // Add default attributes if (!text.isEmpty()) { - if (!testAttribute(attributes, QInputMethodEvent::TextFormat)) { + if (!d->testAttribute(attributes, QInputMethodEvent::TextFormat)) { QTextCharFormat textFormat; textFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, text.length(), textFormat)); } - } else if (d->forceCursorPosition != -1) { - addSelectionAttribute(attributes); + } else if (d->_forceCursorPosition != -1) { + d->addSelectionAttribute(attributes); } - sendPreedit(text, attributes, replaceFrom, replaceLength); + d->sendPreedit(text, attributes, replaceFrom, replaceLength); } QList<QInputMethodEvent::Attribute> InputContext::preeditTextAttributes() const @@ -297,52 +175,6 @@ QRectF InputContext::cursorRectangle() const return d->cursorRectangle; } -QRectF InputContext::keyboardRectangle() const -{ - Q_D(const InputContext); - return d->keyboardRect; -} - -void InputContext::setKeyboardRectangle(QRectF rectangle) -{ - Q_D(InputContext); - if (d->keyboardRect != rectangle) { - d->keyboardRect = rectangle; - emit keyboardRectangleChanged(); - d->inputContext->emitKeyboardRectChanged(); - } -} - -QRectF InputContext::previewRectangle() const -{ - Q_D(const InputContext); - return d->previewRect; -} - -void InputContext::setPreviewRectangle(QRectF rectangle) -{ - Q_D(InputContext); - if (d->previewRect != rectangle) { - d->previewRect = rectangle; - emit previewRectangleChanged(); - } -} - -bool InputContext::previewVisible() const -{ - Q_D(const InputContext); - return d->previewVisible; -} - -void InputContext::setPreviewVisible(bool visible) -{ - Q_D(InputContext); - if (d->previewVisible != visible) { - d->previewVisible = visible; - emit previewVisibleChanged(); - } -} - bool InputContext::animating() const { Q_D(const InputContext); @@ -356,49 +188,14 @@ void InputContext::setAnimating(bool animating) VIRTUALKEYBOARD_DEBUG() << "InputContext::setAnimating():" << animating; d->animating = animating; emit animatingChanged(); - d->inputContext->emitAnimatingChanged(); + d->platformInputContext->emitAnimatingChanged(); } } - QString InputContext::locale() const { Q_D(const InputContext); - return d->inputContext->locale().name(); -} - -void InputContext::setLocale(const QString &locale) -{ - Q_D(InputContext); - VIRTUALKEYBOARD_DEBUG() << "InputContext::setLocale():" << locale; - QLocale newLocale(locale); - if (newLocale != d->inputContext->locale()) { - d->inputContext->setLocale(newLocale); - d->inputContext->setInputDirection(newLocale.textDirection()); - emit localeChanged(); - } -} - -/*! - \internal -*/ -void InputContext::updateAvailableLocales(const QStringList &availableLocales) -{ - Settings *settings = Settings::instance(); - if (settings) - settings->setAvailableLocales(availableLocales); -} - -QObject *InputContext::inputItem() const -{ - Q_D(const InputContext); - return d->inputContext ? d->inputContext->focusObject() : nullptr; -} - -ShiftHandler *InputContext::shiftHandler() const -{ - Q_D(const InputContext); - return d->shiftHandler; + return d->locale(); } InputEngine *InputContext::inputEngine() const @@ -408,26 +205,6 @@ InputEngine *InputContext::inputEngine() const } /*! - \qmlmethod void InputContext::hideInputPanel() - - This method hides the input panel. This method should only be called - when the user initiates the hide, e.g. by pressing a dedicated button - on the keyboard. -*/ -/*! - \fn void QtVirtualKeyboard::InputContext::hideInputPanel() - - This method hides the input panel. This method should only be called - when the user initiates the hide, e.g. by pressing a dedicated button - on the keyboard. -*/ -void InputContext::hideInputPanel() -{ - Q_D(InputContext); - d->inputContext->hideInputPanel(); -} - -/*! \qmlmethod void InputContext::sendKeyClick(int key, string text, int modifiers = 0) Sends a key click event with the given \a key, \a text and \a modifiers to @@ -440,14 +217,14 @@ void InputContext::hideInputPanel() void InputContext::sendKeyClick(int key, const QString &text, int modifiers) { Q_D(InputContext); - if (d->focus && d->inputContext) { + if (d->_focus && d->platformInputContext) { QKeyEvent pressEvent(QEvent::KeyPress, key, Qt::KeyboardModifiers(modifiers), text); QKeyEvent releaseEvent(QEvent::KeyRelease, key, Qt::KeyboardModifiers(modifiers), text); VIRTUALKEYBOARD_DEBUG() << "InputContext::sendKeyClick():" << key; d->stateFlags |= InputContextPrivate::KeyEventState; - d->inputContext->sendKeyEvent(&pressEvent); - d->inputContext->sendKeyEvent(&releaseEvent); + d->platformInputContext->sendKeyEvent(&pressEvent); + d->platformInputContext->sendKeyEvent(&releaseEvent); if (d->activeKeys.isEmpty()) d->stateFlags &= ~InputContextPrivate::KeyEventState; } else { @@ -495,15 +272,15 @@ void InputContext::commit(const QString &text, int replaceFrom, int replaceLengt VIRTUALKEYBOARD_DEBUG() << "InputContext::commit():" << text << replaceFrom << replaceLength; bool preeditChanged = !d->preeditText.isEmpty(); - if (d->inputContext) { + if (d->platformInputContext) { QList<QInputMethodEvent::Attribute> attributes; - addSelectionAttribute(attributes); + d->addSelectionAttribute(attributes); d->preeditText.clear(); d->preeditTextAttributes.clear(); QInputMethodEvent inputEvent(QString(), attributes); inputEvent.setCommitString(text, replaceFrom, replaceLength); d->stateFlags |= InputContextPrivate::InputMethodEventState; - d->inputContext->sendEvent(&inputEvent); + d->platformInputContext->sendEvent(&inputEvent); d->stateFlags &= ~InputContextPrivate::InputMethodEventState; } else { d->preeditText.clear(); @@ -531,12 +308,12 @@ void InputContext::clear() d->preeditText.clear(); d->preeditTextAttributes.clear(); - if (d->inputContext) { + if (d->platformInputContext) { QList<QInputMethodEvent::Attribute> attributes; - addSelectionAttribute(attributes); + d->addSelectionAttribute(attributes); QInputMethodEvent event(QString(), attributes); d->stateFlags |= InputContextPrivate::InputMethodEventState; - d->inputContext->sendEvent(&event); + d->platformInputContext->sendEvent(&event); d->stateFlags &= ~InputContextPrivate::InputMethodEventState; } @@ -547,69 +324,11 @@ void InputContext::clear() /*! \internal */ -bool InputContext::fileExists(const QUrl &fileUrl) -{ - QString fileName; - if (fileUrl.scheme() == QLatin1String("qrc")) { - fileName = QLatin1Char(':') + fileUrl.path(); - } else { - fileName = fileUrl.toLocalFile(); - } - return !fileName.isEmpty() && QFile::exists(fileName); -} - -/*! - \internal -*/ -bool InputContext::hasEnterKeyAction(QObject *item) const -{ - return item != nullptr && qmlAttachedPropertiesObject<EnterKeyAction>(item, false); -} - -/*! - \internal -*/ void InputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos) { QPlatformInputContext::setSelectionOnFocusObject(anchorPos, cursorPos); } -/*! - \internal -*/ -void InputContext::forceCursorPosition(int anchorPosition, int cursorPosition) -{ - Q_D(InputContext); - if (!d->shadow.inputItem()) - return; - if (!d->inputContext->m_visible) - return; - if (d->stateFlags.testFlag(InputContextPrivate::ReselectEventState)) - return; - if (d->stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) - return; - - VIRTUALKEYBOARD_DEBUG() << "InputContext::forceCursorPosition():" << cursorPosition << "anchorPosition:" << anchorPosition; - if (!d->preeditText.isEmpty()) { - d->forceAnchorPosition = -1; - d->forceCursorPosition = cursorPosition; - if (cursorPosition > d->cursorPosition) - d->forceCursorPosition += d->preeditText.length(); - d->inputEngine->update(); - } else { - d->forceAnchorPosition = anchorPosition; - d->forceCursorPosition = cursorPosition; - setPreeditText(""); - if (!d->inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && - cursorPosition > 0 && d->selectedText.isEmpty()) { - d->stateFlags |= InputContextPrivate::ReselectEventState; - if (d->inputEngine->reselect(cursorPosition, InputEngine::WordAtCursor)) - d->stateFlags |= InputContextPrivate::InputMethodClickState; - d->stateFlags &= ~InputContextPrivate::ReselectEventState; - } - } -} - bool InputContext::anchorRectIntersectsClipRect() const { Q_D(const InputContext); @@ -628,334 +347,13 @@ bool InputContext::selectionControlVisible() const return d->selectionControlVisible; } -ShadowInputContext *InputContext::shadow() const +InputContextPrivate *InputContext::priv() const { Q_D(const InputContext); - return const_cast<ShadowInputContext *>(&d->shadow); -} - -void InputContext::onInputItemChanged() -{ - Q_D(InputContext); - if (!inputItem() && !d->activeKeys.isEmpty()) { - // After losing keyboard focus it is impossible to track pressed keys - d->activeKeys.clear(); - d->stateFlags &= ~InputContextPrivate::KeyEventState; - } - d->stateFlags &= ~InputContextPrivate::InputMethodClickState; - - emit inputItemChanged(); -} - -void InputContext::setFocus(bool enable) -{ - Q_D(InputContext); - if (d->focus != enable) { - VIRTUALKEYBOARD_DEBUG() << "InputContext::setFocus():" << enable; - d->focus = enable; - emit focusChanged(); - } - emit focusEditorChanged(); -} - -void InputContext::sendPreedit(const QString &text, const QList<QInputMethodEvent::Attribute> &attributes, int replaceFrom, int replaceLength) -{ - Q_D(InputContext); - VIRTUALKEYBOARD_DEBUG() << "InputContext::sendPreedit():" << text << replaceFrom << replaceLength; - - bool textChanged = d->preeditText != text; - bool attributesChanged = d->preeditTextAttributes != attributes; - - if (textChanged || attributesChanged) { - d->preeditText = text; - d->preeditTextAttributes = attributes; - - if (d->inputContext) { - QInputMethodEvent event(text, attributes); - const bool replace = replaceFrom != 0 || replaceLength > 0; - if (replace) - event.setCommitString(QString(), replaceFrom, replaceLength); - d->stateFlags |= InputContextPrivate::InputMethodEventState; - d->inputContext->sendEvent(&event); - d->stateFlags &= ~InputContextPrivate::InputMethodEventState; - - // Send also to shadow input if only attributes changed. - // In this case the update() may not be called, so the shadow - // input may be out of sync. - if (d->shadow.inputItem() && !replace && !text.isEmpty() && - !textChanged && attributesChanged) { - VIRTUALKEYBOARD_DEBUG() << "InputContext::sendPreedit(shadow):" << text << replaceFrom << replaceLength; - event.setAccepted(true); - QGuiApplication::sendEvent(d->shadow.inputItem(), &event); - } - } - - if (textChanged) - emit preeditTextChanged(); - } - - if (d->preeditText.isEmpty()) - d->preeditTextAttributes.clear(); -} - -void InputContext::reset() -{ - Q_D(InputContext); - d->inputEngine->reset(); -} - -void InputContext::externalCommit() -{ - Q_D(InputContext); - d->inputEngine->update(); -} - -void InputContext::update(Qt::InputMethodQueries queries) -{ - Q_D(InputContext); - - // No need to fetch input clip rectangle during animation - if (!(queries & ~Qt::ImInputItemClipRectangle) && d->animating) - return; - - // fetch - QInputMethodQueryEvent imQueryEvent(Qt::InputMethodQueries(Qt::ImHints | - Qt::ImQueryInput | Qt::ImInputItemClipRectangle)); - d->inputContext->sendEvent(&imQueryEvent); - Qt::InputMethodHints inputMethodHints = Qt::InputMethodHints(imQueryEvent.value(Qt::ImHints).toInt()); - const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt(); - const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt(); - QRectF anchorRectangle; - QRectF cursorRectangle; - if (const QGuiApplication *app = qApp) { - anchorRectangle = app->inputMethod()->anchorRectangle(); - cursorRectangle = app->inputMethod()->cursorRectangle(); - } else { - anchorRectangle = d->anchorRectangle; - cursorRectangle = d->cursorRectangle; - } - QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString(); - QString selectedText = imQueryEvent.value(Qt::ImCurrentSelection).toString(); - - // check against changes - bool newInputMethodHints = inputMethodHints != d->inputMethodHints; - bool newSurroundingText = surroundingText != d->surroundingText; - bool newSelectedText = selectedText != d->selectedText; - bool newAnchorPosition = anchorPosition != d->anchorPosition; - bool newCursorPosition = cursorPosition != d->cursorPosition; - bool newAnchorRectangle = anchorRectangle != d->anchorRectangle; - bool newCursorRectangle = cursorRectangle != d->cursorRectangle; - bool selectionControlVisible = d->inputContext->isInputPanelVisible() && (cursorPosition != anchorPosition) && !inputMethodHints.testFlag(Qt::ImhNoTextHandles); - bool newSelectionControlVisible = selectionControlVisible != d->selectionControlVisible; - - QRectF inputItemClipRect = imQueryEvent.value(Qt::ImInputItemClipRectangle).toRectF(); - QRectF anchorRect = imQueryEvent.value(Qt::ImAnchorRectangle).toRectF(); - QRectF cursorRect = imQueryEvent.value(Qt::ImCursorRectangle).toRectF(); - - bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(anchorRect); - bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != d->anchorRectIntersectsClipRect; - - bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(cursorRect); - bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != d->cursorRectIntersectsClipRect; - - // update - d->inputMethodHints = inputMethodHints; - d->surroundingText = surroundingText; - d->selectedText = selectedText; - d->anchorPosition = anchorPosition; - d->cursorPosition = cursorPosition; - d->anchorRectangle = anchorRectangle; - d->cursorRectangle = cursorRectangle; - d->selectionControlVisible = selectionControlVisible; - d->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect; - d->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect; - - // update input engine - if ((newSurroundingText || newCursorPosition) && - !d->stateFlags.testFlag(InputContextPrivate::InputMethodEventState)) { - d->inputEngine->update(); - } - if (newInputMethodHints) { - d->inputEngine->reset(); - } - - // notify - if (newInputMethodHints) { - emit inputMethodHintsChanged(); - } - if (newSurroundingText) { - emit surroundingTextChanged(); - } - if (newSelectedText) { - emit selectedTextChanged(); - } - if (newAnchorPosition) { - emit anchorPositionChanged(); - } - if (newCursorPosition) { - emit cursorPositionChanged(); - } - if (newAnchorRectangle) { - emit anchorRectangleChanged(); - } - if (newCursorRectangle) { - emit cursorRectangleChanged(); - } - if (newSelectionControlVisible) { - emit selectionControlVisibleChanged(); - } - if (newAnchorRectIntersectsClipRect) { - emit anchorRectIntersectsClipRectChanged(); - } - if (newCursorRectIntersectsClipRect) { - emit cursorRectIntersectsClipRectChanged(); - } - - // word reselection - if (newInputMethodHints || newSurroundingText || newSelectedText) - d->stateFlags &= ~InputContextPrivate::InputMethodClickState; - if ((newSurroundingText || newCursorPosition) && !newSelectedText && (int)d->stateFlags == 0 && - !d->inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && - d->cursorPosition > 0 && d->selectedText.isEmpty()) { - d->stateFlags |= InputContextPrivate::ReselectEventState; - if (d->inputEngine->reselect(d->cursorPosition, InputEngine::WordAtCursor)) - d->stateFlags |= InputContextPrivate::InputMethodClickState; - d->stateFlags &= ~InputContextPrivate::ReselectEventState; - } - - if (!d->stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) { - d->stateFlags |= InputContextPrivate::SyncShadowInputState; - d->shadow.update(queries); - d->stateFlags &= ~InputContextPrivate::SyncShadowInputState; - } -} - -void InputContext::invokeAction(QInputMethod::Action action, int cursorPosition) -{ - Q_D(InputContext); - switch (action) { - case QInputMethod::Click: - if ((int)d->stateFlags == 0) { - if (d->inputEngine->clickPreeditText(cursorPosition)) - break; - - bool reselect = !d->inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && d->selectedText.isEmpty() && cursorPosition < d->preeditText.length(); - if (reselect) { - d->stateFlags |= InputContextPrivate::ReselectEventState; - d->forceCursorPosition = d->cursorPosition + cursorPosition; - d->inputEngine->update(); - d->inputEngine->reselect(d->cursorPosition, InputEngine::WordBeforeCursor); - d->stateFlags &= ~InputContextPrivate::ReselectEventState; - } else if (!d->preeditText.isEmpty() && cursorPosition == d->preeditText.length()) { - d->inputEngine->update(); - } - } - d->stateFlags &= ~InputContextPrivate::InputMethodClickState; - break; - - case QInputMethod::ContextMenu: - break; - } -} - -bool InputContext::filterEvent(const QEvent *event) -{ - QEvent::Type type = event->type(); - if (type == QEvent::KeyPress || type == QEvent::KeyRelease) { - Q_D(InputContext); - const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event); - - // Keep track of pressed keys update key event state - if (type == QEvent::KeyPress) - d->activeKeys += keyEvent->nativeScanCode(); - else if (type == QEvent::KeyRelease) - d->activeKeys -= keyEvent->nativeScanCode(); - - if (d->activeKeys.isEmpty()) - d->stateFlags &= ~InputContextPrivate::KeyEventState; - else - d->stateFlags |= InputContextPrivate::KeyEventState; - -#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION - int key = keyEvent->key(); - if ((key >= Qt::Key_Left && key <= Qt::Key_Down) || key == Qt::Key_Return) { - if (type == QEvent::KeyPress && d->inputContext->isInputPanelVisible()) { - d->activeNavigationKeys += key; - emit navigationKeyPressed(key, keyEvent->isAutoRepeat()); - return true; - } else if (type == QEvent::KeyRelease && d->activeNavigationKeys.contains(key)) { - d->activeNavigationKeys -= key; - emit navigationKeyReleased(key, keyEvent->isAutoRepeat()); - return true; - } - } -#endif - - // Break composing text since the virtual keyboard does not support hard keyboard events - if (!d->preeditText.isEmpty()) - d->inputEngine->update(); - } - return false; -} - -void InputContext::addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes) -{ - Q_D(InputContext); - if (!testAttribute(attributes, QInputMethodEvent::Selection)) { - // Convert Cursor attribute to Selection attribute. - // In this case the cursor is set in pre-edit text, but - // the cursor is not being forced to specific location. - if (d->forceCursorPosition == -1) { - int cursorAttributeIndex = findAttribute(d->preeditTextAttributes, QInputMethodEvent::Cursor); - if (cursorAttributeIndex != -1 && d->preeditTextAttributes[cursorAttributeIndex].length > 0) - d->forceCursorPosition = d->cursorPosition + d->preeditTextAttributes[cursorAttributeIndex].start; - d->forceAnchorPosition = -1; - } - - if (d->forceCursorPosition != -1) { - if (d->forceAnchorPosition != -1) - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceAnchorPosition, d->forceCursorPosition - d->forceAnchorPosition, QVariant())); - else - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceCursorPosition, 0, QVariant())); - } - } - d->forceAnchorPosition = -1; - d->forceCursorPosition = -1; -} - -bool InputContext::testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const -{ - for (const QInputMethodEvent::Attribute &attribute : qAsConst(attributes)) { - if (attribute.type == attributeType) - return true; - } - return false; -} - -int InputContext::findAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const -{ - const int count = attributes.count(); - for (int i = 0; i < count; ++i) { - if (attributes.at(i).type == attributeType) - return i; - } - return -1; + return const_cast<InputContextPrivate *>(d); } /*! - \qmlproperty bool InputContext::focus - - This property is changed when the input method receives or loses focus. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::focus - \brief the focus status. - - This property is changed when the input method receives or loses focus. -*/ - -/*! \qmlproperty bool InputContext::shift This property is changed when the shift status changes. @@ -1102,45 +500,6 @@ int InputContext::findAttribute(const QList<QInputMethodEvent::Attribute> &attri */ /*! - \qmlproperty rect InputContext::keyboardRectangle - - Use this property to set the keyboard rectangle. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::keyboardRectangle - \brief the keyboard rectangle. - - Use this property to set the keyboard rectangle. -*/ - -/*! - \qmlproperty rect InputContext::previewRectangle - - Use this property to set the preview rectangle. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::previewRectangle - \brief the preview rectangle. - - Use this property to set the preview rectangle. -*/ - -/*! - \qmlproperty bool InputContext::previewVisible - - Use this property to set the visibility status of the preview. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::previewVisible - \brief the animating status. - - Use this property to set the visibility status of the preview. -*/ - -/*! \qmlproperty bool InputContext::animating Use this property to set the animating status, for example @@ -1158,40 +517,14 @@ int InputContext::findAttribute(const QList<QInputMethodEvent::Attribute> &attri /*! \qmlproperty string InputContext::locale - Sets the locale for this input context. + This property is changed when the input locale changes. */ /*! \property QtVirtualKeyboard::InputContext::locale \brief the locale. - Sets the locale for this input context. -*/ - -/*! - \qmlproperty QtObject InputContext::inputItem - - This property is changed when the focused input item changes. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::inputItem - \brief the focused input item. - - This property is changed when the focused input item changes. -*/ - -/*! - \qmlproperty ShiftHandler InputContext::shiftHandler - - This property stores the shift handler. -*/ - -/*! - \property QtVirtualKeyboard::InputContext::shiftHandler - \brief the shift handler instance. - - This property stores the shift handler. + This property is changed when the input locale changes. */ /*! @@ -1207,27 +540,5 @@ int InputContext::findAttribute(const QList<QInputMethodEvent::Attribute> &attri This property stores the input engine. */ -/*! - \qmlsignal InputContext::focusEditorChanged() - - This signal is emitted when the focus editor changes. -*/ - -/*! - \fn void QtVirtualKeyboard::InputContext::focusEditorChanged() - - This signal is emitted when the focus editor changes. -*/ - -/*! - \fn void QtVirtualKeyboard::InputContext::navigationKeyPressed(int key, bool isAutoRepeat) - \internal -*/ - -/*! - \fn void QtVirtualKeyboard::InputContext::navigationKeyReleased(int key, bool isAutoRepeat) - \internal -*/ - } // namespace QtVirtualKeyboard QT_END_NAMESPACE diff --git a/src/virtualkeyboard/inputcontext.h b/src/virtualkeyboard/inputcontext.h index fc2010e0..6b2ba379 100644 --- a/src/virtualkeyboard/inputcontext.h +++ b/src/virtualkeyboard/inputcontext.h @@ -41,9 +41,7 @@ QT_BEGIN_NAMESPACE namespace QtVirtualKeyboard { class PlatformInputContext; -class ShadowInputContext; class InputEngine; -class ShiftHandler; class InputContextPrivate; class QVIRTUALKEYBOARD_EXPORT InputContext : public QObject @@ -51,9 +49,8 @@ class QVIRTUALKEYBOARD_EXPORT InputContext : public QObject Q_OBJECT Q_DISABLE_COPY(InputContext) Q_DECLARE_PRIVATE(InputContext) - Q_PROPERTY(bool focus READ focus NOTIFY focusChanged) - Q_PROPERTY(bool shift READ shift WRITE setShift NOTIFY shiftChanged) - Q_PROPERTY(bool capsLock READ capsLock WRITE setCapsLock NOTIFY capsLockChanged) + Q_PROPERTY(bool shift READ shift NOTIFY shiftChanged) + Q_PROPERTY(bool capsLock READ capsLock NOTIFY capsLockChanged) Q_PROPERTY(bool uppercase READ uppercase NOTIFY uppercaseChanged) Q_PROPERTY(int anchorPosition READ anchorPosition NOTIFY anchorPositionChanged) Q_PROPERTY(int cursorPosition READ cursorPosition NOTIFY cursorPositionChanged) @@ -63,28 +60,20 @@ class QVIRTUALKEYBOARD_EXPORT InputContext : public QObject Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged) Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) - Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle WRITE setKeyboardRectangle NOTIFY keyboardRectangleChanged) - Q_PROPERTY(QRectF previewRectangle READ previewRectangle WRITE setPreviewRectangle NOTIFY previewRectangleChanged) - Q_PROPERTY(bool previewVisible READ previewVisible WRITE setPreviewVisible NOTIFY previewVisibleChanged) Q_PROPERTY(bool animating READ animating WRITE setAnimating NOTIFY animatingChanged) - Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged) - Q_PROPERTY(QObject *inputItem READ inputItem NOTIFY inputItemChanged) - Q_PROPERTY(ShiftHandler *shiftHandler READ shiftHandler CONSTANT) + Q_PROPERTY(QString locale READ locale NOTIFY localeChanged) Q_PROPERTY(InputEngine *inputEngine READ inputEngine CONSTANT) Q_PROPERTY(bool selectionControlVisible READ selectionControlVisible NOTIFY selectionControlVisibleChanged) Q_PROPERTY(bool anchorRectIntersectsClipRect READ anchorRectIntersectsClipRect NOTIFY anchorRectIntersectsClipRectChanged) Q_PROPERTY(bool cursorRectIntersectsClipRect READ cursorRectIntersectsClipRect NOTIFY cursorRectIntersectsClipRectChanged) - Q_PROPERTY(ShadowInputContext *shadow READ shadow CONSTANT) + Q_PROPERTY(InputContextPrivate *priv READ priv CONSTANT) public: explicit InputContext(PlatformInputContext *parent = nullptr); ~InputContext(); - bool focus() const; bool shift() const; - void setShift(bool enable); bool capsLock() const; - void setCapsLock(bool enable); bool uppercase() const; int anchorPosition() const; int cursorPosition() const; @@ -96,44 +85,24 @@ public: QString selectedText() const; QRectF anchorRectangle() const; QRectF cursorRectangle() const; - QRectF keyboardRectangle() const; - void setKeyboardRectangle(QRectF rectangle); - QRectF previewRectangle() const; - void setPreviewRectangle(QRectF rectangle); - bool previewVisible() const; - void setPreviewVisible(bool visible); bool animating() const; void setAnimating(bool animating); QString locale() const; - void setLocale(const QString &locale); - Q_INVOKABLE void updateAvailableLocales(const QStringList &availableLocales); - QObject *inputItem() const; - ShiftHandler *shiftHandler() const; InputEngine *inputEngine() const; bool selectionControlVisible() const; bool anchorRectIntersectsClipRect() const; bool cursorRectIntersectsClipRect() const; - ShadowInputContext *shadow() const; + InputContextPrivate *priv() const; - Q_INVOKABLE void hideInputPanel(); Q_INVOKABLE void sendKeyClick(int key, const QString &text, int modifiers = 0); Q_INVOKABLE void commit(); Q_INVOKABLE void commit(const QString &text, int replaceFrom = 0, int replaceLength = 0); Q_INVOKABLE void clear(); - // Helper functions - Q_INVOKABLE bool fileExists(const QUrl &fileUrl); - Q_INVOKABLE bool hasEnterKeyAction(QObject *item) const; - // For selection handles Q_INVOKABLE void setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos); - // For shadow input - Q_INVOKABLE void forceCursorPosition(int anchorPosition, int cursorPosition); - Q_SIGNALS: - void focusChanged(); - void focusEditorChanged(); void preeditTextChanged(); void inputMethodHintsChanged(); void surroundingTextChanged(); @@ -145,35 +114,16 @@ Q_SIGNALS: void shiftChanged(); void capsLockChanged(); void uppercaseChanged(); - void keyboardRectangleChanged(); - void previewRectangleChanged(); - void previewVisibleChanged(); void animatingChanged(); void localeChanged(); - void inputItemChanged(); void selectionControlVisibleChanged(); - void navigationKeyPressed(int key, bool isAutoRepeat); - void navigationKeyReleased(int key, bool isAutoRepeat); void anchorRectIntersectsClipRectChanged(); void cursorRectIntersectsClipRectChanged(); -private Q_SLOTS: - void onInputItemChanged(); - private: - void setFocus(bool enable); - void sendPreedit(const QString &text, const QList<QInputMethodEvent::Attribute> &attributes, int replaceFrom, int replaceLength); - void reset(); - void externalCommit(); - void update(Qt::InputMethodQueries queries); - void invokeAction(QInputMethod::Action action, int cursorPosition); - bool filterEvent(const QEvent *event); - void addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes); - bool testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const; - int findAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const; + friend class InputContextPrivate; -private: - friend class PlatformInputContext; + QScopedPointer<InputContextPrivate> d_ptr; }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/inputcontext_p.cpp b/src/virtualkeyboard/inputcontext_p.cpp new file mode 100644 index 00000000..4f956645 --- /dev/null +++ b/src/virtualkeyboard/inputcontext_p.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtVirtualKeyboard/private/inputcontext_p.h> +#include <QtVirtualKeyboard/private/platforminputcontext_p.h> +#include <QtVirtualKeyboard/private/settings_p.h> +#include <QtVirtualKeyboard/private/shifthandler_p.h> +#include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h> +#include <QtVirtualKeyboard/private/enterkeyaction_p.h> +#include <QtVirtualKeyboard/inputengine.h> + +#include <QGuiApplication> + +QT_BEGIN_NAMESPACE + +bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2) +{ + return attribute1.start == attribute2.start && + attribute1.length == attribute2.length && + attribute1.type == attribute2.type && + attribute1.value == attribute2.value; +} + +namespace QtVirtualKeyboard { + +InputContextPrivate::InputContextPrivate(InputContext *q_ptr, PlatformInputContext *platformInputContext) : + QObject(nullptr), + q_ptr(q_ptr), + platformInputContext(platformInputContext), + inputEngine(nullptr), + _shiftHandler(nullptr), + keyboardRect(), + previewRect(), + _previewVisible(false), + animating(false), + _focus(false), + cursorPosition(0), + anchorPosition(0), + forceAnchorPosition(-1), + _forceCursorPosition(-1), + inputMethodHints(Qt::ImhNone), + preeditText(), + preeditTextAttributes(), + surroundingText(), + selectedText(), + anchorRectangle(), + cursorRectangle(), + selectionControlVisible(false), + anchorRectIntersectsClipRect(false), + cursorRectIntersectsClipRect(false) +#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION + , activeNavigationKeys() +#endif +{ +} + +void InputContextPrivate::init() +{ + Q_Q(InputContext); + Q_ASSERT(inputEngine == nullptr); + inputEngine = new InputEngine(q); + _shiftHandler = new ShiftHandler(q); + inputEngine->init(); + _shiftHandler->init(); + _shadow.setInputContext(q); + if (platformInputContext) { + platformInputContext->setInputContext(q); + QObject::connect(platformInputContext, &PlatformInputContext::focusObjectChanged, this, &InputContextPrivate::onInputItemChanged); + QObject::connect(platformInputContext, &PlatformInputContext::focusObjectChanged, this, &InputContextPrivate::inputItemChanged); + } +} + +InputContextPrivate::~InputContextPrivate() +{ +} + +bool InputContextPrivate::focus() const +{ + return _focus; +} + +void InputContextPrivate::setFocus(bool focus) +{ + if (_focus != focus) { + VIRTUALKEYBOARD_DEBUG() << "InputContextPrivate::setFocus():" << focus; + _focus = focus; + emit focusChanged(); + } +} + +QRectF InputContextPrivate::keyboardRectangle() const +{ + return keyboardRect; +} + +void InputContextPrivate::setKeyboardRectangle(QRectF rectangle) +{ + if (keyboardRect != rectangle) { + keyboardRect = rectangle; + emit keyboardRectangleChanged(); + platformInputContext->emitKeyboardRectChanged(); + } +} + +QRectF InputContextPrivate::previewRectangle() const +{ + return previewRect; +} + +void InputContextPrivate::setPreviewRectangle(QRectF rectangle) +{ + if (previewRect != rectangle) { + previewRect = rectangle; + emit previewRectangleChanged(); + } +} + +bool InputContextPrivate::previewVisible() const +{ + return _previewVisible; +} + +void InputContextPrivate::setPreviewVisible(bool visible) +{ + if (_previewVisible != visible) { + _previewVisible = visible; + emit previewVisibleChanged(); + } +} + +QString InputContextPrivate::locale() const +{ + return platformInputContext->locale().name(); +} + +void InputContextPrivate::setLocale(const QString &locale) +{ + VIRTUALKEYBOARD_DEBUG() << "InputContextPrivate::setLocale():" << locale; + QLocale newLocale(locale); + if (newLocale != platformInputContext->locale()) { + platformInputContext->setLocale(newLocale); + platformInputContext->setInputDirection(newLocale.textDirection()); + emit localeChanged(); + } +} + +QObject *InputContextPrivate::inputItem() const +{ + return platformInputContext ? platformInputContext->focusObject() : nullptr; +} + +ShiftHandler *InputContextPrivate::shiftHandler() const +{ + return _shiftHandler; +} + +ShadowInputContext *InputContextPrivate::shadow() const +{ + return const_cast<ShadowInputContext *>(&_shadow); +} + +bool InputContextPrivate::fileExists(const QUrl &fileUrl) +{ + QString fileName; + if (fileUrl.scheme() == QLatin1String("qrc")) { + fileName = QLatin1Char(':') + fileUrl.path(); + } else { + fileName = fileUrl.toLocalFile(); + } + return !fileName.isEmpty() && QFile::exists(fileName); +} + +bool InputContextPrivate::hasEnterKeyAction(QObject *item) const +{ + return item != nullptr && qmlAttachedPropertiesObject<EnterKeyAction>(item, false); +} + +void InputContextPrivate::hideInputPanel() +{ + platformInputContext->hideInputPanel(); +} + +void InputContextPrivate::updateAvailableLocales(const QStringList &availableLocales) +{ + Settings *settings = Settings::instance(); + if (settings) + settings->setAvailableLocales(availableLocales); +} + +void InputContextPrivate::forceCursorPosition(int anchorPosition, int cursorPosition) +{ + if (!_shadow.inputItem()) + return; + if (!platformInputContext->m_visible) + return; + if (stateFlags.testFlag(InputContextPrivate::ReselectEventState)) + return; + if (stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) + return; + + VIRTUALKEYBOARD_DEBUG() << "InputContextPrivate::forceCursorPosition():" << cursorPosition << "anchorPosition:" << anchorPosition; + if (!preeditText.isEmpty()) { + forceAnchorPosition = -1; + _forceCursorPosition = cursorPosition; + if (cursorPosition > this->cursorPosition) + _forceCursorPosition += preeditText.length(); + commit(); + } else { + forceAnchorPosition = anchorPosition; + _forceCursorPosition = cursorPosition; + Q_Q(InputContext); + q->setPreeditText(QString()); + if (!inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && + cursorPosition > 0 && selectedText.isEmpty()) { + stateFlags |= InputContextPrivate::ReselectEventState; + if (inputEngine->reselect(cursorPosition, InputEngine::WordAtCursor)) + stateFlags |= InputContextPrivate::InputMethodClickState; + stateFlags &= ~InputContextPrivate::ReselectEventState; + } + } +} + +void InputContextPrivate::onInputItemChanged() +{ + if (!inputItem() && !activeKeys.isEmpty()) { + // After losing keyboard focus it is impossible to track pressed keys + activeKeys.clear(); + stateFlags &= ~InputContextPrivate::KeyEventState; + } + stateFlags &= ~InputContextPrivate::InputMethodClickState; +} + +void InputContextPrivate::sendPreedit(const QString &text, const QList<QInputMethodEvent::Attribute> &attributes, int replaceFrom, int replaceLength) +{ + VIRTUALKEYBOARD_DEBUG() << "InputContextPrivate::sendPreedit():" << text << replaceFrom << replaceLength; + + bool textChanged = preeditText != text; + bool attributesChanged = preeditTextAttributes != attributes; + + if (textChanged || attributesChanged) { + preeditText = text; + preeditTextAttributes = attributes; + + if (platformInputContext) { + QInputMethodEvent event(text, attributes); + const bool replace = replaceFrom != 0 || replaceLength > 0; + if (replace) + event.setCommitString(QString(), replaceFrom, replaceLength); + stateFlags |= InputContextPrivate::InputMethodEventState; + platformInputContext->sendEvent(&event); + stateFlags &= ~InputContextPrivate::InputMethodEventState; + + // Send also to shadow input if only attributes changed. + // In this case the update() may not be called, so the shadow + // input may be out of sync. + if (_shadow.inputItem() && !replace && !text.isEmpty() && + !textChanged && attributesChanged) { + VIRTUALKEYBOARD_DEBUG() << "InputContextPrivate::sendPreedit(shadow):" << text << replaceFrom << replaceLength; + event.setAccepted(true); + QGuiApplication::sendEvent(_shadow.inputItem(), &event); + } + } + + if (textChanged) { + Q_Q(InputContext); + emit q->preeditTextChanged(); + } + } + + if (preeditText.isEmpty()) + preeditTextAttributes.clear(); +} + +void InputContextPrivate::reset() +{ + inputEngine->reset(); +} + +void InputContextPrivate::commit() +{ + inputEngine->update(); +} + +void InputContextPrivate::update(Qt::InputMethodQueries queries) +{ + Q_Q(InputContext); + + // No need to fetch input clip rectangle during animation + if (!(queries & ~Qt::ImInputItemClipRectangle) && animating) + return; + + // fetch + QInputMethodQueryEvent imQueryEvent(Qt::InputMethodQueries(Qt::ImHints | + Qt::ImQueryInput | Qt::ImInputItemClipRectangle)); + platformInputContext->sendEvent(&imQueryEvent); + Qt::InputMethodHints inputMethodHints = Qt::InputMethodHints(imQueryEvent.value(Qt::ImHints).toInt()); + const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt(); + const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt(); + QRectF anchorRectangle; + QRectF cursorRectangle; + if (const QGuiApplication *app = qApp) { + anchorRectangle = app->inputMethod()->anchorRectangle(); + cursorRectangle = app->inputMethod()->cursorRectangle(); + } else { + anchorRectangle = this->anchorRectangle; + cursorRectangle = this->cursorRectangle; + } + QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString(); + QString selectedText = imQueryEvent.value(Qt::ImCurrentSelection).toString(); + + // check against changes + bool newInputMethodHints = inputMethodHints != this->inputMethodHints; + bool newSurroundingText = surroundingText != this->surroundingText; + bool newSelectedText = selectedText != this->selectedText; + bool newAnchorPosition = anchorPosition != this->anchorPosition; + bool newCursorPosition = cursorPosition != this->cursorPosition; + bool newAnchorRectangle = anchorRectangle != this->anchorRectangle; + bool newCursorRectangle = cursorRectangle != this->cursorRectangle; + bool selectionControlVisible = platformInputContext->isInputPanelVisible() && (cursorPosition != anchorPosition) && !inputMethodHints.testFlag(Qt::ImhNoTextHandles); + bool newSelectionControlVisible = selectionControlVisible != this->selectionControlVisible; + + QRectF inputItemClipRect = imQueryEvent.value(Qt::ImInputItemClipRectangle).toRectF(); + QRectF anchorRect = imQueryEvent.value(Qt::ImAnchorRectangle).toRectF(); + QRectF cursorRect = imQueryEvent.value(Qt::ImCursorRectangle).toRectF(); + + bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(anchorRect); + bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != this->anchorRectIntersectsClipRect; + + bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(cursorRect); + bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != this->cursorRectIntersectsClipRect; + + // update + this->inputMethodHints = inputMethodHints; + this->surroundingText = surroundingText; + this->selectedText = selectedText; + this->anchorPosition = anchorPosition; + this->cursorPosition = cursorPosition; + this->anchorRectangle = anchorRectangle; + this->cursorRectangle = cursorRectangle; + this->selectionControlVisible = selectionControlVisible; + this->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect; + this->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect; + + // update input engine + if ((newSurroundingText || newCursorPosition) && + !stateFlags.testFlag(InputContextPrivate::InputMethodEventState)) { + commit(); + } + if (newInputMethodHints) { + reset(); + } + + // notify + if (newInputMethodHints) { + emit q->inputMethodHintsChanged(); + } + if (newSurroundingText) { + emit q->surroundingTextChanged(); + } + if (newSelectedText) { + emit q->selectedTextChanged(); + } + if (newAnchorPosition) { + emit q->anchorPositionChanged(); + } + if (newCursorPosition) { + emit q->cursorPositionChanged(); + } + if (newAnchorRectangle) { + emit q->anchorRectangleChanged(); + } + if (newCursorRectangle) { + emit q->cursorRectangleChanged(); + } + if (newSelectionControlVisible) { + emit q->selectionControlVisibleChanged(); + } + if (newAnchorRectIntersectsClipRect) { + emit q->anchorRectIntersectsClipRectChanged(); + } + if (newCursorRectIntersectsClipRect) { + emit q->cursorRectIntersectsClipRectChanged(); + } + + // word reselection + if (newInputMethodHints || newSurroundingText || newSelectedText) + stateFlags &= ~InputContextPrivate::InputMethodClickState; + if ((newSurroundingText || newCursorPosition) && !newSelectedText && (int)stateFlags == 0 && + !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && + cursorPosition > 0 && this->selectedText.isEmpty()) { + stateFlags |= InputContextPrivate::ReselectEventState; + if (inputEngine->reselect(cursorPosition, InputEngine::WordAtCursor)) + stateFlags |= InputContextPrivate::InputMethodClickState; + stateFlags &= ~InputContextPrivate::ReselectEventState; + } + + if (!stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) { + stateFlags |= InputContextPrivate::SyncShadowInputState; + _shadow.update(queries); + stateFlags &= ~InputContextPrivate::SyncShadowInputState; + } +} + +void InputContextPrivate::invokeAction(QInputMethod::Action action, int cursorPosition) +{ + switch (action) { + case QInputMethod::Click: + if ((int)stateFlags == 0) { + if (inputEngine->clickPreeditText(cursorPosition)) + break; + + bool reselect = !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && selectedText.isEmpty() && cursorPosition < preeditText.length(); + if (reselect) { + stateFlags |= InputContextPrivate::ReselectEventState; + _forceCursorPosition = this->cursorPosition + cursorPosition; + commit(); + inputEngine->reselect(this->cursorPosition, InputEngine::WordBeforeCursor); + stateFlags &= ~InputContextPrivate::ReselectEventState; + } else if (!preeditText.isEmpty() && cursorPosition == preeditText.length()) { + commit(); + } + } + stateFlags &= ~InputContextPrivate::InputMethodClickState; + break; + + case QInputMethod::ContextMenu: + break; + } +} + +bool InputContextPrivate::filterEvent(const QEvent *event) +{ + QEvent::Type type = event->type(); + if (type == QEvent::KeyPress || type == QEvent::KeyRelease) { + const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event); + + // Keep track of pressed keys update key event state + if (type == QEvent::KeyPress) + activeKeys += keyEvent->nativeScanCode(); + else if (type == QEvent::KeyRelease) + activeKeys -= keyEvent->nativeScanCode(); + + if (activeKeys.isEmpty()) + stateFlags &= ~InputContextPrivate::KeyEventState; + else + stateFlags |= InputContextPrivate::KeyEventState; + +#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION + int key = keyEvent->key(); + if ((key >= Qt::Key_Left && key <= Qt::Key_Down) || key == Qt::Key_Return) { + if (type == QEvent::KeyPress && platformInputContext->isInputPanelVisible()) { + activeNavigationKeys += key; + emit navigationKeyPressed(key, keyEvent->isAutoRepeat()); + return true; + } else if (type == QEvent::KeyRelease && activeNavigationKeys.contains(key)) { + activeNavigationKeys -= key; + emit navigationKeyReleased(key, keyEvent->isAutoRepeat()); + return true; + } + } +#endif + + // Break composing text since the virtual keyboard does not support hard keyboard events + if (!preeditText.isEmpty()) + commit(); + } + return false; +} + +void InputContextPrivate::addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes) +{ + if (!testAttribute(attributes, QInputMethodEvent::Selection)) { + // Convert Cursor attribute to Selection attribute. + // In this case the cursor is set in pre-edit text, but + // the cursor is not being forced to specific location. + if (_forceCursorPosition == -1) { + int cursorAttributeIndex = findAttribute(preeditTextAttributes, QInputMethodEvent::Cursor); + if (cursorAttributeIndex != -1 && preeditTextAttributes[cursorAttributeIndex].length > 0) + _forceCursorPosition = cursorPosition + preeditTextAttributes[cursorAttributeIndex].start; + forceAnchorPosition = -1; + } + + if (_forceCursorPosition != -1) { + if (forceAnchorPosition != -1) + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, forceAnchorPosition, _forceCursorPosition - forceAnchorPosition, QVariant())); + else + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, _forceCursorPosition, 0, QVariant())); + } + } + forceAnchorPosition = -1; + _forceCursorPosition = -1; +} + +bool InputContextPrivate::testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const +{ + for (const QInputMethodEvent::Attribute &attribute : qAsConst(attributes)) { + if (attribute.type == attributeType) + return true; + } + return false; +} + +int InputContextPrivate::findAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const +{ + const int count = attributes.count(); + for (int i = 0; i < count; ++i) { + if (attributes.at(i).type == attributeType) + return i; + } + return -1; +} + +} // namespace QtVirtualKeyboard +QT_END_NAMESPACE diff --git a/src/virtualkeyboard/inputcontext_p.h b/src/virtualkeyboard/inputcontext_p.h new file mode 100644 index 00000000..be0d6c2b --- /dev/null +++ b/src/virtualkeyboard/inputcontext_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INPUTCONTEXT_P_H +#define INPUTCONTEXT_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 <QObject> +#include <QRectF> +#include <QInputMethodEvent> +#include <QtVirtualKeyboard/inputcontext.h> +#include <QtVirtualKeyboard/private/shadowinputcontext_p.h> + +QT_BEGIN_NAMESPACE +namespace QtVirtualKeyboard { + +class PlatformInputContext; +class ShadowInputContext; +class InputEngine; +class ShiftHandler; +class InputContextPrivate; + +class QVIRTUALKEYBOARD_EXPORT InputContextPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(InputContext) + Q_PROPERTY(bool focus READ focus WRITE setFocus NOTIFY focusChanged) + Q_PROPERTY(QRectF keyboardRectangle READ keyboardRectangle WRITE setKeyboardRectangle NOTIFY keyboardRectangleChanged) + Q_PROPERTY(QRectF previewRectangle READ previewRectangle WRITE setPreviewRectangle NOTIFY previewRectangleChanged) + Q_PROPERTY(bool previewVisible READ previewVisible WRITE setPreviewVisible NOTIFY previewVisibleChanged) + Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged) + Q_PROPERTY(QObject *inputItem READ inputItem NOTIFY inputItemChanged) + Q_PROPERTY(ShiftHandler *shiftHandler READ shiftHandler CONSTANT) + Q_PROPERTY(ShadowInputContext *shadow READ shadow CONSTANT) + + explicit InputContextPrivate(InputContext *q_ptr, PlatformInputContext *platformInputContext); + void init(); + +public: + enum StateFlag { + ReselectEventState = 0x1, + InputMethodEventState = 0x2, + KeyEventState = 0x4, + InputMethodClickState = 0x8, + SyncShadowInputState = 0x10 + }; + Q_DECLARE_FLAGS(StateFlags, StateFlag) + + ~InputContextPrivate(); + + bool focus() const; + void setFocus(bool focus); + QRectF keyboardRectangle() const; + void setKeyboardRectangle(QRectF rectangle); + QRectF previewRectangle() const; + void setPreviewRectangle(QRectF rectangle); + bool previewVisible() const; + void setPreviewVisible(bool visible); + QString locale() const; + void setLocale(const QString &locale); + QObject *inputItem() const; + ShiftHandler *shiftHandler() const; + ShadowInputContext *shadow() const; + + // Helper functions + Q_INVOKABLE bool fileExists(const QUrl &fileUrl); + Q_INVOKABLE bool hasEnterKeyAction(QObject *item) const; + +Q_SIGNALS: + void focusChanged(); + void keyboardRectangleChanged(); + void previewRectangleChanged(); + void previewVisibleChanged(); + void localeChanged(); + void inputItemChanged(); + void navigationKeyPressed(int key, bool isAutoRepeat); + void navigationKeyReleased(int key, bool isAutoRepeat); + +public Q_SLOTS: + void hideInputPanel(); + void updateAvailableLocales(const QStringList &availableLocales); + void forceCursorPosition(int anchorPosition, int cursorPosition); + +private Q_SLOTS: + void onInputItemChanged(); + +private: + void sendPreedit(const QString &text, const QList<QInputMethodEvent::Attribute> &attributes, int replaceFrom, int replaceLength); + void reset(); + void commit(); + void update(Qt::InputMethodQueries queries); + void invokeAction(QInputMethod::Action action, int cursorPosition); + bool filterEvent(const QEvent *event); + void addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes); + bool testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const; + int findAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const; + +private: + InputContext *q_ptr; + PlatformInputContext *platformInputContext; + InputEngine *inputEngine; + ShiftHandler *_shiftHandler; + QRectF keyboardRect; + QRectF previewRect; + bool _previewVisible; + bool animating; + bool _focus; + StateFlags stateFlags; + int cursorPosition; + int anchorPosition; + int forceAnchorPosition; + int _forceCursorPosition; + Qt::InputMethodHints inputMethodHints; + QString preeditText; + QList<QInputMethodEvent::Attribute> preeditTextAttributes; + QString surroundingText; + QString selectedText; + QRectF anchorRectangle; + QRectF cursorRectangle; + bool selectionControlVisible; + bool anchorRectIntersectsClipRect; + bool cursorRectIntersectsClipRect; +#ifdef QT_VIRTUALKEYBOARD_ARROW_KEY_NAVIGATION + QSet<int> activeNavigationKeys; +#endif + QSet<quint32> activeKeys; + ShadowInputContext _shadow; + + friend class PlatformInputContext; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(InputContextPrivate::StateFlags) + +} // namespace QtVirtualKeyboard +QT_END_NAMESPACE + +#endif // INPUTCONTEXT_P_H diff --git a/src/virtualkeyboard/inputengine.cpp b/src/virtualkeyboard/inputengine.cpp index 84b1cf09..23956401 100644 --- a/src/virtualkeyboard/inputengine.cpp +++ b/src/virtualkeyboard/inputengine.cpp @@ -29,6 +29,8 @@ #include <QtVirtualKeyboard/inputengine.h> #include <QtVirtualKeyboard/inputcontext.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> +#include <QtVirtualKeyboard/private/shifthandler_p.h> #include <QtVirtualKeyboard/private/fallbackinputmethod_p.h> #include <QtVirtualKeyboard/trace.h> #include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h> @@ -154,13 +156,17 @@ InputEngine::InputEngine(InputContext *parent) : { Q_D(InputEngine); d->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); - QObject::connect(d->inputContext, &InputContext::localeChanged, this, &InputEngine::updateInputModes); - QObject::connect(this, &InputEngine::inputMethodChanged, this, &InputEngine::updateInputModes); - } +} + +void InputEngine::init() +{ + Q_D(InputEngine); + ShiftHandler *shiftHandler = d->inputContext->priv()->shiftHandler(); + QObject::connect(shiftHandler, &ShiftHandler::shiftChanged, this, &InputEngine::shiftChanged); + QObject::connect(d->inputContext, &InputContext::localeChanged, this, &InputEngine::update); + QObject::connect(d->inputContext, &InputContext::inputMethodHintsChanged, this, &InputEngine::updateSelectionListModels); + QObject::connect(d->inputContext, &InputContext::localeChanged, this, &InputEngine::updateInputModes); + QObject::connect(this, &InputEngine::inputMethodChanged, this, &InputEngine::updateInputModes); d->fallbackInputMethod = new FallbackInputMethod(this); if (d->fallbackInputMethod) d->fallbackInputMethod->setInputEngine(this); @@ -619,7 +625,7 @@ void InputEngine::update() void InputEngine::shiftChanged() { Q_D(InputEngine); - TextCase newCase = d->inputContext->shift() ? Upper : Lower; + TextCase newCase = d->inputContext->priv()->shiftHandler()->shift() ? Upper : Lower; if (d->textCase != newCase) { d->textCase = newCase; if (d->inputMethod) { diff --git a/src/virtualkeyboard/inputengine.h b/src/virtualkeyboard/inputengine.h index 80e13424..f4f5881c 100644 --- a/src/virtualkeyboard/inputengine.h +++ b/src/virtualkeyboard/inputengine.h @@ -59,6 +59,7 @@ class QVIRTUALKEYBOARD_EXPORT InputEngine : public QObject Q_PROPERTY(bool wordCandidateListVisibleHint READ wordCandidateListVisibleHint NOTIFY wordCandidateListVisibleHintChanged) explicit InputEngine(InputContext *parent = nullptr); + void init(); public: enum TextCase { @@ -155,6 +156,7 @@ protected: private: friend class InputContext; + friend class InputContextPrivate; }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/platforminputcontext.cpp b/src/virtualkeyboard/platforminputcontext.cpp index e4a1a736..f575a5c0 100644 --- a/src/virtualkeyboard/platforminputcontext.cpp +++ b/src/virtualkeyboard/platforminputcontext.cpp @@ -29,6 +29,7 @@ #include <QtVirtualKeyboard/private/platforminputcontext_p.h> #include <QtVirtualKeyboard/inputcontext.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/private/shadowinputcontext_p.h> #include <QtVirtualKeyboard/private/abstractinputpanel_p.h> #ifdef QT_VIRTUALKEYBOARD_DESKTOP @@ -74,14 +75,14 @@ void PlatformInputContext::reset() { VIRTUALKEYBOARD_DEBUG() << "PlatformInputContext::reset()"; if (m_inputContext) - m_inputContext->reset(); + m_inputContext->priv()->reset(); } void PlatformInputContext::commit() { VIRTUALKEYBOARD_DEBUG() << "PlatformInputContext::commit()"; if (m_inputContext) - m_inputContext->externalCommit(); + m_inputContext->priv()->commit(); } void PlatformInputContext::update(Qt::InputMethodQueries queries) @@ -99,13 +100,13 @@ void PlatformInputContext::update(Qt::InputMethodQueries queries) if (m_inputContext) { if (enabled) { - m_inputContext->update(queries); + m_inputContext->priv()->update(queries); if (m_visible) updateInputPanelVisible(); } else { hideInputPanel(); } - m_inputContext->setFocus(enabled); + m_inputContext->priv()->setFocus(enabled); } } @@ -113,12 +114,12 @@ void PlatformInputContext::invokeAction(QInputMethod::Action action, int cursorP { VIRTUALKEYBOARD_DEBUG() << "PlatformInputContext::invokeAction():" << action << cursorPosition; if (m_inputContext) - m_inputContext->invokeAction(action, cursorPosition); + m_inputContext->priv()->invokeAction(action, cursorPosition); } QRectF PlatformInputContext::keyboardRect() const { - return m_inputContext ? m_inputContext->keyboardRectangle() : QRectF(); + return m_inputContext ? m_inputContext->priv()->keyboardRectangle() : QRectF(); } bool PlatformInputContext::isAnimating() const @@ -186,8 +187,8 @@ void PlatformInputContext::setFocusObject(QObject *object) { VIRTUALKEYBOARD_DEBUG() << "PlatformInputContext::setFocusObject():" << object; Q_ASSERT(m_inputContext == nullptr || - m_inputContext->shadow()->inputItem() == nullptr || - m_inputContext->shadow()->inputItem() != object); + m_inputContext->priv()->shadow()->inputItem() == nullptr || + m_inputContext->priv()->shadow()->inputItem() != object); if (m_focusObject != object) { if (m_focusObject) m_focusObject->removeEventFilter(this); @@ -207,7 +208,7 @@ InputContext *PlatformInputContext::inputContext() const bool PlatformInputContext::eventFilter(QObject *object, QEvent *event) { if (event != m_filterEvent && object == m_focusObject && m_inputContext) - return m_inputContext->filterEvent(event); + return m_inputContext->priv()->filterEvent(event); return false; } @@ -247,7 +248,7 @@ void PlatformInputContext::setInputContext(InputContext *context) if (m_inputContext) { if (!m_inputPanel) m_inputPanel = new AppInputPanel(this); - connect(m_inputContext, SIGNAL(keyboardRectangleChanged()), SLOT(keyboardRectangleChanged())); + QObject::connect(m_inputContext->priv(), &InputContextPrivate::keyboardRectangleChanged, this, &PlatformInputContext::keyboardRectangleChanged); } else if (m_inputPanel) { m_inputPanel = nullptr; } @@ -255,7 +256,7 @@ void PlatformInputContext::setInputContext(InputContext *context) void PlatformInputContext::keyboardRectangleChanged() { - m_inputPanel->setInputRect(m_inputContext->keyboardRectangle().toRect()); + m_inputPanel->setInputRect(m_inputContext->priv()->keyboardRectangle().toRect()); } void PlatformInputContext::updateInputPanelVisible() diff --git a/src/virtualkeyboard/platforminputcontext_p.h b/src/virtualkeyboard/platforminputcontext_p.h index 30f30bd3..3c072bbd 100644 --- a/src/virtualkeyboard/platforminputcontext_p.h +++ b/src/virtualkeyboard/platforminputcontext_p.h @@ -101,6 +101,7 @@ private slots: private: friend class InputContext; + friend class InputContextPrivate; QPointer<InputContext> m_inputContext; QPointer<AbstractInputPanel> m_inputPanel; QPointer<DesktopInputSelectionControl> m_selectionControl; diff --git a/src/virtualkeyboard/shifthandler.cpp b/src/virtualkeyboard/shifthandler.cpp index 3b867b0d..7da1cb4e 100644 --- a/src/virtualkeyboard/shifthandler.cpp +++ b/src/virtualkeyboard/shifthandler.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include <QtVirtualKeyboard/private/shifthandler_p.h> +#include <QtVirtualKeyboard/private/inputcontext_p.h> #include <QtVirtualKeyboard/inputcontext.h> #include <QtVirtualKeyboard/inputengine.h> #include <QtCore/private/qobject_p.h> @@ -45,10 +46,12 @@ public: ShiftHandlerPrivate() : QObjectPrivate(), inputContext(nullptr), - sentenceEndingCharacters(QString(".!?") + QChar(Qt::Key_exclamdown) + QChar(Qt::Key_questiondown)), + sentenceEndingCharacters(QLatin1String(".!?") + QChar(Qt::Key_exclamdown) + QChar(Qt::Key_questiondown)), autoCapitalizationEnabled(false), toggleShiftEnabled(false), + shift(false), shiftChanged(false), + capsLock(false), resetWhenVisible(false), manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean << QLocale::Thai), manualCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Cangjie << InputEngine::Zhuyin << InputEngine::Hebrew), @@ -61,7 +64,9 @@ public: QString sentenceEndingCharacters; bool autoCapitalizationEnabled; bool toggleShiftEnabled; + bool shift; bool shiftChanged; + bool capsLock; bool resetWhenVisible; QLocale locale; QTime timer; @@ -91,19 +96,20 @@ ShiftHandler::ShiftHandler(InputContext *parent) : { Q_D(ShiftHandler); d->inputContext = parent; - if (d->inputContext) { - connect(d->inputContext, SIGNAL(inputMethodHintsChanged()), SLOT(restart())); - connect(d->inputContext, SIGNAL(inputItemChanged()), SLOT(restart())); - connect(d->inputContext->inputEngine(), SIGNAL(inputModeChanged()), SLOT(restart())); - connect(d->inputContext, SIGNAL(preeditTextChanged()), SLOT(autoCapitalize())); - connect(d->inputContext, SIGNAL(surroundingTextChanged()), SLOT(autoCapitalize())); - connect(d->inputContext, SIGNAL(cursorPositionChanged()), SLOT(autoCapitalize())); - connect(d->inputContext, SIGNAL(shiftChanged()), SLOT(shiftChanged())); - connect(d->inputContext, SIGNAL(capsLockChanged()), SLOT(shiftChanged())); - connect(d->inputContext, SIGNAL(localeChanged()), SLOT(localeChanged())); - connect(qGuiApp->inputMethod(), SIGNAL(visibleChanged()), SLOT(inputMethodVisibleChanged())); - d->locale = QLocale(d->inputContext->locale()); - } +} + +void ShiftHandler::init() +{ + Q_D(ShiftHandler); + connect(d->inputContext, SIGNAL(inputMethodHintsChanged()), SLOT(restart())); + connect(d->inputContext->priv(), SIGNAL(inputItemChanged()), SLOT(restart())); + connect(d->inputContext->inputEngine(), SIGNAL(inputModeChanged()), SLOT(restart())); + connect(d->inputContext, SIGNAL(preeditTextChanged()), SLOT(autoCapitalize())); + connect(d->inputContext, SIGNAL(surroundingTextChanged()), SLOT(autoCapitalize())); + connect(d->inputContext, SIGNAL(cursorPositionChanged()), SLOT(autoCapitalize())); + connect(d->inputContext, SIGNAL(localeChanged()), SLOT(localeChanged())); + connect(qGuiApp->inputMethod(), SIGNAL(visibleChanged()), SLOT(inputMethodVisibleChanged())); + d->locale = QLocale(d->inputContext->locale()); } /*! @@ -142,6 +148,47 @@ bool ShiftHandler::toggleShiftEnabled() const return d->toggleShiftEnabled; } +bool ShiftHandler::shift() const +{ + Q_D(const ShiftHandler); + return d->shift; +} + +void ShiftHandler::setShift(bool enable) +{ + Q_D(ShiftHandler); + if (d->shift != enable) { + d->shift = enable; + d->shiftChanged = true; + emit shiftChanged(); + if (!d->capsLock) + emit uppercaseChanged(); + } +} + +bool ShiftHandler::capsLock() const +{ + Q_D(const ShiftHandler); + return d->capsLock; +} + +void ShiftHandler::setCapsLock(bool enable) +{ + Q_D(ShiftHandler); + if (d->capsLock != enable) { + d->capsLock = enable; + emit capsLockChanged(); + if (!d->shift) + emit uppercaseChanged(); + } +} + +bool ShiftHandler::uppercase() const +{ + Q_D(const ShiftHandler); + return d->shift || d->capsLock; +} + /*! \since 1.2 @@ -170,27 +217,27 @@ void ShiftHandler::toggleShift() if (!d->toggleShiftEnabled) return; if (d->manualShiftLanguageFilter.contains(d->locale.language())) { - d->inputContext->setCapsLock(false); - d->inputContext->setShift(!d->inputContext->shift()); + setCapsLock(false); + setShift(!d->shift); } else if (d->inputContext->inputMethodHints() & Qt::ImhNoAutoUppercase || d->manualCapsInputModeFilter.contains(d->inputContext->inputEngine()->inputMode())) { - bool capsLock = d->inputContext->capsLock(); - d->inputContext->setCapsLock(!capsLock); - d->inputContext->setShift(!capsLock); + bool capsLock = d->capsLock; + setCapsLock(!capsLock); + setShift(!capsLock); } else { - if (d->inputContext->capsLock()) { - d->inputContext->setCapsLock(!d->inputContext->capsLock() && d->inputContext->shift() && !d->shiftChanged); + if (d->capsLock) { + setCapsLock(!d->capsLock && d->shift && !d->shiftChanged); } QStyleHints *style = QGuiApplication::styleHints(); if (d->timer.isNull() || d->timer.elapsed() > style->mouseDoubleClickInterval()) { d->timer.restart(); - } else if (d->timer.elapsed() < style->mouseDoubleClickInterval() && !d->inputContext->capsLock()) { - d->inputContext->setCapsLock(!d->inputContext->capsLock() && d->inputContext->shift() && !d->shiftChanged); + } else if (d->timer.elapsed() < style->mouseDoubleClickInterval() && !d->capsLock) { + setCapsLock(!d->capsLock && d->shift && !d->shiftChanged); } - d->inputContext->setShift(d->inputContext->capsLock() || !d->inputContext->shift()); + setShift(d->capsLock || !d->shift); d->shiftChanged = false; } } @@ -208,7 +255,7 @@ void ShiftHandler::clearToggleShiftTimer() void ShiftHandler::reset() { Q_D(ShiftHandler); - if (d->inputContext->inputItem()) { + if (d->inputContext->priv()->inputItem()) { Qt::InputMethodHints inputMethodHints = d->inputContext->inputMethodHints(); InputEngine::InputMode inputMode = d->inputContext->inputEngine()->inputMode(); bool preferUpperCase = (inputMethodHints & (Qt::ImhPreferUppercase | Qt::ImhUppercaseOnly)); @@ -231,9 +278,9 @@ void ShiftHandler::reset() } setToggleShiftEnabled(toggleShiftEnabled); setAutoCapitalizationEnabled(autoCapitalizationEnabled); - d->inputContext->setCapsLock(preferUpperCase); + setCapsLock(preferUpperCase); if (preferUpperCase) - d->inputContext->setShift(preferUpperCase); + setShift(preferUpperCase); else autoCapitalize(); } @@ -242,25 +289,25 @@ void ShiftHandler::reset() void ShiftHandler::autoCapitalize() { Q_D(ShiftHandler); - if (d->inputContext->capsLock()) + if (d->capsLock) return; if (!d->autoCapitalizationEnabled || !d->inputContext->preeditText().isEmpty()) { - d->inputContext->setShift(false); + setShift(false); } else { int cursorPosition = d->inputContext->cursorPosition(); bool preferLowerCase = d->inputContext->inputMethodHints() & Qt::ImhPreferLowercase; if (cursorPosition == 0) { - d->inputContext->setShift(!preferLowerCase); + setShift(!preferLowerCase); } else { QString text = d->inputContext->surroundingText(); text.truncate(cursorPosition); text = text.trimmed(); if (text.length() == 0) - d->inputContext->setShift(!preferLowerCase); + setShift(!preferLowerCase); else if (text.length() > 0 && d->sentenceEndingCharacters.indexOf(text[text.length() - 1]) >= 0) - d->inputContext->setShift(!preferLowerCase); + setShift(!preferLowerCase); else - d->inputContext->setShift(false); + setShift(false); } } } @@ -276,12 +323,6 @@ void ShiftHandler::restart() reset(); } -void ShiftHandler::shiftChanged() -{ - Q_D(ShiftHandler); - d->shiftChanged = true; -} - void ShiftHandler::localeChanged() { Q_D(ShiftHandler); diff --git a/src/virtualkeyboard/shifthandler_p.h b/src/virtualkeyboard/shifthandler_p.h index d7a31d14..437b52fa 100644 --- a/src/virtualkeyboard/shifthandler_p.h +++ b/src/virtualkeyboard/shifthandler_p.h @@ -58,8 +58,12 @@ class QVIRTUALKEYBOARD_EXPORT ShiftHandler : public QObject Q_PROPERTY(QString sentenceEndingCharacters READ sentenceEndingCharacters WRITE setSentenceEndingCharacters NOTIFY sentenceEndingCharactersChanged) Q_PROPERTY(bool autoCapitalizationEnabled READ autoCapitalizationEnabled NOTIFY autoCapitalizationEnabledChanged) Q_PROPERTY(bool toggleShiftEnabled READ toggleShiftEnabled NOTIFY toggleShiftEnabledChanged) + Q_PROPERTY(bool shift READ shift WRITE setShift NOTIFY shiftChanged) + Q_PROPERTY(bool capsLock READ capsLock WRITE setCapsLock NOTIFY capsLockChanged) + Q_PROPERTY(bool uppercase READ uppercase NOTIFY uppercaseChanged) explicit ShiftHandler(InputContext *parent = nullptr); + void init(); public: ~ShiftHandler(); @@ -68,6 +72,11 @@ public: void setSentenceEndingCharacters(const QString &value); bool autoCapitalizationEnabled() const; bool toggleShiftEnabled() const; + bool shift() const; + void setShift(bool enable); + bool capsLock() const; + void setCapsLock(bool enable); + bool uppercase() const; Q_INVOKABLE void toggleShift(); Q_INVOKABLE void clearToggleShiftTimer(); @@ -76,13 +85,15 @@ signals: void sentenceEndingCharactersChanged(); void toggleShiftEnabledChanged(); void autoCapitalizationEnabledChanged(); + void shiftChanged(); + void capsLockChanged(); + void uppercaseChanged(); private slots: void reset(); void autoCapitalize(); void restart(); void localeChanged(); - void shiftChanged(); void inputMethodVisibleChanged(); private: @@ -91,6 +102,7 @@ private: private: friend class InputContext; + friend class InputContextPrivate; }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro index 48fb5082..a45d25eb 100644 --- a/src/virtualkeyboard/virtualkeyboard.pro +++ b/src/virtualkeyboard/virtualkeyboard.pro @@ -15,6 +15,7 @@ include(../config.pri) SOURCES += \ platforminputcontext.cpp \ inputcontext.cpp \ + inputcontext_p.cpp \ abstractinputmethod.cpp \ plaininputmethod.cpp \ inputengine.cpp \ @@ -39,6 +40,7 @@ SOURCES += \ HEADERS += \ platforminputcontext_p.h \ inputcontext.h \ + inputcontext_p.h \ abstractinputmethod.h \ plaininputmethod_p.h \ inputengine.h \ diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml index c3682b8a..01fc2ede 100644 --- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml +++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml @@ -47,8 +47,8 @@ InputPanel { readonly property int cursorPosition: InputContext.cursorPosition readonly property string preeditText: InputContext.preeditText readonly property string surroundingText: InputContext.surroundingText - readonly property bool autoCapitalizationEnabled: InputContext.shiftHandler.autoCapitalizationEnabled - readonly property bool toggleShiftEnabled: InputContext.shiftHandler.toggleShiftEnabled + readonly property bool autoCapitalizationEnabled: InputContext.priv.shiftHandler.autoCapitalizationEnabled + readonly property bool toggleShiftEnabled: InputContext.priv.shiftHandler.toggleShiftEnabled readonly property string locale: keyboard.locale readonly property string defaultLocale: VirtualKeyboardSettings.locale readonly property var availableLocales: VirtualKeyboardSettings.availableLocales @@ -384,12 +384,12 @@ InputPanel { } else if (typeof key != "number" || key !== Qt.Key_Shift) { // Some layouts (such as Arabic, Hindi) may have a second layout virtualKeyClick(Qt.Key_Shift) - InputContext.shiftHandler.clearToggleShiftTimer() + InputContext.priv.shiftHandler.clearToggleShiftTimer() testcase.waitForRendering(inputPanel) success = keyActionOnCurrentLayoutCb(key) if (!success) { virtualKeyClick(Qt.Key_Shift) - InputContext.shiftHandler.clearToggleShiftTimer() + InputContext.priv.shiftHandler.clearToggleShiftTimer() testcase.waitForRendering(inputPanel) } } @@ -534,15 +534,15 @@ InputPanel { } function toggleShift() { - InputContext.shiftHandler.toggleShift() + InputContext.priv.shiftHandler.toggleShift() } function setShift(shift) { - InputContext.shift = shift + InputContext.priv.shiftHandler.shift = shift } function setCapsLock(capsLock) { - InputContext.capsLock = capsLock + InputContext.priv.shiftHandler.capsLock = capsLock } function style() { |