From bcd0bc94c0b0683cb7e766f6338d98951fcf7a96 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Thu, 29 Mar 2018 14:48:38 +0200 Subject: [PATCH] more keyboard refactoring --- inputcontext/QskInputCompositionModel.cpp | 89 +++++++++++------ inputcontext/QskInputCompositionModel.h | 4 +- inputcontext/QskInputContext.cpp | 113 +++++++++++++--------- inputcontext/QskInputContext.h | 11 ++- inputcontext/QskPinyinCompositionModel.h | 6 +- src/controls/QskVirtualKeyboard.h | 2 +- 6 files changed, 143 insertions(+), 82 deletions(-) diff --git a/inputcontext/QskInputCompositionModel.cpp b/inputcontext/QskInputCompositionModel.cpp index 3a9b8961..654cc1f9 100644 --- a/inputcontext/QskInputCompositionModel.cpp +++ b/inputcontext/QskInputCompositionModel.cpp @@ -23,15 +23,19 @@ static QString qskKeyString( int code ) case Qt::Key_Backspace: case Qt::Key_Muhenkan: return QString(); + case Qt::Key_Return: case Qt::Key_Kanji: - return QChar(QChar::CarriageReturn); + return QChar( QChar::CarriageReturn ); + case Qt::Key_Space: - return QChar(QChar::Space); + return QChar( QChar::Space ); + default: break; } - return QChar(code); + + return QChar( code ); } class QskInputCompositionModel::PrivateData @@ -51,6 +55,14 @@ public: int groupIndex; }; +static inline void sendCompositionEvent( QInputMethodEvent* e ) +{ + if( auto focusObject = QGuiApplication::focusObject() ) + { + QCoreApplication::sendEvent( focusObject, e ); + } +} + QskInputCompositionModel::QskInputCompositionModel(): m_data( new PrivateData ) { @@ -67,6 +79,13 @@ QskInputCompositionModel::~QskInputCompositionModel() void QskInputCompositionModel::composeKey( Qt::Key key ) { + /* + * This operation might be expensive (e.g. for Hunspell) and + * should be done asynchronously to be able to run e.g. suggestions + * in a separate thread to not block the UI. + * TODO + */ + auto inputMethod = QGuiApplication::inputMethod(); if ( !inputMethod ) return; @@ -96,35 +115,51 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) } case Qt::Key_Space: { - if ( !spaceLeft ) + if( !spaceLeft ) + { return; + } - if ( !m_data->preedit.isEmpty() ) + if( !m_data->preedit.isEmpty() ) { - if ( candidateCount() > 0 ) // Commit first candidate - commit( QChar( candidate( 0 ) ) ); - else // Commit what is in the buffer - commit( m_data->preedit.left( spaceLeft ) ); - } - else - { - commit( qskKeyString(key) ); + commit( m_data->preedit.left( spaceLeft ) ); } + + commit( qskKeyString( key ) ); return; } + case Qt::Key_Return: { if ( !spaceLeft ) return; // Commit what is in the buffer - if ( !m_data->preedit.isEmpty() ) + if( !m_data->preedit.isEmpty() ) + { commit( m_data->preedit.left( spaceLeft ) ); - else if ( hints & Qt::ImhMultiLine ) - commit( qskKeyString( key ) ); + } + else if( hints & Qt::ImhMultiLine ) + { + commit( qskKeyString( key ) ); + } +#if 0 + else + { + auto focusWindow = QGuiApplication::focusWindow(); - return; + if( focusWindow ) + { + QKeyEvent keyPress( QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier ); + QKeyEvent keyRelease( QEvent::KeyRelease, Qt::Key_Return, Qt::NoModifier ); QCoreApplication::sendEvent( focusWindow, &keyPress ); + QCoreApplication::sendEvent( focusWindow, &keyRelease ); + } + + return; + } +#endif } + case Qt::Key_Left: case Qt::Key_Right: { @@ -169,16 +204,17 @@ void QskInputCompositionModel::composeKey( Qt::Key key ) m_data->preedit = qskKeyString( key ); displayPreedit = polishPreedit( m_data->preedit ); + if ( !hasIntermediate() ) { - commit(m_data->preedit); + commit( m_data->preedit ); return; } } m_data->preeditAttributes.first().length = displayPreedit.length(); - QInputMethodEvent e(displayPreedit, m_data->preeditAttributes); - sendCompositionEvent(&e); + QInputMethodEvent e( displayPreedit, m_data->preeditAttributes ); + sendCompositionEvent( &e ); } void QskInputCompositionModel::clearPreedit() @@ -228,7 +264,7 @@ void QskInputCompositionModel::backspace() if ( !m_data->preedit.isEmpty() ) { - m_data->preedit.chop(1); + m_data->preedit.chop( 1 ); } else { @@ -258,7 +294,6 @@ void QskInputCompositionModel::moveCursor( Qt::Key key ) if ( !m_data->preedit.isEmpty() ) return; - // ### this should be in the panel: QKeyEvent moveCursorPress( QEvent::KeyPress, key, Qt::NoModifier ); QKeyEvent moveCursorRelease( QEvent::KeyRelease, key, Qt::NoModifier ); #if 1 @@ -280,9 +315,9 @@ bool QskInputCompositionModel::hasIntermediate() const return false; } -bool QskInputCompositionModel::isComposable(const QStringRef& preedit) const +bool QskInputCompositionModel::isComposable( const QStringRef& preedit ) const { - Q_UNUSED(preedit); + Q_UNUSED( preedit ); return false; } @@ -313,10 +348,10 @@ void QskInputCompositionModel::setInputItem( QObject *inputItem ) m_data->inputItem = inputItem; } -bool QskInputCompositionModel::nextGroupIndex(int& index, bool forward) const +bool QskInputCompositionModel::nextGroupIndex( int& index, bool forward ) const { - Q_UNUSED(index); - Q_UNUSED(forward); + Q_UNUSED( index ); + Q_UNUSED( forward ); return false; } diff --git a/inputcontext/QskInputCompositionModel.h b/inputcontext/QskInputCompositionModel.h index 94e5be53..b5cad347 100644 --- a/inputcontext/QskInputCompositionModel.h +++ b/inputcontext/QskInputCompositionModel.h @@ -24,7 +24,7 @@ public: virtual ~QskInputCompositionModel(); void commit( const QString& ); - void commitCandidate( int ); + virtual void commitCandidate( int ); void composeKey( Qt::Key ); void clearPreedit(); @@ -33,7 +33,7 @@ public: virtual Qt::Key candidate( int ) const; int groupIndex() const; - void setGroupIndex(int groupIndex); + void setGroupIndex( int groupIndex ); virtual bool nextGroupIndex( int&, bool = true ) const; virtual QVector< Qt::Key > groups() const; diff --git a/inputcontext/QskInputContext.cpp b/inputcontext/QskInputContext.cpp index 88d5d7f8..93d1059b 100644 --- a/inputcontext/QskInputContext.cpp +++ b/inputcontext/QskInputContext.cpp @@ -14,20 +14,19 @@ #include #include -#include -#include -#include -#include -#include QskInputContext::QskInputContext(): Inherited(), - m_inputCompositionModel( new QskInputCompositionModel ) + m_defaultInputCompositionModel( new QskInputCompositionModel ) { connect( qskSetup, &QskSetup::inputPanelChanged, this, &QskInputContext::setInputPanel ); setInputPanel( qskSetup->inputPanel() ); + QskPinyinCompositionModel* pinyinModel = new QskPinyinCompositionModel; + // For input methods outside skinny, call QskInputPanel::registerCompositionModelForLocale() + inputMethodRegistered( QLocale::Chinese, pinyinModel ); + // We could connect candidatesChanged() here, but we don't emit // the signal in the normal composition model anyhow } @@ -36,6 +35,11 @@ QskInputContext::~QskInputContext() { if ( m_inputPanel ) delete m_inputPanel; + + for( int a = 0; a < m_inputModels.values().count(); a++ ) + { + delete m_inputModels.values()[a]; + } } bool QskInputContext::isValid() const @@ -64,7 +68,7 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) { const Qt::InputMethodHints hints = static_cast< Qt::InputMethodHints >( queryEvent.value( Qt::ImHints ).toInt() ); - Q_UNUSED(hints); + Q_UNUSED( hints ); //ImhHiddenText = 0x1, // might need to disable certain checks //ImhSensitiveData = 0x2, // shouldn't change anything //ImhNoAutoUppercase = 0x4, // if we support auto uppercase, disable it @@ -93,38 +97,28 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) if ( queries & Qt::ImPreferredLanguage ) { const auto locale = queryEvent.value( Qt::ImPreferredLanguage ).toLocale(); - if ( m_inputPanel ) + + auto oldModel = currentInputCompositionModel(); + + if( m_inputPanel ) m_inputPanel->setLocale( locale ); - auto modelChanged = false; - const bool currentModelIsPinyin = - dynamic_cast< QskPinyinCompositionModel* >( m_inputCompositionModel.get() ); - const bool localeIsChinese = locale.language() == QLocale::Chinese; - if ( localeIsChinese && !currentModelIsPinyin ) - { - if ( m_inputPanel ) - m_inputPanel->disconnect( m_inputCompositionModel.get() ); + auto newModel = currentInputCompositionModel(); - m_inputCompositionModel.reset( new QskPinyinCompositionModel ); - modelChanged = true; - } - else if ( !m_inputCompositionModel || ( !localeIsChinese && currentModelIsPinyin ) ) // Install default - { - if ( m_inputPanel ) - m_inputPanel->disconnect( m_inputCompositionModel.get() ); + bool modelChanged = ( oldModel != newModel ); - m_inputCompositionModel.reset( new QskInputCompositionModel ); - modelChanged = true; - } - - if ( modelChanged && m_inputPanel ) + if( modelChanged ) { - QObject::connect( - m_inputCompositionModel.get(), &QskInputCompositionModel::groupsChanged, - m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups ); - QObject::connect( - m_inputCompositionModel.get(), &QskInputCompositionModel::candidatesChanged, - this, &QskInputContext::handleCandidatesChanged ); + if( m_inputPanel ) + { + m_inputPanel->disconnect( oldModel ); + QObject::connect( + newModel, &QskInputCompositionModel::groupsChanged, + m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups ); + QObject::connect( + newModel, &QskInputCompositionModel::candidatesChanged, + this, &QskInputContext::handleCandidatesChanged ); + } } } @@ -212,7 +206,7 @@ void QskInputContext::setFocusObject( QObject* focusObject ) if ( !m_focusObject ) { m_inputItem = nullptr; - m_inputCompositionModel->setInputItem( nullptr ); + currentInputCompositionModel()->setInputItem( nullptr ); return; } @@ -225,7 +219,7 @@ void QskInputContext::setFocusObject( QObject* focusObject ) if( qskNearestFocusScope( focusQuickItem ) != m_inputPanel ) { m_inputItem = focusQuickItem; - m_inputCompositionModel->setInputItem( m_inputItem ); // ### use a signal/slot connection + currentInputCompositionModel()->setInputItem( m_inputItem ); // ### use a signal/slot connection inputItemChanged = true; } } @@ -246,9 +240,31 @@ void QskInputContext::setFocusObject( QObject* focusObject ) update( Qt::InputMethodQuery( Qt::ImQueryAll & ~Qt::ImEnabled ) ); } +void QskInputContext::inputMethodRegistered( const QLocale& locale, QskInputCompositionModel* model ) +{ + auto oldModel = m_inputModels.value( locale, nullptr ); + + if( oldModel != nullptr ) + { + oldModel->deleteLater(); + } + + m_inputModels.insert( locale, model ); +} + +QskInputCompositionModel* QskInputContext::compositionModelForLocale( const QLocale& locale ) const +{ + return m_inputModels.value( locale, m_defaultInputCompositionModel ); +} + +QskInputCompositionModel* QskInputContext::currentInputCompositionModel() const +{ + return m_inputModels.value( locale(), m_defaultInputCompositionModel ); +} + void QskInputContext::invokeAction( QInputMethod::Action action, int cursorPosition ) { - Q_UNUSED(cursorPosition); + Q_UNUSED( cursorPosition ); if ( !m_inputPanel ) return; @@ -256,13 +272,15 @@ void QskInputContext::invokeAction( QInputMethod::Action action, int cursorPosit switch ( static_cast< QskVirtualKeyboard::Action >( action ) ) { case QskVirtualKeyboard::Compose: - m_inputCompositionModel->composeKey( static_cast< Qt::Key >( cursorPosition ) ); + currentInputCompositionModel()->composeKey( static_cast< Qt::Key >( cursorPosition ) ); break; + case QskVirtualKeyboard::SelectGroup: - m_inputCompositionModel->setGroupIndex( cursorPosition ); + currentInputCompositionModel()->setGroupIndex( cursorPosition ); break; + case QskVirtualKeyboard::SelectCandidate: - m_inputCompositionModel->commitCandidate( cursorPosition ); + currentInputCompositionModel()->commitCandidate( cursorPosition ); break; } } @@ -274,10 +292,11 @@ void QskInputContext::emitAnimatingChanged() void QskInputContext::handleCandidatesChanged() { - QVector< Qt::Key > candidates( m_inputCompositionModel->candidateCount() ); - for ( int i = 0; i < candidates.length(); ++i ) + QVector< Qt::Key > candidates( currentInputCompositionModel()->candidateCount() ); + + for( int i = 0; i < candidates.length(); ++i ) { - candidates[i] = static_cast< Qt::Key >( m_inputCompositionModel->candidate( i ) ); + candidates[i] = currentInputCompositionModel()->candidate( i ); } m_inputPanel->setPreeditCandidates( candidates ); @@ -296,8 +315,8 @@ void QskInputContext::setInputPanel( QskVirtualKeyboard* inputPanel ) this, &QPlatformInputContext::emitKeyboardRectChanged ); QObject::disconnect( m_inputPanel, &QskVirtualKeyboard::localeChanged, this, &QPlatformInputContext::emitLocaleChanged ); - if ( m_inputCompositionModel ) - m_inputPanel->disconnect( m_inputCompositionModel.get() ); + if ( currentInputCompositionModel() ) + m_inputPanel->disconnect( currentInputCompositionModel() ); } m_inputPanel = inputPanel; @@ -310,10 +329,10 @@ void QskInputContext::setInputPanel( QskVirtualKeyboard* inputPanel ) this, &QPlatformInputContext::emitKeyboardRectChanged ); QObject::connect( m_inputPanel, &QskVirtualKeyboard::localeChanged, this, &QPlatformInputContext::emitLocaleChanged ); - if ( m_inputCompositionModel ) + if ( currentInputCompositionModel() ) { QObject::connect( - m_inputCompositionModel.get(), &QskInputCompositionModel::groupsChanged, + currentInputCompositionModel(), &QskInputCompositionModel::groupsChanged, m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups ); } } diff --git a/inputcontext/QskInputContext.h b/inputcontext/QskInputContext.h index 034df5a1..42c67bcf 100644 --- a/inputcontext/QskInputContext.h +++ b/inputcontext/QskInputContext.h @@ -7,8 +7,9 @@ #define QSK_INPUT_CONTEXT_H #include -#include +#include #include +#include #include @@ -36,16 +37,22 @@ public: QLocale locale() const override; void setFocusObject( QObject* ) override; + QskInputCompositionModel* compositionModelForLocale( const QLocale& locale ) const; + private Q_SLOTS: void emitAnimatingChanged(); void handleCandidatesChanged(); void setInputPanel( QskVirtualKeyboard* ); + void inputMethodRegistered( const QLocale& locale, QskInputCompositionModel* model ); private: + QskInputCompositionModel* currentInputCompositionModel() const; + QPointer< QObject > m_focusObject; QPointer< QQuickItem > m_inputItem; QPointer< QskVirtualKeyboard > m_inputPanel; - std::unique_ptr< QskInputCompositionModel > m_inputCompositionModel; + QskInputCompositionModel* m_defaultInputCompositionModel; + QHash< QLocale, QskInputCompositionModel* > m_inputModels; }; #endif // QSK_INPUT_CONTEXT_H diff --git a/inputcontext/QskPinyinCompositionModel.h b/inputcontext/QskPinyinCompositionModel.h index 23f3ca58..5c095619 100644 --- a/inputcontext/QskPinyinCompositionModel.h +++ b/inputcontext/QskPinyinCompositionModel.h @@ -14,7 +14,7 @@ class QskPinyinCompositionModel : public QskInputCompositionModel public: QskPinyinCompositionModel(); - ~QskPinyinCompositionModel(); + virtual ~QskPinyinCompositionModel() override; int candidateCount() const override; Qt::Key candidate( int ) const override; @@ -24,8 +24,8 @@ public: protected: // Used for text composition bool hasIntermediate() const override; - QString polishPreedit(const QString& preedit) override; - bool isComposable(const QStringRef& preedit) const override; + QString polishPreedit( const QString& preedit ) override; + bool isComposable( const QStringRef& preedit ) const override; private: void handleGroupIndexChanged(); diff --git a/src/controls/QskVirtualKeyboard.h b/src/controls/QskVirtualKeyboard.h index e8134a36..25d6e045 100644 --- a/src/controls/QskVirtualKeyboard.h +++ b/src/controls/QskVirtualKeyboard.h @@ -22,6 +22,7 @@ class QSK_EXPORT QskVirtualKeyboardButton : public QskPushButton public: QSK_SUBCONTROLS( Panel, Text, TextCancelButton ) + QskVirtualKeyboardButton( int keyIndex, QskVirtualKeyboard* inputPanel, QQuickItem* parent = nullptr ); virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override; @@ -48,7 +49,6 @@ class QSK_EXPORT QskVirtualKeyboard : public QskBox using Inherited = QskBox; public: - QSK_SUBCONTROLS( Panel ) struct KeyData