improving inputcontext

This commit is contained in:
Uwe Rathmann 2018-04-04 15:19:51 +02:00
parent 26f8e1b936
commit 67052eb60a
8 changed files with 89 additions and 119 deletions

View File

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

View File

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

View File

@ -4,11 +4,11 @@
*****************************************************************************/
#include "QskInputCompositionModel.h"
#include "QskInputContext.h"
#include <QGuiApplication>
#include <QInputMethodEvent>
#include <QTextCharFormat>
#include <QWindow>
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 );

View File

@ -11,7 +11,7 @@
#include <memory>
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;

View File

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

View File

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

View File

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

View File

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