improving text input classes

This commit is contained in:
Uwe Rathmann 2018-04-03 20:15:20 +02:00
parent 26417ed49a
commit 777a134645
7 changed files with 243 additions and 81 deletions

View File

@ -29,6 +29,8 @@ public:
QskInputContext::QskInputContext() : QskInputContext::QskInputContext() :
m_data( new PrivateData() ) m_data( new PrivateData() )
{ {
setObjectName( "InputContext" );
m_data->compositionModel = new QskInputCompositionModel(); m_data->compositionModel = new QskInputCompositionModel();
connect( m_data->compositionModel, &QskInputCompositionModel::candidatesChanged, connect( m_data->compositionModel, &QskInputCompositionModel::candidatesChanged,
@ -50,6 +52,7 @@ QskInputContext::~QskInputContext()
delete m_data->inputPanel; delete m_data->inputPanel;
qDeleteAll( m_data->compositionModels ); qDeleteAll( m_data->compositionModels );
delete m_data->compositionModel;
} }
bool QskInputContext::isValid() const bool QskInputContext::isValid() const
@ -137,6 +140,11 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
// Qt::ImPlatformData // hard to say... // Qt::ImPlatformData // hard to say...
} }
QQuickItem* QskInputContext::inputItem()
{
return m_data->inputItem;
}
QRectF QskInputContext::keyboardRect() const QRectF QskInputContext::keyboardRect() const
{ {
if ( m_data->inputPanel if ( m_data->inputPanel
@ -188,7 +196,7 @@ void QskInputContext::showInputPanel()
void QskInputContext::hideInputPanel() void QskInputContext::hideInputPanel()
{ {
if ( !m_data->inputPanel ) if ( m_data->inputPanel == nullptr )
return; return;
auto window = m_data->inputPanel->window(); auto window = m_data->inputPanel->window();
@ -201,6 +209,7 @@ void QskInputContext::hideInputPanel()
bool QskInputContext::isInputPanelVisible() const bool QskInputContext::isInputPanelVisible() const
{ {
auto panel = m_data->inputPanel; auto panel = m_data->inputPanel;
return panel && panel->isVisible() return panel && panel->isVisible()
&& panel->window() && panel->window()->isVisible(); && panel->window() && panel->window()->isVisible();
} }

View File

@ -11,6 +11,7 @@
class QskVirtualKeyboard; class QskVirtualKeyboard;
class QskInputCompositionModel; class QskInputCompositionModel;
class QQuickItem;
class QskInputContext : public QPlatformInputContext class QskInputContext : public QPlatformInputContext
{ {
@ -40,6 +41,8 @@ public:
void setCompositionModel( const QLocale&, QskInputCompositionModel* ); void setCompositionModel( const QLocale&, QskInputCompositionModel* );
Q_INVOKABLE QQuickItem* inputItem();
private Q_SLOTS: private Q_SLOTS:
void handleCandidatesChanged(); void handleCandidatesChanged();
void setInputPanel( QskVirtualKeyboard* ); void setInputPanel( QskVirtualKeyboard* );

View File

@ -427,7 +427,7 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const
//auto policy = QPlatformDialogHelper::UnknownLayout; //auto policy = QPlatformDialogHelper::UnknownLayout;
auto policy = QPlatformDialogHelper::WinLayout; auto policy = QPlatformDialogHelper::WinLayout;
if ( const QPlatformTheme* theme = QGuiApplicationPrivate::platformTheme() ) if ( const auto theme = QGuiApplicationPrivate::platformTheme() )
{ {
const QVariant v = theme->themeHint( QPlatformTheme::DialogButtonBoxLayout ); const QVariant v = theme->themeHint( QPlatformTheme::DialogButtonBoxLayout );
policy = static_cast< QPlatformDialogHelper::ButtonLayout >( v.toInt() ); policy = static_cast< QPlatformDialogHelper::ButtonLayout >( v.toInt() );

View File

@ -8,9 +8,64 @@
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
#include <private/qquicktextinput_p.h> #include <private/qquicktextinput_p.h>
#include <private/qquicktextinput_p_p.h>
QSK_QT_PRIVATE_END QSK_QT_PRIVATE_END
#include <QDebug> static inline void qskUpdateInputMethod(
const QskTextInput*, Qt::InputMethodQueries queries )
{
auto inputMethod = QGuiApplication::inputMethod();
inputMethod->update( queries );
}
static inline void qskBindSignals( const QQuickTextInput* wrappedInput,
QskTextInput* input )
{
QObject::connect( wrappedInput, &QQuickTextInput::textChanged,
input, [ input ] { input->Q_EMIT textChanged( input->text() ); } );
QObject::connect( wrappedInput, &QQuickTextInput::textEdited,
input, [ input ] { input->Q_EMIT textEdited( input->text() ); } );
QObject::connect( wrappedInput, &QQuickTextInput::textChanged,
input, [ input ] { input->Q_EMIT textChanged( input->text() ); } );
QObject::connect( wrappedInput, &QQuickTextInput::selectedTextChanged,
input, [ input ] { input->Q_EMIT selectedTextChanged( input->selectedText() ); } );
QObject::connect( wrappedInput, &QQuickTextInput::validatorChanged,
input, &QskTextInput::validatorChanged );
QObject::connect( wrappedInput, &QQuickTextInput::inputMaskChanged,
input, &QskTextInput::inputMaskChanged );
QObject::connect( wrappedInput, &QQuickTextInput::readOnlyChanged,
input, &QskTextInput::readOnlyChanged );
QObject::connect( wrappedInput, &QQuickTextInput::overwriteModeChanged,
input, &QskTextInput::overwriteModeChanged );
QObject::connect( wrappedInput, &QQuickTextInput::maximumLengthChanged,
input, &QskTextInput::maximumLengthChanged );
QObject::connect( wrappedInput, &QQuickTextInput::echoModeChanged,
input, [ input ] { input->Q_EMIT echoModeChanged( input->echoMode() ); } );
QObject::connect( wrappedInput, &QQuickTextInput::autoScrollChanged,
input, &QskTextInput::autoScrollChanged );
QObject::connect( wrappedInput, &QQuickTextInput::selectByMouseChanged,
input, &QskTextInput::selectByMouseChanged );
QObject::connect( wrappedInput, &QQuickTextInput::persistentSelectionChanged,
input, &QskTextInput::persistentSelectionChanged );
QObject::connect( wrappedInput, &QQuickItem::implicitWidthChanged,
input, &QskControl::resetImplicitSize );
QObject::connect( wrappedInput, &QQuickItem::implicitHeightChanged,
input, &QskControl::resetImplicitSize );
}
namespace namespace
{ {
@ -20,6 +75,8 @@ namespace
TextInput( QQuickItem* parent ) : TextInput( QQuickItem* parent ) :
QQuickTextInput( parent ) QQuickTextInput( parent )
{ {
setActiveFocusOnTab( false );
setFlag( ItemAcceptsInputMethod, false );
} }
void setAlignment( Qt::Alignment alignment ) void setAlignment( Qt::Alignment alignment )
@ -28,10 +85,26 @@ namespace
setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) ); setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) );
} }
protected: inline bool handleEvent( QEvent* event )
virtual void inputMethodEvent( QInputMethodEvent* event ) override
{ {
QQuickTextInput::inputMethodEvent( event ); switch( event->type() )
{
case QEvent::FocusIn:
case QEvent::FocusOut:
{
auto d = QQuickTextInputPrivate::get( this );
d->focusOnPress = true;
d->handleFocusEvent( static_cast< QFocusEvent* >( event ) );
d->focusOnPress = false;
return true;
}
default:
return QQuickTextInput::event( event );
}
} }
}; };
} }
@ -46,81 +119,70 @@ public:
}; };
QskTextInput::QskTextInput( QQuickItem* parent ): QskTextInput::QskTextInput( QQuickItem* parent ):
QskTextInput( QString(), parent )
{
}
QskTextInput::QskTextInput( const QString& text, QQuickItem* parent ):
Inherited( parent ), Inherited( parent ),
m_data( new PrivateData() ) m_data( new PrivateData() )
{ {
auto input = new TextInput( this );
connect( input, &QQuickTextInput::textChanged,
this, [ this ] { Q_EMIT textChanged( this->text() ); } );
connect( input, &QQuickTextInput::textEdited,
this, [ this ] { Q_EMIT textEdited( this->text() ); } );
connect( input, &QQuickTextInput::textChanged,
this, [ this ] { Q_EMIT textChanged( this->text() ); } );
connect( input, &QQuickTextInput::selectedTextChanged,
this, [ this ] { Q_EMIT selectedTextChanged( selectedText() ); } );
connect( input, &QQuickTextInput::validatorChanged,
this, &QskTextInput::validatorChanged );
connect( input, &QQuickTextInput::inputMaskChanged,
this, &QskTextInput::inputMaskChanged );
connect( input, &QQuickTextInput::readOnlyChanged,
this, &QskTextInput::readOnlyChanged );
connect( input, &QQuickTextInput::overwriteModeChanged,
this, &QskTextInput::overwriteModeChanged );
connect( input, &QQuickTextInput::maximumLengthChanged,
this, &QskTextInput::maximumLengthChanged );
connect( input, &QQuickTextInput::echoModeChanged,
this, [ this ] { Q_EMIT echoModeChanged( echoMode() ); } );
connect( input, &QQuickTextInput::autoScrollChanged,
this, &QskTextInput::autoScrollChanged );
connect( input, &QQuickTextInput::selectByMouseChanged,
this, &QskTextInput::selectByMouseChanged );
connect( input, &QQuickTextInput::persistentSelectionChanged,
this, &QskTextInput::persistentSelectionChanged );
connect( input, &QQuickItem::implicitWidthChanged,
this, &QskControl::resetImplicitSize );
connect( input, &QQuickItem::implicitHeightChanged,
this, &QskControl::resetImplicitSize );
input->setAlignment( alignment() );
input->setFont( font() );
input->setText( text );
m_data->textInput = input;
setPolishOnResize( true ); setPolishOnResize( true );
setFocusPolicy( Qt::StrongFocus ); setFocusPolicy( Qt::StrongFocus );
#if 1
input->setActiveFocusOnTab( true ); setFlag( QQuickItem::ItemAcceptsInputMethod );
#endif
setAcceptedMouseButtons( Qt::LeftButton ); m_data->textInput = new TextInput( this );
qskBindSignals( m_data->textInput, this );
setAcceptedMouseButtons( m_data->textInput->acceptedMouseButtons() );
m_data->textInput->setAcceptedMouseButtons( Qt::NoButton );
initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed ); initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
} }
QskTextInput::QskTextInput( const QString& text, QQuickItem* parent ):
QskTextInput( parent )
{
m_data->textInput->setText( text );
}
QskTextInput::~QskTextInput() QskTextInput::~QskTextInput()
{ {
} }
bool QskTextInput::event( QEvent* event )
{
if ( event->type() == QEvent::ShortcutOverride )
{
return m_data->textInput->handleEvent( event );
}
return Inherited::event( event );
}
void QskTextInput::keyPressEvent( QKeyEvent* event )
{
m_data->textInput->handleEvent( event );
}
void QskTextInput::keyReleaseEvent( QKeyEvent* event )
{
Inherited::keyReleaseEvent( event );
}
void QskTextInput::inputMethodEvent( QInputMethodEvent* event )
{
m_data->textInput->handleEvent( event );
}
void QskTextInput::focusInEvent( QFocusEvent* event )
{
m_data->textInput->handleEvent( event );
Inherited::focusInEvent( event );
}
void QskTextInput::focusOutEvent( QFocusEvent* event )
{
m_data->textInput->handleEvent( event );
Inherited::focusOutEvent( event );
}
QSizeF QskTextInput::contentsSizeHint() const QSizeF QskTextInput::contentsSizeHint() const
{ {
using namespace QskAspect; using namespace QskAspect;
@ -136,7 +198,12 @@ QSizeF QskTextInput::contentsSizeHint() const
void QskTextInput::updateLayout() void QskTextInput::updateLayout()
{ {
qskSetItemGeometry( m_data->textInput, subControlRect( Text ) ); auto input = m_data->textInput;
input->setAlignment( alignment() );
input->setFont( font() );
qskSetItemGeometry( input, subControlRect( Text ) );
} }
QString QskTextInput::text() const QString QskTextInput::text() const
@ -162,9 +229,12 @@ void QskTextInput::setFontRole( int role )
if ( oldRole != role ) if ( oldRole != role )
{ {
#if 1 polish();
m_data->textInput->setFont( font() ); resetImplicitSize();
#endif
qskUpdateInputMethod( this,
Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle );
Q_EMIT fontRoleChanged(); Q_EMIT fontRoleChanged();
} }
} }
@ -188,6 +258,8 @@ void QskTextInput::setAlignment( Qt::Alignment alignment )
m_data->textInput->setAlignment( alignment ); m_data->textInput->setAlignment( alignment );
polish();
Q_EMIT alignmentChanged(); Q_EMIT alignmentChanged();
} }
} }
@ -211,6 +283,9 @@ bool QskTextInput::isReadOnly() const
void QskTextInput::setReadOnly( bool on ) void QskTextInput::setReadOnly( bool on )
{ {
m_data->textInput->setReadOnly( on ); m_data->textInput->setReadOnly( on );
m_data->textInput->setFlag( QQuickItem::ItemAcceptsInputMethod, false );
qskUpdateInputMethod( this, Qt::ImEnabled );
} }
bool QskTextInput::isCursorVisible() const bool QskTextInput::isCursorVisible() const
@ -288,6 +363,8 @@ void QskTextInput::setEchoMode( EchoMode mode )
{ {
m_data->textInput->setEchoMode( m_data->textInput->setEchoMode(
static_cast< QQuickTextInput::EchoMode >( mode ) ); static_cast< QQuickTextInput::EchoMode >( mode ) );
qskUpdateInputMethod( this, Qt::ImHints );
} }
QString QskTextInput::displayText() const QString QskTextInput::displayText() const
@ -360,14 +437,37 @@ bool QskTextInput::hasAcceptableInput() const
QVariant QskTextInput::inputMethodQuery( QVariant QskTextInput::inputMethodQuery(
Qt::InputMethodQuery property) const Qt::InputMethodQuery property) const
{ {
return m_data->textInput->inputMethodQuery( property ); return inputMethodQuery( property, QVariant() );
} }
QVariant QskTextInput::inputMethodQuery( QVariant QskTextInput::inputMethodQuery(
Qt::InputMethodQuery query, QVariant argument) const Qt::InputMethodQuery query, QVariant argument) const
{
switch( query )
{
case Qt::ImEnabled:
{
return QVariant( (bool)( flags() & ItemAcceptsInputMethod ) );
}
case Qt::ImFont:
{
return font();
}
case Qt::ImCursorPosition:
{
QVariant v = m_data->textInput->inputMethodQuery( query, argument );
#if 1
if ( v.canConvert< QPointF >() )
v.setValue( v.toPointF() + m_data->textInput->position() );
#endif
return v;
}
default:
{ {
return m_data->textInput->inputMethodQuery( query, argument ); return m_data->textInput->inputMethodQuery( query, argument );
} }
}
}
bool QskTextInput::canUndo() const bool QskTextInput::canUndo() const
{ {
@ -390,8 +490,12 @@ Qt::InputMethodHints QskTextInput::inputMethodHints() const
} }
void QskTextInput::setInputMethodHints(Qt::InputMethodHints hints ) void QskTextInput::setInputMethodHints(Qt::InputMethodHints hints )
{
if ( m_data->textInput->inputMethodHints() != hints )
{ {
m_data->textInput->setInputMethodHints( hints ); m_data->textInput->setInputMethodHints( hints );
qskUpdateInputMethod( this, Qt::ImHints );
}
} }
#include "moc_QskTextInput.cpp" #include "moc_QskTextInput.cpp"

View File

@ -153,6 +153,13 @@ Q_SIGNALS:
void inputMaskChanged( const QString& ); void inputMaskChanged( const QString& );
protected: protected:
virtual bool event( QEvent* ) override;
virtual void keyPressEvent( QKeyEvent* ) override;
virtual void keyReleaseEvent( QKeyEvent* ) override;
virtual void inputMethodEvent( QInputMethodEvent* ) override;
virtual void focusInEvent( QFocusEvent* ) override;
virtual void focusOutEvent( QFocusEvent* ) override;
virtual void updateLayout() override; virtual void updateLayout() override;
private: private:

View File

@ -10,6 +10,13 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QStyleHints> #include <QStyleHints>
QSK_QT_PRIVATE_BEGIN
#include <private/qguiapplication_p.h>
QSK_QT_PRIVATE_END
#include <qpa/qplatformintegration.h>
#include <qpa/qplatforminputcontext.h>
namespace namespace
{ {
struct KeyTable struct KeyTable
@ -137,10 +144,8 @@ QskVirtualKeyboardCandidateButton::QskVirtualKeyboardCandidateButton(
setFlag( QQuickItem::ItemAcceptsInputMethod ); setFlag( QQuickItem::ItemAcceptsInputMethod );
setText( QStringLiteral( " " ) ); // ### setText( QStringLiteral( " " ) ); // ###
connect( this, &QskVirtualKeyboardButton::pressed, this, [ this ]() connect( this, &QskVirtualKeyboardButton::pressed,
{ this, [ this ]() { m_inputPanel->handleCandidateKey( m_index, m_text ); } );
m_inputPanel->handleCandidateKey( m_index, m_text );
} );
} }
void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString& text ) void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString& text )
@ -150,7 +155,8 @@ void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString
setText( m_text ); setText( m_text );
} }
QskAspect::Subcontrol QskVirtualKeyboardCandidateButton::effectiveSubcontrol( QskAspect::Subcontrol subControl ) const QskAspect::Subcontrol QskVirtualKeyboardCandidateButton::effectiveSubcontrol(
QskAspect::Subcontrol subControl ) const
{ {
if( subControl == QskPushButton::Panel ) if( subControl == QskPushButton::Panel )
{ {
@ -317,6 +323,7 @@ QskVirtualKeyboard::QskVirtualKeyboard( QQuickItem* parent ):
const int keyIndex = m_data->keyTable[ m_data->mode ].indexOf( &keyData ); const int keyIndex = m_data->keyTable[ m_data->mode ].indexOf( &keyData );
auto button = new QskVirtualKeyboardButton( keyIndex, this, rowBox ); auto button = new QskVirtualKeyboardButton( keyIndex, this, rowBox );
button->installEventFilter( this );
rowBox->setRetainSizeWhenHidden( button, true ); rowBox->setRetainSizeWhenHidden( button, true );
m_data->keyButtons.append( button ); m_data->keyButtons.append( button );
@ -912,4 +919,35 @@ void QskVirtualKeyboard::setMode( QskVirtualKeyboard::Mode mode )
Q_EMIT modeChanged( m_data->mode ); Q_EMIT modeChanged( m_data->mode );
} }
bool QskVirtualKeyboard::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() == QEvent::InputMethodQuery )
{
/*
Qt/Quick expects that the item associated with the virtual keyboard
always has the focus. But you also find systems, where you have to
navigate and select inside the virtual keyboard.
So we have to fix the receiver.
*/
const auto platformIntegration = QGuiApplicationPrivate::platformIntegration();
if ( const auto inputContext = platformIntegration->inputContext() )
{
QQuickItem* item = nullptr;
if ( QMetaObject::invokeMethod( inputContext, "inputItem",
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, item ) ) )
{
if ( item )
QGuiApplication::sendEvent( item, event );
}
}
return true;
}
return Inherited::eventFilter( object, event );
}
#include "moc_QskVirtualKeyboard.cpp" #include "moc_QskVirtualKeyboard.cpp"

View File

@ -137,6 +137,7 @@ public Q_SLOTS:
void setPreeditCandidates( const QVector< QString >& ); void setPreeditCandidates( const QVector< QString >& );
protected: protected:
virtual bool eventFilter( QObject*, QEvent* ) override;
virtual void geometryChanged( const QRectF&, const QRectF& ) override; virtual void geometryChanged( const QRectF&, const QRectF& ) override;
virtual void updateLayout() override; virtual void updateLayout() override;