diff --git a/examples/automotive/main.cpp b/examples/automotive/main.cpp index 5c70d370..e6a7a725 100644 --- a/examples/automotive/main.cpp +++ b/examples/automotive/main.cpp @@ -36,10 +36,10 @@ int main( int argc, char** argv ) cout << "CTRL-T to change the color scheme, when the \"Default\" skin is active." << endl; QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ), - &skinFactory, SLOT(toggleScheme()), false ); + false, &skinFactory, SLOT(toggleScheme()) ); QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ), - &skinFactory, SLOT(rotateSkin()), false ); + false, &skinFactory, SLOT(rotateSkin()) ); // With CTRL-B you can rotate a couple of visual debug modes SkinnyShortcut::enable( SkinnyShortcut::DebugBackground | diff --git a/src/controls/QskShortcut.cpp b/src/controls/QskShortcut.cpp index 4f9c88d1..8b676ba0 100644 --- a/src/controls/QskShortcut.cpp +++ b/src/controls/QskShortcut.cpp @@ -8,16 +8,18 @@ #include #include #include -#include #include +#include +#include class QskShortcutHandler final : public QObject { public: - static QskShortcutHandler* instance(); + int addShortcut( QQuickWindow*, const QKeySequence&, + const QObject* receiver, const QMetaMethod& ); int addShortcut( QQuickWindow*, const QKeySequence&, - QObject* receiver, const QMetaMethod&, Qt::ConnectionType type ); + const QObject* receiver, QtPrivate::QSlotObjectBase* ); void setEnabled( int id, bool ); void setAutoRepeat( int id, bool repeat ); @@ -26,34 +28,36 @@ protected: virtual bool event( QEvent* event ) override final; private: - QskShortcutHandler() = default; + void cleanUp( QObject* ); static bool contextMatcher( QObject*, Qt::ShortcutContext ); static QShortcutMap& map(); - struct InvokeData + class InvokeData { - QObject* receiver; + public: + ~InvokeData() + { + if ( slotObj ) + slotObj->destroyIfLastRef(); + } + QMetaMethod method; - Qt::ConnectionType connectionType; + QtPrivate::QSlotObjectBase* slotObj; + const QObject* receiver; }; QMap< int, InvokeData > m_invokeDataMap; }; -QskShortcutHandler* QskShortcutHandler::instance() -{ - static QskShortcutHandler handler; - return &handler; -} - inline QShortcutMap& QskShortcutHandler::map() { return QGuiApplicationPrivate::instance()->shortcutMap; } -int QskShortcutHandler::addShortcut( QQuickWindow* window, const QKeySequence& key, - QObject* receiver, const QMetaMethod& method, Qt::ConnectionType connectionType ) +int QskShortcutHandler::addShortcut( + QQuickWindow* window, const QKeySequence& key, + const QObject* receiver, const QMetaMethod& method ) { int id; @@ -65,11 +69,48 @@ int QskShortcutHandler::addShortcut( QQuickWindow* window, const QKeySequence& k InvokeData& data = m_invokeDataMap[ id ]; data.receiver = receiver; data.method = method; - data.connectionType = connectionType; + data.slotObj = nullptr; return id; } +int QskShortcutHandler::addShortcut( + QQuickWindow* window, const QKeySequence& key, + const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) +{ + int id; + + if ( window ) + id = map().addShortcut( window, key, Qt::WindowShortcut, contextMatcher ); + else + id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); + + InvokeData& data = m_invokeDataMap[ id ]; + data.receiver = receiver; + data.slotObj = slotObj; + + if ( receiver ) + connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); + + return id; +} + +void QskShortcutHandler::cleanUp( QObject* receiver ) +{ + map().removeShortcut( 0, receiver ); + + for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); ) + { + if ( it->receiver == receiver ) + { + it = m_invokeDataMap.erase( it ); + continue; + } + + ++it; + } +} + bool QskShortcutHandler::contextMatcher( QObject* obj, Qt::ShortcutContext context ) { switch ( context ) @@ -107,7 +148,17 @@ bool QskShortcutHandler::event( QEvent* event ) if ( it != m_invokeDataMap.constEnd() ) { const InvokeData& data = ( *it ); - data.method.invoke( data.receiver, data.connectionType ); + auto receiver = const_cast< QObject* >( data.receiver ); + + if ( data.slotObj ) + { + void* args[] = { 0 }; + data.slotObj->call( receiver, args ); + } + else + { + data.method.invoke( receiver, Qt::AutoConnection ); + } } return true; @@ -116,16 +167,16 @@ bool QskShortcutHandler::event( QEvent* event ) return false; } +Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler ) + int QskShortcut::addShortcut( const QKeySequence& key, - QObject* receiver, const char* method, - bool autoRepeat, Qt::ConnectionType connectionType ) + bool autoRepeat, const QObject* receiver, const char* method ) { - return addShortcut( nullptr, key, receiver, method, autoRepeat, connectionType ); + return addShortcut( nullptr, key, autoRepeat, receiver, method ); } int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, - QObject* receiver, const char* method, - bool autoRepeat, Qt::ConnectionType connectionType ) + bool autoRepeat, const QObject* receiver, const char* method ) { int id = 0; @@ -139,17 +190,42 @@ int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, if ( methodIndex >= 0 ) { - id = QskShortcutHandler::instance()->addShortcut( window, key, - receiver, metaObject->method( methodIndex ), connectionType ); + id = qskShortcutHandler->addShortcut( + window, key, receiver, metaObject->method( methodIndex ) ); if ( !autoRepeat ) - QskShortcutHandler::instance()->setAutoRepeat( id, false ); + qskShortcutHandler->setAutoRepeat( id, false ); } return id; } +int QskShortcut::addShortcutImpl( const QKeySequence& key, + bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) +{ +#if 1 + if ( receiver ) + { + // how to call the slot in the receiver context, TODO ... + Q_ASSERT( qskShortcutHandler->thread() == receiver->thread() ); + } +#endif + + QQuickWindow* window = nullptr; + int id = qskShortcutHandler->addShortcut( window, key, receiver, slotObj ); + + if ( !autoRepeat ) + qskShortcutHandler->setAutoRepeat( id, false ); + + return id; +} + void QskShortcut::setAutoRepeat( int id, bool on ) { - QskShortcutHandler::instance()->setAutoRepeat( id, on ); + qskShortcutHandler->setAutoRepeat( id, on ); +} + +void QskShortcut::setEnabled( int id, bool on ) +{ + qskShortcutHandler->setEnabled( id, on ); } diff --git a/src/controls/QskShortcut.h b/src/controls/QskShortcut.h index 21eba0ed..70131cd9 100644 --- a/src/controls/QskShortcut.h +++ b/src/controls/QskShortcut.h @@ -7,7 +7,9 @@ #define QSK_SHORTCUT_H #include "QskGlobal.h" -#include + +#include +#include class QQuickWindow; class QKeySequence; @@ -15,15 +17,61 @@ class QObject; namespace QskShortcut { - QSK_EXPORT int addShortcut( const QKeySequence&, - QObject* receiver, const char* method, bool autoRepeat = true, - Qt::ConnectionType type = Qt::AutoConnection ); - - QSK_EXPORT int addShortcut( QQuickWindow*, const QKeySequence&, - QObject* receiver, const char* method, bool autoRepeat = true, - Qt::ConnectionType type = Qt::AutoConnection ); - QSK_EXPORT void setAutoRepeat( int, bool on ); + QSK_EXPORT void setEnabled( int, bool on ); + + QSK_EXPORT int addShortcut( const QKeySequence&, bool autoRepeat, + const QObject* receiver, const char* method ); + + QSK_EXPORT int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, + const QObject* receiver, const char* method ); + + QSK_EXPORT int addShortcutImpl( const QKeySequence&, bool autoRepeat, + const QObject* receiver, QtPrivate::QSlotObjectBase* ); + + // shortcut calling a QObject method + template< typename Func1 > + inline int addShortcut( const QKeySequence& key, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ) + { + typedef QtPrivate::FunctionPointer< Func1 > SlotType; + + Q_STATIC_ASSERT_X( int( SlotType::ArgumentCount ) == 0, + "The slot must not have any arguments."); + + auto slotObj = new QtPrivate::QSlotObject< Func1, + typename SlotType::Arguments, void >(slot); + + return addShortcutImpl( key, autoRepeat, receiver, slotObj ); + } + + // shortcut calling a functor or function pointer + template< typename Func1 > + int addShortcut( const QKeySequence& key, bool autoRepeat, + const QObject* context, Func1 slot ) + { + using namespace QtPrivate; + + typedef FunctionPointer< Func1 > SlotType; + + Q_STATIC_ASSERT_X( int( SlotType::ArgumentCount ) <= 0, + "The slot must not have any arguments."); + + Q_STATIC_ASSERT_X( !SlotType::IsPointerToMemberFunction, + "The slot must be no member function." ); + + auto slotObj = new QFunctorSlotObject< Func1, 0, + typename List_Left< void, 0 >::Value, void >( std::move( slot ) ); + + return addShortcutImpl( key, autoRepeat, context, slotObj ); + } + + // shortcut calling a functor or function pointer + template< typename Func1 > + int addShortcut( const QKeySequence& key, bool autoRepeat, Func1 slot ) + { + return addShortcut( key, autoRepeat, nullptr, slot ); + } } #endif diff --git a/support/SkinnyShortcut.cpp b/support/SkinnyShortcut.cpp index 65372906..d8310302 100644 --- a/support/SkinnyShortcut.cpp +++ b/support/SkinnyShortcut.cpp @@ -32,21 +32,21 @@ void SkinnyShortcut::enable( Types types ) if ( types & RotateSkin ) { QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ), - &s_shortcut, SLOT( rotateSkin() ) ); + false, &s_shortcut, SLOT( rotateSkin() ) ); cout << "CTRL-S to change the skin." << endl; } if ( types & DebugBackground ) { QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ), - &s_shortcut, SLOT( showBackground() ) ); + false, &s_shortcut, SLOT( showBackground() ) ); cout << "CTRL-B to enable visual debugging modes." << endl; } if ( types & DebugStatistics ) { QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ), - &s_shortcut, SLOT( debugStatistics() ) ); + false, &s_shortcut, SLOT( debugStatistics() ) ); cout << "CTRL-K to dump statistics about the items/nodes being currently used." << endl; } @@ -56,7 +56,7 @@ void SkinnyShortcut::enable( Types types ) // when not being implemented by the platform !! QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_Q ), - QGuiApplication::instance(), SLOT( quit() ) ); + false, QGuiApplication::instance(), SLOT( quit() ) ); cout << "CTRL-Q to terminate the application." << endl; } }