improving inputcontext
This commit is contained in:
parent
26f8e1b936
commit
67052eb60a
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user