QskInputContext is no platform input context anymore to avoid forwarding

private APIs to applicaton code
This commit is contained in:
Uwe Rathmann 2018-05-09 15:45:43 +02:00
parent b29f688df2
commit e5d6fe0dc3
3 changed files with 364 additions and 159 deletions

View File

@ -4,12 +4,244 @@
*****************************************************************************/ *****************************************************************************/
#include <qpa/qplatforminputcontextplugin_p.h> #include <qpa/qplatforminputcontextplugin_p.h>
#include <qpa/qplatforminputcontext.h>
#include "QskInputContext.h" #include "QskInputContext.h"
#include "QskPinyinTextPredictor.h" #include "QskPinyinTextPredictor.h"
#include "QskHunspellTextPredictor.h" #include "QskHunspellTextPredictor.h"
#include <QLocale> #include <QLocale>
#include <QRectF>
#include <QEvent>
/*
QPlatformInputContext is no stable public API.
So we forward everything to QskInputContext
*/
class QskPlatformInputContext final : public QPlatformInputContext
{
using Inherited = QPlatformInputContext;
public:
QskPlatformInputContext();
virtual ~QskPlatformInputContext() = default;
virtual bool isValid() const override;
virtual bool hasCapability( Capability ) const override;
virtual void update( Qt::InputMethodQueries ) override;
virtual void invokeAction( QInputMethod::Action, int ) override;
virtual QRectF keyboardRect() const override;
virtual bool isAnimating() const override;
virtual void showInputPanel() override;
virtual void hideInputPanel() override;
virtual bool isInputPanelVisible() const override;
virtual void reset() override;
virtual void commit() override;
virtual void setFocusObject( QObject* ) override;
virtual QLocale locale() const override;
virtual Qt::LayoutDirection inputDirection() const override;
virtual bool filterEvent( const QEvent* ) override;
Q_INVOKABLE QQuickItem* inputItem();
protected:
virtual bool event( QEvent* ) override;
private:
void updateContext();
void updateLocale();
QLocale m_locale;
QPointer< QskInputContext > m_context;
};
QskPlatformInputContext::QskPlatformInputContext()
{
auto context = QskInputContext::instance();
if ( context == nullptr )
{
context = new QskInputContext();
QskInputContext::setInstance( context );
}
updateContext();
updateLocale();
}
void QskPlatformInputContext::updateContext()
{
if ( m_context )
m_context->disconnect( this );
m_context = QskInputContext::instance();
if ( m_context )
{
connect( m_context, &QskInputContext::activeChanged,
this, &QPlatformInputContext::emitInputPanelVisibleChanged );
connect( m_context, &QskInputContext::panelRectChanged,
this, &QPlatformInputContext::emitKeyboardRectChanged );
#if 1
m_context->registerPredictor( QLocale(),
new QskHunspellTextPredictor() );
#endif
#if 0
m_context->registerPredictor(
QLocale::Chinese, new QskPinyinTextPredictor() );
#endif
}
}
void QskPlatformInputContext::updateLocale()
{
if ( m_context )
{
const auto oldLocale = m_locale;
m_locale = m_context->locale();
if ( oldLocale != m_locale )
emitLocaleChanged();
if ( m_locale.textDirection() != oldLocale.textDirection() )
emitInputDirectionChanged( m_locale.textDirection() );
}
}
bool QskPlatformInputContext::isValid() const
{
return true;
}
bool QskPlatformInputContext::hasCapability( Capability ) const
{
// what is QPlatformInputContext::HiddenTextCapability ???
return true;
}
void QskPlatformInputContext::update( Qt::InputMethodQueries queries )
{
if ( m_context )
m_context->update( queries );
}
void QskPlatformInputContext::invokeAction(
QInputMethod::Action action, int cursorPosition )
{
if ( m_context )
{
if ( action == QInputMethod::Click )
m_context->processClickAt( cursorPosition );
}
}
QRectF QskPlatformInputContext::keyboardRect() const
{
if ( m_context )
return m_context->panelRect();
return QRectF();
}
bool QskPlatformInputContext::isAnimating() const
{
// who is interested in this ?
// also: emitAnimatingChanged
return false;
}
void QskPlatformInputContext::showInputPanel()
{
if ( m_context )
m_context->setActive( true );
}
void QskPlatformInputContext::hideInputPanel()
{
if ( m_context )
m_context->setActive( false );
}
bool QskPlatformInputContext::isInputPanelVisible() const
{
if ( m_context )
return m_context->isActive();
return false;
}
void QskPlatformInputContext::reset()
{
if ( m_context )
m_context->commitPrediction( false );
}
void QskPlatformInputContext::commit()
{
if ( m_context )
m_context->commitPrediction( true );
}
void QskPlatformInputContext::setFocusObject( QObject* object )
{
if ( m_context )
m_context->setFocusObject( object );
}
QLocale QskPlatformInputContext::locale() const
{
return m_locale;
}
Qt::LayoutDirection QskPlatformInputContext::inputDirection() const
{
return m_locale.textDirection();
}
bool QskPlatformInputContext::filterEvent( const QEvent* )
{
// called from QXcbKeyboard, but what about other platforms
return false;
}
QQuickItem* QskPlatformInputContext::inputItem()
{
if ( m_context )
return m_context->inputItem();
return nullptr;
}
bool QskPlatformInputContext::event( QEvent* event )
{
switch( event->type() )
{
case QEvent::LocaleChange:
{
updateLocale();
break;
}
case QEvent::PlatformPanel:
{
updateContext();
break;
}
default:
break;
}
return Inherited::event( event );
}
class QskInputContextPlugin final : public QPlatformInputContextPlugin class QskInputContextPlugin final : public QPlatformInputContextPlugin
{ {
@ -22,18 +254,7 @@ public:
{ {
if ( system.compare( QStringLiteral( "skinny" ), Qt::CaseInsensitive ) == 0 ) if ( system.compare( QStringLiteral( "skinny" ), Qt::CaseInsensitive ) == 0 )
{ {
auto context = new QskInputContext(); auto context = new QskPlatformInputContext();
#if 0
context->registerPredictor( QLocale(),
new QskHunspellTextPredictor( this ) );
#endif
#if 0
context->registerPredictor(
QLocale::Chinese, new QskPinyinTextPredictor( this ) );
#endif
return context; return context;
} }

View File

@ -24,47 +24,46 @@ QSK_QT_PRIVATE_BEGIN
QSK_QT_PRIVATE_END QSK_QT_PRIVATE_END
#include <qpa/qplatformintegration.h> #include <qpa/qplatformintegration.h>
#include <qpa/qplatforminputcontext.h>
static QPointer< QskInputPanel > qskInputPanel = nullptr; static QPointer< QskInputContext > qskInputContext = nullptr;
static void qskDeletePanel() static void qskSendToPlatformContext( QEvent::Type type )
{ {
delete qskInputPanel; const auto platformInputContext =
}
static void qskInputPanelHook()
{
qAddPostRoutine( qskDeletePanel );
}
Q_COREAPP_STARTUP_FUNCTION( qskInputPanelHook )
static void qskSetInputPanel( QskInputPanel* inputPanel )
{
if ( inputPanel == qskInputPanel )
return;
delete qskInputPanel;
qskInputPanel = inputPanel;
}
void QskInputContext::setInputPanel( QskInputPanel* inputPanel )
{
if ( inputPanel == qskInputPanel )
return;
qskSetInputPanel( inputPanel );
const auto inputContext =
QGuiApplicationPrivate::platformIntegration()->inputContext(); QGuiApplicationPrivate::platformIntegration()->inputContext();
if ( auto context = qobject_cast< QskInputContext* >( inputContext ) ) if ( platformInputContext )
context->hideInputPanel(); {
QEvent event( type );
QCoreApplication::sendEvent( platformInputContext, &event );
}
} }
QskInputPanel* QskInputContext::inputPanel() static void qskInputContextHook()
{ {
return qskInputPanel; qAddPostRoutine( []{ delete qskInputContext; } );
}
Q_COREAPP_STARTUP_FUNCTION( qskInputContextHook )
void QskInputContext::setInstance( QskInputContext* inputContext )
{
if ( inputContext != qskInputContext )
{
const auto oldContext = qskInputContext;
qskInputContext = inputContext;
if ( oldContext && oldContext->parent() == nullptr )
delete oldContext;
qskSendToPlatformContext( QEvent::PlatformPanel );
}
}
QskInputContext* QskInputContext::instance()
{
return qskInputContext;
} }
static inline uint qskHashLocale( const QLocale& locale ) static inline uint qskHashLocale( const QLocale& locale )
@ -124,8 +123,9 @@ class QskInputContext::PrivateData
public: public:
// item receiving the input // item receiving the input
QPointer< QQuickItem > inputItem; QPointer< QQuickItem > inputItem;
QPointer< QskInputPanel > inputPanel;
// popup or window embedding qskInputPanel // popup or window embedding the panel
QskPopup* inputPopup = nullptr; QskPopup* inputPopup = nullptr;
QskWindow* inputWindow = nullptr; QskWindow* inputWindow = nullptr;
@ -145,22 +145,32 @@ QskInputContext::~QskInputContext()
{ {
} }
bool QskInputContext::isValid() const QQuickItem* QskInputContext::inputItem() const
{
return true;
}
bool QskInputContext::hasCapability( Capability ) const
{
// what is QPlatformInputContext::HiddenTextCapability ???
return true;
}
QQuickItem* QskInputContext::inputItem()
{ {
return m_data->inputItem; return m_data->inputItem;
} }
QskInputPanel* QskInputContext::inputPanel() const
{
if ( m_data->inputPanel == nullptr )
{
auto that = const_cast< QskInputContext* >( this );
auto panel = new QskInputPanel();
panel->setParent( that );
connect( panel, &QQuickItem::visibleChanged,
this, &QskInputContext::activeChanged );
connect( panel, &QskControl::localeChanged,
this, []{ qskSendToPlatformContext( QEvent::LocaleChange ); } );
m_data->inputPanel = panel;
}
return m_data->inputPanel;
}
void QskInputContext::update( Qt::InputMethodQueries queries ) void QskInputContext::update( Qt::InputMethodQueries queries )
{ {
if ( queries & Qt::ImEnabled ) if ( queries & Qt::ImEnabled )
@ -170,28 +180,21 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
if ( !event.value( Qt::ImEnabled ).toBool() ) if ( !event.value( Qt::ImEnabled ).toBool() )
{ {
hideInputPanel(); hidePanel();
return; return;
} }
} }
if ( qskInputPanel ) if ( auto panel = inputPanel() )
qskInputPanel->processInputMethodQueries( queries ); panel->processInputMethodQueries( queries );
} }
QRectF QskInputContext::keyboardRect() const QRectF QskInputContext::panelRect() const
{ {
// is this correct and what is this good for ?
if ( m_data->inputPopup ) if ( m_data->inputPopup )
return m_data->inputPopup->geometry(); return m_data->inputPopup->geometry();
return Inherited::keyboardRect(); return QRectF();
}
bool QskInputContext::isAnimating() const
{
// can be implemented once we have some sliding/fading effects
return false;
} }
QskPopup* QskInputContext::createEmbeddingPopup( QskInputPanel* panel ) QskPopup* QskInputContext::createEmbeddingPopup( QskInputPanel* panel )
@ -238,15 +241,18 @@ QskWindow* QskInputContext::createEmbeddingWindow( QskInputPanel* panel )
return window; return window;
} }
void QskInputContext::showInputPanel() void QskInputContext::showPanel()
{ {
auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() ); auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() );
if ( focusItem == nullptr ) if ( focusItem == nullptr )
return; return;
if ( ( focusItem == qskInputPanel ) auto panel = inputPanel();
|| qskIsAncestorOf( qskInputPanel, focusItem ) ) if ( panel == nullptr )
return;
if ( ( focusItem == panel )
|| qskIsAncestorOf( panel, focusItem ) )
{ {
// ignore: usually the input proxy of the panel // ignore: usually the input proxy of the panel
return; return;
@ -254,26 +260,6 @@ void QskInputContext::showInputPanel()
m_data->inputItem = focusItem; m_data->inputItem = focusItem;
if ( qskInputPanel == nullptr )
qskSetInputPanel( new QskInputPanel() );
connect( qskInputPanel, &QQuickItem::visibleChanged,
this, &QPlatformInputContext::emitInputPanelVisibleChanged,
Qt::UniqueConnection );
connect( qskInputPanel, &QskControl::localeChanged,
this, &QPlatformInputContext::emitLocaleChanged,
Qt::UniqueConnection );
if ( qskInputPanel->parent() == nullptr )
{
/*
Take ownership to avoid, that the panel gets
destroyed together with the popup/window
*/
qskInputPanel->setParent( this );
}
if ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow ) if ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow )
{ {
// The input panel is embedded in a top level window // The input panel is embedded in a top level window
@ -282,7 +268,7 @@ void QskInputContext::showInputPanel()
if ( m_data->inputWindow == nullptr ) if ( m_data->inputWindow == nullptr )
{ {
auto window = createEmbeddingWindow( qskInputPanel ); auto window = createEmbeddingWindow( panel );
if ( window ) if ( window )
{ {
@ -311,7 +297,7 @@ void QskInputContext::showInputPanel()
if ( m_data->inputPopup == nullptr ) if ( m_data->inputPopup == nullptr )
{ {
auto popup = createEmbeddingPopup( qskInputPanel ); auto popup = createEmbeddingPopup( panel );
if ( popup ) if ( popup )
{ {
@ -330,12 +316,12 @@ void QskInputContext::showInputPanel()
m_data->engine->setPredictor( m_data->engine->setPredictor(
m_data->predictorTable.find( locale() ) ); m_data->predictorTable.find( locale() ) );
qskInputPanel->setLocale( locale() ); panel->setLocale( locale() );
qskInputPanel->attachInputItem( m_data->inputItem ); panel->attachInputItem( m_data->inputItem );
qskInputPanel->setEngine( m_data->engine ); panel->setEngine( m_data->engine );
} }
void QskInputContext::hideInputPanel() void QskInputContext::hidePanel()
{ {
if ( m_data->inputPopup ) if ( m_data->inputPopup )
{ {
@ -354,11 +340,11 @@ void QskInputContext::hideInputPanel()
#endif #endif
} }
if ( qskInputPanel ) if ( auto panel = inputPanel() )
{ {
qskInputPanel->setParentItem( nullptr ); panel->setParentItem( nullptr );
qskInputPanel->attachInputItem( nullptr ); panel->attachInputItem( nullptr );
qskInputPanel->setEngine( nullptr ); panel->setEngine( nullptr );
} }
if ( m_data->inputPopup ) if ( m_data->inputPopup )
@ -378,10 +364,23 @@ void QskInputContext::hideInputPanel()
m_data->inputItem = nullptr; m_data->inputItem = nullptr;
} }
bool QskInputContext::isInputPanelVisible() const void QskInputContext::setActive( bool on )
{ {
return qskInputPanel && qskInputPanel->isVisible() if ( on )
&& qskInputPanel->window() && qskInputPanel->window()->isVisible(); showPanel();
else
hidePanel();
}
bool QskInputContext::isActive() const
{
if ( auto panel = inputPanel() )
{
return panel && panel->isVisible()
&& panel->window() && panel->window()->isVisible();
}
return false;
} }
QLocale QskInputContext::locale() const QLocale QskInputContext::locale() const
@ -397,11 +396,6 @@ QLocale QskInputContext::locale() const
return QLocale(); return QLocale();
} }
Qt::LayoutDirection QskInputContext::inputDirection() const
{
return Inherited::inputDirection();
}
void QskInputContext::setFocusObject( QObject* focusObject ) void QskInputContext::setFocusObject( QObject* focusObject )
{ {
if ( m_data->inputItem == nullptr || m_data->inputItem == focusObject ) if ( m_data->inputItem == nullptr || m_data->inputItem == focusObject )
@ -421,7 +415,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
if ( m_data->inputItem->hasFocus() ) if ( m_data->inputItem->hasFocus() )
{ {
/* /*
As long as the focus is noewhere and As long as the focus is nowhere and
the local focus stay on the input item the local focus stay on the input item
we don't care we don't care
*/ */
@ -448,7 +442,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
} }
} }
hideInputPanel(); hidePanel();
m_data->inputItem = nullptr; m_data->inputItem = nullptr;
} }
@ -476,20 +470,17 @@ QskTextPredictor* QskInputContext::registeredPredictor( const QLocale& locale )
return m_data->predictorTable.find( locale ); return m_data->predictorTable.find( locale );
} }
void QskInputContext::invokeAction( QInputMethod::Action, int ) void QskInputContext::processClickAt( int cursorPosition )
{ {
Q_UNUSED( cursorPosition );
} }
void QskInputContext::reset() void QskInputContext::commitPrediction( bool )
{
}
void QskInputContext::commit()
{ {
/* /*
commit is called, when the input item loses the focus. called, when the input item loses the focus.
As it it should be possible to navigate inside of the As it it should be possible to navigate inside of the
inputPanel this is no valid reason to hide the panel. inputPanel what should we do here ?
*/ */
} }
@ -501,15 +492,13 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{ {
case QEvent::Move: case QEvent::Move:
{ {
if ( qskInputPanel ) Q_EMIT panelRectChanged();
emitKeyboardRectChanged();
break; break;
} }
case QEvent::Resize: case QEvent::Resize:
{ {
if ( qskInputPanel ) if ( auto panel = inputPanel() )
qskInputPanel->setSize( m_data->inputWindow->size() ); panel->setSize( m_data->inputWindow->size() );
break; break;
} }
@ -528,7 +517,7 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{ {
case QskEvent::GeometryChange: case QskEvent::GeometryChange:
{ {
emitKeyboardRectChanged(); Q_EMIT panelRectChanged();
break; break;
} }
case QEvent::DeferredDelete: case QEvent::DeferredDelete:
@ -542,10 +531,4 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
return Inherited::eventFilter( object, event ); return Inherited::eventFilter( object, event );
} }
bool QskInputContext::filterEvent( const QEvent* )
{
// called from QXcbKeyboard, but what about other platforms
return false;
}
#include "moc_QskInputContext.cpp" #include "moc_QskInputContext.cpp"

View File

@ -7,7 +7,8 @@
#define QSK_INPUT_CONTEXT_H #define QSK_INPUT_CONTEXT_H
#include "QskGlobal.h" #include "QskGlobal.h"
#include <qpa/qplatforminputcontext.h> #include <QObject>
#include <Qt>
#include <memory> #include <memory>
class QskTextPredictor; class QskTextPredictor;
@ -16,46 +17,35 @@ class QskPopup;
class QskWindow; class QskWindow;
class QQuickItem; class QQuickItem;
class QSK_EXPORT QskInputContext : public QPlatformInputContext class QSK_EXPORT QskInputContext : public QObject
{ {
Q_OBJECT Q_OBJECT
using Inherited = QPlatformInputContext; using Inherited = QObject;
public: public:
QskInputContext(); QskInputContext();
virtual ~QskInputContext(); virtual ~QskInputContext();
virtual bool isValid() const override; virtual QRectF panelRect() const;
virtual bool hasCapability( Capability ) const override;
virtual void update( Qt::InputMethodQueries ) override; virtual void setActive( bool );
virtual void invokeAction( QInputMethod::Action, int ) override; virtual bool isActive() const;
virtual QRectF keyboardRect() const override; virtual QLocale locale() const;
virtual bool isAnimating() const override;
virtual void showInputPanel() override;
virtual void hideInputPanel() override;
virtual bool isInputPanelVisible() const override;
virtual void reset() override;
virtual void commit() override;
virtual void setFocusObject( QObject* ) override;
virtual QLocale locale() const override;
virtual Qt::LayoutDirection inputDirection() const override;
void registerPredictor( const QLocale&, QskTextPredictor* ); void registerPredictor( const QLocale&, QskTextPredictor* );
QskTextPredictor* registeredPredictor( const QLocale& ); QskTextPredictor* registeredPredictor( const QLocale& );
Q_INVOKABLE QQuickItem* inputItem(); virtual QQuickItem* inputItem() const;
virtual QskInputPanel* inputPanel() const;
virtual bool filterEvent( const QEvent* ) override; static QskInputContext* instance();
static void setInstance( QskInputContext* );
static void setInputPanel( QskInputPanel* ); Q_SIGNALS:
static QskInputPanel* inputPanel(); void activeChanged();
void panelRectChanged();
protected: protected:
virtual bool eventFilter( QObject*, QEvent* ) override; virtual bool eventFilter( QObject*, QEvent* ) override;
@ -63,7 +53,18 @@ protected:
virtual QskPopup* createEmbeddingPopup( QskInputPanel* ); virtual QskPopup* createEmbeddingPopup( QskInputPanel* );
virtual QskWindow* createEmbeddingWindow( QskInputPanel* ); virtual QskWindow* createEmbeddingWindow( QskInputPanel* );
virtual void showPanel();
virtual void hidePanel();
private: private:
friend class QskPlatformInputContext;
// called from QskPlatformInputContext
virtual void setFocusObject( QObject* );
virtual void update( Qt::InputMethodQueries );
virtual void processClickAt( int cursorPosition );
virtual void commitPrediction( bool );
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;
}; };