diff --git a/src/controls/QskAbstractTextInput.cpp b/src/controls/QskAbstractTextInput.cpp index d52d6103..c8bcb6fa 100644 --- a/src/controls/QskAbstractTextInput.cpp +++ b/src/controls/QskAbstractTextInput.cpp @@ -7,6 +7,7 @@ #include "QskFontRole.h" #include "QskQuick.h" #include "QskEvent.h" +#include "QskMetaInvokable.h" #include "QskInternalMacros.h" QSK_QT_PRIVATE_BEGIN @@ -18,6 +19,8 @@ QSK_QT_PRIVATE_BEGIN #include #endif +#include + QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskAbstractTextInput, Text ) @@ -116,10 +119,107 @@ static inline void qskForwardEvent( QQuickItem* item, QEvent* event ) } } +namespace +{ + + class PropertyBinder : public QObject + { + Q_OBJECT + + public: + void setup( const QObject*, const QMetaObject*, QObject* ); + + private: + Q_INVOKABLE void forwardNotification() const; + + typedef QPair< int, QMetaMethod > Binding; + typedef QHash< int, Binding > BindingTable; + + Binding bindingOf( const QMetaObject*, int methodIndex ) const; + static QMap< const QMetaObject*, BindingTable > m_tables; + }; + + QMap< const QMetaObject*, PropertyBinder::BindingTable > PropertyBinder::m_tables; + + void PropertyBinder::setup( const QObject* input, + const QMetaObject* mo, QObject* proxy ) + { + setParent( proxy ); + + auto& hash = m_tables[ mo ]; + + if ( hash.isEmpty() ) + { + for ( int i = mo->propertyOffset(); i < mo->propertyCount(); i++ ) + { + const auto property = mo->property( i ); + + const auto signalIndex = property.notifySignalIndex(); + if ( signalIndex >= 0 ) + { + const auto moProxy = proxy->metaObject(); + + const int idx = moProxy->indexOfProperty( property.name() ); + if ( idx >= 0 ) + { + const auto method = moProxy->property( idx ).notifySignal(); + if ( method.isValid() ) + hash.insert( signalIndex, { i, method } ); + } + else + { + // qDebug() << "Missing" << property.name(); + } + } + } + } + + const int idx = staticMetaObject.indexOfMethod( "forwardNotification()" ); + const auto forwardMethod = staticMetaObject.method( idx ); + + for ( auto it = hash.constBegin(); it != hash.constEnd(); ++it ) + { + connect( input, input->metaObject()->method( it.key() ), + this, forwardMethod, Qt::DirectConnection ); + } + } + + void PropertyBinder::forwardNotification() const + { + if ( thread() != QThread::currentThread() ) + return; + + const auto mo = sender()->metaObject(); + + const auto binding = bindingOf( mo, senderSignalIndex() ); + if ( !binding.second.isValid() ) + return; + + const auto property = mo->property( binding.first ); + + const auto value = property.read( sender() ); + void* args[3] = { nullptr, const_cast< void* >( value.data() ), nullptr }; + + qskInvokeMetaMethod( parent(), binding.second, args, Qt::DirectConnection ); + } + + PropertyBinder::Binding PropertyBinder::bindingOf( + const QMetaObject* metaObject, int methodIndex ) const + { + const auto method = metaObject->method( methodIndex ); + + const auto& hash = m_tables[ method.enclosingMetaObject() ]; + return hash.value( methodIndex ); + } +} + class QskAbstractTextInput::PrivateData { public: ActivationModes activationModes; + PropertyBinder binder; + + QQuickItem* input = nullptr; }; QskAbstractTextInput::QskAbstractTextInput( QQuickItem* parent ) @@ -140,6 +240,13 @@ QskAbstractTextInput::~QskAbstractTextInput() { } +void QskAbstractTextInput::setup( QQuickItem* wrappedInput, + const QMetaObject* metaObject ) +{ + m_data->input = wrappedInput; + m_data->binder.setup( wrappedInput, metaObject, this ); +} + QskAbstractTextInput::ActivationModes QskAbstractTextInput::activationModes() const { return m_data->activationModes; @@ -156,52 +263,52 @@ void QskAbstractTextInput::setActivationModes( ActivationModes modes ) bool QskAbstractTextInput::selectByMouse() const { - return wrappedInput()->property( "selectByMouse" ).value< bool >(); + return m_data->input->property( "selectByMouse" ).value< bool >(); } void QskAbstractTextInput::setSelectByMouse( bool on ) { - wrappedInput()->setProperty( "selectByMouse", on ); + m_data->input->setProperty( "selectByMouse", on ); } QString QskAbstractTextInput::text() const { - return wrappedInput()->property( "text" ).value< QString >(); + return m_data->input->property( "text" ).value< QString >(); } void QskAbstractTextInput::setText( const QString& text ) { - wrappedInput()->setProperty( "text", text ); + m_data->input->setProperty( "text", text ); } QString QskAbstractTextInput::preeditText() const { - return wrappedInput()->property( "preeditText" ).value< QString >(); + return m_data->input->property( "preeditText" ).value< QString >(); } void QskAbstractTextInput::clear() { - QMetaObject::invokeMethod( wrappedInput(), "clear" ); + QMetaObject::invokeMethod( m_data->input, "clear" ); } void QskAbstractTextInput::selectAll() { - QMetaObject::invokeMethod( wrappedInput(), "selectAll" ); + QMetaObject::invokeMethod( m_data->input, "selectAll" ); } void QskAbstractTextInput::deselect() { - QMetaObject::invokeMethod( wrappedInput(), "deselect" ); + QMetaObject::invokeMethod( m_data->input, "deselect" ); } bool QskAbstractTextInput::canUndo() const { - return wrappedInput()->property( "canUndo" ).value< bool >(); + return m_data->input->property( "canUndo" ).value< bool >(); } bool QskAbstractTextInput::canRedo() const { - return wrappedInput()->property( "canRedo" ).value< bool >(); + return m_data->input->property( "canRedo" ).value< bool >(); } void QskAbstractTextInput::setFontRole( const QskFontRole& role ) @@ -258,30 +365,30 @@ QVariant QskAbstractTextInput::inputMethodQuery( case Qt::ImInputItemClipRectangle: case Qt::ImCursorRectangle: { - QVariant v = qskInputMethodQuery( wrappedInput(), query, argument ); + QVariant v = qskInputMethodQuery( m_data->input, query, argument ); #if 1 if ( v.canConvert< QRectF >() ) - v.setValue( v.toRectF().translated( wrappedInput()->position() ) ); + v.setValue( v.toRectF().translated( m_data->input->position() ) ); #endif return v; } default: { - return qskInputMethodQuery( wrappedInput(), query, argument ); + return qskInputMethodQuery( m_data->input, query, argument ); } } } Qt::InputMethodHints QskAbstractTextInput::inputMethodHints() const { - return qskInputMethodHints( wrappedInput() ); + return qskInputMethodHints( m_data->input ); } void QskAbstractTextInput::setInputMethodHints( Qt::InputMethodHints hints ) { - if ( qskInputMethodHints( wrappedInput() ) != hints ) + if ( qskInputMethodHints( m_data->input ) != hints ) { - qskSetInputMethodHints( wrappedInput(), hints ); + qskSetInputMethodHints( m_data->input, hints ); qskUpdateInputMethod( this, Qt::ImHints ); } } @@ -446,7 +553,7 @@ void QskAbstractTextInput::inputMethodEvent( QInputMethodEvent* event ) bool QskAbstractTextInput::isReadOnly() const { - return wrappedInput()->property( "readOnly" ).value< bool >(); + return m_data->input->property( "readOnly" ).value< bool >(); } void QskAbstractTextInput::setReadOnly( bool on ) @@ -459,7 +566,7 @@ void QskAbstractTextInput::setReadOnly( bool on ) setFocusPolicy( Qt::NoFocus ); #endif - auto input = wrappedInput(); + auto input = m_data->input; input->setProperty( "readOnly", on ); // we are killing user settings here ? @@ -486,7 +593,7 @@ void QskAbstractTextInput::setEditing( bool on ) setSkinStateFlag( Editing, on ); - auto input = wrappedInput(); + auto input = m_data->input; if ( input->property( "cursorVisible" ).value< bool >() != on ) { @@ -522,42 +629,42 @@ bool QskAbstractTextInput::acceptInput() bool QskAbstractTextInput::overwriteMode() const { - return wrappedInput()->property( "overwriteMode" ).value< bool >(); + return m_data->input->property( "overwriteMode" ).value< bool >(); } void QskAbstractTextInput::setOverwriteMode( bool overwrite ) { - wrappedInput()->setProperty( "overwriteMode", overwrite ); + m_data->input->setProperty( "overwriteMode", overwrite ); } int QskAbstractTextInput::cursorPosition() const { - return wrappedInput()->property( "cursorPosition" ).value< int >(); + return m_data->input->property( "cursorPosition" ).value< int >(); } void QskAbstractTextInput::setCursorPosition( int pos ) { - wrappedInput()->setProperty( "cursorPosition", pos ); + m_data->input->setProperty( "cursorPosition", pos ); } bool QskAbstractTextInput::isCursorVisible() const { - return wrappedInput()->property( "cursorVisible" ).value< bool >(); + return m_data->input->property( "cursorVisible" ).value< bool >(); } void QskAbstractTextInput::setCursorVisible( bool on ) { - wrappedInput()->setProperty( "cursorVisible", on ); + m_data->input->setProperty( "cursorVisible", on ); } void QskAbstractTextInput::setWrapMode( QskTextOptions::WrapMode wrapMode ) { - wrappedInput()->setProperty( "wrapMode", static_cast< int >( wrapMode ) ); + m_data->input->setProperty( "wrapMode", static_cast< int >( wrapMode ) ); } QskTextOptions::WrapMode QskAbstractTextInput::wrapMode() const { - const auto mode = wrappedInput()->property( "wrapMode" ).value< int >(); + const auto mode = m_data->input->property( "wrapMode" ).value< int >(); return static_cast< QskTextOptions::WrapMode >( mode ); } @@ -565,7 +672,7 @@ void QskAbstractTextInput::setAlignment( Qt::Alignment alignment ) { if ( setAlignmentHint( Text, alignment ) ) { - qskSetAlignment( wrappedInput(), alignment ); + qskSetAlignment( m_data->input, alignment ); Q_EMIT alignmentChanged(); } } @@ -574,7 +681,7 @@ void QskAbstractTextInput::resetAlignment() { if ( resetAlignmentHint( Text ) ) { - qskSetAlignment( wrappedInput(), alignment() ); + qskSetAlignment( m_data->input, alignment() ); Q_EMIT alignmentChanged(); } } @@ -582,7 +689,7 @@ void QskAbstractTextInput::resetAlignment() Qt::Alignment QskAbstractTextInput::alignment() const { Qt::Alignment alignment = Qt::AlignLeft; - if ( qobject_cast< const QQuickTextEdit* >( wrappedInput() ) ) + if ( qobject_cast< const QQuickTextEdit* >( m_data->input ) ) alignment |= Qt::AlignTop; else alignment |= Qt::AlignVCenter; @@ -592,25 +699,20 @@ Qt::Alignment QskAbstractTextInput::alignment() const void QskAbstractTextInput::updateLayout() { - QMetaObject::invokeMethod( wrappedInput(), "updateMetrics" ); - qskSetItemGeometry( wrappedInput(), subControlRect( Text ) ); + QMetaObject::invokeMethod( m_data->input, "updateMetrics" ); + qskSetItemGeometry( m_data->input, subControlRect( Text ) ); } void QskAbstractTextInput::updateNode( QSGNode* node ) { - QMetaObject::invokeMethod( wrappedInput(), "updateColors" ); + QMetaObject::invokeMethod( m_data->input, "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 ); + qskForwardEvent( m_data->input, event ); } +#include "QskAbstractTextInput.moc" #include "moc_QskAbstractTextInput.cpp" diff --git a/src/controls/QskAbstractTextInput.h b/src/controls/QskAbstractTextInput.h index 3094d0d4..4d205e83 100644 --- a/src/controls/QskAbstractTextInput.h +++ b/src/controls/QskAbstractTextInput.h @@ -157,9 +157,7 @@ class QSK_EXPORT QskAbstractTextInput : public QskControl protected: QskAbstractTextInput( QQuickItem* parent = nullptr ); - - virtual QQuickItem* wrappedInput() = 0; - const QQuickItem* wrappedInput() const; + void setup( QQuickItem*, const QMetaObject* ); void forwardEvent( QEvent* ); diff --git a/src/controls/QskTextEdit.cpp b/src/controls/QskTextEdit.cpp index 808002d4..b26809a4 100644 --- a/src/controls/QskTextEdit.cpp +++ b/src/controls/QskTextEdit.cpp @@ -4,8 +4,6 @@ *****************************************************************************/ #include "QskTextEdit.h" -#include "QskTextEditSkinlet.h" -#include "QskFontRole.h" #include "QskInternalMacros.h" #include "QskQuick.h" @@ -16,58 +14,6 @@ QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextEdit, TextPanel ) -static inline void qskPropagateReadOnly( QskTextEdit* edit ) -{ - Q_EMIT edit->readOnlyChanged( edit->isReadOnly() ); - - QEvent event( QEvent::ReadOnlyChange ); - QCoreApplication::sendEvent( edit, &event ); -} - -static inline void qskBindSignals( - const QQuickTextEdit* wrappedEdit, QskTextEdit* edit ) -{ - QObject::connect( wrappedEdit, &QQuickTextEdit::textChanged, - edit, [ edit ] { Q_EMIT edit->textChanged( edit->text() ); } ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::preeditTextChanged, - edit, [ edit ] { Q_EMIT edit->preeditTextChanged( edit->preeditText() ); } ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::readOnlyChanged, - edit, [ edit ] { qskPropagateReadOnly( edit ); } ); - - 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() ); } ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::lineCountChanged, - [ edit ] { Q_EMIT edit->lineCountChanged( edit->lineCount() ); } ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::textFormatChanged, - edit, [ edit ]( QQuickTextEdit::TextFormat format ) - { - Q_EMIT edit->textFormatChanged( static_cast< QskTextOptions::TextFormat >( format ) ); - } ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::selectByMouseChanged, - edit, &QskTextEdit::selectByMouseChanged ); - - QObject::connect( wrappedEdit, &QQuickTextEdit::tabStopDistanceChanged, - edit, &QskTextEdit::tabStopDistanceChanged ); - - - QObject::connect( wrappedEdit, &QQuickItem::implicitWidthChanged, - edit, &QskControl::resetImplicitSize ); - - QObject::connect( wrappedEdit, &QQuickItem::implicitHeightChanged, - edit, &QskControl::resetImplicitSize ); -} - namespace { class QuickTextEdit final : public QQuickTextEdit @@ -170,20 +116,14 @@ QskTextEdit::QskTextEdit( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - /* - QQuickTextEdit is a beast of almost 3.5k lines of code, we don't - want to reimplement that - at least not now. - So this is more or less a simple wrapper making everything - conforming to qskinny. - */ - m_data->wrappedEdit = new QuickTextEdit( this ); - qskBindSignals( m_data->wrappedEdit, this ); setAcceptedMouseButtons( m_data->wrappedEdit->acceptedMouseButtons() ); m_data->wrappedEdit->setAcceptedMouseButtons( Qt::NoButton ); initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding ); + + setup( m_data->wrappedEdit, &QQuickTextEdit::staticMetaObject ); } QskTextEdit::~QskTextEdit() @@ -263,10 +203,5 @@ 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 0f08354b..6ad2dbd7 100644 --- a/src/controls/QskTextEdit.h +++ b/src/controls/QskTextEdit.h @@ -49,8 +49,6 @@ class QSK_EXPORT QskTextEdit : public QskAbstractTextInput void tabStopDistanceChanged( qreal ); private: - QQuickItem* wrappedInput() override final; - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index e6228463..030ef80c 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -4,78 +4,17 @@ *****************************************************************************/ #include "QskTextInput.h" -#include "QskTextInputSkinlet.h" -#include "QskFontRole.h" #include "QskQuick.h" #include "QskInternalMacros.h" QSK_QT_PRIVATE_BEGIN #include #include - QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextInput, TextPanel ) - QSK_SYSTEM_STATE( QskTextInput, Error, QskAspect::FirstSystemState << 4 ) -static inline void qskPropagateReadOnly( QskTextInput* input ) -{ - Q_EMIT input->readOnlyChanged( input->isReadOnly() ); - - QEvent event( QEvent::ReadOnlyChange ); - QCoreApplication::sendEvent( input, &event ); -} - -static inline void qskBindSignals( - const QQuickTextInput* wrappedInput, QskTextInput* input ) -{ - QObject::connect( wrappedInput, &QQuickTextInput::textChanged, - input, [ input ] { Q_EMIT input->textChanged( input->text() ); } ); - - QObject::connect( wrappedInput, &QQuickTextInput::displayTextChanged, - input, [ input ] { Q_EMIT input->displayTextChanged( input->displayText() ); } ); - - QObject::connect( wrappedInput, &QQuickTextInput::textEdited, - input, [ input ] { Q_EMIT input->textEdited( input->text() ); } ); - - QObject::connect( wrappedInput, &QQuickTextInput::validatorChanged, - input, &QskTextInput::validatorChanged ); - - QObject::connect( wrappedInput, &QQuickTextInput::inputMaskChanged, - input, &QskTextInput::inputMaskChanged ); - - QObject::connect( wrappedInput, &QQuickTextInput::readOnlyChanged, - input, [ input ] { qskPropagateReadOnly( input ); } ); - - 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 ); - - QObject::connect( wrappedInput, &QQuickTextInput::wrapModeChanged, - input, [ input ] { Q_EMIT input->wrapModeChanged( input->wrapMode() ); } ); - - QObject::connect( wrappedInput, &QQuickTextInput::echoModeChanged, - input, [ input ] { Q_EMIT input->echoModeChanged( input->echoMode() ); } ); - - QObject::connect( wrappedInput, &QQuickTextInput::passwordCharacterChanged, - input, &QskTextInput::passwordCharacterChanged ); - - QObject::connect( wrappedInput, &QQuickTextInput::passwordMaskDelayChanged, - input, &QskTextInput::passwordMaskDelayChanged ); - - QObject::connect( wrappedInput, &QQuickItem::implicitWidthChanged, - input, &QskControl::resetImplicitSize ); - - QObject::connect( wrappedInput, &QQuickItem::implicitHeightChanged, - input, &QskControl::resetImplicitSize ); -} - namespace { class QuickTextInput final : public QQuickTextInput @@ -177,7 +116,6 @@ namespace { event( ev ); } - } class QskTextInput::PrivateData @@ -198,12 +136,13 @@ QskTextInput::QskTextInput( QQuickItem* parent ) */ m_data->wrappedInput = new QuickTextInput( this ); - qskBindSignals( m_data->wrappedInput, this ); setAcceptedMouseButtons( m_data->wrappedInput->acceptedMouseButtons() ); m_data->wrappedInput->setAcceptedMouseButtons( Qt::NoButton ); initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed ); + + setup( m_data->wrappedInput, &QQuickTextInput::staticMetaObject ); } QskTextInput::~QskTextInput() @@ -414,10 +353,5 @@ 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 26bbc1b1..de8d839b 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -86,9 +86,6 @@ class QSK_EXPORT QskTextInput : public QskAbstractTextInput void validatorChanged(); void inputMaskChanged( const QString& ); - protected: - QQuickItem* wrappedInput() override final; - private: class PrivateData; std::unique_ptr< PrivateData > m_data;