From 67052eb60ac891e354732f8cf504503215d4c465 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 4 Apr 2018 15:19:51 +0200 Subject: [PATCH] improving inputcontext --- inputcontext/QskHunspellCompositionModel.cpp | 7 +- inputcontext/QskHunspellCompositionModel.h | 6 +- inputcontext/QskInputCompositionModel.cpp | 122 ++++++------------- inputcontext/QskInputCompositionModel.h | 11 +- inputcontext/QskInputContext.cpp | 49 +++++--- inputcontext/QskInputContext.h | 6 +- inputcontext/QskPinyinCompositionModel.cpp | 5 +- inputcontext/QskPinyinCompositionModel.h | 2 +- 8 files changed, 89 insertions(+), 119 deletions(-) diff --git a/inputcontext/QskHunspellCompositionModel.cpp b/inputcontext/QskHunspellCompositionModel.cpp index ca50f5f6..9ab2e4f9 100644 --- a/inputcontext/QskHunspellCompositionModel.cpp +++ b/inputcontext/QskHunspellCompositionModel.cpp @@ -10,13 +10,12 @@ public: QVector< QString > candidates; }; -QskHunspellCompositionModel::QskHunspellCompositionModel( QObject* parent ): - Inherited( parent ), +QskHunspellCompositionModel::QskHunspellCompositionModel( QskInputContext* context ): + Inherited( context ), m_data( new PrivateData() ) { #if 1 - // ship with code if license allows: - // loading the language specific one depending on the locale + // TODO: loading the language specific one depending on the locale m_data->hunspellHandle = Hunspell_create( "/usr/share/hunspell/en_US.aff", diff --git a/inputcontext/QskHunspellCompositionModel.h b/inputcontext/QskHunspellCompositionModel.h index 178d3321..e45266c4 100644 --- a/inputcontext/QskHunspellCompositionModel.h +++ b/inputcontext/QskHunspellCompositionModel.h @@ -13,7 +13,7 @@ class QskHunspellCompositionModel : public QskInputCompositionModel using Inherited = QskInputCompositionModel; public: - QskHunspellCompositionModel( QObject* parent = nullptr ); + QskHunspellCompositionModel( QskInputContext* context ); virtual ~QskHunspellCompositionModel() override; virtual bool supportsSuggestions() const override final; @@ -24,8 +24,8 @@ public: protected: virtual bool hasIntermediate() const override; - virtual QString polishPreedit( const QString& preedit ) override; - virtual bool isComposable( const QStringRef& preedit ) const override; + virtual QString polishPreedit( const QString& ) override; + virtual bool isComposable( const QStringRef& ) const override; private: class PrivateData; diff --git a/inputcontext/QskInputCompositionModel.cpp b/inputcontext/QskInputCompositionModel.cpp index 17905e92..f3ef1328 100644 --- a/inputcontext/QskInputCompositionModel.cpp +++ b/inputcontext/QskInputCompositionModel.cpp @@ -4,11 +4,11 @@ *****************************************************************************/ #include "QskInputCompositionModel.h" +#include "QskInputContext.h" #include #include #include -#include static inline QString qskKeyString( int code ) { @@ -36,38 +36,20 @@ static inline QString qskKeyString( int code ) return QChar( code ); } -static inline void qskSendKeyEvents( QObject* receiver, int key ) -{ - QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier ); - QCoreApplication::sendEvent( receiver, &keyPress ); - - QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier ); - QCoreApplication::sendEvent( receiver, &keyRelease ); -} - class QskInputCompositionModel::PrivateData { public: - PrivateData() : - inputItem( nullptr ), - groupIndex( 0 ) - { - } - // QInputMethod QString preedit; QTextCharFormat preeditFormat; QList< QInputMethodEvent::Attribute > preeditAttributes; - QObject* inputItem; - int groupIndex; + int groupIndex = 0; }; -QskInputCompositionModel::QskInputCompositionModel( QObject* parent ): - QObject( parent ), +QskInputCompositionModel::QskInputCompositionModel( QskInputContext* context ): + QObject( context ), m_data( new PrivateData ) { - m_data->groupIndex = 0; - m_data->preeditFormat.setFontUnderline( true ); m_data->preeditAttributes.append( QInputMethodEvent::Attribute( QInputMethodEvent::TextFormat, 0, 0, m_data->preeditFormat ) ); @@ -77,6 +59,11 @@ QskInputCompositionModel::~QskInputCompositionModel() { } +QskInputContext* QskInputCompositionModel::context() const +{ + return qobject_cast< QskInputContext* >( parent() ); +} + bool QskInputCompositionModel::supportsSuggestions() const { return false; @@ -91,23 +78,16 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) * TODO */ - auto inputMethod = QGuiApplication::inputMethod(); - if ( !inputMethod ) - return; - - if ( !m_data->inputItem ) - return; - - QInputMethodQueryEvent queryEvent( + const auto queryEvent = context()->queryInputMethod( Qt::ImSurroundingText | Qt::ImMaximumTextLength | Qt::ImHints ); - QCoreApplication::sendEvent( m_data->inputItem, &queryEvent ); + const auto hints = static_cast< Qt::InputMethodHints >( queryEvent.value( Qt::ImHints ).toInt() ); const int maxLength = queryEvent.value( Qt::ImMaximumTextLength ).toInt(); const int currentLength = queryEvent.value( Qt::ImSurroundingText ).toString().length(); int spaceLeft = -1; - if ( !( hints& Qt::ImhMultiLine ) && maxLength > 0 ) + if ( !( hints & Qt::ImhMultiLine ) && maxLength > 0 ) spaceLeft = maxLength - currentLength; switch ( key ) @@ -115,7 +95,21 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) case Qt::Key_Backspace: case Qt::Key_Muhenkan: { - backspace(); + if ( !m_data->preedit.isEmpty() ) + { + m_data->preedit.chop( 1 ); + + const QString displayText = polishPreedit( m_data->preedit ); + m_data->preeditAttributes.first().length = displayText.length(); + + QInputMethodEvent event( displayText, m_data->preeditAttributes ); + sendCompositionEvent( &event ); + } + else + { + // Backspace one character only if preedit was inactive + sendKeyEvents( Qt::Key_Backspace ); + } return; } case Qt::Key_Space: @@ -150,8 +144,7 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) } else { - if( auto focusWindow = QGuiApplication::focusWindow() ) - qskSendKeyEvents( focusWindow, Qt::Key_Return ); + sendKeyEvents( Qt::Key_Return ); } return; @@ -160,7 +153,9 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) case Qt::Key_Left: case Qt::Key_Right: { - moveCursor( key ); + if ( m_data->preedit.isEmpty() ) + sendKeyEvents( key ); + return; } default: @@ -256,54 +251,20 @@ void QskInputCompositionModel::commitCandidate( int index ) commit( candidate( index ) ); } -void QskInputCompositionModel::backspace() +void QskInputCompositionModel::sendCompositionEvent( QInputMethodEvent* event ) { - if ( m_data->inputItem == nullptr ) - return; - - if ( !m_data->preedit.isEmpty() ) - { - m_data->preedit.chop( 1 ); - - const QString displayText = polishPreedit( m_data->preedit ); - m_data->preeditAttributes.first().length = displayText.length(); - - QInputMethodEvent event( displayText, m_data->preeditAttributes ); - sendCompositionEvent( &event ); - } - else - { - // Backspace one character only if preedit was inactive - qskSendKeyEvents( m_data->inputItem, Qt::Key_Backspace ); - } + context()->sendEventToInputItem( event ); } -void QskInputCompositionModel::moveCursor( Qt::Key key ) +void QskInputCompositionModel::sendKeyEvents( int key ) { - if ( key != Qt::Key_Left && key != Qt::Key_Right ) - return; + auto context = this->context(); - if ( !m_data->inputItem ) - return; + QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier ); + context->sendEventToInputItem( &keyPress ); - // Moving cursor is disabled when preedit is active. - if ( !m_data->preedit.isEmpty() ) - return; - - QKeyEvent moveCursorPress( QEvent::KeyPress, key, Qt::NoModifier ); - QKeyEvent moveCursorRelease( QEvent::KeyRelease, key, Qt::NoModifier ); -#if 1 - QFocusEvent focusIn( QEvent::FocusIn ); // hack to display the cursor -#endif - QCoreApplication::sendEvent( m_data->inputItem, &focusIn ); - QCoreApplication::sendEvent( m_data->inputItem, &moveCursorPress ); - QCoreApplication::sendEvent( m_data->inputItem, &moveCursorRelease ); -} - -void QskInputCompositionModel::sendCompositionEvent( QInputMethodEvent* e ) -{ - if ( m_data->inputItem ) - QCoreApplication::sendEvent( m_data->inputItem, e ); + QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier ); + context->sendEventToInputItem( &keyRelease ); } bool QskInputCompositionModel::hasIntermediate() const @@ -340,11 +301,6 @@ QVector< Qt::Key > QskInputCompositionModel::groups() const return QVector< Qt::Key >(); } -void QskInputCompositionModel::setInputItem( QObject* inputItem ) -{ - m_data->inputItem = inputItem; -} - bool QskInputCompositionModel::nextGroupIndex( int& index, bool forward ) const { Q_UNUSED( index ); diff --git a/inputcontext/QskInputCompositionModel.h b/inputcontext/QskInputCompositionModel.h index 5e299ce8..99e01626 100644 --- a/inputcontext/QskInputCompositionModel.h +++ b/inputcontext/QskInputCompositionModel.h @@ -11,7 +11,7 @@ #include class QInputMethodEvent; -class QStringList; +class QskInputContext; class QskInputCompositionModel : public QObject { @@ -20,7 +20,7 @@ class QskInputCompositionModel : public QObject Q_PROPERTY( QVector< Qt::Key > groups READ groups NOTIFY groupsChanged ) public: - QskInputCompositionModel( QObject* parent = nullptr ); + QskInputCompositionModel( QskInputContext* context ); virtual ~QskInputCompositionModel(); // to determine whether to show the suggestion bar: @@ -41,22 +41,21 @@ public: virtual QVector< Qt::Key > groups() const; - void setInputItem( QObject* inputItem ); - protected: // Used for text composition virtual bool hasIntermediate() const; virtual QString polishPreedit( const QString& preedit ); virtual bool isComposable( const QStringRef& preedit ) const; + QskInputContext* context() const; + Q_SIGNALS: void groupsChanged( const QVector< Qt::Key >& ); void candidatesChanged(); private: - void backspace(); - void moveCursor( Qt::Key key ); void sendCompositionEvent( QInputMethodEvent* e ); + void sendKeyEvents( int key ); class PrivateData; std::unique_ptr< PrivateData > m_data; diff --git a/inputcontext/QskInputContext.cpp b/inputcontext/QskInputContext.cpp index 0cd7722b..617ad3d9 100644 --- a/inputcontext/QskInputContext.cpp +++ b/inputcontext/QskInputContext.cpp @@ -26,7 +26,7 @@ public: QHash< QLocale, QskInputCompositionModel* > compositionModels; }; -QskInputContext::QskInputContext() : +QskInputContext::QskInputContext(): m_data( new PrivateData() ) { setObjectName( "InputContext" ); @@ -70,8 +70,7 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) if ( m_data->inputItem == nullptr ) return; - QInputMethodQueryEvent queryEvent( queries ); - QCoreApplication::sendEvent( m_data->inputItem, &queryEvent ); + const auto queryEvent = queryInputMethod( queries ); // Qt::ImCursorRectangle // Qt::ImFont @@ -83,9 +82,10 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) if ( queries & Qt::ImHints ) { - const Qt::InputMethodHints hints = - static_cast< Qt::InputMethodHints >( queryEvent.value( Qt::ImHints ).toInt() ); - Q_UNUSED( hints ); +#if 0 + const auto hints = static_cast< Qt::InputMethodHints >( + queryEvent.value( Qt::ImHints ).toInt() ); + //ImhHiddenText = 0x1, // might need to disable certain checks //ImhSensitiveData = 0x2, // shouldn't change anything //ImhNoAutoUppercase = 0x4, // if we support auto uppercase, disable it @@ -109,6 +109,7 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) //ImhEmailCharactersOnly // disable certain symbols (email-only kb?) //ImhUrlCharactersOnly // disable certain symbols (url-only kb?) //ImhLatinOnly // disable chinese input +#endif } if ( queries & Qt::ImPreferredLanguage ) @@ -149,6 +150,11 @@ QQuickItem* QskInputContext::inputItem() return m_data->inputItem; } +void QskInputContext::setInputItem( QQuickItem* item ) +{ + m_data->inputItem = item; +} + QRectF QskInputContext::keyboardRect() const { if ( m_data->inputPanel @@ -232,8 +238,7 @@ void QskInputContext::setFocusObject( QObject* focusObject ) { if ( focusObject == nullptr ) { - m_data->inputItem = nullptr; - compositionModel()->setInputItem( nullptr ); + setInputItem( nullptr ); return; } @@ -245,17 +250,14 @@ void QskInputContext::setFocusObject( QObject* focusObject ) // Do not change the input item when panel buttons get the focus: if( qskNearestFocusScope( focusItem ) != m_data->inputPanel ) { - m_data->inputItem = focusItem; - compositionModel()->setInputItem( focusItem ); + setInputItem( focusItem ); inputItemChanged = true; } } if( inputItemChanged ) { - QInputMethodQueryEvent queryEvent( Qt::ImEnabled ); - QCoreApplication::sendEvent( m_data->inputItem, &queryEvent ); - + const auto queryEvent = queryInputMethod( Qt::ImEnabled ); if ( !queryEvent.value( Qt::ImEnabled ).toBool() ) { hideInputPanel(); @@ -403,17 +405,26 @@ void QskInputContext::commit() { } -bool QskInputContext::eventFilter( QObject* object, QEvent* event ) +bool QskInputContext::filterEvent( const QEvent* event ) { - if ( object == m_data->inputItem ) - return filterEvent( event ); - + // called from QXcbKeyboard, but what about other platforms + Q_UNUSED( event ) return false; } -bool QskInputContext::filterEvent( const QEvent* ) +QInputMethodQueryEvent QskInputContext::queryInputMethod( + Qt::InputMethodQueries queries ) const { - return false; + QInputMethodQueryEvent event( queries ); + sendEventToInputItem( &event ); + + return event; +} + +void QskInputContext::sendEventToInputItem( QEvent* event ) const +{ + if ( m_data->inputItem && event ) + QCoreApplication::sendEvent( m_data->inputItem, event ); } #include "moc_QskInputContext.cpp" diff --git a/inputcontext/QskInputContext.h b/inputcontext/QskInputContext.h index 5da3adab..928d1055 100644 --- a/inputcontext/QskInputContext.h +++ b/inputcontext/QskInputContext.h @@ -12,6 +12,7 @@ class QskVirtualKeyboard; class QskInputCompositionModel; class QQuickItem; +class QInputMethodQueryEvent; class QskInputContext : public QPlatformInputContext { @@ -48,14 +49,17 @@ public: Q_INVOKABLE QQuickItem* inputItem(); - virtual bool eventFilter( QObject*, QEvent * ) override; virtual bool filterEvent( const QEvent* ) override; + QInputMethodQueryEvent queryInputMethod( Qt::InputMethodQueries ) const; + void sendEventToInputItem( QEvent* ) const; + private Q_SLOTS: void handleCandidatesChanged(); void setInputPanel( QskVirtualKeyboard* ); private: + void setInputItem( QQuickItem* ); QskInputCompositionModel* compositionModel() const; class PrivateData; diff --git a/inputcontext/QskPinyinCompositionModel.cpp b/inputcontext/QskPinyinCompositionModel.cpp index ca515f9e..30a66a36 100644 --- a/inputcontext/QskPinyinCompositionModel.cpp +++ b/inputcontext/QskPinyinCompositionModel.cpp @@ -4,6 +4,7 @@ *****************************************************************************/ #include "QskPinyinCompositionModel.h" +#include "QskInputContext.h" #include "pinyinime.h" @@ -18,8 +19,8 @@ public: QVector< Qt::Key > groups; }; -QskPinyinCompositionModel::QskPinyinCompositionModel( QObject* parent ): - Inherited( parent ), +QskPinyinCompositionModel::QskPinyinCompositionModel( QskInputContext* context ): + Inherited( context ), m_data( new PrivateData ) { #if 1 diff --git a/inputcontext/QskPinyinCompositionModel.h b/inputcontext/QskPinyinCompositionModel.h index 994edd79..4821a0ee 100644 --- a/inputcontext/QskPinyinCompositionModel.h +++ b/inputcontext/QskPinyinCompositionModel.h @@ -13,7 +13,7 @@ class QskPinyinCompositionModel : public QskInputCompositionModel using Inherited = QskInputCompositionModel; public: - QskPinyinCompositionModel( QObject* parent = nullptr ); + QskPinyinCompositionModel( QskInputContext* ); virtual ~QskPinyinCompositionModel() override; virtual bool supportsSuggestions() const override final;