more keyboard refactoring

This commit is contained in:
Peter Hartmann 2018-03-29 14:48:38 +02:00
parent 66677067d5
commit bcd0bc94c0
6 changed files with 143 additions and 82 deletions

View File

@ -23,14 +23,18 @@ static QString qskKeyString( int code )
case Qt::Key_Backspace: case Qt::Key_Backspace:
case Qt::Key_Muhenkan: case Qt::Key_Muhenkan:
return QString(); return QString();
case Qt::Key_Return: case Qt::Key_Return:
case Qt::Key_Kanji: case Qt::Key_Kanji:
return QChar( QChar::CarriageReturn ); return QChar( QChar::CarriageReturn );
case Qt::Key_Space: case Qt::Key_Space:
return QChar( QChar::Space ); return QChar( QChar::Space );
default: default:
break; break;
} }
return QChar( code ); return QChar( code );
} }
@ -51,6 +55,14 @@ public:
int groupIndex; int groupIndex;
}; };
static inline void sendCompositionEvent( QInputMethodEvent* e )
{
if( auto focusObject = QGuiApplication::focusObject() )
{
QCoreApplication::sendEvent( focusObject, e );
}
}
QskInputCompositionModel::QskInputCompositionModel(): QskInputCompositionModel::QskInputCompositionModel():
m_data( new PrivateData ) m_data( new PrivateData )
{ {
@ -67,6 +79,13 @@ QskInputCompositionModel::~QskInputCompositionModel()
void QskInputCompositionModel::composeKey( Qt::Key key ) 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(); auto inputMethod = QGuiApplication::inputMethod();
if ( !inputMethod ) if ( !inputMethod )
return; return;
@ -97,21 +116,19 @@ void QskInputCompositionModel::composeKey( Qt::Key key )
case Qt::Key_Space: case Qt::Key_Space:
{ {
if( !spaceLeft ) if( !spaceLeft )
{
return; 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 ) ); commit( m_data->preedit.left( spaceLeft ) );
} }
else
{
commit( qskKeyString( key ) ); commit( qskKeyString( key ) );
}
return; return;
} }
case Qt::Key_Return: case Qt::Key_Return:
{ {
if ( !spaceLeft ) if ( !spaceLeft )
@ -119,12 +136,30 @@ void QskInputCompositionModel::composeKey( Qt::Key key )
// Commit what is in the buffer // Commit what is in the buffer
if( !m_data->preedit.isEmpty() ) if( !m_data->preedit.isEmpty() )
{
commit( m_data->preedit.left( spaceLeft ) ); commit( m_data->preedit.left( spaceLeft ) );
}
else if( hints & Qt::ImhMultiLine ) else if( hints & Qt::ImhMultiLine )
{
commit( qskKeyString( key ) ); commit( qskKeyString( key ) );
}
#if 0
else
{
auto focusWindow = QGuiApplication::focusWindow();
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; return;
} }
#endif
}
case Qt::Key_Left: case Qt::Key_Left:
case Qt::Key_Right: case Qt::Key_Right:
{ {
@ -169,6 +204,7 @@ void QskInputCompositionModel::composeKey( Qt::Key key )
m_data->preedit = qskKeyString( key ); m_data->preedit = qskKeyString( key );
displayPreedit = polishPreedit( m_data->preedit ); displayPreedit = polishPreedit( m_data->preedit );
if ( !hasIntermediate() ) if ( !hasIntermediate() )
{ {
commit( m_data->preedit ); commit( m_data->preedit );
@ -258,7 +294,6 @@ void QskInputCompositionModel::moveCursor( Qt::Key key )
if ( !m_data->preedit.isEmpty() ) if ( !m_data->preedit.isEmpty() )
return; return;
// ### this should be in the panel:
QKeyEvent moveCursorPress( QEvent::KeyPress, key, Qt::NoModifier ); QKeyEvent moveCursorPress( QEvent::KeyPress, key, Qt::NoModifier );
QKeyEvent moveCursorRelease( QEvent::KeyRelease, key, Qt::NoModifier ); QKeyEvent moveCursorRelease( QEvent::KeyRelease, key, Qt::NoModifier );
#if 1 #if 1

View File

@ -24,7 +24,7 @@ public:
virtual ~QskInputCompositionModel(); virtual ~QskInputCompositionModel();
void commit( const QString& ); void commit( const QString& );
void commitCandidate( int ); virtual void commitCandidate( int );
void composeKey( Qt::Key ); void composeKey( Qt::Key );
void clearPreedit(); void clearPreedit();

View File

@ -14,20 +14,19 @@
#include <QskSetup.h> #include <QskSetup.h>
#include <QGuiApplication> #include <QGuiApplication>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWindow>
#include <QRectF>
QskInputContext::QskInputContext(): QskInputContext::QskInputContext():
Inherited(), Inherited(),
m_inputCompositionModel( new QskInputCompositionModel ) m_defaultInputCompositionModel( new QskInputCompositionModel )
{ {
connect( qskSetup, &QskSetup::inputPanelChanged, connect( qskSetup, &QskSetup::inputPanelChanged,
this, &QskInputContext::setInputPanel ); this, &QskInputContext::setInputPanel );
setInputPanel( qskSetup->inputPanel() ); 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 // We could connect candidatesChanged() here, but we don't emit
// the signal in the normal composition model anyhow // the signal in the normal composition model anyhow
} }
@ -36,6 +35,11 @@ QskInputContext::~QskInputContext()
{ {
if ( m_inputPanel ) if ( m_inputPanel )
delete m_inputPanel; delete m_inputPanel;
for( int a = 0; a < m_inputModels.values().count(); a++ )
{
delete m_inputModels.values()[a];
}
} }
bool QskInputContext::isValid() const bool QskInputContext::isValid() const
@ -93,40 +97,30 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
if ( queries & Qt::ImPreferredLanguage ) if ( queries & Qt::ImPreferredLanguage )
{ {
const auto locale = queryEvent.value( Qt::ImPreferredLanguage ).toLocale(); const auto locale = queryEvent.value( Qt::ImPreferredLanguage ).toLocale();
auto oldModel = currentInputCompositionModel();
if( m_inputPanel ) if( m_inputPanel )
m_inputPanel->setLocale( locale ); m_inputPanel->setLocale( locale );
auto modelChanged = false; auto newModel = currentInputCompositionModel();
const bool currentModelIsPinyin =
dynamic_cast< QskPinyinCompositionModel* >( m_inputCompositionModel.get() ); bool modelChanged = ( oldModel != newModel );
const bool localeIsChinese = locale.language() == QLocale::Chinese;
if ( localeIsChinese && !currentModelIsPinyin ) if( modelChanged )
{ {
if( m_inputPanel ) if( m_inputPanel )
m_inputPanel->disconnect( m_inputCompositionModel.get() );
m_inputCompositionModel.reset( new QskPinyinCompositionModel );
modelChanged = true;
}
else if ( !m_inputCompositionModel || ( !localeIsChinese && currentModelIsPinyin ) ) // Install default
{
if ( m_inputPanel )
m_inputPanel->disconnect( m_inputCompositionModel.get() );
m_inputCompositionModel.reset( new QskInputCompositionModel );
modelChanged = true;
}
if ( modelChanged && m_inputPanel )
{ {
m_inputPanel->disconnect( oldModel );
QObject::connect( QObject::connect(
m_inputCompositionModel.get(), &QskInputCompositionModel::groupsChanged, newModel, &QskInputCompositionModel::groupsChanged,
m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups ); m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups );
QObject::connect( QObject::connect(
m_inputCompositionModel.get(), &QskInputCompositionModel::candidatesChanged, newModel, &QskInputCompositionModel::candidatesChanged,
this, &QskInputContext::handleCandidatesChanged ); this, &QskInputContext::handleCandidatesChanged );
} }
} }
}
// Qt::ImAbsolutePosition // Qt::ImAbsolutePosition
// Qt::ImTextBeforeCursor // important for chinese // Qt::ImTextBeforeCursor // important for chinese
@ -212,7 +206,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
if ( !m_focusObject ) if ( !m_focusObject )
{ {
m_inputItem = nullptr; m_inputItem = nullptr;
m_inputCompositionModel->setInputItem( nullptr ); currentInputCompositionModel()->setInputItem( nullptr );
return; return;
} }
@ -225,7 +219,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
if( qskNearestFocusScope( focusQuickItem ) != m_inputPanel ) if( qskNearestFocusScope( focusQuickItem ) != m_inputPanel )
{ {
m_inputItem = focusQuickItem; m_inputItem = focusQuickItem;
m_inputCompositionModel->setInputItem( m_inputItem ); // ### use a signal/slot connection currentInputCompositionModel()->setInputItem( m_inputItem ); // ### use a signal/slot connection
inputItemChanged = true; inputItemChanged = true;
} }
} }
@ -246,6 +240,28 @@ void QskInputContext::setFocusObject( QObject* focusObject )
update( Qt::InputMethodQuery( Qt::ImQueryAll & ~Qt::ImEnabled ) ); 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 ) void QskInputContext::invokeAction( QInputMethod::Action action, int cursorPosition )
{ {
Q_UNUSED( cursorPosition ); Q_UNUSED( cursorPosition );
@ -256,13 +272,15 @@ void QskInputContext::invokeAction( QInputMethod::Action action, int cursorPosit
switch ( static_cast< QskVirtualKeyboard::Action >( action ) ) switch ( static_cast< QskVirtualKeyboard::Action >( action ) )
{ {
case QskVirtualKeyboard::Compose: case QskVirtualKeyboard::Compose:
m_inputCompositionModel->composeKey( static_cast< Qt::Key >( cursorPosition ) ); currentInputCompositionModel()->composeKey( static_cast< Qt::Key >( cursorPosition ) );
break; break;
case QskVirtualKeyboard::SelectGroup: case QskVirtualKeyboard::SelectGroup:
m_inputCompositionModel->setGroupIndex( cursorPosition ); currentInputCompositionModel()->setGroupIndex( cursorPosition );
break; break;
case QskVirtualKeyboard::SelectCandidate: case QskVirtualKeyboard::SelectCandidate:
m_inputCompositionModel->commitCandidate( cursorPosition ); currentInputCompositionModel()->commitCandidate( cursorPosition );
break; break;
} }
} }
@ -274,10 +292,11 @@ void QskInputContext::emitAnimatingChanged()
void QskInputContext::handleCandidatesChanged() void QskInputContext::handleCandidatesChanged()
{ {
QVector< Qt::Key > candidates( m_inputCompositionModel->candidateCount() ); QVector< Qt::Key > candidates( currentInputCompositionModel()->candidateCount() );
for( int i = 0; i < candidates.length(); ++i ) 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 ); m_inputPanel->setPreeditCandidates( candidates );
@ -296,8 +315,8 @@ void QskInputContext::setInputPanel( QskVirtualKeyboard* inputPanel )
this, &QPlatformInputContext::emitKeyboardRectChanged ); this, &QPlatformInputContext::emitKeyboardRectChanged );
QObject::disconnect( m_inputPanel, &QskVirtualKeyboard::localeChanged, QObject::disconnect( m_inputPanel, &QskVirtualKeyboard::localeChanged,
this, &QPlatformInputContext::emitLocaleChanged ); this, &QPlatformInputContext::emitLocaleChanged );
if ( m_inputCompositionModel ) if ( currentInputCompositionModel() )
m_inputPanel->disconnect( m_inputCompositionModel.get() ); m_inputPanel->disconnect( currentInputCompositionModel() );
} }
m_inputPanel = inputPanel; m_inputPanel = inputPanel;
@ -310,10 +329,10 @@ void QskInputContext::setInputPanel( QskVirtualKeyboard* inputPanel )
this, &QPlatformInputContext::emitKeyboardRectChanged ); this, &QPlatformInputContext::emitKeyboardRectChanged );
QObject::connect( m_inputPanel, &QskVirtualKeyboard::localeChanged, QObject::connect( m_inputPanel, &QskVirtualKeyboard::localeChanged,
this, &QPlatformInputContext::emitLocaleChanged ); this, &QPlatformInputContext::emitLocaleChanged );
if ( m_inputCompositionModel ) if ( currentInputCompositionModel() )
{ {
QObject::connect( QObject::connect(
m_inputCompositionModel.get(), &QskInputCompositionModel::groupsChanged, currentInputCompositionModel(), &QskInputCompositionModel::groupsChanged,
m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups ); m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups );
} }
} }

View File

@ -7,8 +7,9 @@
#define QSK_INPUT_CONTEXT_H #define QSK_INPUT_CONTEXT_H
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
#include <QPointer> #include <QHash>
#include <QQuickItem> #include <QQuickItem>
#include <QPointer>
#include <memory> #include <memory>
@ -36,16 +37,22 @@ public:
QLocale locale() const override; QLocale locale() const override;
void setFocusObject( QObject* ) override; void setFocusObject( QObject* ) override;
QskInputCompositionModel* compositionModelForLocale( const QLocale& locale ) const;
private Q_SLOTS: private Q_SLOTS:
void emitAnimatingChanged(); void emitAnimatingChanged();
void handleCandidatesChanged(); void handleCandidatesChanged();
void setInputPanel( QskVirtualKeyboard* ); void setInputPanel( QskVirtualKeyboard* );
void inputMethodRegistered( const QLocale& locale, QskInputCompositionModel* model );
private: private:
QskInputCompositionModel* currentInputCompositionModel() const;
QPointer< QObject > m_focusObject; QPointer< QObject > m_focusObject;
QPointer< QQuickItem > m_inputItem; QPointer< QQuickItem > m_inputItem;
QPointer< QskVirtualKeyboard > m_inputPanel; QPointer< QskVirtualKeyboard > m_inputPanel;
std::unique_ptr< QskInputCompositionModel > m_inputCompositionModel; QskInputCompositionModel* m_defaultInputCompositionModel;
QHash< QLocale, QskInputCompositionModel* > m_inputModels;
}; };
#endif // QSK_INPUT_CONTEXT_H #endif // QSK_INPUT_CONTEXT_H

View File

@ -14,7 +14,7 @@ class QskPinyinCompositionModel : public QskInputCompositionModel
public: public:
QskPinyinCompositionModel(); QskPinyinCompositionModel();
~QskPinyinCompositionModel(); virtual ~QskPinyinCompositionModel() override;
int candidateCount() const override; int candidateCount() const override;
Qt::Key candidate( int ) const override; Qt::Key candidate( int ) const override;

View File

@ -22,6 +22,7 @@ class QSK_EXPORT QskVirtualKeyboardButton : public QskPushButton
public: public:
QSK_SUBCONTROLS( Panel, Text, TextCancelButton ) QSK_SUBCONTROLS( Panel, Text, TextCancelButton )
QskVirtualKeyboardButton( int keyIndex, QskVirtualKeyboard* inputPanel, QQuickItem* parent = nullptr ); QskVirtualKeyboardButton( int keyIndex, QskVirtualKeyboard* inputPanel, QQuickItem* parent = nullptr );
virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override; virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override;
@ -48,7 +49,6 @@ class QSK_EXPORT QskVirtualKeyboard : public QskBox
using Inherited = QskBox; using Inherited = QskBox;
public: public:
QSK_SUBCONTROLS( Panel ) QSK_SUBCONTROLS( Panel )
struct KeyData struct KeyData