From e19561465408f483b8c222309f1b51365f14db11 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 12 Jun 2018 08:20:48 +0200 Subject: [PATCH] input panel again --- inputcontext/QskInputContextPlugin.cpp | 10 +- playground/inputpanel/main.cpp | 12 +- skins/material/QskMaterialSkin.cpp | 4 +- skins/squiek/QskSquiekSkin.cpp | 4 +- src/inputpanel/QskInputContext.cpp | 211 ++++++++-------- src/inputpanel/QskInputContext.h | 28 ++- src/inputpanel/QskInputPanel.cpp | 327 ++++++++++++------------- src/inputpanel/QskInputPanel.h | 51 ++-- src/inputpanel/QskInputPanelBox.cpp | 92 ++++--- src/inputpanel/QskInputPanelBox.h | 14 +- src/src.pro | 4 +- 11 files changed, 373 insertions(+), 384 deletions(-) diff --git a/inputcontext/QskInputContextPlugin.cpp b/inputcontext/QskInputContextPlugin.cpp index 4bbe5f78..b9e61024 100644 --- a/inputcontext/QskInputContextPlugin.cpp +++ b/inputcontext/QskInputContextPlugin.cpp @@ -20,10 +20,10 @@ namespace { - class InputContext : public QskInputContext + class InputContextFactory : public QskInputContextFactory { public: - virtual QskTextPredictor* textPredictor( const QLocale& locale ) const + virtual QskTextPredictor* createPredictor( const QLocale& locale ) const { #if HUNSPELL /* @@ -34,7 +34,7 @@ namespace return new QskHunspellTextPredictor(); #endif - return QskInputContext::textPredictor( locale ); + return QskInputContextFactory::createPredictor( locale ); } }; } @@ -94,7 +94,9 @@ QskPlatformInputContext::QskPlatformInputContext() auto context = QskInputContext::instance(); if ( context == nullptr ) { - context = new InputContext(); + context = new QskInputContext(); + context->setFactory( new InputContextFactory() ); + QskInputContext::setInstance( context ); } diff --git a/playground/inputpanel/main.cpp b/playground/inputpanel/main.cpp index 9c361a2c..2ad85859 100644 --- a/playground/inputpanel/main.cpp +++ b/playground/inputpanel/main.cpp @@ -307,15 +307,17 @@ int main( int argc, char* argv[] ) QskInputContext::setInputEngine( ... ); #endif - Window window; - window.setColor( "PapayaWhip" ); - window.resize( 600, 600 ); - window.show(); + Window window1; + window1.setObjectName( "Window 1" ); + window1.setColor( "PapayaWhip" ); + window1.resize( 600, 600 ); + window1.show(); #if 0 Window window2; + window2.setObjectName( "Window 2" ); window2.setColor( "Pink" ); - window2.setX( window.x() + 100 ); + window2.setX( window1.x() + 100 ); window2.resize( 600, 600 ); window2.show(); #endif diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index c9d9fbd7..8461ed10 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -547,7 +547,7 @@ void QskMaterialSkin::initTabViewHints() void QskMaterialSkin::initInputPanelHints() { using namespace QskAspect; - using Q = QskInputPanel; + using Q = QskInputPanelBox; const ColorPalette& pal = m_data->palette; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 1a57f8f5..d01999a0 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -618,7 +618,7 @@ void QskSquiekSkin::initTabViewHints() void QskSquiekSkin::initInputPanelHints() { using namespace QskAspect; - using Q = QskInputPanel; + using Q = QskInputPanelBox; setMargins( Q::Panel | Padding, 5 ); setPanel( Q::Panel, Raised ); diff --git a/src/inputpanel/QskInputContext.cpp b/src/inputpanel/QskInputContext.cpp index 93e2b49f..230f6481 100644 --- a/src/inputpanel/QskInputContext.cpp +++ b/src/inputpanel/QskInputContext.cpp @@ -4,8 +4,8 @@ *****************************************************************************/ #include "QskInputContext.h" -#include "QskInputEngine.h" #include "QskInputPanel.h" +#include "QskInputPanelBox.h" #include "QskLinearBox.h" #include "QskDialog.h" @@ -26,54 +26,51 @@ QSK_QT_PRIVATE_END namespace { - class InputEngine final : public QskInputEngine + class Panel final : public QskInputPanel { public: - virtual void attachToPanel( QQuickItem* item ) override + Panel( QQuickItem* parent = nullptr ): + QskInputPanel( parent ) { - if ( m_panel ) - m_panel->attachInputItem( item ); + setAutoLayoutChildren( true ); + + m_box = new QskInputPanelBox( this ); + + connect( m_box, &QskInputPanelBox::keySelected, + this, &QskInputPanel::keySelected ); + + connect( m_box, &QskInputPanelBox::predictiveTextSelected, + this, &QskInputPanel::predictiveTextSelected ); } - virtual QskControl* createPanel() override + virtual void attachItem( QQuickItem* item ) 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; + m_box->attachInputItem( item ); } virtual QQuickItem* inputProxy() const override { - if ( m_panel ) - { - if ( m_panel->panelHints() & QskInputPanel::InputProxy ) - return m_panel->inputProxy(); - } - - return nullptr; + return m_box->inputProxy(); } + + virtual void setPrompt( const QString& prompt ) override + { + m_box->setInputPrompt( prompt ); + } + virtual void setPredictionEnabled( bool on ) override { - if ( m_panel ) - m_panel->setPanelHint( QskInputPanel::Prediction, on ); + m_box->setPanelHint( QskInputPanelBox::Prediction, on ); } - virtual void showPrediction( const QStringList& prediction ) override + virtual void setPrediction( const QStringList& prediction ) override { - if ( m_panel ) - m_panel->setPrediction( prediction ); + m_box->setPrediction( prediction ); } private: - QPointer< QskInputPanel > m_panel; + QskInputPanelBox* m_box; }; - } static QPointer< QskInputContext > qskInputContext = nullptr; @@ -121,56 +118,50 @@ class QskInputContext::PrivateData public: // item receiving the input QPointer< QQuickItem > inputItem; + QPointer< QskInputPanel > panel; // popup or window embedding the panel QskPopup* inputPopup = nullptr; QskWindow* inputWindow = nullptr; - QPointer< QskInputEngine > inputEngine; + QPointer< QskInputContextFactory > factory; }; QskInputContext::QskInputContext(): m_data( new PrivateData() ) { setObjectName( "InputContext" ); - setEngine( new InputEngine() ); } QskInputContext::~QskInputContext() { } -void QskInputContext::setEngine( QskInputEngine* engine ) +void QskInputContext::setFactory( QskInputContextFactory* factory ) { - if ( m_data->inputEngine == engine ) + if ( m_data->factory == factory ) return; - if ( m_data->inputEngine && m_data->inputEngine->parent() == this ) - { - m_data->inputEngine->disconnect( this ); + if ( m_data->factory && m_data->factory->parent() == this ) + delete m_data->factory; - if ( m_data->inputEngine->parent() == this ) - delete m_data->inputEngine; - } + m_data->factory = factory; - 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 ); } ); - } + if ( factory && factory->parent() == nullptr ) + factory->setParent( this ); } -QskInputEngine* QskInputContext::engine() const +QskInputContextFactory* QskInputContext::factory() const { - return m_data->inputEngine; + return m_data->factory; +} + +QskTextPredictor* QskInputContext::textPredictor( const QLocale& locale ) +{ + if ( m_data->factory ) + return m_data->factory->createPredictor( locale ); + + return nullptr; } QQuickItem* QskInputContext::inputItem() const @@ -178,19 +169,6 @@ QQuickItem* QskInputContext::inputItem() const return m_data->inputItem; } -QskControl* QskInputContext::inputPanel() const -{ - if ( m_data->inputEngine == nullptr ) - return nullptr; - - auto panel = m_data->inputEngine->panel( true ); - - if ( panel && panel->parent() != this ) - panel->setParent( const_cast< QskInputContext* >( this ) ); - - return panel; -} - void QskInputContext::update( Qt::InputMethodQueries queries ) { if ( queries & Qt::ImEnabled ) @@ -205,8 +183,8 @@ void QskInputContext::update( Qt::InputMethodQueries queries ) } } - if ( m_data->inputEngine ) - m_data->inputEngine->updateInputPanel( queries ); + if ( m_data->panel ) + m_data->panel->updateInputPanel( queries ); } QRectF QskInputContext::panelRect() const @@ -217,7 +195,7 @@ QRectF QskInputContext::panelRect() const return QRectF(); } -QskPopup* QskInputContext::createEmbeddingPopup( QskControl* panel ) +QskPopup* QskInputContext::createEmbeddingPopup( QskInputPanel* panel ) { auto popup = new QskPopup(); @@ -228,10 +206,7 @@ QskPopup* QskInputContext::createEmbeddingPopup( QskControl* panel ) auto box = new QskLinearBox( popup ); box->addItem( panel ); - Qt::Alignment alignment = Qt::AlignVCenter; - if ( m_data->inputEngine ) - alignment = m_data->inputEngine->panelAlignment() & Qt::AlignVertical_Mask; - + const auto alignment = panel->alignment() & Qt::AlignVertical_Mask; popup->setOverlay( alignment == Qt::AlignVCenter ); switch( alignment ) @@ -257,7 +232,7 @@ QskPopup* QskInputContext::createEmbeddingPopup( QskControl* panel ) return popup; } -QskWindow* QskInputContext::createEmbeddingWindow( QskControl* panel ) +QskWindow* QskInputContext::createEmbeddingWindow( QskInputPanel* panel ) { auto window = new QskWindow(); @@ -273,18 +248,42 @@ QskWindow* QskInputContext::createEmbeddingWindow( QskControl* panel ) return window; } +void QskInputContext::ensurePanel() +{ + if ( m_data->panel ) + return; + + QskInputPanel* panel = nullptr; + + if ( m_data->factory ) + panel = m_data->factory->createPanel(); + + if ( panel == nullptr ) + panel = new Panel(); + + panel->setParent( const_cast< QskInputContext* >( this ) ); + + connect( panel, &QskInputPanel::visibleChanged, + this, &QskInputContext::activeChanged, + Qt::UniqueConnection ); + + connect( panel, &QskInputPanel::localeChanged, + this, [] { qskSendToPlatformContext( QEvent::LocaleChange ); }, + Qt::UniqueConnection ); + + m_data->panel = panel; +} + void QskInputContext::showPanel() { auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() ); if ( focusItem == nullptr ) return; - auto panel = inputPanel(); - if ( panel == nullptr ) - return; + ensurePanel(); - if ( ( focusItem == panel ) - || qskIsAncestorOf( panel, focusItem ) ) + if ( ( focusItem == m_data->panel ) + || qskIsAncestorOf( m_data->panel, focusItem ) ) { // ignore: usually the input proxy of the panel return; @@ -300,7 +299,7 @@ void QskInputContext::showPanel() if ( m_data->inputWindow == nullptr ) { - auto window = createEmbeddingWindow( panel ); + auto window = createEmbeddingWindow( m_data->panel ); if ( window ) { @@ -329,7 +328,7 @@ void QskInputContext::showPanel() if ( m_data->inputPopup == nullptr ) { - auto popup = createEmbeddingPopup( panel ); + auto popup = createEmbeddingPopup( m_data->panel ); if ( popup ) { @@ -345,8 +344,7 @@ void QskInputContext::showPanel() } } - if ( m_data->inputEngine ) - m_data->inputEngine->attachInputItem( m_data->inputItem ); + m_data->panel->attachInputItem( m_data->inputItem ); } void QskInputContext::hidePanel() @@ -368,12 +366,12 @@ void QskInputContext::hidePanel() #endif } - if ( m_data->inputEngine ) + if ( m_data->panel ) { - if ( auto panel = m_data->inputEngine->panel( false ) ) - panel->setParentItem( nullptr ); + m_data->panel->setParentItem( nullptr ); + m_data->panel->disconnect( this ); - m_data->inputEngine->attachInputItem( nullptr ); + m_data->panel->attachInputItem( nullptr ); } if ( m_data->inputPopup ) @@ -410,11 +408,8 @@ bool QskInputContext::isActive() const QLocale QskInputContext::locale() const { - if ( m_data->inputEngine ) - { - if ( auto panel = m_data->inputEngine->panel( false ) ) - return panel->locale(); - } + if ( m_data->panel ) + return m_data->panel->locale(); return QLocale(); } @@ -469,11 +464,6 @@ void QskInputContext::setFocusObject( QObject* focusObject ) m_data->inputItem = nullptr; } -QskTextPredictor* QskInputContext::textPredictor( const QLocale& ) const -{ - return nullptr; -} - void QskInputContext::processClickAt( int cursorPosition ) { Q_UNUSED( cursorPosition ); @@ -484,7 +474,7 @@ void QskInputContext::commitPrediction( bool ) /* called, when the input item loses the focus. As it it should be possible to navigate inside of the - inputPanel what should we do here ? + panel what should we do here ? */ } @@ -501,8 +491,8 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event ) } case QEvent::Resize: { - if ( auto panel = inputPanel() ) - panel->setSize( m_data->inputWindow->size() ); + if ( m_data->panel ) + m_data->panel->setSize( m_data->inputWindow->size() ); break; } @@ -535,4 +525,23 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event ) return Inherited::eventFilter( object, event ); } +QskInputContextFactory::QskInputContextFactory( QObject* parent ): + QObject( parent ) +{ +} + +QskInputContextFactory::~QskInputContextFactory() +{ +} + +QskTextPredictor* QskInputContextFactory::createPredictor( const QLocale& ) const +{ + return nullptr; +} + +QskInputPanel* QskInputContextFactory::createPanel() const +{ + return new Panel(); +} + #include "moc_QskInputContext.cpp" diff --git a/src/inputpanel/QskInputContext.h b/src/inputpanel/QskInputContext.h index 35b02e1b..394d4924 100644 --- a/src/inputpanel/QskInputContext.h +++ b/src/inputpanel/QskInputContext.h @@ -12,12 +12,24 @@ #include class QskTextPredictor; -class QskControl; +class QskInputPanel; class QskInputEngine; class QskPopup; class QskWindow; class QQuickItem; +class QSK_EXPORT QskInputContextFactory : public QObject +{ + Q_OBJECT + +public: + QskInputContextFactory( QObject* parent = nullptr ); + virtual ~QskInputContextFactory(); + + virtual QskTextPredictor* createPredictor( const QLocale& ) const; + virtual QskInputPanel* createPanel() const; +}; + class QSK_EXPORT QskInputContext : public QObject { Q_OBJECT @@ -28,8 +40,8 @@ public: QskInputContext(); virtual ~QskInputContext(); - void setEngine( QskInputEngine* ); - QskInputEngine* engine() const; + void setFactory( QskInputContextFactory* ); + QskInputContextFactory* factory() const; QRectF panelRect() const; @@ -43,7 +55,7 @@ public: static QskInputContext* instance(); static void setInstance( QskInputContext* ); - virtual QskTextPredictor* textPredictor( const QLocale& ) const; + QskTextPredictor* textPredictor( const QLocale& locale ); Q_SIGNALS: void activeChanged(); @@ -52,14 +64,12 @@ Q_SIGNALS: protected: virtual bool eventFilter( QObject*, QEvent* ) override; - virtual QskPopup* createEmbeddingPopup( QskControl* ); - virtual QskWindow* createEmbeddingWindow( QskControl* ); + virtual QskPopup* createEmbeddingPopup( QskInputPanel* ); + virtual QskWindow* createEmbeddingWindow( QskInputPanel* ); virtual void showPanel(); virtual void hidePanel(); - QskControl* inputPanel() const; - private: friend class QskPlatformInputContext; @@ -69,6 +79,8 @@ private: virtual void processClickAt( int cursorPosition ); virtual void commitPrediction( bool ); + void ensurePanel(); + class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/inputpanel/QskInputPanel.cpp b/src/inputpanel/QskInputPanel.cpp index c7d32c40..63b16c46 100644 --- a/src/inputpanel/QskInputPanel.cpp +++ b/src/inputpanel/QskInputPanel.cpp @@ -3,18 +3,27 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#include "QskInputEngine.h" +#include "QskInputPanel.h" #include "QskInputContext.h" #include "QskTextPredictor.h" -#include "QskControl.h" #include #include -static inline QQuickItem* qskReceiverItem( const QskInputEngine* engine ) +static inline QQuickItem* qskReceiverItem( const QskInputPanel* panel ) { - auto item = engine->inputProxy(); - return item ? item : engine->inputItem(); + if ( auto item = panel->inputProxy() ) + return item; + + return panel->inputItem(); +} + +static inline bool qskUsePrediction( Qt::InputMethodHints hints ) +{ + constexpr Qt::InputMethodHints mask = + Qt::ImhNoPredictiveText | Qt::ImhExclusiveInputMask | Qt::ImhHiddenText; + + return ( hints & mask ) == 0; } static inline void qskSendReplaceText( QQuickItem* receiver, const QString& text ) @@ -73,55 +82,21 @@ static inline void qskSendKey( QQuickItem* receiver, int key ) 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 ) -{ - // Special case entry codes here, else default to the symbol - switch ( keyCode ) - { - case Qt::Key_Shift: - case Qt::Key_CapsLock: - case Qt::Key_Mode_switch: - case Qt::Key_Backspace: - case Qt::Key_Muhenkan: - return QString(); - - case Qt::Key_Return: - case Qt::Key_Kanji: - return QChar( QChar::CarriageReturn ); - - case Qt::Key_Space: - return QChar( QChar::Space ); - - default: - break; - } - - return QChar( keyCode ); -} - namespace { class KeyProcessor { public: - class Result + struct Result { - public: int key = 0; QString text; bool isFinal = true; }; - Result processKey( int key, Qt::InputMethodHints inputHints, + Result processKey( + int key, Qt::InputMethodHints inputHints, QskTextPredictor* predictor, int spaceLeft ) { Result result; @@ -183,7 +158,7 @@ namespace { if ( !m_preedit.isEmpty() && spaceLeft) { - m_preedit += qskKeyString( key ); + m_preedit += keyString( key ); m_preedit = m_preedit.left( spaceLeft ); result.text = m_preedit; @@ -206,7 +181,7 @@ namespace } } - const QString text = qskKeyString( key ); + const QString text = keyString( key ); if ( predictor ) { @@ -242,15 +217,40 @@ namespace } private: + inline QString keyString( int keyCode ) const + { + // Special case entry codes here, else default to the symbol + switch ( keyCode ) + { + case Qt::Key_Shift: + case Qt::Key_CapsLock: + case Qt::Key_Mode_switch: + case Qt::Key_Backspace: + case Qt::Key_Muhenkan: + return QString(); + + case Qt::Key_Return: + case Qt::Key_Kanji: + return QChar( QChar::CarriageReturn ); + + case Qt::Key_Space: + return QChar( QChar::Space ); + + default: + break; + } + + return QChar( keyCode ); + } + QString m_preedit; }; } -class QskInputEngine::PrivateData +class QskInputPanel::PrivateData { public: KeyProcessor keyProcessor; - QPointer< QskControl > panel; QPointer< QQuickItem > inputItem; QLocale predictorLocale; @@ -260,17 +260,30 @@ public: bool hasPredictorLocale = false; }; -QskInputEngine::QskInputEngine( QObject* parent ): +QskInputPanel::QskInputPanel( QQuickItem* parent ): Inherited( parent ), m_data( new PrivateData() ) { + setAutoLayoutChildren( true ); + initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained ); + + connect( this, &QskInputPanel::keySelected, + this, &QskInputPanel::commitKey ); + + connect( this, &QskInputPanel::predictiveTextSelected, + this, &QskInputPanel::commitPredictiveText ); + + connect( this, &QskControl::localeChanged, + this, &QskInputPanel::updateLocale ); + + updateLocale( locale() ); } -QskInputEngine::~QskInputEngine() +QskInputPanel::~QskInputPanel() { } -void QskInputEngine::attachInputItem( QQuickItem* item ) +void QskInputPanel::attachInputItem( QQuickItem* item ) { if ( item == m_data->inputItem ) return; @@ -288,108 +301,65 @@ void QskInputEngine::attachInputItem( QQuickItem* item ) m_data->keyProcessor.reset(); m_data->inputHints = 0; - attachToPanel( item ); + attachItem( item ); Qt::InputMethodQueries queries = Qt::ImQueryAll; queries &= ~Qt::ImEnabled; updateInputPanel( queries ); + + if ( inputProxy() ) + { + // hiding the cursor in item + const QInputMethodEvent::Attribute attribute( + QInputMethodEvent::Cursor, 0, 0, QVariant() ); + + QInputMethodEvent event( QString(), { attribute } ); + QCoreApplication::sendEvent( item, &event ); + } } else { - attachToPanel( nullptr ); + attachItem( nullptr ); } } -void QskInputEngine::updateInputPanel( - Qt::InputMethodQueries queries ) +void QskInputPanel::updateInputPanel( Qt::InputMethodQueries queries ) { - auto item = inputItem(); - if ( item == nullptr ) + if ( m_data->inputItem == nullptr ) return; QInputMethodQueryEvent event( queries ); - QCoreApplication::sendEvent( item, &event ); + QCoreApplication::sendEvent( m_data->inputItem, &event ); if ( queries & Qt::ImHints ) { m_data->inputHints = static_cast< Qt::InputMethodHints >( event.value( Qt::ImHints ).toInt() ); - updatePanel(); + setPredictionEnabled( + m_data->predictor && qskUsePrediction( m_data->inputHints ) ); } if ( queries & Qt::ImPreferredLanguage ) { - if ( m_data->panel ) - { - m_data->panel->setLocale( - event.value( Qt::ImPreferredLanguage ).toLocale() ); - } + 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; -} - -void QskInputEngine::updateLocale( const QLocale& locale ) +void QskInputPanel::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 ) +void QskInputPanel::resetPredictor( const QLocale& locale ) { auto predictor = QskInputContext::instance()->textPredictor( locale ); @@ -409,49 +379,22 @@ void QskInputEngine::resetPredictor( const QLocale& locale ) } } + m_data->predictor = predictor; + if ( predictor ) { if ( predictor->parent() == nullptr ) predictor->setParent( this ); connect( predictor, &QskTextPredictor::predictionChanged, - this, &QskInputEngine::updatePrediction ); + this, &QskInputPanel::updatePrediction ); } - m_data->predictor = predictor; + setPredictionEnabled( + predictor && qskUsePrediction( m_data->inputHints ) ); } -void QskInputEngine::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 QskInputEngine::applyText( const QString& text, bool isFinal ) -{ - qskSendText( qskReceiverItem( this ), text, isFinal ); -} - -void QskInputEngine::applyKey( int key ) -{ - // control keys like left/right - qskSendKey( qskReceiverItem( this ), key ); -} - -void QskInputEngine::commitPredictiveText( int index ) +void QskInputPanel::commitPredictiveText( int index ) { QString text; @@ -463,33 +406,57 @@ void QskInputEngine::commitPredictiveText( int index ) m_data->keyProcessor.reset(); - showPrediction( QStringList() ); - applyText( text, true ); + setPrediction( QStringList() ); + + qskSendText( qskReceiverItem( this ), text, true ); } -void QskInputEngine::updatePrediction() +void QskInputPanel::updatePrediction() { if ( m_data->predictor ) - showPrediction( m_data->predictor->candidates() ); + setPrediction( m_data->predictor->candidates() ); } -void QskInputEngine::setPredictionEnabled( bool on ) +QQuickItem* QskInputPanel::inputProxy() const { - Q_UNUSED( on ) + return nullptr; } -void QskInputEngine::showPrediction( const QStringList& ) +QQuickItem* QskInputPanel::inputItem() const +{ + return m_data->inputItem; +} + +void QskInputPanel::setPrompt( const QString& ) { } -void QskInputEngine::commitKey( int key ) +void QskInputPanel::setPredictionEnabled( bool ) +{ +} + +void QskInputPanel::setPrediction( const QStringList& ) +{ +} + +Qt::Alignment QskInputPanel::alignment() const +{ + /* + When we have an input proxy, we don't care if + the input item becomes hidden + */ + + return inputProxy() ? Qt::AlignVCenter : Qt::AlignBottom; +} + +void QskInputPanel::commitKey( int key ) { int spaceLeft = -1; if ( !( m_data->inputHints & Qt::ImhMultiLine ) ) { QInputMethodQueryEvent event1( Qt::ImMaximumTextLength ); - QCoreApplication::sendEvent( inputItem(), &event1 ); + QCoreApplication::sendEvent( m_data->inputItem, &event1 ); const int maxChars = event1.value( Qt::ImMaximumTextLength ).toInt(); if ( maxChars >= 0 ) @@ -509,30 +476,42 @@ void QskInputEngine::commitKey( int key ) const auto result = m_data->keyProcessor.processKey( key, m_data->inputHints, predictor, spaceLeft ); - if ( result.key ) + switch( result.key ) { - switch( result.key ) + case 0: { - case Qt::Key_Return: + if ( !result.text.isEmpty() ) { - applyInput( true ); - break; - } - case Qt::Key_Escape: - { - applyInput( false ); - break; - } - default: - { - applyKey( result.key ); + qskSendText( qskReceiverItem( this ), + result.text, result.isFinal ); } + break; + } + case Qt::Key_Return: + { + if ( auto proxy = inputProxy() ) + { + // using input method query instead + const auto value = proxy->property( "text" ); + if ( value.canConvert< QString >() ) + { + qskSendReplaceText( m_data->inputItem, value.toString() ); + } + } + + qskSendKey( m_data->inputItem, result.key ); + break; + } + case Qt::Key_Escape: + { + qskSendKey( m_data->inputItem, result.key ); + break; + } + default: + { + qskSendKey( qskReceiverItem( this ), result.key ); } - } - else if ( !result.text.isEmpty() ) - { - applyText( result.text, result.isFinal ); } } -#include "moc_QskInputEngine.cpp" +#include "moc_QskInputPanel.cpp" diff --git a/src/inputpanel/QskInputPanel.h b/src/inputpanel/QskInputPanel.h index 30854703..2488ea68 100644 --- a/src/inputpanel/QskInputPanel.h +++ b/src/inputpanel/QskInputPanel.h @@ -3,62 +3,55 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#ifndef QSK_INPUT_ENGINE_H -#define QSK_INPUT_ENGINE_H +#ifndef QSK_INPUT_PANEL_H +#define QSK_INPUT_PANEL_H #include "QskGlobal.h" -#include +#include "QskControl.h" #include class QskTextPredictor; -class QskControl; -class QQuickItem; -class QLocale; +class QString; +class QStringList; -class QSK_EXPORT QskInputEngine : public QObject +class QSK_EXPORT QskInputPanel : public QskControl { Q_OBJECT - using Inherited = QObject; + using Inherited = QskControl; public: - QskInputEngine( QObject* parent = nullptr ); - virtual ~QskInputEngine() override; + QskInputPanel( QQuickItem* parent = nullptr ); + virtual ~QskInputPanel() override; - virtual void attachInputItem( QQuickItem* ); - virtual void updateInputPanel( Qt::InputMethodQueries ); - - QskControl* panel( bool doCreate ); - virtual Qt::Alignment panelAlignment() const; + void attachInputItem( QQuickItem* ); + void updateInputPanel( Qt::InputMethodQueries ); virtual QQuickItem* inputProxy() const; - virtual QQuickItem* inputItem() const; + QQuickItem* inputItem() const; + + virtual Qt::Alignment alignment() const; public Q_SLOTS: void commitKey( int keyCode ); void commitPredictiveText( int index ); Q_SIGNALS: - void activeChanged(); - void localeChanged(); + void keySelected( int keyCode ); + void predictiveTextSelected( int ); + +public Q_SLOTS: + virtual void setPrompt( const QString& ); + virtual void setPrediction( const QStringList& ); + virtual void setPredictionEnabled( bool ); protected: - virtual QskControl* createPanel() = 0; - virtual void attachToPanel( QQuickItem* ) = 0; - - virtual void setPredictionEnabled( bool on ); - virtual void showPrediction( const QStringList& ); - - void applyInput( bool success ); - void applyText( const QString&, bool isFinal ); - void applyKey( int keyCode ); + virtual void attachItem( QQuickItem* ) = 0; private: void resetPredictor( const QLocale& ); void updatePrediction(); - void updatePanel(); - void updateLocale( const QLocale& ); class PrivateData; diff --git a/src/inputpanel/QskInputPanelBox.cpp b/src/inputpanel/QskInputPanelBox.cpp index cc00ae24..e7b122d0 100644 --- a/src/inputpanel/QskInputPanelBox.cpp +++ b/src/inputpanel/QskInputPanelBox.cpp @@ -3,7 +3,7 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#include "QskInputPanel.h" +#include "QskInputPanelBox.h" #include "QskVirtualKeyboard.h" #include "QskInputPredictionBar.h" #include "QskTextInput.h" @@ -13,18 +13,17 @@ #include #include #include -#include namespace { class TextInputProxy final : public QskTextInput { public: - TextInputProxy( QskInputPanel* panel, QQuickItem* parentItem = nullptr ): + TextInputProxy( QskInputPanelBox* panelBox, QQuickItem* parentItem = nullptr ): QskTextInput( parentItem ), - m_panel( panel ) + m_panelBox( panelBox ) { - setObjectName( "InputPanelInputProxy" ); + setObjectName( "InputBoxProxy" ); setFocusPolicy( Qt::NoFocus ); } @@ -32,10 +31,10 @@ namespace QskAspect::Subcontrol subControl ) const override { if ( subControl == QskTextInput::Panel ) - return m_panel->effectiveSubcontrol( QskInputPanel::ProxyPanel ); + return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyPanel ); if ( subControl == QskTextInput::Text ) - return m_panel->effectiveSubcontrol( QskInputPanel::ProxyText ); + return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyText ); return subControl; } @@ -50,15 +49,15 @@ namespace } private: - QskInputPanel* m_panel; + QskInputPanelBox* m_panelBox; }; } -QSK_SUBCONTROL( QskInputPanel, Panel ) -QSK_SUBCONTROL( QskInputPanel, ProxyPanel ) -QSK_SUBCONTROL( QskInputPanel, ProxyText ) +QSK_SUBCONTROL( QskInputPanelBox, Panel ) +QSK_SUBCONTROL( QskInputPanelBox, ProxyPanel ) +QSK_SUBCONTROL( QskInputPanelBox, ProxyText ) -class QskInputPanel::PrivateData +class QskInputPanelBox::PrivateData { public: QPointer< QQuickItem > inputItem; @@ -69,28 +68,25 @@ public: QskInputPredictionBar* predictionBar; QskVirtualKeyboard* keyboard; - int maxChars = -1; - - QskInputPanel::PanelHints panelHints = QskInputPanel::InputProxy; + QskInputPanelBox::PanelHints panelHints = QskInputPanelBox::InputProxy; }; -QskInputPanel::QskInputPanel( QQuickItem* parent ): +QskInputPanelBox::QskInputPanelBox( QQuickItem* parent ): Inherited( parent ), m_data( new PrivateData() ) { setAutoLayoutChildren( true ); - initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained ); m_data->prompt = new QskTextLabel(); m_data->prompt->setVisible( false ); m_data->inputProxy = new TextInputProxy( this, nullptr ); m_data->inputProxy->setVisible( - m_data->panelHints & QskInputPanel::InputProxy ); + m_data->panelHints & QskInputPanelBox::InputProxy ); m_data->predictionBar = new QskInputPredictionBar(); m_data->predictionBar->setVisible( - m_data->panelHints & QskInputPanel::Prediction ); + m_data->panelHints & QskInputPanelBox::Prediction ); m_data->keyboard = new QskVirtualKeyboard(); @@ -105,17 +101,17 @@ QskInputPanel::QskInputPanel( QQuickItem* parent ): m_data->layout = layout; connect( m_data->predictionBar, &QskInputPredictionBar::predictiveTextSelected, - this, &QskInputPanel::predictiveTextSelected ); + this, &QskInputPanelBox::predictiveTextSelected ); connect( m_data->keyboard, &QskVirtualKeyboard::keySelected, - this, &QskInputPanel::keySelected ); + this, &QskInputPanelBox::keySelected ); } -QskInputPanel::~QskInputPanel() +QskInputPanelBox::~QskInputPanelBox() { } -void QskInputPanel::setPanelHint( PanelHint hint, bool on ) +void QskInputPanelBox::setPanelHint( PanelHint hint, bool on ) { if ( on ) setPanelHints( m_data->panelHints | hint ); @@ -123,17 +119,17 @@ void QskInputPanel::setPanelHint( PanelHint hint, bool on ) setPanelHints( m_data->panelHints & ~hint ); } -void QskInputPanel::setPanelHints( PanelHints hints ) +void QskInputPanelBox::setPanelHints( PanelHints hints ) { if ( hints == m_data->panelHints ) return; m_data->panelHints = hints; - m_data->inputProxy->setVisible( hints & QskInputPanel::InputProxy ); - m_data->predictionBar->setVisible( hints & QskInputPanel::Prediction ); + m_data->inputProxy->setVisible( hints & QskInputPanelBox::InputProxy ); + m_data->predictionBar->setVisible( hints & QskInputPanelBox::Prediction ); - const bool showPrompt = ( hints & QskInputPanel::InputProxy ) + const bool showPrompt = ( hints & QskInputPanelBox::InputProxy ) && !m_data->prompt->text().isEmpty(); m_data->prompt->setVisible( showPrompt ); @@ -141,12 +137,12 @@ void QskInputPanel::setPanelHints( PanelHints hints ) Q_EMIT panelHintsChanged(); } -QskInputPanel::PanelHints QskInputPanel::panelHints() const +QskInputPanelBox::PanelHints QskInputPanelBox::panelHints() const { return m_data->panelHints; } -void QskInputPanel::attachInputItem( QQuickItem* item ) +void QskInputPanelBox::attachInputItem( QQuickItem* item ) { if ( item == m_data->inputItem ) return; @@ -155,55 +151,51 @@ void QskInputPanel::attachInputItem( QQuickItem* item ) if ( item ) { - if ( m_data->panelHints & QskInputPanel::InputProxy ) + if ( m_data->panelHints & QskInputPanelBox::InputProxy ) { m_data->inputProxy->setupFrom( item ); m_data->inputProxy->setEditing( true ); - - // hiding the cursor in item - const QInputMethodEvent::Attribute attribute( - QInputMethodEvent::Cursor, 0, 0, QVariant() ); - - QInputMethodEvent event( QString(), { attribute } ); - QCoreApplication::sendEvent( item, &event ); } } } -QQuickItem* QskInputPanel::attachedInputItem() const +QQuickItem* QskInputPanelBox::attachedInputItem() const { return m_data->inputItem; } -QQuickItem* QskInputPanel::inputProxy() const +QQuickItem* QskInputPanelBox::inputProxy() const { - return m_data->inputProxy; + if ( m_data->panelHints & QskInputPanelBox::InputProxy ) + return m_data->inputProxy; + + return nullptr; } -QskAspect::Subcontrol QskInputPanel::effectiveSubcontrol( +QskAspect::Subcontrol QskInputPanelBox::effectiveSubcontrol( QskAspect::Subcontrol subControl ) const { if( subControl == QskBox::Panel ) - return QskInputPanel::Panel; + return QskInputPanelBox::Panel; #if 1 // TODO ... - if( subControl == QskInputPanel::ProxyPanel ) + if( subControl == QskInputPanelBox::ProxyPanel ) return QskTextInput::Panel; - if( subControl == QskInputPanel::ProxyText ) + if( subControl == QskInputPanelBox::ProxyText ) return QskTextInput::Text; #endif return subControl; } -QString QskInputPanel::inputPrompt() const +QString QskInputPanelBox::inputPrompt() const { return m_data->prompt->text(); } -void QskInputPanel::setInputPrompt( const QString& text ) +void QskInputPanelBox::setInputPrompt( const QString& text ) { auto prompt = m_data->prompt; @@ -211,19 +203,19 @@ void QskInputPanel::setInputPrompt( const QString& text ) { prompt->setText( text ); - if ( m_data->panelHints & QskInputPanel::InputProxy ) + if ( m_data->panelHints & QskInputPanelBox::InputProxy ) prompt->setVisible( !text.isEmpty() ); Q_EMIT inputPromptChanged( text ); } } -void QskInputPanel::setPrediction( const QStringList& prediction ) +void QskInputPanelBox::setPrediction( const QStringList& prediction ) { m_data->predictionBar->setPrediction( prediction ); } -void QskInputPanel::keyPressEvent( QKeyEvent* event ) +void QskInputPanelBox::keyPressEvent( QKeyEvent* event ) { int keyCode = -1; @@ -254,4 +246,4 @@ void QskInputPanel::keyPressEvent( QKeyEvent* event ) } } -#include "moc_QskInputPanel.cpp" +#include "moc_QskInputPanelBox.cpp" diff --git a/src/inputpanel/QskInputPanelBox.h b/src/inputpanel/QskInputPanelBox.h index 3d367342..130b32a5 100644 --- a/src/inputpanel/QskInputPanelBox.h +++ b/src/inputpanel/QskInputPanelBox.h @@ -3,8 +3,8 @@ * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ -#ifndef QSK_INPUT_PANEL_H -#define QSK_INPUT_PANEL_H +#ifndef QSK_INPUT_PANEL_BOX_H +#define QSK_INPUT_PANEL_BOX_H #include "QskGlobal.h" #include "QskBox.h" @@ -14,7 +14,7 @@ class QskInputEngine; class QString; class QLocale; -class QSK_EXPORT QskInputPanel : public QskBox +class QSK_EXPORT QskInputPanelBox : public QskBox { Q_OBJECT @@ -38,8 +38,8 @@ public: Q_ENUM( PanelHint ) Q_DECLARE_FLAGS( PanelHints, PanelHint ) - QskInputPanel( QQuickItem* parent = nullptr ); - virtual ~QskInputPanel() override; + QskInputPanelBox( QQuickItem* parent = nullptr ); + virtual ~QskInputPanelBox() override; void attachInputItem( QQuickItem* ); QQuickItem* attachedInputItem() const; @@ -75,7 +75,7 @@ private: std::unique_ptr< PrivateData > m_data; }; -Q_DECLARE_OPERATORS_FOR_FLAGS( QskInputPanel::PanelHints ) -Q_DECLARE_METATYPE( QskInputPanel::PanelHints ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QskInputPanelBox::PanelHints ) +Q_DECLARE_METATYPE( QskInputPanelBox::PanelHints ) #endif diff --git a/src/src.pro b/src/src.pro index 4acd8e6d..41997cbb 100644 --- a/src/src.pro +++ b/src/src.pro @@ -300,15 +300,15 @@ SOURCES += \ SOURCES += \ inputpanel/QskTextPredictor.cpp \ inputpanel/QskInputContext.cpp \ - inputpanel/QskInputEngine.cpp \ inputpanel/QskInputPanel.cpp \ + inputpanel/QskInputPanelBox.cpp \ inputpanel/QskInputPredictionBar.cpp \ inputpanel/QskVirtualKeyboard.cpp HEADERS += \ inputpanel/QskTextPredictor.h \ inputpanel/QskInputContext.h \ - inputpanel/QskInputEngine.h \ inputpanel/QskInputPanel.h \ + inputpanel/QskInputPanelBox.h \ inputpanel/QskInputPredictionBar.h \ inputpanel/QskVirtualKeyboard.h