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/qplatforminputcontext.h>
#include "QskInputContext.h"
#include "QskPinyinTextPredictor.h"
#include "QskHunspellTextPredictor.h"
#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
{
@ -22,18 +254,7 @@ public:
{
if ( system.compare( QStringLiteral( "skinny" ), Qt::CaseInsensitive ) == 0 )
{
auto context = new QskInputContext();
#if 0
context->registerPredictor( QLocale(),
new QskHunspellTextPredictor( this ) );
#endif
#if 0
context->registerPredictor(
QLocale::Chinese, new QskPinyinTextPredictor( this ) );
#endif
auto context = new QskPlatformInputContext();
return context;
}

View File

@ -24,47 +24,46 @@ QSK_QT_PRIVATE_BEGIN
QSK_QT_PRIVATE_END
#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;
}
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 =
const auto platformInputContext =
QGuiApplicationPrivate::platformIntegration()->inputContext();
if ( auto context = qobject_cast< QskInputContext* >( inputContext ) )
context->hideInputPanel();
if ( platformInputContext )
{
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 )
@ -124,8 +123,9 @@ class QskInputContext::PrivateData
public:
// item receiving the input
QPointer< QQuickItem > inputItem;
QPointer< QskInputPanel > inputPanel;
// popup or window embedding qskInputPanel
// popup or window embedding the panel
QskPopup* inputPopup = nullptr;
QskWindow* inputWindow = nullptr;
@ -145,22 +145,32 @@ QskInputContext::~QskInputContext()
{
}
bool QskInputContext::isValid() const
{
return true;
}
bool QskInputContext::hasCapability( Capability ) const
{
// what is QPlatformInputContext::HiddenTextCapability ???
return true;
}
QQuickItem* QskInputContext::inputItem()
QQuickItem* QskInputContext::inputItem() const
{
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 )
{
if ( queries & Qt::ImEnabled )
@ -170,28 +180,21 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
if ( !event.value( Qt::ImEnabled ).toBool() )
{
hideInputPanel();
hidePanel();
return;
}
}
if ( qskInputPanel )
qskInputPanel->processInputMethodQueries( queries );
if ( auto panel = inputPanel() )
panel->processInputMethodQueries( queries );
}
QRectF QskInputContext::keyboardRect() const
QRectF QskInputContext::panelRect() const
{
// is this correct and what is this good for ?
if ( m_data->inputPopup )
return m_data->inputPopup->geometry();
return Inherited::keyboardRect();
}
bool QskInputContext::isAnimating() const
{
// can be implemented once we have some sliding/fading effects
return false;
return QRectF();
}
QskPopup* QskInputContext::createEmbeddingPopup( QskInputPanel* panel )
@ -238,15 +241,18 @@ QskWindow* QskInputContext::createEmbeddingWindow( QskInputPanel* panel )
return window;
}
void QskInputContext::showInputPanel()
void QskInputContext::showPanel()
{
auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() );
if ( focusItem == nullptr )
return;
if ( ( focusItem == qskInputPanel )
|| qskIsAncestorOf( qskInputPanel, focusItem ) )
auto panel = inputPanel();
if ( panel == nullptr )
return;
if ( ( focusItem == panel )
|| qskIsAncestorOf( panel, focusItem ) )
{
// ignore: usually the input proxy of the panel
return;
@ -254,26 +260,6 @@ void QskInputContext::showInputPanel()
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 )
{
// The input panel is embedded in a top level window
@ -282,7 +268,7 @@ void QskInputContext::showInputPanel()
if ( m_data->inputWindow == nullptr )
{
auto window = createEmbeddingWindow( qskInputPanel );
auto window = createEmbeddingWindow( panel );
if ( window )
{
@ -311,7 +297,7 @@ void QskInputContext::showInputPanel()
if ( m_data->inputPopup == nullptr )
{
auto popup = createEmbeddingPopup( qskInputPanel );
auto popup = createEmbeddingPopup( panel );
if ( popup )
{
@ -330,12 +316,12 @@ void QskInputContext::showInputPanel()
m_data->engine->setPredictor(
m_data->predictorTable.find( locale() ) );
qskInputPanel->setLocale( locale() );
qskInputPanel->attachInputItem( m_data->inputItem );
qskInputPanel->setEngine( m_data->engine );
panel->setLocale( locale() );
panel->attachInputItem( m_data->inputItem );
panel->setEngine( m_data->engine );
}
void QskInputContext::hideInputPanel()
void QskInputContext::hidePanel()
{
if ( m_data->inputPopup )
{
@ -354,11 +340,11 @@ void QskInputContext::hideInputPanel()
#endif
}
if ( qskInputPanel )
if ( auto panel = inputPanel() )
{
qskInputPanel->setParentItem( nullptr );
qskInputPanel->attachInputItem( nullptr );
qskInputPanel->setEngine( nullptr );
panel->setParentItem( nullptr );
panel->attachInputItem( nullptr );
panel->setEngine( nullptr );
}
if ( m_data->inputPopup )
@ -378,10 +364,23 @@ void QskInputContext::hideInputPanel()
m_data->inputItem = nullptr;
}
bool QskInputContext::isInputPanelVisible() const
void QskInputContext::setActive( bool on )
{
return qskInputPanel && qskInputPanel->isVisible()
&& qskInputPanel->window() && qskInputPanel->window()->isVisible();
if ( on )
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
@ -397,11 +396,6 @@ QLocale QskInputContext::locale() const
return QLocale();
}
Qt::LayoutDirection QskInputContext::inputDirection() const
{
return Inherited::inputDirection();
}
void QskInputContext::setFocusObject( QObject* focusObject )
{
if ( m_data->inputItem == nullptr || m_data->inputItem == focusObject )
@ -421,7 +415,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
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
we don't care
*/
@ -448,7 +442,7 @@ void QskInputContext::setFocusObject( QObject* focusObject )
}
}
hideInputPanel();
hidePanel();
m_data->inputItem = nullptr;
}
@ -476,20 +470,17 @@ QskTextPredictor* QskInputContext::registeredPredictor( const QLocale& 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::commit()
void QskInputContext::commitPrediction( bool )
{
/*
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
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:
{
if ( qskInputPanel )
emitKeyboardRectChanged();
Q_EMIT panelRectChanged();
break;
}
case QEvent::Resize:
{
if ( qskInputPanel )
qskInputPanel->setSize( m_data->inputWindow->size() );
if ( auto panel = inputPanel() )
panel->setSize( m_data->inputWindow->size() );
break;
}
@ -528,7 +517,7 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{
case QskEvent::GeometryChange:
{
emitKeyboardRectChanged();
Q_EMIT panelRectChanged();
break;
}
case QEvent::DeferredDelete:
@ -542,10 +531,4 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* 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"

View File

@ -7,7 +7,8 @@
#define QSK_INPUT_CONTEXT_H
#include "QskGlobal.h"
#include <qpa/qplatforminputcontext.h>
#include <QObject>
#include <Qt>
#include <memory>
class QskTextPredictor;
@ -16,46 +17,35 @@ class QskPopup;
class QskWindow;
class QQuickItem;
class QSK_EXPORT QskInputContext : public QPlatformInputContext
class QSK_EXPORT QskInputContext : public QObject
{
Q_OBJECT
using Inherited = QPlatformInputContext;
using Inherited = QObject;
public:
QskInputContext();
virtual ~QskInputContext();
virtual bool isValid() const override;
virtual bool hasCapability( Capability ) const override;
virtual QRectF panelRect() const;
virtual void update( Qt::InputMethodQueries ) override;
virtual void invokeAction( QInputMethod::Action, int ) override;
virtual void setActive( bool );
virtual bool isActive() const;
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 QLocale locale() const;
void registerPredictor( const QLocale&, QskTextPredictor* );
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* );
static QskInputPanel* inputPanel();
Q_SIGNALS:
void activeChanged();
void panelRectChanged();
protected:
virtual bool eventFilter( QObject*, QEvent* ) override;
@ -63,7 +53,18 @@ protected:
virtual QskPopup* createEmbeddingPopup( QskInputPanel* );
virtual QskWindow* createEmbeddingWindow( QskInputPanel* );
virtual void showPanel();
virtual void hidePanel();
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;
std::unique_ptr< PrivateData > m_data;
};