diff --git a/inputcontext/QskInputContext.cpp b/inputcontext/QskInputContext.cpp index 8f952a4a..a82b6761 100644 --- a/inputcontext/QskInputContext.cpp +++ b/inputcontext/QskInputContext.cpp @@ -276,7 +276,9 @@ void QskInputContext::showInputPanel() if ( inputPanel == nullptr ) { auto panel = new QskInputPanel(); + panel->setParent( this ); + panel->setInputProxy( true ); setInputPanel( panel ); } @@ -301,7 +303,14 @@ void QskInputContext::showInputPanel() inputPanel->setParentItem( inputWindow->contentItem() ); - inputWindow->resize( 800, 240 ); // ### what size? + QSizeF size; + if ( auto control = qobject_cast< const QskControl* >( inputPanel ) ) + size = control->sizeHint(); + + if ( size.isEmpty() ) + size = QSizeF( 800, 240 ); // ### what size? + + inputWindow->resize( size.toSize() ); inputWindow->show(); inputWindow->installEventFilter( this ); @@ -320,15 +329,28 @@ void QskInputContext::showInputPanel() else { auto popup = new QskPopup( m_data->inputItem->window()->contentItem() ); + popup->setAutoLayoutChildren( true ); popup->setTransparentForPositioner( false ); popup->setOverlay( false ); popup->setModal( true ); auto box = new QskLinearBox( popup ); - box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge ); box->addItem( inputPanel ); + if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) ) + { + if ( panel->hasInputProxy() ) + { + popup->setOverlay( true ); + } + } + + if ( !popup->hasOverlay() ) + { + box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge ); + } + inputPopup = popup; } diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 6350a165..c9d9fbd7 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,7 @@ void QskMaterialSkin::initHints() initDialogButtonHints(); initFocusIndicatorHints(); initInputPanelHints(); + initVirtualKeyboardHints(); initListViewHints(); initPageIndicatorHints(); initPopupHints(); @@ -543,6 +545,19 @@ void QskMaterialSkin::initTabViewHints() } void QskMaterialSkin::initInputPanelHints() +{ + using namespace QskAspect; + using Q = QskInputPanel; + + const ColorPalette& pal = m_data->palette; + + setBoxShape( Q::Panel, 0 ); + setBoxBorderMetrics( Q::Panel, 0 ); + setGradient( Q::Panel, pal.darker150 ); + setBoxBorderColors( Q::Panel, pal.baseColor ); +} + +void QskMaterialSkin::initVirtualKeyboardHints() { using namespace QskAspect; using Q = QskVirtualKeyboard; diff --git a/skins/material/QskMaterialSkin.h b/skins/material/QskMaterialSkin.h index 8d7ac018..2ab2a4d8 100644 --- a/skins/material/QskMaterialSkin.h +++ b/skins/material/QskMaterialSkin.h @@ -30,6 +30,7 @@ private: void initDialogButtonHints(); void initFocusIndicatorHints(); void initInputPanelHints(); + void initVirtualKeyboardHints(); void initListViewHints(); void initPageIndicatorHints(); void initPopupHints(); diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 441552f3..d2ee85f4 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -242,6 +243,7 @@ void QskSquiekSkin::initHints() initDialogButtonHints(); initFocusIndicatorHints(); initInputPanelHints(); + initVirtualKeyboardHints(); initListViewHints(); initPageIndicatorHints(); initPopupHints(); @@ -612,6 +614,15 @@ void QskSquiekSkin::initTabViewHints() } void QskSquiekSkin::initInputPanelHints() +{ + using namespace QskAspect; + using Q = QskInputPanel; + + setMargins( Q::Panel | Padding, 5 ); + setPanel( Q::Panel, Raised ); +} + +void QskSquiekSkin::initVirtualKeyboardHints() { using namespace QskAspect; using Q = QskVirtualKeyboard; diff --git a/skins/squiek/QskSquiekSkin.h b/skins/squiek/QskSquiekSkin.h index 9881e704..7d2cf30a 100644 --- a/skins/squiek/QskSquiekSkin.h +++ b/skins/squiek/QskSquiekSkin.h @@ -30,6 +30,7 @@ private: void initDialogButtonBoxHints(); void initFocusIndicatorHints(); void initInputPanelHints(); + void initVirtualKeyboardHints(); void initListViewHints(); void initPageIndicatorHints(); void initPopupHints(); diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 33eddee5..7b277e8c 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -657,6 +657,12 @@ static inline QMarginsF qskEffectivePadding( const QskSkinnable* skinnable, ); } +QMarginsF QskSkinnable::innerPadding( + QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const +{ + return qskEffectivePadding( this, aspect, outerBoxSize, true ); +} + QSizeF QskSkinnable::innerBoxSize( QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const { diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index c58278df..69d43c0f 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -132,6 +132,8 @@ public: QRectF innerBox( QskAspect::Aspect, const QRectF& outerBox ) const; QRectF outerBox( QskAspect::Aspect, const QRectF& innerBox ) const; + QMarginsF innerPadding( QskAspect::Aspect, const QSizeF& ) const; + QRectF subControlRect( QskAspect::Subcontrol ) const; virtual const QskSkinlet* effectiveSkinlet() const; @@ -157,8 +159,8 @@ protected: void setSkinStateFlag( QskAspect::State, bool = true ); virtual void updateNode( QSGNode* ); - QskSkinHintTable &hintTable(); - const QskSkinHintTable &hintTable() const; + QskSkinHintTable& hintTable(); + const QskSkinHintTable& hintTable() const; private: QVariant animatedValue( QskAspect::Aspect, QskSkinHintStatus* ) const; diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 04fc5cea..eec07b84 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -13,6 +13,7 @@ #endif QSK_QT_PRIVATE_BEGIN +// we need to access QQuickTextInputPrivate::hasAcceptableInput #define private public #include #include @@ -89,6 +90,7 @@ namespace { auto d = QQuickTextInputPrivate::get( this ); + // QQuickTextInputPrivate::checkIsValid ??? const auto state = static_cast< int >( d->hasAcceptableInput( d->m_text ) ); bool isAcceptable = ( state == QValidator::Acceptable ); @@ -121,7 +123,7 @@ namespace } virtual QSGNode* updatePaintNode( - QSGNode *oldNode, UpdatePaintNodeData* data ) override + QSGNode* oldNode, UpdatePaintNodeData* data ) override { updateColors(); return Inherited::updatePaintNode( oldNode, data ); @@ -146,7 +148,7 @@ namespace void TextInput::setEditing( bool on ) { auto d = QQuickTextInputPrivate::get( this ); - + if ( d->cursorVisible == on ) return; @@ -157,13 +159,6 @@ namespace { if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() ) d->updatePasswordEchoEditing( false ); - - const auto status = d->hasAcceptableInput( d->m_text ); - if ( status == QQuickTextInputPrivate::AcceptableInput ) - { - if ( d->fixup() ) - Q_EMIT editingFinished(); - } } polish(); @@ -224,6 +219,7 @@ class QskTextInput::PrivateData { public: TextInput* textInput; + QString description; // f.e. used as prompt in QskInputPanel unsigned int activationModes : 3; }; @@ -281,20 +277,20 @@ bool QskTextInput::event( QEvent* event ) void QskTextInput::keyPressEvent( QKeyEvent* event ) { - if ( isEditing() ) + if ( isEditing() ) { switch( event->key() ) { case Qt::Key_Enter: case Qt::Key_Return: { - if ( m_data->textInput->fixup() ) + if ( fixup() ) { QGuiApplication::inputMethod()->commit(); - - if ( !( inputMethodHints() & Qt::ImhMultiLine) ) + + if ( !( inputMethodHints() & Qt::ImhMultiLine ) ) setEditing( false ); - } + } break; } #if 1 @@ -302,6 +298,7 @@ void QskTextInput::keyPressEvent( QKeyEvent* event ) { QGuiApplication::inputMethod()->hide(); setEditing( false ); + break; } #endif @@ -312,7 +309,7 @@ void QskTextInput::keyPressEvent( QKeyEvent* event ) } return; - } + } if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() ) { @@ -385,9 +382,9 @@ void QskTextInput::focusOutEvent( QFocusEvent* event ) #if 1 if ( event->reason() != Qt::ActiveWindowFocusReason && event->reason() != Qt::PopupFocusReason ) - { + { m_data->textInput->deselect(); - } + } #endif if ( m_data->activationModes & ActivationOnFocus ) @@ -444,6 +441,20 @@ void QskTextInput::setText( const QString& text ) m_data->textInput->setText( text ); } +void QskTextInput::setDescription( const QString& text ) +{ + if ( m_data->description != text ) + { + m_data->description = text; + Q_EMIT descriptionChanged( text ); + } +} + +QString QskTextInput::description() const +{ + return m_data->description; +} + QskTextInput::ActivationModes QskTextInput::activationModes() const { return static_cast< QskTextInput::ActivationModes >( m_data->activationModes ); @@ -553,6 +564,15 @@ void QskTextInput::setEditing( bool on ) } else { + auto d = QQuickTextInputPrivate::get( m_data->textInput ); + + const auto status = d->hasAcceptableInput( d->m_text ); + if ( status == QQuickTextInputPrivate::AcceptableInput ) + { + if ( fixup() ) + m_data->textInput->Q_EMIT editingFinished(); + } + #if 0 inputMethod->reset(); #endif @@ -636,36 +656,36 @@ QString QskTextInput::displayText() const QString QskTextInput::preeditText() const { -#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + auto d = QQuickTextInputPrivate::get( m_data->textInput ); + return d->m_textLayout.preeditAreaText(); + return m_data->textInput->preeditText(); -#else - return QString(); -#endif } +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + bool QskTextInput::overwriteMode() const { -#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) return m_data->textInput->overwriteMode(); -#else - return false; -#endif } void QskTextInput::setOverwriteMode( bool overwrite ) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) m_data->textInput->setOverwriteMode( overwrite ); -#else - Q_UNUSED( overwrite ) -#endif } +#endif + bool QskTextInput::hasAcceptableInput() const { return m_data->textInput->hasAcceptableInput(); } +bool QskTextInput::fixup() +{ + return m_data->textInput->fixup(); +} + QVariant QskTextInput::inputMethodQuery( Qt::InputMethodQuery property) const { diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index 25995ae9..36753896 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -16,6 +16,9 @@ class QSK_EXPORT QskTextInput : public QskControl Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged ) + Q_PROPERTY( QString description READ description + WRITE setDescription NOTIFY descriptionChanged ) + Q_PROPERTY( int fontRole READ fontRole WRITE setFontRole NOTIFY fontRoleChanged ) @@ -38,7 +41,7 @@ public: { NoActivation, - ActivationOnFocus = 1 << 0 , + ActivationOnFocus = 1 << 0, ActivationOnMouse = 1 << 1, ActivationOnKey = 1 << 2, @@ -66,6 +69,9 @@ public: QString text() const; + void setDescription( const QString& ); + QString description() const; + void setFontRole( int role ); int fontRole() const; @@ -98,12 +104,16 @@ public: void setEchoMode( EchoMode ); QString displayText() const; + QString preeditText() const; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) bool overwriteMode() const; void setOverwriteMode( bool ); +#endif - bool hasAcceptableInput() const; + virtual bool hasAcceptableInput() const; + virtual bool fixup(); virtual QVariant inputMethodQuery( Qt::InputMethodQuery ) const override; QVariant inputMethodQuery( Qt::InputMethodQuery, QVariant argument) const; @@ -131,11 +141,16 @@ Q_SIGNALS: void textChanged( const QString& ); void textEdited( const QString& ); + void descriptionChanged( const QString& ); + void textOptionsChanged(); void fontRoleChanged(); void alignmentChanged(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) void overwriteModeChanged( bool ); +#endif + void maximumLengthChanged( int ); void echoModeChanged( EchoMode ); diff --git a/src/inputpanel/QskInputPanel.cpp b/src/inputpanel/QskInputPanel.cpp index e5aeef36..1d4a39f8 100644 --- a/src/inputpanel/QskInputPanel.cpp +++ b/src/inputpanel/QskInputPanel.cpp @@ -6,6 +6,8 @@ #include "QskInputPanel.h" #include "QskVirtualKeyboard.h" #include "QskInputSuggestionBar.h" +#include "QskTextInput.h" +#include "QskTextLabel.h" #include "QskLinearBox.h" #include @@ -99,31 +101,68 @@ QString qskNativeLocaleString( const QLocale& locale ) } } +namespace +{ + class TextInput : public QskTextInput + { + public: + TextInput( QQuickItem* parentItem = nullptr ): + QskTextInput( parentItem ) + { + } + }; +} + +QSK_SUBCONTROL( QskInputPanel, Panel ) + class QskInputPanel::PrivateData { public: + PrivateData(): + hasInputProxy( true ) + { + } + QskLinearBox* layout; + QskTextLabel* prompt; + TextInput* textInput; QskInputSuggestionBar* suggestionBar; QskVirtualKeyboard* keyboard; + + bool hasInputProxy : 1; }; QskInputPanel::QskInputPanel( QQuickItem* parent ): - QskControl( parent ), + Inherited( parent ), m_data( new PrivateData() ) { setAutoLayoutChildren( true ); initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained ); - m_data->layout = new QskLinearBox( Qt::Vertical, this ); + m_data->prompt = new QskTextLabel(); + m_data->prompt->setVisible( false ); - m_data->suggestionBar = new QskInputSuggestionBar( m_data->layout ); + m_data->textInput = new TextInput(); + m_data->textInput->setVisible( m_data->hasInputProxy ); + + m_data->suggestionBar = new QskInputSuggestionBar(); m_data->suggestionBar->setVisible( false ); + m_data->keyboard = new QskVirtualKeyboard(); + + auto layout = new QskLinearBox( Qt::Vertical, this ); + + layout->addItem( m_data->prompt, Qt::AlignLeft | Qt::AlignHCenter ); + layout->addItem( m_data->textInput, Qt::AlignLeft | Qt::AlignHCenter ); + layout->addStretch( 10 ); + layout->addItem( m_data->suggestionBar ); + layout->addItem( m_data->keyboard ); + + m_data->layout = layout; + connect( m_data->suggestionBar, &QskInputSuggestionBar::suggested, this, &QskInputPanel::commitCandidate ); - m_data->keyboard = new QskVirtualKeyboard( m_data->layout ); - connect( m_data->keyboard, &QskVirtualKeyboard::keySelected, this, &QskInputPanel::commitKey ); } @@ -132,6 +171,15 @@ QskInputPanel::~QskInputPanel() { } +QskAspect::Subcontrol QskInputPanel::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const +{ + if( subControl == QskBox::Panel ) + return QskInputPanel::Panel; + + return subControl; +} + qreal QskInputPanel::heightForWidth( qreal width ) const { /* @@ -143,14 +191,26 @@ qreal QskInputPanel::heightForWidth( qreal width ) const width -= margins.left() + margins.right(); + const auto padding = innerPadding( + Panel, QSizeF( width, width ) ); + + width -= padding.left() + padding.right(); + qreal height = m_data->keyboard->heightForWidth( width ); - if ( m_data->suggestionBar->isVisible() ) + const QskControl* controls[] = + { m_data->prompt, m_data->textInput, m_data->suggestionBar }; + + for ( auto control : controls ) { - height += m_data->layout->spacing(); - height += m_data->suggestionBar->sizeHint().height(); + if ( control->isVisible() ) + { + height += m_data->layout->spacing(); + height += control->sizeHint().height(); + } } + height += padding.top() + padding.bottom(); height += margins.top() + margins.bottom(); return height; @@ -162,18 +222,72 @@ qreal QskInputPanel::widthForHeight( qreal height ) const height -= margins.top() + margins.bottom(); - if ( m_data->suggestionBar->isVisible() ) + const auto padding = innerPadding( + Panel, QSizeF( height, height ) ); + + height -= padding.top() + padding.bottom(); + + const QskControl* controls[] = + { m_data->prompt, m_data->textInput, m_data->suggestionBar }; + + for ( auto control : controls ) { - height -= m_data->layout->spacing(); - height -= m_data->suggestionBar->sizeHint().height(); + if ( control->isVisible() ) + { + height -= m_data->layout->spacing(); + height -= control->sizeHint().height(); + } } qreal width = m_data->keyboard->widthForHeight( height ); + + width += padding.left() + padding.right(); width += margins.left() + margins.right(); return width; } +QString QskInputPanel::inputPrompt() const +{ + return m_data->prompt->text(); +} + +void QskInputPanel::setInputPrompt( const QString& text ) +{ + auto prompt = m_data->prompt; + + if ( text != prompt->text() ) + { + prompt->setText( text ); + + if ( m_data->hasInputProxy ) + prompt->setVisible( !text.isEmpty() ); + + Q_EMIT inputPromptChanged( text ); + } +} + +bool QskInputPanel::hasInputProxy() const +{ + return m_data->hasInputProxy; +} + +void QskInputPanel::setInputProxy( bool on ) +{ + if ( m_data->hasInputProxy == on ) + return; + + m_data->hasInputProxy = on; + m_data->textInput->setVisible( on ); + + auto prompt = m_data->prompt; + + if ( on ) + prompt->setVisible( !prompt->text().isEmpty() ); + else + prompt->setVisible( false ); +} + bool QskInputPanel::isCandidatesEnabled() const { return m_data->suggestionBar->isVisible(); diff --git a/src/inputpanel/QskInputPanel.h b/src/inputpanel/QskInputPanel.h index 811bf27b..125ac915 100644 --- a/src/inputpanel/QskInputPanel.h +++ b/src/inputpanel/QskInputPanel.h @@ -7,37 +7,58 @@ #define QSK_INPUT_PANEL_H #include "QskGlobal.h" -#include "QskControl.h" +#include "QskBox.h" class QString; class QLocale; template class QVector< QString >; -class QSK_EXPORT QskInputPanel: public QskControl +class QSK_EXPORT QskInputPanel: public QskBox { Q_OBJECT - using Inherited = QskControl; + using Inherited = QskBox; + + Q_PROPERTY( bool inputProxy READ hasInputProxy + WRITE setInputProxy NOTIFY inputProxyChanged ) + + Q_PROPERTY( QString inputPrompt READ inputPrompt + WRITE setInputPrompt NOTIFY inputPromptChanged ) + public: + QSK_SUBCONTROLS( Panel ) + enum Action { Compose = 0x10, - SelectCandidate = 0x11, + SelectCandidate = 0x11 }; Q_ENUM( Action ) QskInputPanel( QQuickItem* parent = nullptr ); virtual ~QskInputPanel() override; + bool hasInputProxy() const; + QString inputPrompt() const; + bool isCandidatesEnabled() const; QVector< QString > candidates() const; virtual qreal heightForWidth( qreal width ) const override; virtual qreal widthForHeight( qreal height ) const override; + virtual QskAspect::Subcontrol effectiveSubcontrol( + QskAspect::Subcontrol ) const override; + +Q_SIGNALS: + void inputProxyChanged( bool ); + void inputPromptChanged( const QString& ); + public Q_SLOTS: + void setInputPrompt( const QString& ); + void setInputProxy( bool ); void setCandidatesEnabled( bool ); void setCandidates( const QVector< QString >& ); diff --git a/src/inputpanel/QskVirtualKeyboard.cpp b/src/inputpanel/QskVirtualKeyboard.cpp index 6ffa3b5b..83b189fc 100644 --- a/src/inputpanel/QskVirtualKeyboard.cpp +++ b/src/inputpanel/QskVirtualKeyboard.cpp @@ -260,20 +260,38 @@ QskAspect::Subcontrol QskVirtualKeyboard::effectiveSubcontrol( return subControl; } - QskVirtualKeyboard::Mode QskVirtualKeyboard::mode() const { return m_data->mode; } +QSizeF QskVirtualKeyboard::contentsSizeHint() const +{ + constexpr qreal ratio = qreal( RowCount ) / ColumnCount; + + const qreal w = 600; + return QSizeF( w, ratio * w ); +} + qreal QskVirtualKeyboard::heightForWidth( qreal width ) const { + /* + Not necessarily correct, when + subControlRect( Panel ) != contentsRect: TODO ... + */ constexpr qreal ratio = qreal( RowCount ) / ColumnCount; const auto margins = this->margins(); width -= margins.left() + margins.right(); + const auto padding = innerPadding( + Panel, QSizeF( width, width ) ); + + width -= padding.left() + padding.right(); + qreal height = width * ratio; + + height += padding.top() + padding.bottom(); height += margins.top() + margins.bottom(); return height; @@ -286,10 +304,17 @@ qreal QskVirtualKeyboard::widthForHeight( qreal height ) const height -= margins.top() + margins.bottom(); - qreal width = height / ratio; - width += margins.left() + margins.right(); + const auto padding = innerPadding( + Panel, QSizeF( height, height ) ); - return height; + height -= padding.top() + padding.bottom(); + + qreal width = height / ratio; + + width += padding.left() + padding.right(); + width += padding.left() + padding.right(); + + return width; } void QskVirtualKeyboard::updateLayout() diff --git a/src/inputpanel/QskVirtualKeyboard.h b/src/inputpanel/QskVirtualKeyboard.h index 8724ff7a..d51ee192 100644 --- a/src/inputpanel/QskVirtualKeyboard.h +++ b/src/inputpanel/QskVirtualKeyboard.h @@ -40,6 +40,7 @@ public: virtual qreal heightForWidth( qreal width ) const override; virtual qreal widthForHeight( qreal height ) const override; + virtual QSizeF contentsSizeHint() const override; Q_SIGNALS: void modeChanged( Mode );