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,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 )
return;
if ( !m_data->preedit.isEmpty() )
if( !spaceLeft )
{
return;
}
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( 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 )
}
else if( hints & Qt::ImhMultiLine )
{
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;
}
#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;
}

View File

@ -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;

View File

@ -14,20 +14,19 @@
#include <QskSetup.h>
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWindow>
#include <QRectF>
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,40 +97,30 @@ 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 )
{
if( m_inputPanel )
{
m_inputPanel->disconnect( oldModel );
QObject::connect(
m_inputCompositionModel.get(), &QskInputCompositionModel::groupsChanged,
newModel, &QskInputCompositionModel::groupsChanged,
m_inputPanel.data(), &QskVirtualKeyboard::setPreeditGroups );
QObject::connect(
m_inputCompositionModel.get(), &QskInputCompositionModel::candidatesChanged,
newModel, &QskInputCompositionModel::candidatesChanged,
this, &QskInputContext::handleCandidatesChanged );
}
}
}
// Qt::ImAbsolutePosition
// Qt::ImTextBeforeCursor // important for chinese
@ -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 );
}
}

View File

@ -7,8 +7,9 @@
#define QSK_INPUT_CONTEXT_H
#include <qpa/qplatforminputcontext.h>
#include <QPointer>
#include <QHash>
#include <QQuickItem>
#include <QPointer>
#include <memory>
@ -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

View File

@ -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();

View File

@ -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