From 4fb981976765c21a7368d77e6cb283a3b1df12aa Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 15 Jan 2025 18:46:30 +0100 Subject: [PATCH] QskAbstractTextInput introduced - trying to bring the implementation of QskTextEdit/QskTextInput together. work in progress --- designsystems/fluent2/QskFluent2Skin.cpp | 133 ++-- designsystems/fusion/QskFusionSkin.cpp | 73 ++- designsystems/material3/QskMaterial3Skin.cpp | 86 ++- src/CMakeLists.txt | 2 + src/controls/QskAbstractTextInput.cpp | 616 +++++++++++++++++++ src/controls/QskAbstractTextInput.h | 194 ++++++ src/controls/QskTextEdit.cpp | 502 +-------------- src/controls/QskTextEdit.h | 144 +---- src/controls/QskTextEditSkinlet.cpp | 2 - src/controls/QskTextEditSkinlet.h | 2 - src/controls/QskTextInput.cpp | 541 +--------------- src/controls/QskTextInput.h | 134 +--- src/controls/QskTextInputSkinlet.cpp | 2 - src/controls/QskTextInputSkinlet.h | 2 - 14 files changed, 1062 insertions(+), 1371 deletions(-) create mode 100644 src/controls/QskAbstractTextInput.cpp create mode 100644 src/controls/QskAbstractTextInput.h diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index 2f49aa24..dcfd97ba 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -299,11 +299,8 @@ namespace void setupTabViewMetrics(); void setupTabViewColors( QskAspect::Section, const QskFluent2Theme& ); - template< typename Q > - void setupTextControlMetrics(); - - template< typename Q, typename SK > - void setupTextControlColors( QskAspect::Section section, const QskFluent2Theme& theme ); + void setupTextInputMetrics(); + void setupTextInputColors( QskAspect::Section, const QskFluent2Theme& ); void setupTextAreaMetrics(); void setupTextAreaColors( QskAspect::Section, const QskFluent2Theme& ); @@ -367,6 +364,7 @@ void Editor::setupMetrics() setupTabButtonMetrics(); setupTabBarMetrics(); setupTabViewMetrics(); + setupTextInputMetrics(); setupTextAreaMetrics(); setupTextFieldMetrics(); setupTextLabelMetrics(); @@ -407,6 +405,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the setupTabButtonColors( section, theme ); setupTabBarColors( section, theme ); setupTabViewColors( section, theme ); + setupTextInputColors( section, theme ); setupTextAreaColors( section, theme ); setupTextFieldColors( section, theme ); setupTextLabelColors( section, theme ); @@ -1794,8 +1793,34 @@ void Editor::setupTextLabelColors( setColor( Q::Text | section, pal.fillColor.text.primary ); } -template< typename Q > void Editor::setupTextControlMetrics() +void Editor::setupTextInputMetrics() { + using Q = QskAbstractTextInput; + setFontRole( Q::Text, Fluent2::Body ); +} + +void Editor::setupTextInputColors( + QskAspect::Section section, const QskFluent2Theme& theme ) +{ + using Q = QskAbstractTextInput; + + const auto& pal = theme.palette; + + const auto text = Q::Text | section; + +#if 1 + setColor( text, pal.fillColor.text.primary ); + setColor( text | Q::Selected, pal.fillColor.textOnAccent.selectedText ); + setColor( text | Q::Disabled, pal.fillColor.text.disabled ); +#endif +} + +void Editor::setupTextAreaMetrics() +{ + using Q = QskTextArea; + + // === + setStrutSize( Q::TextPanel, { -1, 30_px } ); setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } ); @@ -1805,97 +1830,123 @@ template< typename Q > void Editor::setupTextControlMetrics() setBoxShape( Q::TextPanel, 3_px ); - setFontRole( Q::Text, Fluent2::Body ); - - setAlignment( Q::Placeholder, alignment( Q::Text ) ); + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignTop ); setFontRole( Q::Placeholder, fontRole( Q::Text ) ); } -template< typename Q, typename SK > void Editor::setupTextControlColors( - QskAspect::Section section, const QskFluent2Theme& theme) +void Editor::setupTextAreaColors( + QskAspect::Section section, const QskFluent2Theme& theme ) { using A = QskAspect; + using Q = QskTextArea; const auto& pal = theme.palette; - setColor( Q::TextPanel | SK::Selected, pal.fillColor.accent.selectedTextBackground ); - setColor( Q::Text | SK::Selected, pal.fillColor.textOnAccent.selectedText ); + setColor( Q::TextPanel | Q::Selected, pal.fillColor.accent.selectedTextBackground ); setColor( Q::Placeholder, pal.fillColor.text.secondary ); for( const auto state : { A::NoState, Q::Hovered, Q::Focused, Q::Editing, Q::Disabled } ) { - QRgb panelColor, borderColor1, borderColor2, textColor; + QRgb panelColor, borderColor1, borderColor2; if ( state == A::NoState ) { panelColor = pal.fillColor.control.defaultColor; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.elevation.textControl.border[1]; - textColor = pal.fillColor.text.primary; } else if ( state == Q::Hovered ) { panelColor = pal.fillColor.control.secondary; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.elevation.textControl.border[1]; - textColor = pal.fillColor.text.primary; } else if ( ( state == Q::Focused ) || ( state == Q::Editing ) ) { panelColor = pal.fillColor.control.inputActive; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.fillColor.accent.defaultColor; - textColor = pal.fillColor.text.primary; } else if ( state == Q::Disabled ) { panelColor = pal.fillColor.control.disabled; borderColor1 = borderColor2 = pal.strokeColor.control.defaultColor; - textColor = pal.fillColor.text.disabled; } const auto panel = Q::TextPanel | section | state; - const auto text = Q::Text | section | state; panelColor = rgbSolid( panelColor, pal.background.solid.base ); setGradient( panel, panelColor ); setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor ); - - setColor( text, textColor ); } } - -void Editor::setupTextAreaMetrics() -{ - using Q = QskTextArea; - - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop ); - - setupTextControlMetrics< Q >(); -} - -void Editor::setupTextAreaColors( - QskAspect::Section section, const QskFluent2Theme& theme ) -{ - setupTextControlColors< QskTextArea, QskTextAreaSkinlet >( section, theme ); -} - void Editor::setupTextFieldMetrics() { using Q = QskTextField; - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); + // ============= - setupTextControlMetrics< Q >(); + setStrutSize( Q::TextPanel, { -1, 30_px } ); + setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } ); + + setBoxBorderMetrics( Q::TextPanel, 1_px ); + for( const auto& state : { Q::Focused, Q::Editing } ) + setBoxBorderMetrics( Q::TextPanel | state, { 1_px, 1_px, 1_px, 2_px } ); + + setBoxShape( Q::TextPanel, 3_px ); + + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); + setFontRole( Q::Placeholder, fontRole( Q::Text ) ); } void Editor::setupTextFieldColors( QskAspect::Section section, const QskFluent2Theme& theme ) { - setupTextControlColors< QskTextField, QskTextFieldSkinlet >( section, theme ); -} + using A = QskAspect; + using Q = QskTextField; + const auto& pal = theme.palette; + + setColor( Q::TextPanel | Q::Selected, pal.fillColor.accent.selectedTextBackground ); + setColor( Q::Placeholder, pal.fillColor.text.secondary ); + + for( const auto state : { A::NoState, Q::Hovered, Q::Focused, Q::Editing, Q::Disabled } ) + { + QRgb panelColor, borderColor1, borderColor2; + + if ( state == A::NoState ) + { + panelColor = pal.fillColor.control.defaultColor; + borderColor1 = pal.elevation.textControl.border[0]; + borderColor2 = pal.elevation.textControl.border[1]; + } + else if ( state == Q::Hovered ) + { + panelColor = pal.fillColor.control.secondary; + borderColor1 = pal.elevation.textControl.border[0]; + borderColor2 = pal.elevation.textControl.border[1]; + } + else if ( ( state == Q::Focused ) || ( state == Q::Editing ) ) + { + panelColor = pal.fillColor.control.inputActive; + borderColor1 = pal.elevation.textControl.border[0]; + borderColor2 = pal.fillColor.accent.defaultColor; + } + else if ( state == Q::Disabled ) + { + panelColor = pal.fillColor.control.disabled; + borderColor1 = borderColor2 = pal.strokeColor.control.defaultColor; + } + + const auto panel = Q::TextPanel | section | state; + + panelColor = rgbSolid( panelColor, pal.background.solid.base ); + + setGradient( panel, panelColor ); + setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor ); + } +} void Editor::setupSwitchButtonMetrics() { using Q = QskSwitchButton; diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index d034cd86..7708f091 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -43,9 +43,7 @@ #include #include #include -#include #include -#include #include #include @@ -145,8 +143,7 @@ namespace Q_INVOKABLE void setupTabBar(); Q_INVOKABLE void setupTabView(); - template< typename Q, typename SK > - void setupTextControl(); + Q_INVOKABLE void setupTextInput(); Q_INVOKABLE void setupTextArea(); Q_INVOKABLE void setupTextField(); Q_INVOKABLE void setupTextLabel(); @@ -389,9 +386,29 @@ void Editor::setupTextLabel() setBoxBorderColors( Q::Panel, QskRgb::lighter( m_pal.outline, 108 ) ); } -template< typename Q, typename SK > -void Editor::setupTextControl() +void Editor::setupTextInput() { + using Q = QskAbstractTextInput; + using A = QskAspect; + using P = QPalette; + + for ( auto state : { A::NoState, Q::Disabled } ) + { + const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled; + + setColor( Q::Text | state, m_pal.color( colorGroup, P::Text ) ); + setColor( Q::Text | Q::Selected | state, m_pal.color( colorGroup, P::HighlightedText ) ); + } +} + +void Editor::setupTextArea() +{ + using Q = QskTextArea; + + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignTop ); + + // ======== + using A = QskAspect; using P = QPalette; @@ -400,10 +417,8 @@ void Editor::setupTextControl() const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled; setColor( Q::TextPanel | state, m_pal.color( colorGroup, P::Base ) ); - setColor( Q::TextPanel | SK::Selected | state, m_pal.color( colorGroup, P::Highlight ) ); + setColor( Q::TextPanel | Q::Selected | state, m_pal.color( colorGroup, P::Highlight ) ); - setColor( Q::Text | state, m_pal.color( colorGroup, P::Text ) ); - setColor( Q::Text | SK::Selected | state, m_pal.color( colorGroup, P::HighlightedText ) ); setColor( Q::Placeholder, m_pal.color( colorGroup, P::PlaceholderText ) ); } @@ -420,26 +435,38 @@ void Editor::setupTextControl() setPadding( Q::TextPanel, 4_px ); } -void Editor::setupTextArea() -{ - using Q = QskTextArea; - using SK = QskTextAreaSkinlet; - - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop ); - setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignTop ); - - setupTextControl< Q, SK >(); -} - void Editor::setupTextField() { using Q = QskTextField; - using SK = QskTextFieldSkinlet; - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); - setupTextControl< Q, SK >(); + // === + + using A = QskAspect; + using P = QPalette; + + for ( auto state : { A::NoState, Q::Disabled } ) + { + const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled; + + setColor( Q::TextPanel | state, m_pal.color( colorGroup, P::Base ) ); + setColor( Q::TextPanel | Q::Selected | state, m_pal.color( colorGroup, P::Highlight ) ); + + setColor( Q::Placeholder, m_pal.color( colorGroup, P::PlaceholderText ) ); + } + + setColor( Q::TextPanel | Q::ReadOnly, m_pal.disabled( P::Base ) ); + + setBoxBorderMetrics( Q::TextPanel, 1_px ); + + setBoxBorderColors( Q::TextPanel, m_pal.outline ); +#ifdef SHOW_FOCUS + setBoxBorderColors( Q::TextPanel | Q::Focused, m_pal.highlightedOutline ); +#endif + + setBoxShape( Q::TextPanel, 2_px ); + setPadding( Q::TextPanel, 4_px ); } void Editor::setupProgressBar() diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 63ba72b2..73cc05d1 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -12,7 +12,6 @@ #include "QskMaterial3ProgressBarSkinlet.h" #include "QskMaterial3SliderSkinlet.h" #include "QskMaterial3TextFieldSkinlet.h" -#include #include @@ -188,8 +187,7 @@ namespace Q_INVOKABLE void setupTabBar(); Q_INVOKABLE void setupTabView(); - template< typename Q, typename SK > - void setupTextControl(); + Q_INVOKABLE void setupTextInput(); Q_INVOKABLE void setupTextArea(); Q_INVOKABLE void setupTextField(); Q_INVOKABLE void setupTextLabel(); @@ -437,13 +435,64 @@ void Editor::setupTextLabel() setPadding( Q::Panel, 5_px ); } -template< typename Q, typename SK > -void Editor::setupTextControl() +void Editor::setupTextInput() { + using Q = QskAbstractTextInput; + + setColor( Q::Text, m_pal.onSurface ); + setFontRole( Q::Text, BodyLarge ); + setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); +} + +void Editor::setupTextArea() +{ + using Q = QskTextArea; + + // ========== + setStrutSize( Q::Panel, -1.0, 56_px ); setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } ); setGradient( Q::Panel, m_pal.surfaceVariant ); - setColor( Q::TextPanel | SK::Selected, m_pal.primary12 ); + setColor( Q::TextPanel | Q::Selected, m_pal.primary12 ); + setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); + setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_px } ); + setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant ); + setSpacing( Q::Panel, 8_px ); + + const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant, + m_pal.surfaceVariant, m_pal.hoverOpacity ); + setGradient( Q::Panel | Q::Hovered, hoverColor ); + + const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, + m_pal.surfaceVariant, m_pal.focusOpacity ); + setGradient( Q::Panel | Q::Focused, focusColor ); + + // ### Also add a pressed state + + + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); + + const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); + setGradient( Q::Panel | Q::Disabled, disabledPanelColor ); + setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); + + // PlaceholderText + + setColor( Q::Placeholder, color( Q::Text ) ); + setFontRole( Q::Placeholder, BodyLarge ); + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignTop ); +} + +void Editor::setupTextField() +{ + using Q = QskTextField; + + // === + + setStrutSize( Q::Panel, -1.0, 56_px ); + setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } ); + setGradient( Q::Panel, m_pal.surfaceVariant ); + setColor( Q::TextPanel | Q::Selected, m_pal.primary12 ); setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_px } ); setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant ); @@ -459,7 +508,6 @@ void Editor::setupTextControl() // ### Also add a pressed state - setColor( Q::Text, m_pal.onSurface ); setFontRole( Q::Text, BodyLarge ); setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); @@ -468,33 +516,11 @@ void Editor::setupTextControl() setGradient( Q::Panel | Q::Disabled, disabledPanelColor ); setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); - setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); - // PlaceholderText setColor( Q::Placeholder, color( Q::Text ) ); setFontRole( Q::Placeholder, BodyLarge ); - setAlignment( Q::Placeholder, alignment( Q::Text ) ); -} - -void Editor::setupTextArea() -{ - using Q = QskTextArea; - using SK = QskTextEditSkinlet; - - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop ); - - setupTextControl< Q, SK >(); -} - -void Editor::setupTextField() -{ - using Q = QskTextField; - using SK = QskTextInputSkinlet; - - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - - setupTextControl< Q, SK >(); + setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); } void Editor::setupProgressBar() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b694ae5..94684cd6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,6 +197,7 @@ endif() list(APPEND HEADERS controls/QskAbstractButton.h + controls/QskAbstractTextInput.h controls/QskAnimationHint.h controls/QskAnimator.h controls/QskMainView.h @@ -306,6 +307,7 @@ list(APPEND PRIVATE_HEADERS list(APPEND SOURCES controls/QskAbstractButton.cpp + controls/QskAbstractTextInput.cpp controls/QskAnimator.cpp controls/QskAnimationHint.cpp controls/QskMainView.cpp diff --git a/src/controls/QskAbstractTextInput.cpp b/src/controls/QskAbstractTextInput.cpp new file mode 100644 index 00000000..d52d6103 --- /dev/null +++ b/src/controls/QskAbstractTextInput.cpp @@ -0,0 +1,616 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskAbstractTextInput.h" +#include "QskFontRole.h" +#include "QskQuick.h" +#include "QskEvent.h" +#include "QskInternalMacros.h" + +QSK_QT_PRIVATE_BEGIN +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) +#include +#endif + +QSK_QT_PRIVATE_END + +QSK_SUBCONTROL( QskAbstractTextInput, Text ) + +QSK_SYSTEM_STATE( QskAbstractTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) +QSK_SYSTEM_STATE( QskAbstractTextInput, Editing, QskAspect::FirstSystemState << 2 ) +QSK_SYSTEM_STATE( QskAbstractTextInput, Selected, QskAspect::FirstSystemState << 3 ) + +static inline void qskUpdateInputMethodFont( const QskAbstractTextInput* input ) +{ + const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle; + qskUpdateInputMethod( input, queries ); +} + +static inline QVariant qskInputMethodQuery( + const QQuickItem* item, Qt::InputMethodQuery query, QVariant argument ) +{ + if ( auto input = qobject_cast< const QQuickTextInput* >( item ) ) + return input->inputMethodQuery( query, argument ); + + if ( auto edit = qobject_cast< const QQuickTextEdit* >( item ) ) + return edit->inputMethodQuery( query, argument ); + + return QVariant(); +} + +static inline Qt::InputMethodHints qskInputMethodHints( const QQuickItem* item ) +{ + if ( auto input = qobject_cast< const QQuickTextInput* >( item ) ) + return input->inputMethodHints(); + + if ( auto edit = qobject_cast< const QQuickTextEdit* >( item ) ) + return edit->inputMethodHints(); + + return Qt::InputMethodHints(); +} + +static inline void qskSetInputMethodHints( + QQuickItem* item, Qt::InputMethodHints hints ) +{ + if ( auto input = qobject_cast< QQuickTextInput* >( item ) ) + input->setInputMethodHints( hints ); + else if ( auto edit = qobject_cast< QQuickTextEdit* >( item ) ) + edit->setInputMethodHints( hints ); +} + +inline void qskSetAlignment( QQuickItem* item, Qt::Alignment alignment ) +{ + item->setProperty( "horizontalAlignment", int( alignment ) & 0x0f ); + item->setProperty( "verticalAlignment", int( alignment ) & 0xf0 ); +} + +static inline void qskTranslateMouseEventPosition( + QMouseEvent* mouseEvent, const QPointF& offset ) +{ +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + auto& point = mouseEvent->point(0); + + QMutableEventPoint::setPosition( + point, point.position() + offset ); +#else + mouseEvent->setLocalPos( mouseEvent->localPos() + offset ); +#endif +} + +static inline void qskForwardEvent( QQuickItem* item, QEvent* event ) +{ + switch( static_cast< int >( event->type() ) ) + { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + { + const auto pos = item->position(); + + auto mouseEvent = static_cast< QMouseEvent* >( event ); + + /* + As the event was sent for the parent item + we have to translate the position into + our coordinate system. + */ + qskTranslateMouseEventPosition( mouseEvent, -pos ); + + QMetaObject::invokeMethod( item, "handleEvent", + Qt::DirectConnection, Q_ARG( QEvent*, event ) ); + + qskTranslateMouseEventPosition( mouseEvent, pos ); + + break; + } + default: + QMetaObject::invokeMethod( item, "handleEvent", + Qt::DirectConnection, Q_ARG( QEvent*, event ) ); + } +} + +class QskAbstractTextInput::PrivateData +{ + public: + ActivationModes activationModes; +}; + +QskAbstractTextInput::QskAbstractTextInput( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData() ) +{ + m_data->activationModes = ActivationOnMouse | ActivationOnKey; + + setPolishOnResize( true ); + + setAcceptHoverEvents( true ); + setFocusPolicy( Qt::StrongFocus ); + + setFlag( QQuickItem::ItemAcceptsInputMethod ); +} + +QskAbstractTextInput::~QskAbstractTextInput() +{ +} + +QskAbstractTextInput::ActivationModes QskAbstractTextInput::activationModes() const +{ + return m_data->activationModes; +} + +void QskAbstractTextInput::setActivationModes( ActivationModes modes ) +{ + if ( m_data->activationModes != modes ) + { + m_data->activationModes = modes; + Q_EMIT activationModesChanged(); + } +} + +bool QskAbstractTextInput::selectByMouse() const +{ + return wrappedInput()->property( "selectByMouse" ).value< bool >(); +} + +void QskAbstractTextInput::setSelectByMouse( bool on ) +{ + wrappedInput()->setProperty( "selectByMouse", on ); +} + +QString QskAbstractTextInput::text() const +{ + return wrappedInput()->property( "text" ).value< QString >(); +} + +void QskAbstractTextInput::setText( const QString& text ) +{ + wrappedInput()->setProperty( "text", text ); +} + +QString QskAbstractTextInput::preeditText() const +{ + return wrappedInput()->property( "preeditText" ).value< QString >(); +} + +void QskAbstractTextInput::clear() +{ + QMetaObject::invokeMethod( wrappedInput(), "clear" ); +} + +void QskAbstractTextInput::selectAll() +{ + QMetaObject::invokeMethod( wrappedInput(), "selectAll" ); +} + +void QskAbstractTextInput::deselect() +{ + QMetaObject::invokeMethod( wrappedInput(), "deselect" ); +} + +bool QskAbstractTextInput::canUndo() const +{ + return wrappedInput()->property( "canUndo" ).value< bool >(); +} + +bool QskAbstractTextInput::canRedo() const +{ + return wrappedInput()->property( "canRedo" ).value< bool >(); +} + +void QskAbstractTextInput::setFontRole( const QskFontRole& role ) +{ + if ( setFontRoleHint( Text, role ) ) + { + qskUpdateInputMethodFont( this ); + Q_EMIT fontRoleChanged(); + } +} + +void QskAbstractTextInput::resetFontRole() +{ + if ( resetFontRoleHint( Text ) ) + { + qskUpdateInputMethodFont( this ); + Q_EMIT fontRoleChanged(); + } +} + +QskFontRole QskAbstractTextInput::fontRole() const +{ + return fontRoleHint( Text ); +} + +QFont QskAbstractTextInput::font() const +{ + return effectiveFont( Text ); +} + +QVariant QskAbstractTextInput::inputMethodQuery( + Qt::InputMethodQuery property ) const +{ + return inputMethodQuery( property, QVariant() ); +} + +QVariant QskAbstractTextInput::inputMethodQuery( + Qt::InputMethodQuery query, const QVariant& argument ) const +{ + switch ( query ) + { + case Qt::ImEnabled: + { + return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) ); + } + case Qt::ImFont: + { + return font(); + } + case Qt::ImPreferredLanguage: + { + return locale(); + } + case Qt::ImInputItemClipRectangle: + case Qt::ImCursorRectangle: + { + QVariant v = qskInputMethodQuery( wrappedInput(), query, argument ); +#if 1 + if ( v.canConvert< QRectF >() ) + v.setValue( v.toRectF().translated( wrappedInput()->position() ) ); +#endif + return v; + } + default: + { + return qskInputMethodQuery( wrappedInput(), query, argument ); + } + } +} + +Qt::InputMethodHints QskAbstractTextInput::inputMethodHints() const +{ + return qskInputMethodHints( wrappedInput() ); +} + +void QskAbstractTextInput::setInputMethodHints( Qt::InputMethodHints hints ) +{ + if ( qskInputMethodHints( wrappedInput() ) != hints ) + { + qskSetInputMethodHints( wrappedInput(), hints ); + qskUpdateInputMethod( this, Qt::ImHints ); + } +} + +bool QskAbstractTextInput::event( QEvent* event ) +{ + if ( event->type() == QEvent::LocaleChange ) + qskUpdateInputMethod( this, Qt::ImPreferredLanguage ); + + if ( event->type() == QEvent::ShortcutOverride ) + { + forwardEvent( event ); + return event->isAccepted(); + } + + return Inherited::event( event ); +} + +void QskAbstractTextInput::mousePressEvent( QMouseEvent* event ) +{ + forwardEvent( event ); + + if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() ) + setEditing( true ); +} + +void QskAbstractTextInput::mouseMoveEvent( QMouseEvent* event ) +{ + forwardEvent( event ); +} + +void QskAbstractTextInput::mouseReleaseEvent( QMouseEvent* event ) +{ + forwardEvent( event ); + + if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() ) + setEditing( true ); +} + +void QskAbstractTextInput::mouseDoubleClickEvent( QMouseEvent* event ) +{ + forwardEvent( event ); +} + +void QskAbstractTextInput::keyPressEvent( QKeyEvent* event ) +{ + if ( isEditing() ) + { + switch ( event->key() ) + { + case Qt::Key_Escape: + { + setEditing( false ); + break; + } + + case Qt::Key_Enter: + case Qt::Key_Return: + { + const auto hints = inputMethodQuery( Qt::ImHints ).toInt(); + if ( !( hints & Qt::ImhMultiLine ) ) + { + if ( acceptInput() ) + { + QGuiApplication::inputMethod()->commit(); + setEditing( false ); + } + } + + break; + } + } + + if ( isEditing() ) + { + forwardEvent( event ); + } + else + { + // When returning from a virtual keyboard + qskForceActiveFocus( this, Qt::PopupFocusReason ); + } + } + else + { + if ( ( activationModes() & ActivationOnKey ) && !event->isAutoRepeat() ) + { + if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space ) + { + setEditing( true ); + return; + } + } + + Inherited::keyPressEvent( event ); + } +} + +void QskAbstractTextInput::keyReleaseEvent( QKeyEvent* event ) +{ + Inherited::keyReleaseEvent( event ); +} + +void QskAbstractTextInput::focusInEvent( QFocusEvent* event ) +{ + if ( activationModes() & ActivationOnFocus ) + { + switch ( event->reason() ) + { + case Qt::ActiveWindowFocusReason: + case Qt::PopupFocusReason: + break; + + default: +#if 1 + // auto selecting the complete text ??? +#endif + setEditing( true ); + } + } + + Inherited::focusInEvent( event ); +} + +void QskAbstractTextInput::focusOutEvent( QFocusEvent* event ) +{ + switch ( event->reason() ) + { + case Qt::ActiveWindowFocusReason: + case Qt::PopupFocusReason: + { + break; + } + default: + { + deselect(); + setEditing( false ); + } + } + + Inherited::focusOutEvent( event ); +} + +void QskAbstractTextInput::inputMethodEvent( QInputMethodEvent* event ) +{ + const bool hadCursor = isCursorVisible(); + + forwardEvent( event ); + + if ( isCursorVisible() && !hadCursor ) + { + /* + The initial InputMethod events might be sent from the + platform depending on focus. Unfortunately an + empty dummy event ( = no attributes ) leads to showing + the cursor. + */ + if ( isEditing() ) + setCursorVisible( false ); + } +} + +bool QskAbstractTextInput::isReadOnly() const +{ + return wrappedInput()->property( "readOnly" ).value< bool >(); +} + +void QskAbstractTextInput::setReadOnly( bool on ) +{ + if ( on == isReadOnly() ) + return; + +#if 1 + // do we want to be able to restore the previous policy ? + setFocusPolicy( Qt::NoFocus ); +#endif + + auto input = wrappedInput(); + input->setProperty( "readOnly", on ); + + // we are killing user settings here ? + input->setFlag( QQuickItem::ItemAcceptsInputMethod, !on ); + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) + qskUpdateInputMethod( this, Qt::ImReadOnly ); +#else + qskUpdateInputMethod( this, Qt::ImEnabled ); +#endif + + setSkinStateFlag( ReadOnly, on ); +} + +bool QskAbstractTextInput::isEditing() const +{ + return hasSkinState( Editing ); +} + +void QskAbstractTextInput::setEditing( bool on ) +{ + if ( isReadOnly() || on == isEditing() ) + return; + + setSkinStateFlag( Editing, on ); + + auto input = wrappedInput(); + + if ( input->property( "cursorVisible" ).value< bool >() != on ) + { + input->setProperty( "cursorVisible", on ); + + if ( auto textInput = qobject_cast< QQuickTextInput* >( input ) ) + { + auto d = QQuickTextInputPrivate::get( textInput ); + d->setBlinkingCursorEnabled( on ); + + if ( !on ) + { + if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() ) + d->updatePasswordEchoEditing( false ); + } + } + + input->polish(); + input->update(); + } + + if ( !on && acceptInput() ) + QMetaObject::invokeMethod( input, "editingFinished" ); + + qskInputMethodSetVisible( this, on ); + Q_EMIT editingChanged( on ); +} + +bool QskAbstractTextInput::acceptInput() +{ + return true; +} + +bool QskAbstractTextInput::overwriteMode() const +{ + return wrappedInput()->property( "overwriteMode" ).value< bool >(); +} + +void QskAbstractTextInput::setOverwriteMode( bool overwrite ) +{ + wrappedInput()->setProperty( "overwriteMode", overwrite ); +} + +int QskAbstractTextInput::cursorPosition() const +{ + return wrappedInput()->property( "cursorPosition" ).value< int >(); +} + +void QskAbstractTextInput::setCursorPosition( int pos ) +{ + wrappedInput()->setProperty( "cursorPosition", pos ); +} + +bool QskAbstractTextInput::isCursorVisible() const +{ + return wrappedInput()->property( "cursorVisible" ).value< bool >(); +} + +void QskAbstractTextInput::setCursorVisible( bool on ) +{ + wrappedInput()->setProperty( "cursorVisible", on ); +} + +void QskAbstractTextInput::setWrapMode( QskTextOptions::WrapMode wrapMode ) +{ + wrappedInput()->setProperty( "wrapMode", static_cast< int >( wrapMode ) ); +} + +QskTextOptions::WrapMode QskAbstractTextInput::wrapMode() const +{ + const auto mode = wrappedInput()->property( "wrapMode" ).value< int >(); + return static_cast< QskTextOptions::WrapMode >( mode ); +} + +void QskAbstractTextInput::setAlignment( Qt::Alignment alignment ) +{ + if ( setAlignmentHint( Text, alignment ) ) + { + qskSetAlignment( wrappedInput(), alignment ); + Q_EMIT alignmentChanged(); + } +} + +void QskAbstractTextInput::resetAlignment() +{ + if ( resetAlignmentHint( Text ) ) + { + qskSetAlignment( wrappedInput(), alignment() ); + Q_EMIT alignmentChanged(); + } +} + +Qt::Alignment QskAbstractTextInput::alignment() const +{ + Qt::Alignment alignment = Qt::AlignLeft; + if ( qobject_cast< const QQuickTextEdit* >( wrappedInput() ) ) + alignment |= Qt::AlignTop; + else + alignment |= Qt::AlignVCenter; + + return alignmentHint( Text, alignment ); +} + +void QskAbstractTextInput::updateLayout() +{ + QMetaObject::invokeMethod( wrappedInput(), "updateMetrics" ); + qskSetItemGeometry( wrappedInput(), subControlRect( Text ) ); +} + +void QskAbstractTextInput::updateNode( QSGNode* node ) +{ + QMetaObject::invokeMethod( wrappedInput(), "updateColors" ); + Inherited::updateNode( node ); +} + +const QQuickItem* QskAbstractTextInput::wrappedInput() const +{ + auto that = const_cast< QskAbstractTextInput* >( this ); + return that->wrappedInput(); +} + +void QskAbstractTextInput::forwardEvent( QEvent* event ) +{ + qskForwardEvent( wrappedInput(), event ); +} + +#include "moc_QskAbstractTextInput.cpp" diff --git a/src/controls/QskAbstractTextInput.h b/src/controls/QskAbstractTextInput.h new file mode 100644 index 00000000..3094d0d4 --- /dev/null +++ b/src/controls/QskAbstractTextInput.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_ABSTRACT_TEXT_INPUT_H +#define QSK_ABSTRACT_TEXT_INPUT_H + +#include "QskControl.h" +#include "QskTextOptions.h" + +class QskFontRole; + +class QSK_EXPORT QskAbstractTextInput : public QskControl +{ + Q_OBJECT + + Q_PROPERTY( QString text READ text + WRITE setText NOTIFY textChanged USER true ) + + Q_PROPERTY( QString preeditText READ preeditText + NOTIFY preeditTextChanged ) + + Q_PROPERTY( bool editing READ isEditing + WRITE setEditing NOTIFY editingChanged ) + + Q_PROPERTY( bool readOnly READ isReadOnly + WRITE setReadOnly NOTIFY readOnlyChanged ) + + Q_PROPERTY( ActivationModes activationModes READ activationModes + WRITE setActivationModes NOTIFY activationModesChanged ) + + Q_PROPERTY( QskFontRole fontRole READ fontRole + WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) + + Q_PROPERTY( QFont font READ font ) + + Q_PROPERTY( bool overwriteMode READ overwriteMode + WRITE setOverwriteMode NOTIFY overwriteModeChanged ) + + Q_PROPERTY( bool cursorVisible READ isCursorVisible + WRITE setCursorVisible NOTIFY cursorVisibleChanged ) + + Q_PROPERTY( int cursorPosition READ cursorPosition + WRITE setCursorPosition NOTIFY cursorPositionChanged ) + + Q_PROPERTY( Qt::Alignment alignment READ alignment + WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged ) + + Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode + WRITE setWrapMode NOTIFY wrapModeChanged ) + + Q_PROPERTY( bool selectByMouse READ selectByMouse + WRITE setSelectByMouse NOTIFY selectByMouseChanged ) + + Q_PROPERTY( bool canUndo READ canUndo NOTIFY canUndoChanged ) + Q_PROPERTY( bool canRedo READ canRedo NOTIFY canRedoChanged ) + + using Inherited = QskControl; + + public: + QSK_SUBCONTROLS( Text ) + QSK_STATES( ReadOnly, Editing, Selected ) + + enum ActivationMode + { + NoActivation, + + ActivationOnFocus = 1 << 0, + ActivationOnMouse = 1 << 1, + ActivationOnKey = 1 << 2, + + ActivationOnInput = ActivationOnMouse | ActivationOnKey, + ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey + }; + + Q_ENUM( ActivationMode ) + Q_DECLARE_FLAGS( ActivationModes, ActivationMode ) + + ~QskAbstractTextInput() override; + + QString text() const; + QString preeditText() const; + + bool isReadOnly() const; + void setReadOnly( bool ); + + bool isEditing() const; + + void setActivationModes( ActivationModes ); + ActivationModes activationModes() const; + + void setSelectByMouse( bool ); + bool selectByMouse() const; + + void setAlignment( Qt::Alignment ); + void resetAlignment(); + Qt::Alignment alignment() const; + + void setWrapMode( QskTextOptions::WrapMode ); + QskTextOptions::WrapMode wrapMode() const; + + void setFontRole( const QskFontRole& role ); + void resetFontRole(); + QskFontRole fontRole() const; + + QFont font() const; + + bool overwriteMode() const; + void setOverwriteMode( bool ); + + bool isCursorVisible() const; + void setCursorVisible( bool ); + + int cursorPosition() const; + void setCursorPosition( int ); + + QVariant inputMethodQuery( Qt::InputMethodQuery ) const override; + QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& ) const; + + Qt::InputMethodHints inputMethodHints() const; + void setInputMethodHints( Qt::InputMethodHints ); + + bool canUndo() const; + bool canRedo() const; + + public Q_SLOTS: + void setText( const QString& ); + void setEditing( bool ); + + void clear(); + void deselect(); + void selectAll(); + + Q_SIGNALS: + void editingChanged( bool ); + void readOnlyChanged( bool ); + void activationModesChanged(); + void fontRoleChanged(); + void overwriteModeChanged( bool ); + void cursorPositionChanged( int ); + void cursorVisibleChanged( bool ); + void selectByMouseChanged( bool ); + + void wrapModeChanged( QskTextOptions::WrapMode ); + void alignmentChanged(); + + void textChanged( const QString& ); + void textEdited( const QString& ); + void displayTextChanged( const QString& ); + void preeditTextChanged( const QString& ); + +#if 1 + void canUndoChanged( bool ); + void canRedoChanged( bool ); +#endif + + protected: + QskAbstractTextInput( QQuickItem* parent = nullptr ); + + virtual QQuickItem* wrappedInput() = 0; + const QQuickItem* wrappedInput() const; + + void forwardEvent( QEvent* ); + + bool event( QEvent* ) override; + + void mousePressEvent( QMouseEvent* ) override; + void mouseMoveEvent( QMouseEvent* ) override; + void mouseReleaseEvent( QMouseEvent* ) override; + void mouseDoubleClickEvent( QMouseEvent* ) override; + + void keyPressEvent( QKeyEvent* ) override; + void keyReleaseEvent( QKeyEvent* ) override; + + void focusInEvent( QFocusEvent* ) override; + void focusOutEvent( QFocusEvent* ) override; + + void inputMethodEvent( QInputMethodEvent* ) override; + + virtual bool acceptInput(); + + void updateLayout() override; + void updateNode( QSGNode* ) override; + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QskAbstractTextInput::ActivationModes ) +Q_DECLARE_METATYPE( QskAbstractTextInput::ActivationModes ) + +#endif diff --git a/src/controls/QskTextEdit.cpp b/src/controls/QskTextEdit.cpp index 8390a360..808002d4 100644 --- a/src/controls/QskTextEdit.cpp +++ b/src/controls/QskTextEdit.cpp @@ -12,19 +12,9 @@ QSK_QT_PRIVATE_BEGIN #include #include - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) -#include -#endif - QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextEdit, TextPanel ) -QSK_SUBCONTROL( QskTextEdit, Text ) - -QSK_SYSTEM_STATE( QskTextEdit, ReadOnly, QskAspect::FirstSystemState << 1 ) -QSK_SYSTEM_STATE( QskTextEdit, Editing, QskAspect::FirstSystemState << 2 ) -QSK_SYSTEM_STATE( QskTextEdit, Error, QskAspect::FirstSystemState << 4 ) static inline void qskPropagateReadOnly( QskTextEdit* edit ) { @@ -34,19 +24,6 @@ static inline void qskPropagateReadOnly( QskTextEdit* edit ) QCoreApplication::sendEvent( edit, &event ); } -static inline void qskTranslateMouseEventPosition( - QMouseEvent* mouseEvent, const QPointF& offset ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - auto& point = mouseEvent->point( 0 ); - - QMutableEventPoint::setPosition( - point, point.position() + offset ); -#else - mouseEvent->setLocalPos( mouseEvent->localPos() + offset ); -#endif -} - static inline void qskBindSignals( const QQuickTextEdit* wrappedEdit, QskTextEdit* edit ) { @@ -62,6 +39,9 @@ static inline void qskBindSignals( QObject::connect( wrappedEdit, &QQuickTextEdit::overwriteModeChanged, edit, &QskTextEdit::overwriteModeChanged ); + QObject::connect( wrappedEdit, &QQuickTextEdit::cursorPositionChanged, + edit, [ edit ] { Q_EMIT edit->cursorPositionChanged( edit->cursorPosition() ); } ); + QObject::connect( wrappedEdit, &QQuickTextEdit::wrapModeChanged, edit, [ edit ] { Q_EMIT edit->wrapModeChanged( edit->wrapMode() ); } ); @@ -92,57 +72,22 @@ namespace { class QuickTextEdit final : public QQuickTextEdit { + Q_OBJECT + using Inherited = QQuickTextEdit; public: QuickTextEdit( QskTextEdit* ); - void setEditing( bool on ); - inline void setAlignment( Qt::Alignment alignment ) { setHAlign( ( HAlignment ) ( int( alignment ) & 0x0f ) ); setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) ); } - void updateColors(); - void updateMetrics(); - - inline bool handleEvent( QEvent* event ) - { - bool ok; - - switch( static_cast< int >( event->type() ) ) - { - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - { - auto mouseEvent = static_cast< QMouseEvent* >( event ); - - /* - As the event was sent for the parent item - we have to translate the position into - our coordinate system. - */ - qskTranslateMouseEventPosition( mouseEvent, -position() ); - ok = this->event( mouseEvent ); - qskTranslateMouseEventPosition( mouseEvent, position() ); - - break; - } - default: - ok = this->event( event ); - } - - return ok; - } - - inline bool hasSelectedText() const - { - return !selectedText().isEmpty(); - } + Q_INVOKABLE void updateColors(); + Q_INVOKABLE void updateMetrics(); + Q_INVOKABLE void handleEvent( QEvent* ev ) { event( ev ); } protected: @@ -192,19 +137,6 @@ namespace this, &QuickTextEdit::updateClip ); } - void QuickTextEdit::setEditing( bool on ) - { - auto d = QQuickTextEditPrivate::get( this ); - - if ( d->cursorVisible == on ) - return; - - setCursorVisible( on ); - - polish(); - update(); - } - void QuickTextEdit::updateMetrics() { auto textEdit = static_cast< const QskTextEdit* >( parentItem() ); @@ -221,7 +153,7 @@ namespace setColor( input->color( Q::Text ) ); - const auto state = QskTextEditSkinlet::Selected; + const auto state = QskTextEdit::Selected; setSelectionColor( input->color( Q::TextPanel | state ) ); setSelectedTextColor( input->color( Q::Text | state ) ); @@ -232,23 +164,12 @@ class QskTextEdit::PrivateData { public: QuickTextEdit* wrappedEdit; - - ActivationModes activationModes; }; QskTextEdit::QskTextEdit( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - m_data->activationModes = ActivationOnMouse | ActivationOnKey; - - setPolishOnResize( true ); - - setAcceptHoverEvents( true ); - setFocusPolicy( Qt::StrongFocus ); - - setFlag( QQuickItem::ItemAcceptsInputMethod ); - /* QQuickTextEdit is a beast of almost 3.5k lines of code, we don't want to reimplement that - at least not now. @@ -269,341 +190,6 @@ QskTextEdit::~QskTextEdit() { } -bool QskTextEdit::event( QEvent* event ) -{ - if ( event->type() == QEvent::ShortcutOverride ) - { - return m_data->wrappedEdit->handleEvent( event ); - } - else if ( event->type() == QEvent::LocaleChange ) - { - qskUpdateInputMethod( this, Qt::ImPreferredLanguage ); - } - - return Inherited::event( event ); -} - -void QskTextEdit::keyPressEvent( QKeyEvent* event ) -{ - if ( isEditing() ) - { - switch ( event->key() ) - { -#if 1 - case Qt::Key_Escape: - { - setEditing( false ); - qskForceActiveFocus( this, Qt::PopupFocusReason ); - break; - } -#endif - default: - { - m_data->wrappedEdit->handleEvent( event ); - } - } - - return; - } - - if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() ) - { - if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space ) - { - setEditing( true ); - return; - } - } - - Inherited::keyPressEvent( event ); -} - -void QskTextEdit::keyReleaseEvent( QKeyEvent* event ) -{ - Inherited::keyReleaseEvent( event ); -} - -void QskTextEdit::mousePressEvent( QMouseEvent* event ) -{ - m_data->wrappedEdit->handleEvent( event ); - - if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() ) - setEditing( true ); -} - -void QskTextEdit::mouseMoveEvent( QMouseEvent* event ) -{ - m_data->wrappedEdit->handleEvent( event ); -} - -void QskTextEdit::mouseReleaseEvent( QMouseEvent* event ) -{ - m_data->wrappedEdit->handleEvent( event ); - - if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() ) - setEditing( true ); -} - -void QskTextEdit::mouseDoubleClickEvent( QMouseEvent* event ) -{ - m_data->wrappedEdit->handleEvent( event ); -} - -void QskTextEdit::inputMethodEvent( QInputMethodEvent* event ) -{ - m_data->wrappedEdit->handleEvent( event ); -} - -void QskTextEdit::focusInEvent( QFocusEvent* event ) -{ - if ( m_data->activationModes & ActivationOnFocus ) - { - switch ( event->reason() ) - { - case Qt::ActiveWindowFocusReason: - case Qt::PopupFocusReason: - break; - - default: -#if 1 - // auto selecting the complete text ??? -#endif - setEditing( true ); - } - } - - Inherited::focusInEvent( event ); -} - -void QskTextEdit::focusOutEvent( QFocusEvent* event ) -{ - switch ( event->reason() ) - { - case Qt::ActiveWindowFocusReason: - case Qt::PopupFocusReason: - { - break; - } - default: - { - m_data->wrappedEdit->deselect(); - setEditing( false ); - } - } - - Inherited::focusOutEvent( event ); -} - -void QskTextEdit::updateLayout() -{ - m_data->wrappedEdit->updateMetrics(); - qskSetItemGeometry( m_data->wrappedEdit, subControlRect( Text ) ); -} - -void QskTextEdit::updateNode( QSGNode* node ) -{ - m_data->wrappedEdit->updateColors(); - Inherited::updateNode( node ); -} - -QString QskTextEdit::text() const -{ - return m_data->wrappedEdit->text(); -} - -void QskTextEdit::setText( const QString& text ) -{ - m_data->wrappedEdit->setText( text ); -} - -void QskTextEdit::clear() -{ - m_data->wrappedEdit->clear(); -} - -void QskTextEdit::selectAll() -{ - m_data->wrappedEdit->selectAll(); -} - -QskTextEdit::ActivationModes QskTextEdit::activationModes() const -{ - return static_cast< QskTextEdit::ActivationModes >( m_data->activationModes ); -} - -void QskTextEdit::setActivationModes( ActivationModes modes ) -{ - if ( static_cast< ActivationModes >( m_data->activationModes ) != modes ) - { - m_data->activationModes = modes; - Q_EMIT activationModesChanged(); - } -} - -static inline void qskUpdateInputMethodFont( const QskTextEdit* input ) -{ - const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle; - qskUpdateInputMethod( input, queries ); -} - -void QskTextEdit::setFontRole( const QskFontRole& role ) -{ - if ( setFontRoleHint( Text, role ) ) - { - qskUpdateInputMethodFont( this ); - Q_EMIT fontRoleChanged(); - } -} - -void QskTextEdit::resetFontRole() -{ - if ( resetFontRoleHint( Text ) ) - { - qskUpdateInputMethodFont( this ); - Q_EMIT fontRoleChanged(); - } -} - -QskFontRole QskTextEdit::fontRole() const -{ - return fontRoleHint( Text ); -} - -void QskTextEdit::setAlignment( Qt::Alignment alignment ) -{ - if ( setAlignmentHint( Text, alignment ) ) - { - m_data->wrappedEdit->setAlignment( alignment ); - Q_EMIT alignmentChanged(); - } -} - -void QskTextEdit::resetAlignment() -{ - if ( resetAlignmentHint( Text ) ) - { - m_data->wrappedEdit->setAlignment( alignment() ); - Q_EMIT alignmentChanged(); - } -} - -Qt::Alignment QskTextEdit::alignment() const -{ - return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop ); -} - -void QskTextEdit::setWrapMode( QskTextOptions::WrapMode wrapMode ) -{ - m_data->wrappedEdit->setWrapMode( - static_cast< QQuickTextEdit::WrapMode >( wrapMode ) ); -} - -QskTextOptions::WrapMode QskTextEdit::wrapMode() const -{ - return static_cast< QskTextOptions::WrapMode >( - m_data->wrappedEdit->wrapMode() ); -} - -void QskTextEdit::setSelectByMouse( bool on ) -{ - m_data->wrappedEdit->setSelectByMouse( on ); -} - -bool QskTextEdit::selectByMouse() const -{ - return m_data->wrappedEdit->selectByMouse(); -} - -QFont QskTextEdit::font() const -{ - return effectiveFont( QskTextEdit::Text ); -} - -bool QskTextEdit::isReadOnly() const -{ - return m_data->wrappedEdit->isReadOnly(); -} - -void QskTextEdit::setReadOnly( bool on ) -{ - auto edit = m_data->wrappedEdit; - - if ( edit->isReadOnly() == on ) - return; - -#if 1 - // do we want to be able to restore the previous policy ? - setFocusPolicy( Qt::NoFocus ); -#endif - - edit->setReadOnly( on ); - - // we are killing user settings here ? - edit->setFlag( QQuickItem::ItemAcceptsInputMethod, !on ); - qskUpdateInputMethod( this, Qt::ImEnabled ); - - setSkinStateFlag( ReadOnly, on ); -} - -void QskTextEdit::setEditing( bool on ) -{ - if ( isReadOnly() || on == isEditing() ) - return; - - setSkinStateFlag( Editing, on ); - m_data->wrappedEdit->setEditing( on ); - - if ( on ) - { -#if 0 - updateInputMethod( Qt::ImCursorRectangle | Qt::ImAnchorRectangle ); - QGuiApplication::inputMethod()->inputDirection -#endif - qskInputMethodSetVisible( this, true ); - } - else - { - Q_EMIT m_data->wrappedEdit->editingFinished(); - -#if 0 - inputMethod->reset(); -#endif - qskInputMethodSetVisible( this, false ); - } - - Q_EMIT editingChanged( on ); -} - -bool QskTextEdit::isEditing() const -{ - return hasSkinState( Editing ); -} - -int QskTextEdit::cursorPosition() const -{ - return m_data->wrappedEdit->cursorPosition(); -} - -void QskTextEdit::setCursorPosition( int pos ) -{ - m_data->wrappedEdit->setCursorPosition( pos ); -} - -QString QskTextEdit::preeditText() const -{ - return m_data->wrappedEdit->preeditText(); -} - -bool QskTextEdit::overwriteMode() const -{ - return m_data->wrappedEdit->overwriteMode(); -} - -void QskTextEdit::setOverwriteMode( bool overwrite ) -{ - m_data->wrappedEdit->setOverwriteMode( overwrite ); -} - void QskTextEdit::setTextFormat( QskTextOptions::TextFormat textFormat ) { m_data->wrappedEdit->setTextFormat( @@ -621,70 +207,6 @@ int QskTextEdit::lineCount() const return m_data->wrappedEdit->lineCount(); } -QVariant QskTextEdit::inputMethodQuery( - Qt::InputMethodQuery property ) const -{ - return inputMethodQuery( property, QVariant() ); -} - -QVariant QskTextEdit::inputMethodQuery( - Qt::InputMethodQuery query, const QVariant& argument ) const -{ - switch ( query ) - { - case Qt::ImEnabled: - { - return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) ); - } - case Qt::ImFont: - { - return font(); - } - case Qt::ImPreferredLanguage: - { - return locale(); - } - case Qt::ImInputItemClipRectangle: - case Qt::ImCursorRectangle: - { - QVariant v = m_data->wrappedEdit->inputMethodQuery( query, argument ); -#if 1 - if ( v.canConvert< QRectF >() ) - v.setValue( v.toRectF().translated( m_data->wrappedEdit->position() ) ); -#endif - return v; - } - default: - { - return m_data->wrappedEdit->inputMethodQuery( query, argument ); - } - } -} - -bool QskTextEdit::canUndo() const -{ - return m_data->wrappedEdit->canUndo(); -} - -bool QskTextEdit::canRedo() const -{ - return m_data->wrappedEdit->canRedo(); -} - -Qt::InputMethodHints QskTextEdit::inputMethodHints() const -{ - return m_data->wrappedEdit->inputMethodHints(); -} - -void QskTextEdit::setInputMethodHints( Qt::InputMethodHints hints ) -{ - if ( m_data->wrappedEdit->inputMethodHints() != hints ) - { - m_data->wrappedEdit->setInputMethodHints( hints ); - qskUpdateInputMethod( this, Qt::ImHints ); - } -} - int QskTextEdit::tabStopDistance() const { return m_data->wrappedEdit->tabStopDistance(); @@ -741,4 +263,10 @@ void QskTextEdit::setupFrom( const QQuickItem* item ) } } +QQuickItem* QskTextEdit::wrappedInput() +{ + return m_data->wrappedEdit; +} + +#include "QskTextEdit.moc" #include "moc_QskTextEdit.cpp" diff --git a/src/controls/QskTextEdit.h b/src/controls/QskTextEdit.h index 7f4c518c..0f08354b 100644 --- a/src/controls/QskTextEdit.h +++ b/src/controls/QskTextEdit.h @@ -6,185 +6,53 @@ #ifndef QSK_TEXT_EDIT_H #define QSK_TEXT_EDIT_H -#include "QskControl.h" +#include "QskAbstractTextInput.h" #include "QskTextOptions.h" -class QValidator; -class QskFontRole; - -class QSK_EXPORT QskTextEdit : public QskControl +class QSK_EXPORT QskTextEdit : public QskAbstractTextInput { Q_OBJECT - Q_PROPERTY( QString text READ text - WRITE setText NOTIFY textChanged USER true ) - - Q_PROPERTY( QskFontRole fontRole READ fontRole - WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) - - Q_PROPERTY( QFont font READ font ) - - Q_PROPERTY( Qt::Alignment alignment READ alignment - WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged ) - - Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode - WRITE setWrapMode NOTIFY wrapModeChanged ) - Q_PROPERTY( int lineCount READ lineCount NOTIFY lineCountChanged ) Q_PROPERTY( QskTextOptions::TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged ) - Q_PROPERTY( ActivationModes activationModes READ activationModes - WRITE setActivationModes NOTIFY activationModesChanged ) - - Q_PROPERTY( bool selectByMouse READ selectByMouse - WRITE setSelectByMouse NOTIFY selectByMouseChanged ) - - Q_PROPERTY( bool editing READ isEditing - WRITE setEditing NOTIFY editingChanged ) - Q_PROPERTY( qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged ) - using Inherited = QskControl; + using Inherited = QskAbstractTextInput; public: - QSK_SUBCONTROLS( TextPanel, Text ) - QSK_STATES( ReadOnly, Editing, Error ) - - enum ActivationMode - { - NoActivation, - - ActivationOnFocus = 1 << 0, - ActivationOnMouse = 1 << 1, - ActivationOnKey = 1 << 2, - - ActivationOnInput = ActivationOnMouse | ActivationOnKey, - ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey - }; - - Q_ENUM( ActivationMode ) - Q_DECLARE_FLAGS( ActivationModes, ActivationMode ) + QSK_SUBCONTROLS( TextPanel ) QskTextEdit( QQuickItem* parent = nullptr ); - ~QskTextEdit() override; void setupFrom( const QQuickItem* ); - QString text() const; - - void setFontRole( const QskFontRole& role ); - void resetFontRole(); - QskFontRole fontRole() const; - - void setAlignment( Qt::Alignment ); - void resetAlignment(); - Qt::Alignment alignment() const; - - void setWrapMode( QskTextOptions::WrapMode ); - QskTextOptions::WrapMode wrapMode() const; - void setTextFormat( QskTextOptions::TextFormat ); QskTextOptions::TextFormat textFormat() const; int lineCount() const; - void setActivationModes( ActivationModes ); - ActivationModes activationModes() const; - - void setSelectByMouse( bool ); - bool selectByMouse() const; - - bool isEditing() const; - - QFont font() const; - - bool isReadOnly() const; - void setReadOnly( bool ); - - int cursorPosition() const; - void setCursorPosition( int ); - - QString preeditText() const; - - bool overwriteMode() const; - void setOverwriteMode( bool ); - - QVariant inputMethodQuery( Qt::InputMethodQuery ) const override; - QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& argument ) const; - - bool canUndo() const; - bool canRedo() const; - - Qt::InputMethodHints inputMethodHints() const; - void setInputMethodHints( Qt::InputMethodHints ); - int tabStopDistance() const; void setTabStopDistance( qreal ); - public Q_SLOTS: - void clear(); - void selectAll(); - void setText( const QString& ); - void setEditing( bool ); - Q_SIGNALS: - void textChanged( const QString& ); - void preeditTextChanged( const QString& ); - - void editingChanged( bool ); - - void activationModesChanged(); - void readOnlyChanged( bool ); void panelChanged( bool ); - - void displayTextChanged( const QString& ); - - void textEdited( const QString& ); void placeholderTextChanged( const QString& ); - void fontRoleChanged(); - void alignmentChanged(); - void wrapModeChanged( QskTextOptions::WrapMode ); - void lineCountChanged( int ); - - void selectByMouseChanged( bool ); - void textFormatChanged( QskTextOptions::TextFormat ); - void overwriteModeChanged( bool ); - void tabStopDistanceChanged( qreal ); - protected: - bool event( QEvent* ) override; - - void inputMethodEvent( QInputMethodEvent* ) override; - - void focusInEvent( QFocusEvent* ) override; - void focusOutEvent( QFocusEvent* ) override; - - void mousePressEvent( QMouseEvent* ) override; - void mouseMoveEvent( QMouseEvent* ) override; - void mouseReleaseEvent( QMouseEvent* ) override; - void mouseDoubleClickEvent( QMouseEvent* ) override; - - void keyPressEvent( QKeyEvent* ) override; - void keyReleaseEvent( QKeyEvent* ) override; - - void updateLayout() override; - void updateNode( QSGNode* ) override; - private: + QQuickItem* wrappedInput() override final; + class PrivateData; std::unique_ptr< PrivateData > m_data; }; -Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextEdit::ActivationModes ) -Q_DECLARE_METATYPE( QskTextEdit::ActivationModes ) - #endif diff --git a/src/controls/QskTextEditSkinlet.cpp b/src/controls/QskTextEditSkinlet.cpp index 020b957a..3c139f56 100644 --- a/src/controls/QskTextEditSkinlet.cpp +++ b/src/controls/QskTextEditSkinlet.cpp @@ -10,8 +10,6 @@ using Q = QskTextEdit; -QSK_SYSTEM_STATE( QskTextEditSkinlet, Selected, QskAspect::FirstSystemState << 3 ) - QskTextEditSkinlet::QskTextEditSkinlet( QskSkin* skin ) : Inherited( skin ) { diff --git a/src/controls/QskTextEditSkinlet.h b/src/controls/QskTextEditSkinlet.h index 0d858239..c27c1169 100644 --- a/src/controls/QskTextEditSkinlet.h +++ b/src/controls/QskTextEditSkinlet.h @@ -13,8 +13,6 @@ class QSK_EXPORT QskTextEditSkinlet : public QskSkinlet using Inherited = QskSkinlet; public: - QSK_STATES( Selected ) - enum NodeRole : quint8 { TextPanelRole, diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 1d0e750a..e6228463 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -13,17 +13,10 @@ QSK_QT_PRIVATE_BEGIN #include #include -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) -#include -#endif - QSK_QT_PRIVATE_END -QSK_SUBCONTROL( QskTextInput, Text ) QSK_SUBCONTROL( QskTextInput, TextPanel ) -QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) -QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) QSK_SYSTEM_STATE( QskTextInput, Error, QskAspect::FirstSystemState << 4 ) static inline void qskPropagateReadOnly( QskTextInput* input ) @@ -34,19 +27,6 @@ static inline void qskPropagateReadOnly( QskTextInput* input ) QCoreApplication::sendEvent( input, &event ); } -static inline void qskTranslateMouseEventPosition( - QMouseEvent* mouseEvent, const QPointF& offset ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - auto& point = mouseEvent->point(0); - - QMutableEventPoint::setPosition( - point, point.position() + offset ); -#else - mouseEvent->setLocalPos( mouseEvent->localPos() + offset ); -#endif -} - static inline void qskBindSignals( const QQuickTextInput* wrappedInput, QskTextInput* input ) { @@ -71,6 +51,9 @@ static inline void qskBindSignals( QObject::connect( wrappedInput, &QQuickTextInput::overwriteModeChanged, input, &QskTextInput::overwriteModeChanged ); + QObject::connect( wrappedInput, &QQuickTextInput::cursorPositionChanged, + input, [ input ] { Q_EMIT input->cursorPositionChanged( input->cursorPosition() ); } ); + QObject::connect( wrappedInput, &QQuickTextInput::maximumLengthChanged, input, &QskTextInput::maximumLengthChanged ); @@ -97,13 +80,13 @@ namespace { class QuickTextInput final : public QQuickTextInput { + Q_OBJECT + using Inherited = QQuickTextInput; public: QuickTextInput( QskTextInput* ); - void setEditing( bool on ); - inline void setAlignment( Qt::Alignment alignment ) { setHAlign( ( HAlignment ) ( int( alignment ) & 0x0f ) ); @@ -115,60 +98,9 @@ namespace return QQuickTextInputPrivate::get( this )->fixup(); } - void updateColors(); - void updateMetrics(); - - inline bool handleEvent( QEvent* event ) - { - bool ok; - - switch( static_cast< int >( event->type() ) ) - { - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - { - auto mouseEvent = static_cast< QMouseEvent* >( event ); - - /* - As the event was sent for the parent item - we have to translate the position into - our coordinate system. - */ - qskTranslateMouseEventPosition( mouseEvent, -position() ); - ok = this->event( mouseEvent ); - qskTranslateMouseEventPosition( mouseEvent, position() ); - - break; - } - case QEvent::InputMethod: - { - const bool hadCursor = isCursorVisible(); - - ok = this->event( event ); - - if ( isCursorVisible() && !hadCursor ) - { - /* - The initial InputMethod events might be sent from the - platform depending on focus. Unfortunately an - empty dummy event ( = no attributes ) leads to showing - the cursor. - */ - auto input = static_cast< const QskTextInput* >( parentItem() ); - if ( !input->isEditing() ) - setCursorVisible( false ); - } - - break; - } - default: - ok = this->event( event ); - } - - return ok; - } + Q_INVOKABLE void updateColors(); + Q_INVOKABLE void updateMetrics(); + Q_INVOKABLE void handleEvent( QEvent* ); protected: @@ -218,25 +150,6 @@ namespace this, &QuickTextInput::updateClip ); } - void QuickTextInput::setEditing( bool on ) - { - auto d = QQuickTextInputPrivate::get( this ); - - if ( d->cursorVisible == on ) - return; - - setCursorVisible( on ); - d->setBlinkingCursorEnabled( on ); - - if ( !on ) - { - if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() ) - d->updatePasswordEchoEditing( false ); - } - - polish(); - update(); - } void QuickTextInput::updateMetrics() { @@ -254,34 +167,29 @@ namespace setColor( input->color( Q::Text ) ); - const auto state = QskTextInputSkinlet::Selected; + const auto state = QskTextInput::Selected; setSelectionColor( input->color( Q::TextPanel | state ) ); setSelectedTextColor( input->color( Q::Text | state ) ); } + + void QuickTextInput::handleEvent( QEvent* ev ) + { + event( ev ); + } + } class QskTextInput::PrivateData { public: QuickTextInput* wrappedInput; - - ActivationModes activationModes; }; QskTextInput::QskTextInput( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - m_data->activationModes = ActivationOnMouse | ActivationOnKey; - - setPolishOnResize( true ); - - setAcceptHoverEvents( true ); - setFocusPolicy( Qt::StrongFocus ); - - setFlag( QQuickItem::ItemAcceptsInputMethod ); - /* QQuickTextInput is a beast of almost 5k lines of code, we don't want to reimplement that - at least not now. @@ -302,355 +210,11 @@ QskTextInput::~QskTextInput() { } -bool QskTextInput::event( QEvent* event ) -{ - if ( event->type() == QEvent::ShortcutOverride ) - { - return m_data->wrappedInput->handleEvent( event ); - } - else if ( event->type() == QEvent::LocaleChange ) - { - qskUpdateInputMethod( this, Qt::ImPreferredLanguage ); - } - - return Inherited::event( event ); -} - -void QskTextInput::keyPressEvent( QKeyEvent* event ) -{ - if ( isEditing() ) - { - switch ( event->key() ) - { - case Qt::Key_Enter: - case Qt::Key_Return: - { - const auto hints = inputMethodQuery( Qt::ImHints ).toInt(); - if ( !( hints & Qt::ImhMultiLine ) ) - { - if ( hasAcceptableInput() || fixup() ) - { - QGuiApplication::inputMethod()->commit(); - setEditing( false ); - } - } - - break; - } -#if 1 - case Qt::Key_Escape: - { - setEditing( false ); - break; - } -#endif - } - - if ( isEditing() ) - { - m_data->wrappedInput->handleEvent( event ); - } - else - { - // When returning from a virtual keyboard - qskForceActiveFocus( this, Qt::PopupFocusReason ); - } - - return; - } - - if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() ) - { - if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space ) - { - setEditing( true ); - return; - } - } - - Inherited::keyPressEvent( event ); -} - -void QskTextInput::keyReleaseEvent( QKeyEvent* event ) -{ - Inherited::keyReleaseEvent( event ); -} - -void QskTextInput::mousePressEvent( QMouseEvent* event ) -{ - m_data->wrappedInput->handleEvent( event ); - - if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() ) - setEditing( true ); -} - -void QskTextInput::mouseMoveEvent( QMouseEvent* event ) -{ - m_data->wrappedInput->handleEvent( event ); -} - -void QskTextInput::mouseReleaseEvent( QMouseEvent* event ) -{ - m_data->wrappedInput->handleEvent( event ); - - if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() ) - setEditing( true ); -} - -void QskTextInput::mouseDoubleClickEvent( QMouseEvent* event ) -{ - m_data->wrappedInput->handleEvent( event ); -} - -void QskTextInput::inputMethodEvent( QInputMethodEvent* event ) -{ - m_data->wrappedInput->handleEvent( event ); -} - -void QskTextInput::focusInEvent( QFocusEvent* event ) -{ - if ( m_data->activationModes & ActivationOnFocus ) - { - switch ( event->reason() ) - { - case Qt::ActiveWindowFocusReason: - case Qt::PopupFocusReason: - break; - - default: -#if 1 - // auto selecting the complete text ??? -#endif - setEditing( true ); - } - } - - Inherited::focusInEvent( event ); -} - -void QskTextInput::focusOutEvent( QFocusEvent* event ) -{ - switch ( event->reason() ) - { - case Qt::ActiveWindowFocusReason: - case Qt::PopupFocusReason: - { - break; - } - default: - { - m_data->wrappedInput->deselect(); - setEditing( false ); - } - } - - Inherited::focusOutEvent( event ); -} - -void QskTextInput::updateLayout() -{ - m_data->wrappedInput->updateMetrics(); - qskSetItemGeometry( m_data->wrappedInput, subControlRect( Text ) ); -} - -void QskTextInput::updateNode( QSGNode* node ) -{ - m_data->wrappedInput->updateColors(); - Inherited::updateNode( node ); -} - -QString QskTextInput::text() const -{ - return m_data->wrappedInput->text(); -} - -void QskTextInput::setText( const QString& text ) -{ - m_data->wrappedInput->setText( text ); -} - -void QskTextInput::clear() -{ - m_data->wrappedInput->clear(); -} - -void QskTextInput::selectAll() -{ - m_data->wrappedInput->selectAll(); -} - -QskTextInput::ActivationModes QskTextInput::activationModes() const -{ - return static_cast< QskTextInput::ActivationModes >( m_data->activationModes ); -} - -void QskTextInput::setActivationModes( ActivationModes modes ) -{ - if ( static_cast< ActivationModes >( m_data->activationModes ) != modes ) - { - m_data->activationModes = modes; - Q_EMIT activationModesChanged(); - } -} - -static inline void qskUpdateInputMethodFont( const QskTextInput* input ) -{ - const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle; - qskUpdateInputMethod( input, queries ); -} - -void QskTextInput::setFontRole( const QskFontRole& role ) -{ - if ( setFontRoleHint( Text, role ) ) - { - qskUpdateInputMethodFont( this ); - Q_EMIT fontRoleChanged(); - } -} - -void QskTextInput::resetFontRole() -{ - if ( resetFontRoleHint( Text ) ) - { - qskUpdateInputMethodFont( this ); - Q_EMIT fontRoleChanged(); - } -} - -QskFontRole QskTextInput::fontRole() const -{ - return fontRoleHint( Text ); -} - -void QskTextInput::setAlignment( Qt::Alignment alignment ) -{ - if ( setAlignmentHint( Text, alignment ) ) - { - m_data->wrappedInput->setAlignment( alignment ); - Q_EMIT alignmentChanged(); - } -} - -void QskTextInput::resetAlignment() -{ - if ( resetAlignmentHint( Text ) ) - { - m_data->wrappedInput->setAlignment( alignment() ); - Q_EMIT alignmentChanged(); - } -} - -Qt::Alignment QskTextInput::alignment() const -{ - return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop ); -} - -void QskTextInput::setWrapMode( QskTextOptions::WrapMode wrapMode ) -{ - m_data->wrappedInput->setWrapMode( - static_cast< QQuickTextInput::WrapMode >( wrapMode ) ); -} - -QskTextOptions::WrapMode QskTextInput::wrapMode() const -{ - return static_cast< QskTextOptions::WrapMode >( - m_data->wrappedInput->wrapMode() ); -} - -void QskTextInput::setSelectByMouse( bool on ) -{ - m_data->wrappedInput->setSelectByMouse( on ); -} - -bool QskTextInput::selectByMouse() const -{ - return m_data->wrappedInput->selectByMouse(); -} - -QFont QskTextInput::font() const -{ - return effectiveFont( QskTextInput::Text ); -} - -bool QskTextInput::isReadOnly() const -{ - return m_data->wrappedInput->isReadOnly(); -} - -void QskTextInput::setReadOnly( bool on ) -{ - auto input = m_data->wrappedInput; - - if ( input->isReadOnly() == on ) - return; - -#if 1 - // do we want to be able to restore the previous policy ? - setFocusPolicy( Qt::NoFocus ); -#endif - - input->setReadOnly( on ); - - // we are killing user settings here ? - input->setFlag( QQuickItem::ItemAcceptsInputMethod, !on ); - qskUpdateInputMethod( this, Qt::ImEnabled ); -#if 0 - qskUpdateInputMethod( this, Qt::ImReadOnly ); // since 6.2 -#endif - - setSkinStateFlag( ReadOnly, on ); -} - -void QskTextInput::setEditing( bool on ) -{ - if ( isReadOnly() || on == isEditing() ) - return; - - setSkinStateFlag( Editing, on ); - m_data->wrappedInput->setEditing( on ); - - if ( on ) - { -#if 0 - updateInputMethod( Qt::ImCursorRectangle | Qt::ImAnchorRectangle ); - QGuiApplication::inputMethod()->inputDirection -#endif - qskInputMethodSetVisible( this, true ); - } - else - { - if ( hasAcceptableInput() || fixup() ) - Q_EMIT m_data->wrappedInput->editingFinished(); - -#if 0 - inputMethod->reset(); -#endif - qskInputMethodSetVisible( this, false ); - } - - Q_EMIT editingChanged( on ); -} - -bool QskTextInput::isEditing() const -{ - return hasSkinState( Editing ); -} - void QskTextInput::ensureVisible( int position ) { m_data->wrappedInput->ensureVisible( position ); } -int QskTextInput::cursorPosition() const -{ - return m_data->wrappedInput->cursorPosition(); -} - -void QskTextInput::setCursorPosition( int pos ) -{ - m_data->wrappedInput->setCursorPosition( pos ); -} - int QskTextInput::maxLength() const { return m_data->wrappedInput->maxLength(); @@ -734,22 +298,6 @@ QString QskTextInput::displayText() const return m_data->wrappedInput->displayText(); } -QString QskTextInput::preeditText() const -{ - const auto d = QQuickTextInputPrivate::get( m_data->wrappedInput ); - return d->m_textLayout.preeditAreaText(); -} - -bool QskTextInput::overwriteMode() const -{ - return m_data->wrappedInput->overwriteMode(); -} - -void QskTextInput::setOverwriteMode( bool overwrite ) -{ - m_data->wrappedInput->setOverwriteMode( overwrite ); -} - bool QskTextInput::hasAcceptableInput() const { return m_data->wrappedInput->hasAcceptableInput(); @@ -760,58 +308,9 @@ bool QskTextInput::fixup() return m_data->wrappedInput->fixup(); } -QVariant QskTextInput::inputMethodQuery( - Qt::InputMethodQuery property ) const +bool QskTextInput::acceptInput() { - return inputMethodQuery( property, QVariant() ); -} - -QVariant QskTextInput::inputMethodQuery( - Qt::InputMethodQuery query, const QVariant& argument ) const -{ - switch ( query ) - { - case Qt::ImEnabled: - { - return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) ); - } - case Qt::ImFont: - { - return font(); - } - case Qt::ImPreferredLanguage: - { - return locale(); - } - case Qt::ImInputItemClipRectangle: - case Qt::ImCursorRectangle: - { - QVariant v = m_data->wrappedInput->inputMethodQuery( query, argument ); -#if 1 - if ( v.canConvert< QRectF >() ) - v.setValue( v.toRectF().translated( m_data->wrappedInput->position() ) ); -#endif - return v; - } - default: - { - return m_data->wrappedInput->inputMethodQuery( query, argument ); - } - } -} - -Qt::InputMethodHints QskTextInput::inputMethodHints() const -{ - return m_data->wrappedInput->inputMethodHints(); -} - -void QskTextInput::setInputMethodHints( Qt::InputMethodHints hints ) -{ - if ( m_data->wrappedInput->inputMethodHints() != hints ) - { - m_data->wrappedInput->setInputMethodHints( hints ); - qskUpdateInputMethod( this, Qt::ImHints ); - } + return hasAcceptableInput() || fixup(); } void QskTextInput::setupFrom( const QQuickItem* item ) @@ -915,4 +414,10 @@ void QskTextInput::setupFrom( const QQuickItem* item ) setEchoMode( echoMode ); } +QQuickItem* QskTextInput::wrappedInput() +{ + return m_data->wrappedInput; +} + +#include "QskTextInput.moc" #include "moc_QskTextInput.cpp" diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index c2ef09f3..26bbc1b1 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -6,36 +6,15 @@ #ifndef QSK_TEXT_INPUT_H #define QSK_TEXT_INPUT_H -#include "QskControl.h" +#include "QskAbstractTextInput.h" #include "QskTextOptions.h" class QValidator; -class QskFontRole; -class QSK_EXPORT QskTextInput : public QskControl +class QSK_EXPORT QskTextInput : public QskAbstractTextInput { Q_OBJECT - Q_PROPERTY( QString text READ text - WRITE setText NOTIFY textChanged USER true ) - - Q_PROPERTY( QskFontRole fontRole READ fontRole - WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) - - Q_PROPERTY( QFont font READ font ) - - Q_PROPERTY( Qt::Alignment alignment READ alignment - WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged ) - - Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode - WRITE setWrapMode NOTIFY wrapModeChanged ) - - Q_PROPERTY( ActivationModes activationModes READ activationModes - WRITE setActivationModes NOTIFY activationModesChanged ) - - Q_PROPERTY( bool editing READ isEditing - WRITE setEditing NOTIFY editingChanged ) - Q_PROPERTY( EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged ) @@ -47,30 +26,12 @@ class QSK_EXPORT QskTextInput : public QskControl WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay NOTIFY passwordMaskDelayChanged ) - Q_PROPERTY( bool selectByMouse READ selectByMouse - WRITE setSelectByMouse ) - - using Inherited = QskControl; + using Inherited = QskAbstractTextInput; public: - QSK_SUBCONTROLS( TextPanel, Text ) + QSK_SUBCONTROLS( TextPanel ) - QSK_STATES( ReadOnly, Editing, Error ) - - enum ActivationMode : quint8 - { - NoActivation, - - ActivationOnFocus = 1 << 0, - ActivationOnMouse = 1 << 1, - ActivationOnKey = 1 << 2, - - ActivationOnInput = ActivationOnMouse | ActivationOnKey, - ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey - }; - - Q_ENUM( ActivationMode ) - Q_DECLARE_FLAGS( ActivationModes, ActivationMode ) + QSK_STATES( Error ) enum EchoMode : quint8 { @@ -82,39 +43,11 @@ class QSK_EXPORT QskTextInput : public QskControl Q_ENUM( EchoMode ) + QskTextInput( QQuickItem* parent = nullptr ); ~QskTextInput() override; void setupFrom( const QQuickItem* ); - QString text() const; - - void setFontRole( const QskFontRole& role ); - void resetFontRole(); - QskFontRole fontRole() const; - - void setAlignment( Qt::Alignment ); - void resetAlignment(); - Qt::Alignment alignment() const; - - void setWrapMode( QskTextOptions::WrapMode ); - QskTextOptions::WrapMode wrapMode() const; - - void setActivationModes( ActivationModes ); - ActivationModes activationModes() const; - - void setSelectByMouse( bool ); - bool selectByMouse() const; - - bool isEditing() const; - - QFont font() const; - - bool isReadOnly() const; - void setReadOnly( bool ); - - int cursorPosition() const; - void setCursorPosition( int ); - int maxLength() const; void setMaxLength( int ); @@ -136,44 +69,14 @@ class QSK_EXPORT QskTextInput : public QskControl void resetPasswordMaskDelay(); QString displayText() const; - QString preeditText() const; - - bool overwriteMode() const; - void setOverwriteMode( bool ); bool hasAcceptableInput() const; bool fixup(); - - QVariant inputMethodQuery( Qt::InputMethodQuery ) const override; - QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& argument ) const; - - Qt::InputMethodHints inputMethodHints() const; - void setInputMethodHints( Qt::InputMethodHints ); + bool acceptInput() override; void ensureVisible( int position ); - public Q_SLOTS: - void clear(); - void selectAll(); - void setText( const QString& ); - void setEditing( bool ); - Q_SIGNALS: - void editingChanged( bool ); - - void activationModesChanged(); - void readOnlyChanged( bool ); - - void textChanged( const QString& ); - void displayTextChanged( const QString& ); - - void textEdited( const QString& ); - - void fontRoleChanged(); - void alignmentChanged(); - void wrapModeChanged( QskTextOptions::WrapMode ); - - void overwriteModeChanged( bool ); void maximumLengthChanged( int ); void echoModeChanged( EchoMode ); @@ -184,32 +87,11 @@ class QSK_EXPORT QskTextInput : public QskControl void inputMaskChanged( const QString& ); protected: - QskTextInput( QQuickItem* parent = nullptr ); - - bool event( QEvent* ) override; - - void inputMethodEvent( QInputMethodEvent* ) override; - - void focusInEvent( QFocusEvent* ) override; - void focusOutEvent( QFocusEvent* ) override; - - void mousePressEvent( QMouseEvent* ) override; - void mouseMoveEvent( QMouseEvent* ) override; - void mouseReleaseEvent( QMouseEvent* ) override; - void mouseDoubleClickEvent( QMouseEvent* ) override; - - void keyPressEvent( QKeyEvent* ) override; - void keyReleaseEvent( QKeyEvent* ) override; - - void updateLayout() override; - void updateNode( QSGNode* ) override; + QQuickItem* wrappedInput() override final; private: class PrivateData; std::unique_ptr< PrivateData > m_data; }; -Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextInput::ActivationModes ) -Q_DECLARE_METATYPE( QskTextInput::ActivationModes ) - #endif diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index fe52b92b..6991d4b4 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -10,8 +10,6 @@ using Q = QskTextInput; -QSK_SYSTEM_STATE( QskTextInputSkinlet, Selected, QskAspect::FirstSystemState << 3 ) - QskTextInputSkinlet::QskTextInputSkinlet( QskSkin* skin ) : Inherited( skin ) { diff --git a/src/controls/QskTextInputSkinlet.h b/src/controls/QskTextInputSkinlet.h index e36081c5..0fe94b2d 100644 --- a/src/controls/QskTextInputSkinlet.h +++ b/src/controls/QskTextInputSkinlet.h @@ -13,8 +13,6 @@ class QSK_EXPORT QskTextInputSkinlet : public QskSkinlet using Inherited = QskSkinlet; public: - QSK_STATES( Selected ) - enum NodeRole : quint8 { TextPanelRole,