inputpanel classes reorganized, coming closer to something useful
This commit is contained in:
parent
ce50d49312
commit
9f78660f30
|
@ -4,7 +4,8 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "QskInputContext.h"
|
#include "QskInputContext.h"
|
||||||
#include "QskInputManager.h"
|
#include "QskInputEngine.h"
|
||||||
|
#include "QskInputPanel.h"
|
||||||
|
|
||||||
#include "QskLinearBox.h"
|
#include "QskLinearBox.h"
|
||||||
#include "QskDialog.h"
|
#include "QskDialog.h"
|
||||||
|
@ -23,6 +24,58 @@ QSK_QT_PRIVATE_END
|
||||||
#include <qpa/qplatformintegration.h>
|
#include <qpa/qplatformintegration.h>
|
||||||
#include <qpa/qplatforminputcontext.h>
|
#include <qpa/qplatforminputcontext.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class InputEngine final : public QskInputEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void attachToPanel( QQuickItem* item ) override
|
||||||
|
{
|
||||||
|
if ( m_panel )
|
||||||
|
m_panel->attachInputItem( item );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QskControl* createPanel() override
|
||||||
|
{
|
||||||
|
m_panel = new QskInputPanel();
|
||||||
|
|
||||||
|
connect( m_panel, &QskInputPanel::keySelected,
|
||||||
|
this, &QskInputEngine::commitKey, Qt::UniqueConnection );
|
||||||
|
|
||||||
|
connect( m_panel, &QskInputPanel::predictiveTextSelected,
|
||||||
|
this, &QskInputEngine::commitPredictiveText, Qt::UniqueConnection );
|
||||||
|
|
||||||
|
return m_panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QQuickItem* inputProxy() const override
|
||||||
|
{
|
||||||
|
if ( m_panel )
|
||||||
|
{
|
||||||
|
if ( m_panel->panelHints() & QskInputPanel::InputProxy )
|
||||||
|
return m_panel->inputProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
virtual void setPredictionEnabled( bool on ) override
|
||||||
|
{
|
||||||
|
if ( m_panel )
|
||||||
|
m_panel->setPanelHint( QskInputPanel::Prediction, on );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void showPrediction( const QStringList& prediction ) override
|
||||||
|
{
|
||||||
|
if ( m_panel )
|
||||||
|
m_panel->setPrediction( prediction );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer< QskInputPanel > m_panel;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static QPointer< QskInputContext > qskInputContext = nullptr;
|
static QPointer< QskInputContext > qskInputContext = nullptr;
|
||||||
|
|
||||||
static void qskSendToPlatformContext( QEvent::Type type )
|
static void qskSendToPlatformContext( QEvent::Type type )
|
||||||
|
@ -39,7 +92,7 @@ static void qskSendToPlatformContext( QEvent::Type type )
|
||||||
|
|
||||||
static void qskInputContextHook()
|
static void qskInputContextHook()
|
||||||
{
|
{
|
||||||
qAddPostRoutine( []{ delete qskInputContext; } );
|
qAddPostRoutine( [] { delete qskInputContext; } );
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_COREAPP_STARTUP_FUNCTION( qskInputContextHook )
|
Q_COREAPP_STARTUP_FUNCTION( qskInputContextHook )
|
||||||
|
@ -73,20 +126,53 @@ public:
|
||||||
QskPopup* inputPopup = nullptr;
|
QskPopup* inputPopup = nullptr;
|
||||||
QskWindow* inputWindow = nullptr;
|
QskWindow* inputWindow = nullptr;
|
||||||
|
|
||||||
QskInputManager* inputManager = nullptr;
|
QPointer< QskInputEngine > inputEngine;
|
||||||
};
|
};
|
||||||
|
|
||||||
QskInputContext::QskInputContext():
|
QskInputContext::QskInputContext():
|
||||||
m_data( new PrivateData() )
|
m_data( new PrivateData() )
|
||||||
{
|
{
|
||||||
setObjectName( "InputContext" );
|
setObjectName( "InputContext" );
|
||||||
m_data->inputManager = new QskInputManager( this );
|
setEngine( new InputEngine() );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskInputContext::~QskInputContext()
|
QskInputContext::~QskInputContext()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QskInputContext::setEngine( QskInputEngine* engine )
|
||||||
|
{
|
||||||
|
if ( m_data->inputEngine == engine )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( m_data->inputEngine && m_data->inputEngine->parent() == this )
|
||||||
|
{
|
||||||
|
m_data->inputEngine->disconnect( this );
|
||||||
|
|
||||||
|
if ( m_data->inputEngine->parent() == this )
|
||||||
|
delete m_data->inputEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_data->inputEngine = engine;
|
||||||
|
|
||||||
|
if ( engine )
|
||||||
|
{
|
||||||
|
if ( engine->parent() == nullptr )
|
||||||
|
engine->setParent( this );
|
||||||
|
|
||||||
|
connect( engine, &QskInputEngine::activeChanged,
|
||||||
|
this, &QskInputContext::activeChanged );
|
||||||
|
|
||||||
|
connect( engine, &QskInputEngine::localeChanged,
|
||||||
|
this, [] { qskSendToPlatformContext( QEvent::LocaleChange ); } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QskInputEngine* QskInputContext::engine() const
|
||||||
|
{
|
||||||
|
return m_data->inputEngine;
|
||||||
|
}
|
||||||
|
|
||||||
QQuickItem* QskInputContext::inputItem() const
|
QQuickItem* QskInputContext::inputItem() const
|
||||||
{
|
{
|
||||||
return m_data->inputItem;
|
return m_data->inputItem;
|
||||||
|
@ -94,19 +180,14 @@ QQuickItem* QskInputContext::inputItem() const
|
||||||
|
|
||||||
QskControl* QskInputContext::inputPanel() const
|
QskControl* QskInputContext::inputPanel() const
|
||||||
{
|
{
|
||||||
auto panel = m_data->inputManager->panel( true );
|
if ( m_data->inputEngine == nullptr )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
if ( panel->parent() != this )
|
auto panel = m_data->inputEngine->panel( true );
|
||||||
{
|
|
||||||
|
if ( panel && panel->parent() != this )
|
||||||
panel->setParent( const_cast< QskInputContext* >( this ) );
|
panel->setParent( const_cast< QskInputContext* >( this ) );
|
||||||
|
|
||||||
connect( panel, &QQuickItem::visibleChanged,
|
|
||||||
this, &QskInputContext::activeChanged, Qt::UniqueConnection );
|
|
||||||
|
|
||||||
connect( panel, &QskControl::localeChanged,
|
|
||||||
this, [] { qskSendToPlatformContext( QEvent::LocaleChange ); } );
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +205,8 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data->inputManager->processInputMethodQueries( queries );
|
if ( m_data->inputEngine )
|
||||||
|
m_data->inputEngine->updateInputPanel( queries );
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF QskInputContext::panelRect() const
|
QRectF QskInputContext::panelRect() const
|
||||||
|
@ -146,8 +228,9 @@ QskPopup* QskInputContext::createEmbeddingPopup( QskControl* panel )
|
||||||
auto box = new QskLinearBox( popup );
|
auto box = new QskLinearBox( popup );
|
||||||
box->addItem( panel );
|
box->addItem( panel );
|
||||||
|
|
||||||
const auto alignment =
|
Qt::Alignment alignment = Qt::AlignVCenter;
|
||||||
m_data->inputManager->panelAlignment() & Qt::AlignVertical_Mask;
|
if ( m_data->inputEngine )
|
||||||
|
alignment = m_data->inputEngine->panelAlignment() & Qt::AlignVertical_Mask;
|
||||||
|
|
||||||
popup->setOverlay( alignment == Qt::AlignVCenter );
|
popup->setOverlay( alignment == Qt::AlignVCenter );
|
||||||
|
|
||||||
|
@ -262,7 +345,8 @@ void QskInputContext::showPanel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data->inputManager->attachInputItem( m_data->inputItem );
|
if ( m_data->inputEngine )
|
||||||
|
m_data->inputEngine->attachInputItem( m_data->inputItem );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::hidePanel()
|
void QskInputContext::hidePanel()
|
||||||
|
@ -284,10 +368,13 @@ void QskInputContext::hidePanel()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( auto panel = m_data->inputManager->panel( false ) )
|
if ( m_data->inputEngine )
|
||||||
panel->setParentItem( nullptr );
|
{
|
||||||
|
if ( auto panel = m_data->inputEngine->panel( false ) )
|
||||||
|
panel->setParentItem( nullptr );
|
||||||
|
|
||||||
m_data->inputManager->attachInputItem( nullptr );
|
m_data->inputEngine->attachInputItem( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
if ( m_data->inputPopup )
|
if ( m_data->inputPopup )
|
||||||
m_data->inputPopup->deleteLater();
|
m_data->inputPopup->deleteLater();
|
||||||
|
@ -325,13 +412,8 @@ bool QskInputContext::isActive() const
|
||||||
|
|
||||||
QLocale QskInputContext::locale() const
|
QLocale QskInputContext::locale() const
|
||||||
{
|
{
|
||||||
if ( m_data->inputItem )
|
if ( auto panel = inputPanel() )
|
||||||
{
|
return panel->locale();
|
||||||
QInputMethodQueryEvent event( Qt::ImPreferredLanguage );
|
|
||||||
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
|
||||||
|
|
||||||
return event.value( Qt::ImPreferredLanguage ).toLocale();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QLocale();
|
return QLocale();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
class QskTextPredictor;
|
class QskTextPredictor;
|
||||||
class QskControl;
|
class QskControl;
|
||||||
class QskInputManager;
|
class QskInputEngine;
|
||||||
class QskPopup;
|
class QskPopup;
|
||||||
class QskWindow;
|
class QskWindow;
|
||||||
class QQuickItem;
|
class QQuickItem;
|
||||||
|
@ -28,8 +28,8 @@ public:
|
||||||
QskInputContext();
|
QskInputContext();
|
||||||
virtual ~QskInputContext();
|
virtual ~QskInputContext();
|
||||||
|
|
||||||
void setManager( QskInputManager* );
|
void setEngine( QskInputEngine* );
|
||||||
QskInputManager manager();
|
QskInputEngine* engine() const;
|
||||||
|
|
||||||
QRectF panelRect() const;
|
QRectF panelRect() const;
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,82 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "QskInputEngine.h"
|
#include "QskInputEngine.h"
|
||||||
|
#include "QskInputContext.h"
|
||||||
#include "QskTextPredictor.h"
|
#include "QskTextPredictor.h"
|
||||||
|
#include "QskControl.h"
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QVector>
|
#include <QTextCharFormat>
|
||||||
#include <QString>
|
|
||||||
|
static inline QQuickItem* qskReceiverItem( const QskInputEngine* engine )
|
||||||
|
{
|
||||||
|
auto item = engine->inputProxy();
|
||||||
|
return item ? item : engine->inputItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qskSendReplaceText( QQuickItem* receiver, const QString& text )
|
||||||
|
{
|
||||||
|
if ( receiver == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QInputMethodEvent::Attribute attribute(
|
||||||
|
QInputMethodEvent::Selection, 0, 32767, QVariant() );
|
||||||
|
|
||||||
|
QInputMethodEvent event1( QString(), { attribute } );
|
||||||
|
QCoreApplication::sendEvent( receiver, &event1 );
|
||||||
|
|
||||||
|
QInputMethodEvent event2;
|
||||||
|
event2.setCommitString( text );
|
||||||
|
|
||||||
|
QCoreApplication::sendEvent( receiver, &event2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qskSendText( QQuickItem* receiver,
|
||||||
|
const QString& text, bool isFinal )
|
||||||
|
{
|
||||||
|
if ( receiver == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( isFinal )
|
||||||
|
{
|
||||||
|
QInputMethodEvent event;
|
||||||
|
event.setCommitString( text );
|
||||||
|
|
||||||
|
QCoreApplication::sendEvent( receiver, &event );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setFontUnderline( true );
|
||||||
|
|
||||||
|
const QInputMethodEvent::Attribute attribute(
|
||||||
|
QInputMethodEvent::TextFormat, 0, text.length(), format );
|
||||||
|
|
||||||
|
QInputMethodEvent event( text, { attribute } );
|
||||||
|
|
||||||
|
QCoreApplication::sendEvent( receiver, &event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qskSendKey( QQuickItem* receiver, int key )
|
||||||
|
{
|
||||||
|
if ( receiver == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
|
||||||
|
QCoreApplication::sendEvent( receiver, &keyPress );
|
||||||
|
|
||||||
|
QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
|
||||||
|
QCoreApplication::sendEvent( receiver, &keyRelease );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool qskUsePrediction( Qt::InputMethodHints hints )
|
||||||
|
{
|
||||||
|
constexpr Qt::InputMethodHints mask =
|
||||||
|
Qt::ImhNoPredictiveText | Qt::ImhExclusiveInputMask | Qt::ImhHiddenText;
|
||||||
|
|
||||||
|
return ( hints & mask ) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline QString qskKeyString( int keyCode )
|
static inline QString qskKeyString( int keyCode )
|
||||||
{
|
{
|
||||||
|
@ -36,23 +107,161 @@ static inline QString qskKeyString( int keyCode )
|
||||||
return QChar( keyCode );
|
return QChar( keyCode );
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool qskUsePrediction( Qt::InputMethodHints hints )
|
namespace
|
||||||
{
|
{
|
||||||
constexpr Qt::InputMethodHints mask =
|
class KeyProcessor
|
||||||
Qt::ImhNoPredictiveText | Qt::ImhExclusiveInputMask;
|
{
|
||||||
|
public:
|
||||||
|
class Result
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int key = 0;
|
||||||
|
|
||||||
return ( hints & mask ) == 0;
|
QString text;
|
||||||
|
bool isFinal = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result processKey( int key, Qt::InputMethodHints inputHints,
|
||||||
|
QskTextPredictor* predictor, int spaceLeft )
|
||||||
|
{
|
||||||
|
Result result;
|
||||||
|
|
||||||
|
// First we have to handle the control keys
|
||||||
|
|
||||||
|
switch ( key )
|
||||||
|
{
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
case Qt::Key_Muhenkan:
|
||||||
|
{
|
||||||
|
if ( predictor )
|
||||||
|
{
|
||||||
|
if ( !m_preedit.isEmpty() )
|
||||||
|
{
|
||||||
|
m_preedit.chop( 1 );
|
||||||
|
|
||||||
|
result.text = m_preedit;
|
||||||
|
result.isFinal = false;
|
||||||
|
|
||||||
|
predictor->request( m_preedit );
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.key = Qt::Key_Backspace;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case Qt::Key_Return:
|
||||||
|
{
|
||||||
|
if ( predictor )
|
||||||
|
{
|
||||||
|
if ( !m_preedit.isEmpty() )
|
||||||
|
{
|
||||||
|
if ( spaceLeft )
|
||||||
|
{
|
||||||
|
result.text = m_preedit.left( spaceLeft );
|
||||||
|
result.isFinal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !( inputHints & Qt::ImhMultiLine ) )
|
||||||
|
{
|
||||||
|
result.key = Qt::Key_Return;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Qt::Key_Space:
|
||||||
|
{
|
||||||
|
if ( predictor )
|
||||||
|
{
|
||||||
|
if ( !m_preedit.isEmpty() && spaceLeft)
|
||||||
|
{
|
||||||
|
m_preedit += qskKeyString( key );
|
||||||
|
m_preedit = m_preedit.left( spaceLeft );
|
||||||
|
|
||||||
|
result.text = m_preedit;
|
||||||
|
result.isFinal = true;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Qt::Key_Left:
|
||||||
|
case Qt::Key_Right:
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
{
|
||||||
|
result.key = key;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString text = qskKeyString( key );
|
||||||
|
|
||||||
|
if ( predictor )
|
||||||
|
{
|
||||||
|
m_preedit += text;
|
||||||
|
|
||||||
|
predictor->request( m_preedit );
|
||||||
|
|
||||||
|
if ( predictor->candidateCount() > 0 )
|
||||||
|
{
|
||||||
|
result.text = m_preedit;
|
||||||
|
result.isFinal = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.text = m_preedit.left( spaceLeft );
|
||||||
|
result.isFinal = true;
|
||||||
|
|
||||||
|
m_preedit.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.text = text;
|
||||||
|
result.isFinal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_preedit.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_preedit;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class QskInputEngine::PrivateData
|
class QskInputEngine::PrivateData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
KeyProcessor keyProcessor;
|
||||||
|
QPointer< QskControl > panel;
|
||||||
|
QPointer< QQuickItem > inputItem;
|
||||||
|
|
||||||
|
QLocale predictorLocale;
|
||||||
QPointer< QskTextPredictor > predictor;
|
QPointer< QskTextPredictor > predictor;
|
||||||
QString preedit;
|
|
||||||
|
Qt::InputMethodHints inputHints = 0;
|
||||||
|
bool hasPredictorLocale = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
QskInputEngine::QskInputEngine( QObject* parent ):
|
QskInputEngine::QskInputEngine( QObject* parent ):
|
||||||
QObject( parent ),
|
Inherited( parent ),
|
||||||
m_data( new PrivateData() )
|
m_data( new PrivateData() )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -61,12 +270,139 @@ QskInputEngine::~QskInputEngine()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputEngine::setPredictor( QskTextPredictor* predictor )
|
void QskInputEngine::attachInputItem( QQuickItem* item )
|
||||||
{
|
{
|
||||||
if ( predictor == m_data->predictor )
|
if ( item == m_data->inputItem )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
reset();
|
m_data->inputItem = item;
|
||||||
|
|
||||||
|
if ( item )
|
||||||
|
{
|
||||||
|
auto context = QskInputContext::instance();
|
||||||
|
const auto locale = context->locale();
|
||||||
|
|
||||||
|
if ( m_data->predictor )
|
||||||
|
m_data->predictor->reset();
|
||||||
|
|
||||||
|
m_data->keyProcessor.reset();
|
||||||
|
m_data->inputHints = 0;
|
||||||
|
|
||||||
|
attachToPanel( item );
|
||||||
|
|
||||||
|
Qt::InputMethodQueries queries = Qt::ImQueryAll;
|
||||||
|
queries &= ~Qt::ImEnabled;
|
||||||
|
|
||||||
|
updateInputPanel( queries );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attachToPanel( nullptr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::updateInputPanel(
|
||||||
|
Qt::InputMethodQueries queries )
|
||||||
|
{
|
||||||
|
auto item = inputItem();
|
||||||
|
if ( item == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QInputMethodQueryEvent event( queries );
|
||||||
|
QCoreApplication::sendEvent( item, &event );
|
||||||
|
|
||||||
|
if ( queries & Qt::ImHints )
|
||||||
|
{
|
||||||
|
m_data->inputHints = static_cast< Qt::InputMethodHints >(
|
||||||
|
event.value( Qt::ImHints ).toInt() );
|
||||||
|
|
||||||
|
updatePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( queries & Qt::ImPreferredLanguage )
|
||||||
|
{
|
||||||
|
if ( m_data->panel )
|
||||||
|
{
|
||||||
|
m_data->panel->setLocale(
|
||||||
|
event.value( Qt::ImPreferredLanguage ).toLocale() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QskControl* QskInputEngine::panel( bool doCreate )
|
||||||
|
{
|
||||||
|
if ( m_data->panel == nullptr && doCreate )
|
||||||
|
{
|
||||||
|
auto panel = createPanel();
|
||||||
|
|
||||||
|
connect( panel, &QQuickItem::visibleChanged,
|
||||||
|
this, &QskInputEngine::activeChanged );
|
||||||
|
|
||||||
|
connect( panel, &QskControl::localeChanged,
|
||||||
|
this, &QskInputEngine::updateLocale );
|
||||||
|
|
||||||
|
m_data->panel = panel;
|
||||||
|
updateLocale( m_data->panel->locale() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_data->panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Alignment QskInputEngine::panelAlignment() const
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
When we have an input proxy, we don't care if
|
||||||
|
the input item becomes hidden
|
||||||
|
*/
|
||||||
|
|
||||||
|
return inputProxy() ? Qt::AlignVCenter : Qt::AlignBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLocale QskInputEngine::locale() const
|
||||||
|
{
|
||||||
|
if ( m_data->panel )
|
||||||
|
return m_data->panel->locale();
|
||||||
|
|
||||||
|
return QLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::updateLocale( const QLocale& locale )
|
||||||
|
{
|
||||||
|
if ( !m_data->hasPredictorLocale || locale != m_data->predictorLocale )
|
||||||
|
{
|
||||||
|
m_data->hasPredictorLocale = true;
|
||||||
|
m_data->predictorLocale = locale;
|
||||||
|
resetPredictor( locale );
|
||||||
|
|
||||||
|
m_data->keyProcessor.reset();
|
||||||
|
updatePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT localeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::updatePanel()
|
||||||
|
{
|
||||||
|
setPredictionEnabled(
|
||||||
|
m_data->predictor && qskUsePrediction( m_data->inputHints ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* QskInputEngine::inputItem() const
|
||||||
|
{
|
||||||
|
return m_data->inputItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* QskInputEngine::inputProxy() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::resetPredictor( const QLocale& locale )
|
||||||
|
{
|
||||||
|
auto predictor = QskInputContext::instance()->textPredictor( locale );
|
||||||
|
|
||||||
|
if ( predictor == m_data->predictor )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( m_data->predictor )
|
if ( m_data->predictor )
|
||||||
{
|
{
|
||||||
|
@ -87,172 +423,124 @@ void QskInputEngine::setPredictor( QskTextPredictor* predictor )
|
||||||
predictor->setParent( this );
|
predictor->setParent( this );
|
||||||
|
|
||||||
connect( predictor, &QskTextPredictor::predictionChanged,
|
connect( predictor, &QskTextPredictor::predictionChanged,
|
||||||
this, &QskInputEngine::predictionChanged );
|
this, &QskInputEngine::updatePrediction );
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data->predictor = predictor;
|
m_data->predictor = predictor;
|
||||||
}
|
}
|
||||||
|
|
||||||
QskTextPredictor* QskInputEngine::predictor() const
|
void QskInputEngine::applyInput( bool success )
|
||||||
{
|
{
|
||||||
return m_data->predictor;
|
auto item = inputItem();
|
||||||
|
if ( item == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( success )
|
||||||
|
{
|
||||||
|
if ( auto proxy = inputProxy() )
|
||||||
|
{
|
||||||
|
const auto value = proxy->property( "text" );
|
||||||
|
if ( value.canConvert< QString >() )
|
||||||
|
qskSendReplaceText( item, value.toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qskSendKey( item, success ? Qt::Key_Return : Qt::Key_Escape );
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList QskInputEngine::prediction() const
|
void QskInputEngine::applyText( const QString& text, bool isFinal )
|
||||||
{
|
{
|
||||||
QStringList candidates;
|
qskSendText( qskReceiverItem( this ), text, isFinal );
|
||||||
|
|
||||||
if ( const auto predictor = m_data->predictor )
|
|
||||||
{
|
|
||||||
const auto count = predictor->candidateCount();
|
|
||||||
candidates.reserve( count );
|
|
||||||
|
|
||||||
for( int i = 0; i < count; i++ )
|
|
||||||
candidates += predictor->candidate( i );
|
|
||||||
}
|
|
||||||
|
|
||||||
return candidates;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QskInputEngine::Result QskInputEngine::processKey( int key,
|
void QskInputEngine::applyKey( int key )
|
||||||
Qt::InputMethodHints inputHints, int spaceLeft )
|
|
||||||
{
|
{
|
||||||
QskInputEngine::Result result;
|
// control keys like left/right
|
||||||
|
qskSendKey( qskReceiverItem( this ), key );
|
||||||
auto& preedit = m_data->preedit;
|
|
||||||
|
|
||||||
QskTextPredictor* predictor = nullptr;
|
|
||||||
if ( qskUsePrediction( inputHints ) )
|
|
||||||
predictor = m_data->predictor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
First we have to handle the control keys
|
|
||||||
*/
|
|
||||||
switch ( key )
|
|
||||||
{
|
|
||||||
case Qt::Key_Backspace:
|
|
||||||
case Qt::Key_Muhenkan:
|
|
||||||
{
|
|
||||||
if ( predictor )
|
|
||||||
{
|
|
||||||
if ( !preedit.isEmpty() )
|
|
||||||
{
|
|
||||||
preedit.chop( 1 );
|
|
||||||
|
|
||||||
result.text = preedit;
|
|
||||||
result.isFinal = false;
|
|
||||||
|
|
||||||
predictor->request( preedit );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.key = Qt::Key_Backspace;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case Qt::Key_Return:
|
|
||||||
{
|
|
||||||
if ( predictor )
|
|
||||||
{
|
|
||||||
if ( !preedit.isEmpty() )
|
|
||||||
{
|
|
||||||
if ( spaceLeft )
|
|
||||||
{
|
|
||||||
result.text = preedit.left( spaceLeft );
|
|
||||||
result.isFinal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !( inputHints & Qt::ImhMultiLine ) )
|
|
||||||
{
|
|
||||||
result.key = Qt::Key_Return;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Qt::Key_Space:
|
|
||||||
{
|
|
||||||
if ( predictor )
|
|
||||||
{
|
|
||||||
if ( !preedit.isEmpty() && spaceLeft)
|
|
||||||
{
|
|
||||||
preedit += qskKeyString( key );
|
|
||||||
preedit = preedit.left( spaceLeft );
|
|
||||||
|
|
||||||
result.text = preedit;
|
|
||||||
result.isFinal = true;
|
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Qt::Key_Left:
|
|
||||||
case Qt::Key_Right:
|
|
||||||
case Qt::Key_Escape:
|
|
||||||
{
|
|
||||||
result.key = key;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString text = qskKeyString( key );
|
|
||||||
|
|
||||||
if ( predictor )
|
|
||||||
{
|
|
||||||
preedit += text;
|
|
||||||
|
|
||||||
predictor->request( preedit );
|
|
||||||
|
|
||||||
if ( predictor->candidateCount() > 0 )
|
|
||||||
{
|
|
||||||
result.text = preedit;
|
|
||||||
result.isFinal = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.text = preedit.left( spaceLeft );
|
|
||||||
result.isFinal = true;
|
|
||||||
|
|
||||||
preedit.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.text = text;
|
|
||||||
result.isFinal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QskInputEngine::predictiveText( int index ) const
|
void QskInputEngine::commitPredictiveText( int index )
|
||||||
{
|
{
|
||||||
if ( const QskTextPredictor* predictor = m_data->predictor )
|
QString text;
|
||||||
|
|
||||||
|
if ( m_data->predictor )
|
||||||
{
|
{
|
||||||
if ( index >= 0 && index < predictor->candidateCount() )
|
text = m_data->predictor->candidate( index );
|
||||||
return predictor->candidate( index );
|
m_data->predictor->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
m_data->keyProcessor.reset();
|
||||||
|
|
||||||
|
showPrediction( QStringList() );
|
||||||
|
applyText( text, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputEngine::reset()
|
void QskInputEngine::updatePrediction()
|
||||||
{
|
{
|
||||||
if ( m_data->predictor )
|
if ( m_data->predictor )
|
||||||
m_data->predictor->reset();
|
showPrediction( m_data->predictor->candidates() );
|
||||||
|
}
|
||||||
|
|
||||||
m_data->preedit.clear();
|
void QskInputEngine::setPredictionEnabled( bool on )
|
||||||
|
{
|
||||||
|
Q_UNUSED( on )
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::showPrediction( const QStringList& )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputEngine::commitKey( int key )
|
||||||
|
{
|
||||||
|
int spaceLeft = -1;
|
||||||
|
|
||||||
|
if ( !( m_data->inputHints & Qt::ImhMultiLine ) )
|
||||||
|
{
|
||||||
|
QInputMethodQueryEvent event1( Qt::ImMaximumTextLength );
|
||||||
|
QCoreApplication::sendEvent( inputItem(), &event1 );
|
||||||
|
|
||||||
|
const int maxChars = event1.value( Qt::ImMaximumTextLength ).toInt();
|
||||||
|
if ( maxChars >= 0 )
|
||||||
|
{
|
||||||
|
QInputMethodQueryEvent event2( Qt::ImSurroundingText );
|
||||||
|
QCoreApplication::sendEvent( qskReceiverItem( this ), &event2 );
|
||||||
|
|
||||||
|
const auto text = event2.value( Qt::ImSurroundingText ).toString();
|
||||||
|
spaceLeft = maxChars - text.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QskTextPredictor* predictor = nullptr;
|
||||||
|
if ( qskUsePrediction( m_data->inputHints ) )
|
||||||
|
predictor = m_data->predictor;
|
||||||
|
|
||||||
|
const auto result = m_data->keyProcessor.processKey(
|
||||||
|
key, m_data->inputHints, predictor, spaceLeft );
|
||||||
|
|
||||||
|
if ( result.key )
|
||||||
|
{
|
||||||
|
switch( result.key )
|
||||||
|
{
|
||||||
|
case Qt::Key_Return:
|
||||||
|
{
|
||||||
|
applyInput( true );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
{
|
||||||
|
applyInput( false );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
applyKey( result.key );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( !result.text.isEmpty() )
|
||||||
|
{
|
||||||
|
applyText( result.text, result.isFinal );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_QskInputEngine.cpp"
|
#include "moc_QskInputEngine.cpp"
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class QskTextPredictor;
|
class QskTextPredictor;
|
||||||
|
class QskControl;
|
||||||
class QQuickItem;
|
class QQuickItem;
|
||||||
class QStringList;
|
class QLocale;
|
||||||
|
|
||||||
class QSK_EXPORT QskInputEngine : public QObject
|
class QSK_EXPORT QskInputEngine : public QObject
|
||||||
{
|
{
|
||||||
|
@ -21,33 +22,47 @@ class QSK_EXPORT QskInputEngine : public QObject
|
||||||
using Inherited = QObject;
|
using Inherited = QObject;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Result
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int key = 0;
|
|
||||||
|
|
||||||
QString text;
|
|
||||||
bool isFinal = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
QskInputEngine( QObject* parent = nullptr );
|
QskInputEngine( QObject* parent = nullptr );
|
||||||
virtual ~QskInputEngine();
|
virtual ~QskInputEngine() override;
|
||||||
|
|
||||||
void setPredictor( QskTextPredictor* );
|
virtual void attachInputItem( QQuickItem* );
|
||||||
QskTextPredictor* predictor() const;
|
virtual void updateInputPanel( Qt::InputMethodQueries );
|
||||||
|
|
||||||
virtual Result processKey( int key,
|
QskControl* panel( bool doCreate );
|
||||||
Qt::InputMethodHints, int spaceLeft = -1 );
|
virtual Qt::Alignment panelAlignment() const;
|
||||||
|
|
||||||
QString predictiveText( int ) const;
|
virtual QQuickItem* inputProxy() const;
|
||||||
QStringList prediction() const;
|
virtual QQuickItem* inputItem() const;
|
||||||
|
|
||||||
void reset();
|
QLocale locale() const;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void commitKey( int keyCode );
|
||||||
|
void commitPredictiveText( int index );
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void predictionChanged();
|
void activeChanged();
|
||||||
|
void localeChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QskControl* createPanel() = 0;
|
||||||
|
virtual void attachToPanel( QQuickItem* ) = 0;
|
||||||
|
|
||||||
|
virtual void setPredictionEnabled( bool on );
|
||||||
|
virtual void showPrediction( const QStringList& );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void applyInput( bool success );
|
||||||
|
void applyText( const QString&, bool isFinal );
|
||||||
|
void applyKey( int keyCode );
|
||||||
|
|
||||||
|
void resetPredictor( const QLocale& );
|
||||||
|
void updatePrediction();
|
||||||
|
|
||||||
|
void updatePanel();
|
||||||
|
|
||||||
|
void updateLocale( const QLocale& );
|
||||||
|
|
||||||
class PrivateData;
|
class PrivateData;
|
||||||
std::unique_ptr< PrivateData > m_data;
|
std::unique_ptr< PrivateData > m_data;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,369 +0,0 @@
|
||||||
/******************************************************************************
|
|
||||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
||||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "QskInputManager.h"
|
|
||||||
#include "QskInputPanel.h"
|
|
||||||
#include "QskInputEngine.h"
|
|
||||||
#include "QskInputContext.h"
|
|
||||||
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QTextCharFormat>
|
|
||||||
|
|
||||||
static inline QQuickItem* qskReceiverItem( const QskInputManager* manager )
|
|
||||||
{
|
|
||||||
auto item = manager->inputProxy();
|
|
||||||
return item ? item : manager->inputItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void qskSendReplaceText( QQuickItem* receiver, const QString& text )
|
|
||||||
{
|
|
||||||
if ( receiver == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
QInputMethodEvent::Attribute attribute(
|
|
||||||
QInputMethodEvent::Selection, 0, 32767, QVariant() );
|
|
||||||
|
|
||||||
QInputMethodEvent event1( QString(), { attribute } );
|
|
||||||
QCoreApplication::sendEvent( receiver, &event1 );
|
|
||||||
|
|
||||||
QInputMethodEvent event2;
|
|
||||||
event2.setCommitString( text );
|
|
||||||
|
|
||||||
QCoreApplication::sendEvent( receiver, &event2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void qskSendText( QQuickItem* receiver,
|
|
||||||
const QString& text, bool isFinal )
|
|
||||||
{
|
|
||||||
if ( receiver == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( isFinal )
|
|
||||||
{
|
|
||||||
QInputMethodEvent event;
|
|
||||||
event.setCommitString( text );
|
|
||||||
|
|
||||||
QCoreApplication::sendEvent( receiver, &event );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QTextCharFormat format;
|
|
||||||
format.setFontUnderline( true );
|
|
||||||
|
|
||||||
const QInputMethodEvent::Attribute attribute(
|
|
||||||
QInputMethodEvent::TextFormat, 0, text.length(), format );
|
|
||||||
|
|
||||||
QInputMethodEvent event( text, { attribute } );
|
|
||||||
|
|
||||||
QCoreApplication::sendEvent( receiver, &event );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void qskSendKey( QQuickItem* receiver, int key )
|
|
||||||
{
|
|
||||||
if ( receiver == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
|
|
||||||
QCoreApplication::sendEvent( receiver, &keyPress );
|
|
||||||
|
|
||||||
QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
|
|
||||||
QCoreApplication::sendEvent( receiver, &keyRelease );
|
|
||||||
}
|
|
||||||
|
|
||||||
class QskInputManager::PrivateData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QPointer< QskInputEngine > engine;
|
|
||||||
QPointer< QskControl > panel;
|
|
||||||
|
|
||||||
QLocale predictorLocale;
|
|
||||||
Qt::InputMethodHints inputHints = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
QskInputManager::QskInputManager( QObject* parent ):
|
|
||||||
Inherited( parent ),
|
|
||||||
m_data( new PrivateData() )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QskInputManager::~QskInputManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::attachInputItem( QQuickItem* item )
|
|
||||||
{
|
|
||||||
auto panel = qobject_cast< QskInputPanel* >( m_data->panel );
|
|
||||||
if ( panel == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( item )
|
|
||||||
{
|
|
||||||
auto context = QskInputContext::instance();
|
|
||||||
const auto locale = context->locale();
|
|
||||||
|
|
||||||
updateEngine( locale );
|
|
||||||
m_data->engine->reset();
|
|
||||||
|
|
||||||
panel->setLocale( locale );
|
|
||||||
panel->attachInputItem( item );
|
|
||||||
|
|
||||||
Qt::InputMethodQueries queries = Qt::ImQueryAll;
|
|
||||||
queries &= ~Qt::ImEnabled;
|
|
||||||
|
|
||||||
processInputMethodQueries( queries );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panel->attachInputItem( nullptr );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::processInputMethodQueries(
|
|
||||||
Qt::InputMethodQueries queries )
|
|
||||||
{
|
|
||||||
if ( queries & Qt::ImHints )
|
|
||||||
{
|
|
||||||
m_data->inputHints = 0;
|
|
||||||
|
|
||||||
if ( auto item = inputItem() )
|
|
||||||
{
|
|
||||||
QInputMethodQueryEvent event( Qt::ImHints );
|
|
||||||
QCoreApplication::sendEvent( item, &event );
|
|
||||||
|
|
||||||
m_data->inputHints = static_cast< Qt::InputMethodHints >(
|
|
||||||
event.value( Qt::ImHints ).toInt() );
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePanel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QskControl* QskInputManager::panel( bool doCreate )
|
|
||||||
{
|
|
||||||
if ( m_data->panel == nullptr && doCreate )
|
|
||||||
{
|
|
||||||
m_data->panel = createPanel();
|
|
||||||
|
|
||||||
connect( m_data->panel, &QskControl::localeChanged,
|
|
||||||
this, &QskInputManager::updatePredictor, Qt::UniqueConnection );
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_data->panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::Alignment QskInputManager::panelAlignment() const
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
{
|
|
||||||
if ( panel->panelHints() & QskInputPanel::InputProxy )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
When the panel has an input proxy we don't need to see
|
|
||||||
the input item and can put the panel in the center
|
|
||||||
*/
|
|
||||||
|
|
||||||
return Qt::AlignVCenter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Qt::AlignBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
QskControl* QskInputManager::createPanel()
|
|
||||||
{
|
|
||||||
auto panel = new QskInputPanel();
|
|
||||||
|
|
||||||
connect( panel, &QskInputPanel::keySelected,
|
|
||||||
this, &QskInputManager::commitKey, Qt::UniqueConnection );
|
|
||||||
|
|
||||||
connect( panel, &QskInputPanel::predictiveTextSelected,
|
|
||||||
this, &QskInputManager::commitPredictiveText, Qt::UniqueConnection );
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
QskInputEngine* QskInputManager::createEngine()
|
|
||||||
{
|
|
||||||
return new QskInputEngine();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::updateEngine( const QLocale& locale )
|
|
||||||
{
|
|
||||||
bool updatePredictor;
|
|
||||||
|
|
||||||
if ( m_data->engine == nullptr)
|
|
||||||
{
|
|
||||||
auto engine = createEngine();
|
|
||||||
if ( engine->parent() == nullptr )
|
|
||||||
engine->setParent( this );
|
|
||||||
|
|
||||||
connect( engine, &QskInputEngine::predictionChanged,
|
|
||||||
this, &QskInputManager::updatePrediction );
|
|
||||||
|
|
||||||
m_data->engine = engine;
|
|
||||||
updatePredictor = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updatePredictor = ( locale != m_data->predictorLocale );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( updatePredictor )
|
|
||||||
{
|
|
||||||
auto context = QskInputContext::instance();
|
|
||||||
|
|
||||||
m_data->predictorLocale = locale;
|
|
||||||
m_data->engine->setPredictor( context->textPredictor( locale ) );
|
|
||||||
updatePanel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::updatePredictor()
|
|
||||||
{
|
|
||||||
if ( m_data->panel && m_data->engine )
|
|
||||||
{
|
|
||||||
auto context = QskInputContext::instance();
|
|
||||||
const auto locale = context->locale();
|
|
||||||
|
|
||||||
if ( m_data->predictorLocale != locale )
|
|
||||||
m_data->engine->setPredictor( context->textPredictor( locale ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::updatePanel()
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
{
|
|
||||||
const auto mask = Qt::ImhNoPredictiveText
|
|
||||||
| Qt::ImhExclusiveInputMask | Qt::ImhHiddenText;
|
|
||||||
|
|
||||||
panel->setPanelHint( QskInputPanel::Prediction,
|
|
||||||
m_data->engine->predictor() && !( m_data->inputHints & mask ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* QskInputManager::inputItem() const
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
return panel->attachedInputItem();
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* QskInputManager::inputProxy() const
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
{
|
|
||||||
if ( panel->panelHints() & QskInputPanel::InputProxy )
|
|
||||||
return panel->inputProxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::applyInput( bool success )
|
|
||||||
{
|
|
||||||
auto item = inputItem();
|
|
||||||
if ( item == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( success )
|
|
||||||
{
|
|
||||||
if ( auto proxy = inputProxy() )
|
|
||||||
{
|
|
||||||
const auto value = proxy->property( "text" );
|
|
||||||
if ( value.canConvert<QString>() )
|
|
||||||
qskSendReplaceText( item, value.toString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qskSendKey( item, success ? Qt::Key_Return : Qt::Key_Escape );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::applyText( const QString& text, bool isFinal )
|
|
||||||
{
|
|
||||||
qskSendText( qskReceiverItem( this ), text, isFinal );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::applyKey( int key )
|
|
||||||
{
|
|
||||||
// control keys like left/right
|
|
||||||
qskSendKey( qskReceiverItem( this ), key );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::commitPredictiveText( int index )
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
{
|
|
||||||
panel->setPrediction( QStringList() );
|
|
||||||
|
|
||||||
const QString text = m_data->engine->predictiveText( index );
|
|
||||||
|
|
||||||
m_data->engine->reset();
|
|
||||||
applyText( text, true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::updatePrediction()
|
|
||||||
{
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->panel ) )
|
|
||||||
panel->setPrediction( m_data->engine->prediction() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputManager::commitKey( int key )
|
|
||||||
{
|
|
||||||
auto panel = qobject_cast< QskInputPanel* >( m_data->panel );
|
|
||||||
if ( panel == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
int spaceLeft = -1;
|
|
||||||
|
|
||||||
if ( !( m_data->inputHints & Qt::ImhMultiLine ) )
|
|
||||||
{
|
|
||||||
QInputMethodQueryEvent event1( Qt::ImMaximumTextLength );
|
|
||||||
QCoreApplication::sendEvent( panel->attachedInputItem(), &event1 );
|
|
||||||
|
|
||||||
const int maxChars = event1.value( Qt::ImMaximumTextLength ).toInt();
|
|
||||||
if ( maxChars >= 0 )
|
|
||||||
{
|
|
||||||
QInputMethodQueryEvent event2( Qt::ImSurroundingText );
|
|
||||||
QCoreApplication::sendEvent( qskReceiverItem( this ), &event2 );
|
|
||||||
|
|
||||||
const auto text = event2.value( Qt::ImSurroundingText ).toString();
|
|
||||||
spaceLeft = maxChars - text.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = m_data->engine->processKey(
|
|
||||||
key, m_data->inputHints, spaceLeft );
|
|
||||||
|
|
||||||
if ( result.key )
|
|
||||||
{
|
|
||||||
switch( result.key )
|
|
||||||
{
|
|
||||||
case Qt::Key_Return:
|
|
||||||
{
|
|
||||||
applyInput( true );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Qt::Key_Escape:
|
|
||||||
{
|
|
||||||
applyInput( false );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
applyKey( result.key );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( !result.text.isEmpty() )
|
|
||||||
{
|
|
||||||
applyText( result.text, result.isFinal );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_QskInputManager.cpp"
|
|
|
@ -1,59 +0,0 @@
|
||||||
/******************************************************************************
|
|
||||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
||||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef QSK_INPUT_MANAGER_H
|
|
||||||
#define QSK_INPUT_MANAGER_H
|
|
||||||
|
|
||||||
#include "QskGlobal.h"
|
|
||||||
#include <QObject>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class QskInputEngine;
|
|
||||||
class QskControl;
|
|
||||||
class QQuickItem;
|
|
||||||
class QLocale;
|
|
||||||
|
|
||||||
class QSK_EXPORT QskInputManager : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
using Inherited = QObject;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QskInputManager( QObject* parent = nullptr );
|
|
||||||
virtual ~QskInputManager() override;
|
|
||||||
|
|
||||||
virtual void attachInputItem( QQuickItem* );
|
|
||||||
virtual void processInputMethodQueries( Qt::InputMethodQueries );
|
|
||||||
|
|
||||||
QskControl* panel( bool doCreate );
|
|
||||||
virtual Qt::Alignment panelAlignment() const;
|
|
||||||
|
|
||||||
virtual QQuickItem* inputProxy() const;
|
|
||||||
virtual QQuickItem* inputItem() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QskControl* createPanel();
|
|
||||||
virtual QskInputEngine* createEngine();
|
|
||||||
|
|
||||||
void updatePredictor();
|
|
||||||
void updateEngine( const QLocale& );
|
|
||||||
|
|
||||||
private:
|
|
||||||
void applyInput( bool success );
|
|
||||||
void applyText( const QString&, bool isFinal );
|
|
||||||
void applyKey( int keyCode );
|
|
||||||
|
|
||||||
void commitKey( int keyCode );
|
|
||||||
void commitPredictiveText( int index );
|
|
||||||
|
|
||||||
void updatePrediction();
|
|
||||||
void updatePanel();
|
|
||||||
|
|
||||||
class PrivateData;
|
|
||||||
std::unique_ptr< PrivateData > m_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -21,4 +21,17 @@ QskTextPredictor::Attributes QskTextPredictor::attributes() const
|
||||||
return m_attributes;
|
return m_attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList QskTextPredictor::candidates() const
|
||||||
|
{
|
||||||
|
const auto count = candidateCount();
|
||||||
|
|
||||||
|
QStringList candidates;
|
||||||
|
candidates.reserve( count );
|
||||||
|
|
||||||
|
for( int i = 0; i < count; i++ )
|
||||||
|
candidates += candidate( i );
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_QskTextPredictor.cpp"
|
#include "moc_QskTextPredictor.cpp"
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <QskGlobal.h>
|
#include <QskGlobal.h>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
class QStringList;
|
||||||
|
|
||||||
// abstract base class for input methods for retrieving predictive text
|
// abstract base class for input methods for retrieving predictive text
|
||||||
|
|
||||||
class QSK_EXPORT QskTextPredictor : public QObject
|
class QSK_EXPORT QskTextPredictor : public QObject
|
||||||
|
@ -32,6 +34,8 @@ public:
|
||||||
virtual int candidateCount() const = 0;
|
virtual int candidateCount() const = 0;
|
||||||
virtual QString candidate( int ) const = 0;
|
virtual QString candidate( int ) const = 0;
|
||||||
|
|
||||||
|
virtual QStringList candidates() const;
|
||||||
|
|
||||||
Attributes attributes() const;
|
Attributes attributes() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|
|
@ -300,7 +300,6 @@ SOURCES += \
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
inputpanel/QskTextPredictor.cpp \
|
inputpanel/QskTextPredictor.cpp \
|
||||||
inputpanel/QskInputContext.cpp \
|
inputpanel/QskInputContext.cpp \
|
||||||
inputpanel/QskInputManager.cpp \
|
|
||||||
inputpanel/QskInputEngine.cpp \
|
inputpanel/QskInputEngine.cpp \
|
||||||
inputpanel/QskInputPanel.cpp \
|
inputpanel/QskInputPanel.cpp \
|
||||||
inputpanel/QskInputPredictionBar.cpp \
|
inputpanel/QskInputPredictionBar.cpp \
|
||||||
|
@ -309,7 +308,6 @@ SOURCES += \
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
inputpanel/QskTextPredictor.h \
|
inputpanel/QskTextPredictor.h \
|
||||||
inputpanel/QskInputContext.h \
|
inputpanel/QskInputContext.h \
|
||||||
inputpanel/QskInputManager.h \
|
|
||||||
inputpanel/QskInputEngine.h \
|
inputpanel/QskInputEngine.h \
|
||||||
inputpanel/QskInputPanel.h \
|
inputpanel/QskInputPanel.h \
|
||||||
inputpanel/QskInputPredictionBar.h \
|
inputpanel/QskInputPredictionBar.h \
|
||||||
|
|
Loading…
Reference in New Issue