diff --git a/src/controls/QskShortcut.cpp b/src/controls/QskShortcut.cpp index 8b676ba0..ccc43236 100644 --- a/src/controls/QskShortcut.cpp +++ b/src/controls/QskShortcut.cpp @@ -4,106 +4,157 @@ *****************************************************************************/ #include "QskShortcut.h" +#include "QskControl.h" -#include -#include #include -#include +#include +#include #include #include +#include + class QskShortcutHandler final : public QObject { public: - int addShortcut( QQuickWindow*, const QKeySequence&, - const QObject* receiver, const QMetaMethod& ); + QskShortcutHandler(); - int addShortcut( QQuickWindow*, const QKeySequence&, + int addShortcut( QQuickItem*, const QKeySequence&, + const QObject* receiver, const char* method ); + + int addShortcut( QQuickItem*, const QKeySequence&, const QObject* receiver, QtPrivate::QSlotObjectBase* ); void setEnabled( int id, bool ); void setAutoRepeat( int id, bool repeat ); -protected: - virtual bool event( QEvent* event ) override final; + virtual bool eventFilter( QObject*, QEvent* ) override final; private: + int addShortcut( QQuickItem*, const QKeySequence&, + const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* ); + void cleanUp( QObject* ); static bool contextMatcher( QObject*, Qt::ShortcutContext ); static QShortcutMap& map(); - class InvokeData + class Data { public: - ~InvokeData() + Data(): + item( nullptr ), + receiver( nullptr ), + slotObj( nullptr ) + { + } + + ~Data() { if ( slotObj ) slotObj->destroyIfLastRef(); } + QQuickItem* item; + const QObject* receiver; QMetaMethod method; QtPrivate::QSlotObjectBase* slotObj; - const QObject* receiver; }; - QMap< int, InvokeData > m_invokeDataMap; + std::map< int, Data > m_invokeDataMap; }; +QskShortcutHandler::QskShortcutHandler() +{ + installEventFilter( this ); +} + inline QShortcutMap& QskShortcutHandler::map() { return QGuiApplicationPrivate::instance()->shortcutMap; } int QskShortcutHandler::addShortcut( - QQuickWindow* window, const QKeySequence& key, - const QObject* receiver, const QMetaMethod& method ) + QQuickItem* item, const QKeySequence& key, + const QObject* receiver, const char* method ) { - int id; + int id = 0; - if ( window ) - id = map().addShortcut( window, key, Qt::WindowShortcut, contextMatcher ); - else - id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); + if ( receiver ) + { + const QMetaObject* metaObject = receiver->metaObject(); - InvokeData& data = m_invokeDataMap[ id ]; - data.receiver = receiver; - data.method = method; - data.slotObj = nullptr; + const int methodIndex = metaObject->indexOfMethod( + QMetaObject::normalizedSignature( method ).constData() + 1 ); + + if ( methodIndex >= 0 ) + { + id = addShortcut( item, key, + receiver, metaObject->method( methodIndex ), nullptr ); + } + } return id; } +int QskShortcutHandler::addShortcut( QQuickItem* item, const QKeySequence& key, + const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) +{ + return addShortcut( item, key, receiver, QMetaMethod(), slotObj ); +} + int QskShortcutHandler::addShortcut( - QQuickWindow* window, const QKeySequence& key, - const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) + QQuickItem* item, const QKeySequence& key, + const QObject* receiver, const QMetaMethod& method, + QtPrivate::QSlotObjectBase* slotObj ) { - int id; + if ( receiver == nullptr ) + return 0; - if ( window ) - id = map().addShortcut( window, key, Qt::WindowShortcut, contextMatcher ); +#if 1 + // multiple connections ???? + connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); +#endif + + int id = 0; + + if ( item ) + { + item->installEventFilter( this ); + if ( item != receiver ) + connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); + + id = map().addShortcut( item, key, Qt::WindowShortcut, contextMatcher ); + } else + { id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); + } - InvokeData& data = m_invokeDataMap[ id ]; + auto& data = m_invokeDataMap[ id ]; + + data.item = item; data.receiver = receiver; - data.slotObj = slotObj; - if ( receiver ) - connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); + if ( slotObj ) + data.slotObj = slotObj; + else + data.method = method; return id; } -void QskShortcutHandler::cleanUp( QObject* receiver ) +void QskShortcutHandler::cleanUp( QObject* object ) { - map().removeShortcut( 0, receiver ); - for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); ) { - if ( it->receiver == receiver ) + const auto& data = it->second; + + if ( data.receiver == object || data.item == object ) { + map().removeShortcut( it->first, this ); it = m_invokeDataMap.erase( it ); + continue; } @@ -111,21 +162,41 @@ void QskShortcutHandler::cleanUp( QObject* receiver ) } } -bool QskShortcutHandler::contextMatcher( QObject* obj, Qt::ShortcutContext context ) +bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext context ) { - switch ( context ) + if ( context == Qt::ApplicationShortcut ) + return true; + + if ( context == Qt::WindowShortcut ) { - case Qt::ApplicationShortcut: + const auto focusWindow = QGuiApplication::focusWindow(); + + if ( auto item = qobject_cast< const QQuickItem* >( object ) ) { - return true; + const auto window = item->window(); + if ( window == nullptr || window != focusWindow ) + { + return false; + } + + while ( item ) + { + /* + We have to find out if the active focus is inside + the surronding shortcut scope. + */ + if ( QskControl::isShortcutScope( item ) ) + { + if ( !item->hasFocus() ) + return false; + } + } + + item = item->parentItem(); } - case Qt::WindowShortcut: - { - return obj == QGuiApplication::focusWindow(); - } - default: - return false; } + + return false; } void QskShortcutHandler::setEnabled( int id, bool enabled ) @@ -138,32 +209,51 @@ void QskShortcutHandler::setAutoRepeat( int id, bool repeat ) map().setShortcutAutoRepeat( repeat, id, this ); } -bool QskShortcutHandler::event( QEvent* event ) +bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event ) { - if ( event->type() == QEvent::Shortcut ) + if ( event->type() != QEvent::Shortcut ) + return false; + + const QShortcutEvent* se = static_cast< const QShortcutEvent* >( event ); + +#if 0 + // do we want to handle this ??? + if ( se->isAmbiguous() ) + .... +#endif + + const auto it = m_invokeDataMap.find( se->shortcutId() ); + if ( it != m_invokeDataMap.end() ) { - const QShortcutEvent* se = static_cast< const QShortcutEvent* >( event ); + const Data& data = it->second; - const auto it = m_invokeDataMap.constFind( se->shortcutId() ); - if ( it != m_invokeDataMap.constEnd() ) + Q_ASSERT( data.item == nullptr || data.item == object ); + + auto receiver = const_cast< QObject* >( data.receiver ); + + if ( data.slotObj ) { - const InvokeData& data = ( *it ); - auto receiver = const_cast< QObject* >( data.receiver ); + void* args[] = { 0 }; - if ( data.slotObj ) + if ( receiver && receiver->thread() != thread() ) { - void* args[] = { 0 }; - data.slotObj->call( receiver, args ); + QCoreApplication::postEvent( receiver, + new QMetaCallEvent( data.slotObj, nullptr, 0, 0, nullptr, args ) ); } else { - data.method.invoke( receiver, Qt::AutoConnection ); + data.slotObj->call( receiver, args ); } } + else + { + data.method.invoke( receiver, Qt::AutoConnection ); + } return true; } + // seems like someone else is also interested in shortcuts return false; } @@ -172,30 +262,28 @@ Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler ) int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, const QObject* receiver, const char* method ) { - return addShortcut( nullptr, key, autoRepeat, receiver, method ); + QQuickWindow* window = nullptr; + return addShortcut( window, key, autoRepeat, receiver, method ); } int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, bool autoRepeat, const QObject* receiver, const char* method ) { - int id = 0; + auto item = window ? window->contentItem() : nullptr; - if ( receiver == nullptr ) - return id; + int id = qskShortcutHandler->addShortcut( item, key, receiver, method ); + if ( id && !autoRepeat ) + qskShortcutHandler->setAutoRepeat( id, false ); - const QMetaObject* metaObject = receiver->metaObject(); + return id; +} - const int methodIndex = metaObject->indexOfMethod( - QMetaObject::normalizedSignature( method ).constData() + 1 ); - - if ( methodIndex >= 0 ) - { - id = qskShortcutHandler->addShortcut( - window, key, receiver, metaObject->method( methodIndex ) ); - - if ( !autoRepeat ) - qskShortcutHandler->setAutoRepeat( id, false ); - } +int QskShortcut::addShortcut( QQuickItem* item, const QKeySequence& key, + bool autoRepeat, const QObject* receiver, const char* method ) +{ + int id = qskShortcutHandler->addShortcut( item, key, receiver, method ); + if ( id && !autoRepeat ) + qskShortcutHandler->setAutoRepeat( id, false ); return id; } @@ -203,18 +291,8 @@ int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, 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 ) + int id = qskShortcutHandler->addShortcut( nullptr, key, receiver, slotObj ); + if ( id && !autoRepeat ) qskShortcutHandler->setAutoRepeat( id, false ); return id; diff --git a/src/controls/QskShortcut.h b/src/controls/QskShortcut.h index 70131cd9..8d0788e0 100644 --- a/src/controls/QskShortcut.h +++ b/src/controls/QskShortcut.h @@ -7,71 +7,91 @@ #define QSK_SHORTCUT_H #include "QskGlobal.h" - -#include #include class QQuickWindow; +class QQuickItem; class QKeySequence; -class QObject; -namespace QskShortcut +class QSK_EXPORT QskShortcut { - QSK_EXPORT void setAutoRepeat( int, bool on ); - QSK_EXPORT void setEnabled( int, bool on ); - - QSK_EXPORT int addShortcut( const QKeySequence&, bool autoRepeat, +public: + static int addShortcut( const QKeySequence&, bool autoRepeat, const QObject* receiver, const char* method ); - QSK_EXPORT int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, + static 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* ); + static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, + const QObject* receiver, const char* method ); // 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 ); - } + static int addShortcut( const QKeySequence&, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ); // 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 ); - } + static int addShortcut( const QKeySequence&, bool autoRepeat, + const QObject* context, Func1 slot ); // 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 ); - } + static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot ); + + static void setAutoRepeat( int, bool on ); + static void setEnabled( int, bool on ); + +private: + QskShortcut() = delete; + ~QskShortcut() = delete; + + static int addShortcutImpl( const QKeySequence&, bool autoRepeat, + const QObject* receiver, QtPrivate::QSlotObjectBase* ); +}; + +// shortcut calling a QObject method +template< typename Func1 > +inline int QskShortcut::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 QskShortcut::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 QskShortcut::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 d8310302..4554400b 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 ), - false, &s_shortcut, SLOT( rotateSkin() ) ); + false, &s_shortcut, &SkinnyShortcut::rotateSkin ); cout << "CTRL-S to change the skin." << endl; } if ( types & DebugBackground ) { QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ), - false, &s_shortcut, SLOT( showBackground() ) ); + false, &s_shortcut, &SkinnyShortcut::showBackground ); cout << "CTRL-B to enable visual debugging modes." << endl; } if ( types & DebugStatistics ) { QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ), - false, &s_shortcut, SLOT( debugStatistics() ) ); + false, &s_shortcut, &SkinnyShortcut::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 ), - false, QGuiApplication::instance(), SLOT( quit() ) ); + false, QGuiApplication::instance(), &QGuiApplication::quit ); cout << "CTRL-Q to terminate the application." << endl; } }