From 777a1346452b97d0d6cda5e22db3fc80932dce56 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 3 Apr 2018 20:15:20 +0200 Subject: [PATCH] improving text input classes --- inputcontext/QskInputContext.cpp | 13 +- inputcontext/QskInputContext.h | 3 + src/controls/QskSkin.cpp | 2 +- src/controls/QskTextInput.cpp | 250 ++++++++++++++++++++-------- src/controls/QskTextInput.h | 7 + src/controls/QskVirtualKeyboard.cpp | 48 +++++- src/controls/QskVirtualKeyboard.h | 1 + 7 files changed, 243 insertions(+), 81 deletions(-) diff --git a/inputcontext/QskInputContext.cpp b/inputcontext/QskInputContext.cpp index 82e4cf79..828b5c96 100644 --- a/inputcontext/QskInputContext.cpp +++ b/inputcontext/QskInputContext.cpp @@ -29,6 +29,8 @@ public: QskInputContext::QskInputContext() : m_data( new PrivateData() ) { + setObjectName( "InputContext" ); + m_data->compositionModel = new QskInputCompositionModel(); connect( m_data->compositionModel, &QskInputCompositionModel::candidatesChanged, @@ -50,6 +52,7 @@ QskInputContext::~QskInputContext() delete m_data->inputPanel; qDeleteAll( m_data->compositionModels ); + delete m_data->compositionModel; } bool QskInputContext::isValid() const @@ -137,6 +140,11 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) // Qt::ImPlatformData // hard to say... } +QQuickItem* QskInputContext::inputItem() +{ + return m_data->inputItem; +} + QRectF QskInputContext::keyboardRect() const { if ( m_data->inputPanel @@ -188,7 +196,7 @@ void QskInputContext::showInputPanel() void QskInputContext::hideInputPanel() { - if ( !m_data->inputPanel ) + if ( m_data->inputPanel == nullptr ) return; auto window = m_data->inputPanel->window(); @@ -201,8 +209,9 @@ void QskInputContext::hideInputPanel() bool QskInputContext::isInputPanelVisible() const { auto panel = m_data->inputPanel; + return panel && panel->isVisible() - && panel->window() && panel->window()->isVisible(); + && panel->window() && panel->window()->isVisible(); } QLocale QskInputContext::locale() const diff --git a/inputcontext/QskInputContext.h b/inputcontext/QskInputContext.h index b5b6fcb1..470d9f5d 100644 --- a/inputcontext/QskInputContext.h +++ b/inputcontext/QskInputContext.h @@ -11,6 +11,7 @@ class QskVirtualKeyboard; class QskInputCompositionModel; +class QQuickItem; class QskInputContext : public QPlatformInputContext { @@ -40,6 +41,8 @@ public: void setCompositionModel( const QLocale&, QskInputCompositionModel* ); + Q_INVOKABLE QQuickItem* inputItem(); + private Q_SLOTS: void handleCandidatesChanged(); void setInputPanel( QskVirtualKeyboard* ); diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index 98f1c60a..6fb80c87 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -427,7 +427,7 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const //auto policy = QPlatformDialogHelper::UnknownLayout; auto policy = QPlatformDialogHelper::WinLayout; - if ( const QPlatformTheme* theme = QGuiApplicationPrivate::platformTheme() ) + if ( const auto theme = QGuiApplicationPrivate::platformTheme() ) { const QVariant v = theme->themeHint( QPlatformTheme::DialogButtonBoxLayout ); policy = static_cast< QPlatformDialogHelper::ButtonLayout >( v.toInt() ); diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 3596f116..38f768ff 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -8,9 +8,64 @@ QSK_QT_PRIVATE_BEGIN #include +#include QSK_QT_PRIVATE_END -#include +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 { @@ -20,6 +75,8 @@ namespace TextInput( QQuickItem* parent ) : QQuickTextInput( parent ) { + setActiveFocusOnTab( false ); + setFlag( ItemAcceptsInputMethod, false ); } void setAlignment( Qt::Alignment alignment ) @@ -28,10 +85,26 @@ namespace setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) ); } - protected: - virtual void inputMethodEvent( QInputMethodEvent* event ) override + inline bool handleEvent( QEvent* event ) { - 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( QString(), parent ) -{ -} - -QskTextInput::QskTextInput( const QString& text, QQuickItem* parent ): Inherited( parent ), 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 ); setFocusPolicy( Qt::StrongFocus ); -#if 1 - input->setActiveFocusOnTab( true ); -#endif - setAcceptedMouseButtons( Qt::LeftButton ); + + setFlag( QQuickItem::ItemAcceptsInputMethod ); + + 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 ); } +QskTextInput::QskTextInput( const QString& text, QQuickItem* parent ): + QskTextInput( parent ) +{ + m_data->textInput->setText( text ); +} + 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 { using namespace QskAspect; @@ -136,7 +198,12 @@ QSizeF QskTextInput::contentsSizeHint() const 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 @@ -162,9 +229,12 @@ void QskTextInput::setFontRole( int role ) if ( oldRole != role ) { -#if 1 - m_data->textInput->setFont( font() ); -#endif + polish(); + resetImplicitSize(); + + qskUpdateInputMethod( this, + Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle ); + Q_EMIT fontRoleChanged(); } } @@ -188,6 +258,8 @@ void QskTextInput::setAlignment( Qt::Alignment alignment ) m_data->textInput->setAlignment( alignment ); + polish(); + Q_EMIT alignmentChanged(); } } @@ -211,6 +283,9 @@ bool QskTextInput::isReadOnly() const void QskTextInput::setReadOnly( bool on ) { m_data->textInput->setReadOnly( on ); + m_data->textInput->setFlag( QQuickItem::ItemAcceptsInputMethod, false ); + + qskUpdateInputMethod( this, Qt::ImEnabled ); } bool QskTextInput::isCursorVisible() const @@ -288,6 +363,8 @@ void QskTextInput::setEchoMode( EchoMode mode ) { m_data->textInput->setEchoMode( static_cast< QQuickTextInput::EchoMode >( mode ) ); + + qskUpdateInputMethod( this, Qt::ImHints ); } QString QskTextInput::displayText() const @@ -360,13 +437,36 @@ bool QskTextInput::hasAcceptableInput() const QVariant QskTextInput::inputMethodQuery( Qt::InputMethodQuery property) const { - return m_data->textInput->inputMethodQuery( property ); + return inputMethodQuery( property, QVariant() ); } QVariant QskTextInput::inputMethodQuery( Qt::InputMethodQuery query, QVariant argument) const { - return m_data->textInput->inputMethodQuery( query, argument ); + 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 ); + } + } } bool QskTextInput::canUndo() const @@ -391,7 +491,11 @@ Qt::InputMethodHints QskTextInput::inputMethodHints() const void QskTextInput::setInputMethodHints(Qt::InputMethodHints hints ) { - m_data->textInput->setInputMethodHints( hints ); + if ( m_data->textInput->inputMethodHints() != hints ) + { + m_data->textInput->setInputMethodHints( hints ); + qskUpdateInputMethod( this, Qt::ImHints ); + } } #include "moc_QskTextInput.cpp" diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index d3f69092..c3b09023 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -153,6 +153,13 @@ Q_SIGNALS: void inputMaskChanged( const QString& ); 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; private: diff --git a/src/controls/QskVirtualKeyboard.cpp b/src/controls/QskVirtualKeyboard.cpp index e5b4d6ee..cd4d6c4f 100644 --- a/src/controls/QskVirtualKeyboard.cpp +++ b/src/controls/QskVirtualKeyboard.cpp @@ -10,6 +10,13 @@ #include #include +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +#include +#include + namespace { struct KeyTable @@ -137,10 +144,8 @@ QskVirtualKeyboardCandidateButton::QskVirtualKeyboardCandidateButton( setFlag( QQuickItem::ItemAcceptsInputMethod ); setText( QStringLiteral( " " ) ); // ### - connect( this, &QskVirtualKeyboardButton::pressed, this, [ this ]() - { - m_inputPanel->handleCandidateKey( m_index, m_text ); - } ); + connect( this, &QskVirtualKeyboardButton::pressed, + this, [ this ]() { m_inputPanel->handleCandidateKey( m_index, m_text ); } ); } void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString& text ) @@ -150,7 +155,8 @@ void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString setText( m_text ); } -QskAspect::Subcontrol QskVirtualKeyboardCandidateButton::effectiveSubcontrol( QskAspect::Subcontrol subControl ) const +QskAspect::Subcontrol QskVirtualKeyboardCandidateButton::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const { if( subControl == QskPushButton::Panel ) { @@ -317,6 +323,7 @@ QskVirtualKeyboard::QskVirtualKeyboard( QQuickItem* parent ): const int keyIndex = m_data->keyTable[ m_data->mode ].indexOf( &keyData ); auto button = new QskVirtualKeyboardButton( keyIndex, this, rowBox ); + button->installEventFilter( this ); rowBox->setRetainSizeWhenHidden( button, true ); m_data->keyButtons.append( button ); @@ -912,4 +919,35 @@ void QskVirtualKeyboard::setMode( QskVirtualKeyboard::Mode 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" diff --git a/src/controls/QskVirtualKeyboard.h b/src/controls/QskVirtualKeyboard.h index 0061dd86..c31cad27 100644 --- a/src/controls/QskVirtualKeyboard.h +++ b/src/controls/QskVirtualKeyboard.h @@ -137,6 +137,7 @@ public Q_SLOTS: void setPreeditCandidates( const QVector< QString >& ); protected: + virtual bool eventFilter( QObject*, QEvent* ) override; virtual void geometryChanged( const QRectF&, const QRectF& ) override; virtual void updateLayout() override;