From c66292f605eb5379cb1881b5b2f6208cbec228c6 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 5 Dec 2017 17:40:21 +0100 Subject: [PATCH] QskShortcut improved --- examples/desktop/main.cpp | 6 ++ src/controls/QskShortcut.cpp | 190 ++++++++++++++++++++++------------- src/controls/QskShortcut.h | 187 ++++++++++++++++++++++++++++------ 3 files changed, 282 insertions(+), 101 deletions(-) diff --git a/examples/desktop/main.cpp b/examples/desktop/main.cpp index 38caf28a..e5a8ce69 100644 --- a/examples/desktop/main.cpp +++ b/examples/desktop/main.cpp @@ -13,8 +13,11 @@ #include #include #include +#include #include +#include +#include class SubWindow : public QskSubWindow { @@ -30,6 +33,9 @@ public: setSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::MinimumExpanding ); + + QskShortcut::addShortcut( this, QKeySequence( Qt::Key_P ), true, + [=] { qDebug() << graphicSource; } ); } }; diff --git a/src/controls/QskShortcut.cpp b/src/controls/QskShortcut.cpp index ccc43236..c529e220 100644 --- a/src/controls/QskShortcut.cpp +++ b/src/controls/QskShortcut.cpp @@ -14,67 +14,70 @@ #include +static inline QShortcutMap& qskShortcutMap() +{ + return QGuiApplicationPrivate::instance()->shortcutMap; +} + class QskShortcutHandler final : public QObject { public: QskShortcutHandler(); - int addShortcut( QQuickItem*, const QKeySequence&, + int add( QQuickItem*, const QKeySequence&, const QObject* receiver, const char* method ); - int addShortcut( QQuickItem*, const QKeySequence&, + int add( QQuickItem*, const QKeySequence&, const QObject* receiver, QtPrivate::QSlotObjectBase* ); + void remove( int id ); + void setEnabled( int id, bool ); void setAutoRepeat( int id, bool repeat ); virtual bool eventFilter( QObject*, QEvent* ) override final; private: - int addShortcut( QQuickItem*, const QKeySequence&, + int insert( QQuickItem*, const QKeySequence&, const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* ); void cleanUp( QObject* ); static bool contextMatcher( QObject*, Qt::ShortcutContext ); - static QShortcutMap& map(); - class Data + class InvokeData { public: - Data(): + InvokeData(): item( nullptr ), receiver( nullptr ), - slotObj( nullptr ) + slotObject( nullptr ) { } - ~Data() + ~InvokeData() { - if ( slotObj ) - slotObj->destroyIfLastRef(); + if ( slotObject ) + slotObject->destroyIfLastRef(); } QQuickItem* item; const QObject* receiver; QMetaMethod method; - QtPrivate::QSlotObjectBase* slotObj; + QtPrivate::QSlotObjectBase* slotObject; }; - std::map< int, Data > m_invokeDataMap; + std::map< int, InvokeData > m_invokeDataMap; }; +Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler ) + QskShortcutHandler::QskShortcutHandler() { installEventFilter( this ); } -inline QShortcutMap& QskShortcutHandler::map() -{ - return QGuiApplicationPrivate::instance()->shortcutMap; -} - -int QskShortcutHandler::addShortcut( +int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key, const QObject* receiver, const char* method ) { @@ -89,7 +92,7 @@ int QskShortcutHandler::addShortcut( if ( methodIndex >= 0 ) { - id = addShortcut( item, key, + id = insert( item, key, receiver, metaObject->method( methodIndex ), nullptr ); } } @@ -97,38 +100,40 @@ int QskShortcutHandler::addShortcut( return id; } -int QskShortcutHandler::addShortcut( QQuickItem* item, const QKeySequence& key, +int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) { - return addShortcut( item, key, receiver, QMetaMethod(), slotObj ); + return insert( item, key, receiver, QMetaMethod(), slotObj ); } -int QskShortcutHandler::addShortcut( +int QskShortcutHandler::insert( QQuickItem* item, const QKeySequence& key, const QObject* receiver, const QMetaMethod& method, - QtPrivate::QSlotObjectBase* slotObj ) + QtPrivate::QSlotObjectBase* slotObject ) { - if ( receiver == nullptr ) - return 0; - -#if 1 - // multiple connections ???? - connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); -#endif + if ( receiver ) + { + receiver->disconnect( this ); + connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); + } int id = 0; + auto& map = qskShortcutMap(); + if ( item ) { - item->installEventFilter( this ); if ( item != receiver ) + { + item->disconnect( this ); connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); + } - id = map().addShortcut( item, key, Qt::WindowShortcut, contextMatcher ); + id = map.addShortcut( item, key, Qt::WindowShortcut, contextMatcher ); } else { - id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); + id = map.addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); } auto& data = m_invokeDataMap[ id ]; @@ -136,23 +141,66 @@ int QskShortcutHandler::addShortcut( data.item = item; data.receiver = receiver; - if ( slotObj ) - data.slotObj = slotObj; + if ( slotObject ) + data.slotObject = slotObject; else data.method = method; return id; } +void QskShortcutHandler::remove( int id ) +{ + auto it = m_invokeDataMap.find( id ); + if ( it == m_invokeDataMap.end() ) + return; + + auto& map = qskShortcutMap(); + map.removeShortcut( id, nullptr ); + + const QQuickItem* item = it->second.item; + const QObject* receiver = it->second.receiver; + + m_invokeDataMap.erase( it ); + + /* + Finally let's check if we can disconnect + from the destroyed signals + */ + for ( const auto& entry : qskAsConst( m_invokeDataMap ) ) + { + if ( item == nullptr && receiver == nullptr ) + break; + + if ( entry.second.item == item ) + item = nullptr; + + if ( entry.second.receiver == receiver ) + receiver = nullptr; + } + + if ( item ) + item->disconnect( this ); + + if ( receiver && receiver != item ) + receiver->disconnect( this ); +} + void QskShortcutHandler::cleanUp( QObject* object ) { + /* + When item != receiver we might remain being connected + to destroyed signals we are not interested in. + */ + auto& map = qskShortcutMap(); + for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); ) { const auto& data = it->second; - if ( data.receiver == object || data.item == object ) + if ( data.item == object || data.receiver == object ) { - map().removeShortcut( it->first, this ); + map.removeShortcut( it->first, nullptr ); it = m_invokeDataMap.erase( it ); continue; @@ -190,9 +238,13 @@ bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext co if ( !item->hasFocus() ) return false; } + + item = item->parentItem(); } - item = item->parentItem(); + // we want to process the following QShortcutEvent + object->installEventFilter( qskShortcutHandler ); + return true; } } @@ -201,12 +253,14 @@ bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext co void QskShortcutHandler::setEnabled( int id, bool enabled ) { - map().setShortcutEnabled( enabled, id, this ); + auto& map = qskShortcutMap(); + map.setShortcutEnabled( enabled, id, this ); } void QskShortcutHandler::setAutoRepeat( int id, bool repeat ) { - map().setShortcutAutoRepeat( repeat, id, this ); + auto& map = qskShortcutMap(); + map.setShortcutAutoRepeat( repeat, id, this ); } bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event ) @@ -214,6 +268,9 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event ) if ( event->type() != QEvent::Shortcut ) return false; + if ( object != this ) + object->removeEventFilter( this ); + const QShortcutEvent* se = static_cast< const QShortcutEvent* >( event ); #if 0 @@ -225,29 +282,29 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event ) const auto it = m_invokeDataMap.find( se->shortcutId() ); if ( it != m_invokeDataMap.end() ) { - const Data& data = it->second; + const auto& invokeData = it->second; - Q_ASSERT( data.item == nullptr || data.item == object ); + Q_ASSERT( invokeData.item == nullptr || invokeData.item == object ); - auto receiver = const_cast< QObject* >( data.receiver ); + auto receiver = const_cast< QObject* >( invokeData.receiver ); - if ( data.slotObj ) + if ( invokeData.slotObject ) { void* args[] = { 0 }; if ( receiver && receiver->thread() != thread() ) { QCoreApplication::postEvent( receiver, - new QMetaCallEvent( data.slotObj, nullptr, 0, 0, nullptr, args ) ); + new QMetaCallEvent( invokeData.slotObject, nullptr, 0, 0, nullptr, args ) ); } else { - data.slotObj->call( receiver, args ); + invokeData.slotObject->call( receiver, args ); } } else { - data.method.invoke( receiver, Qt::AutoConnection ); + invokeData.method.invoke( receiver, Qt::AutoConnection ); } return true; @@ -257,41 +314,25 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event ) return false; } -Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler ) - -int QskShortcut::addShortcut( const QKeySequence& key, +int QskShortcut::addMethod( QQuickItem* item, const QKeySequence& key, bool autoRepeat, const QObject* receiver, const char* method ) { - QQuickWindow* window = nullptr; - return addShortcut( window, key, autoRepeat, receiver, method ); -} + if ( receiver == nullptr ) + { + return 0; + } -int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, - bool autoRepeat, const QObject* receiver, const char* method ) -{ - auto item = window ? window->contentItem() : nullptr; - - int id = qskShortcutHandler->addShortcut( item, key, receiver, method ); + int id = qskShortcutHandler->add( item, key, receiver, method ); if ( id && !autoRepeat ) qskShortcutHandler->setAutoRepeat( id, false ); return id; } -int QskShortcut::addShortcut( QQuickItem* item, const QKeySequence& key, - bool autoRepeat, const QObject* receiver, const char* method ) +int QskShortcut::addSlotObject( QQuickItem* item, const QKeySequence& key, + bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObject ) { - int id = qskShortcutHandler->addShortcut( item, key, receiver, method ); - if ( id && !autoRepeat ) - qskShortcutHandler->setAutoRepeat( id, false ); - - return id; -} - -int QskShortcut::addShortcutImpl( const QKeySequence& key, - bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) -{ - int id = qskShortcutHandler->addShortcut( nullptr, key, receiver, slotObj ); + int id = qskShortcutHandler->add( item, key, receiver, slotObject ); if ( id && !autoRepeat ) qskShortcutHandler->setAutoRepeat( id, false ); @@ -307,3 +348,8 @@ void QskShortcut::setEnabled( int id, bool on ) { qskShortcutHandler->setEnabled( id, on ); } + +void QskShortcut::removeShortcut( int id ) +{ + qskShortcutHandler->remove( id ); +} diff --git a/src/controls/QskShortcut.h b/src/controls/QskShortcut.h index 8d0788e0..2c7bd033 100644 --- a/src/controls/QskShortcut.h +++ b/src/controls/QskShortcut.h @@ -7,15 +7,20 @@ #define QSK_SHORTCUT_H #include "QskGlobal.h" -#include +#include -class QQuickWindow; class QQuickItem; class QKeySequence; class QSK_EXPORT QskShortcut { public: + static void setAutoRepeat( int, bool on ); + static void setEnabled( int, bool on ); + + static void removeShortcut( int ); + + // -- traditional slots static int addShortcut( const QKeySequence&, bool autoRepeat, const QObject* receiver, const char* method ); @@ -25,50 +30,65 @@ public: static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, const QObject* receiver, const char* method ); - // shortcut calling a QObject method + // -- calling a QObject method template< typename Func1 > 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 > + static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ); + + template< typename Func1 > + static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ); + + // -- calling a functor or function pointer inside a thread context template< typename Func1 > static int addShortcut( const QKeySequence&, bool autoRepeat, const QObject* context, Func1 slot ); - // shortcut calling a functor or function pointer + template< typename Func1 > + static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, + const QObject* context, Func1 slot ); + + template< typename Func1 > + static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, + const QObject* context, Func1 slot ); + + // -- calling a functor or function pointer template< typename Func1 > static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot ); - static void setAutoRepeat( int, bool on ); - static void setEnabled( int, bool on ); + template< typename Func1 > + static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, Func1 slot ); + + template< typename Func1 > + static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, Func1 slot ); private: QskShortcut() = delete; ~QskShortcut() = delete; - static int addShortcutImpl( const QKeySequence&, bool autoRepeat, + static int addMethod( QQuickItem*, const QKeySequence&, bool autoRepeat, + const QObject* receiver, const char* method ); + + template< typename Func1 > + static int addMemberSlotObject( QQuickItem*, const QKeySequence&, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ); + + template< typename Func1 > + static int addFunctionSlotObject( QQuickItem*, const QKeySequence&, + bool autoRepeat, const QObject* context, Func1 slot ); + + static int addSlotObject( + QQuickItem* item, 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, +inline int QskShortcut::addFunctionSlotObject( + QQuickItem* item, const QKeySequence& key, bool autoRepeat, const QObject* context, Func1 slot ) { using namespace QtPrivate; @@ -84,14 +104,123 @@ int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, auto slotObj = new QFunctorSlotObject< Func1, 0, typename List_Left< void, 0 >::Value, void >( std::move( slot ) ); - return addShortcutImpl( key, autoRepeat, context, slotObj ); + return addSlotObject( item, key, autoRepeat, context, slotObj ); +} + + +// -- traditional slots + +inline int QskShortcut::addShortcut( + QQuickItem* item, const QKeySequence& key, bool autoRepeat, + const QObject* receiver, const char* method ) +{ + return addMethod( item, key, autoRepeat, receiver, method ); +} + +inline int QskShortcut::addShortcut( + const QKeySequence& key, bool autoRepeat, + const QObject* receiver, const char* method ) +{ + return addMethod( nullptr, key, autoRepeat, receiver, method ); +} + +inline int QskShortcut::addShortcut( + QQuickWindow* window, const QKeySequence& key, bool autoRepeat, + const QObject* receiver, const char* method ) +{ + auto item = window ? window->contentItem() : nullptr; + return addMethod( item, key, autoRepeat, receiver, method ); +} + +// -- calling a QObject method +template< typename Func1 > +inline int QskShortcut::addMemberSlotObject( + QQuickItem* item, 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 slotObject = new QtPrivate::QSlotObject< Func1, + typename SlotType::Arguments, void >(slot); + + return addSlotObject( item, key, autoRepeat, receiver, slotObject ); +} + +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickItem* item, const QKeySequence& key, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ) +{ + return addMemberSlotObject( item, key, autoRepeat, receiver, slot ); +} + + +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickWindow* window, const QKeySequence& key, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ) +{ + auto item = window ? window->contentItem() : nullptr; + return addMemberSlotObject( item, key, autoRepeat, receiver, slot ); +} + +template< typename Func1 > +inline int QskShortcut::addShortcut( + const QKeySequence& key, bool autoRepeat, + const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ) +{ + return addMemberSlotObject( nullptr, key, autoRepeat, receiver, slot ); +} + +// -- calling a functor or function pointer with context +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickItem* item, const QKeySequence& key, bool autoRepeat, + const QObject* context, Func1 slot ) +{ + return addFunctionSlotObject( item, key, autoRepeat, context, slot ); +} + +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickWindow* window, const QKeySequence& key, bool autoRepeat, + const QObject* context, Func1 slot ) +{ + auto item = window ? window->contentItem() : nullptr; + return addFunctionSlotObject( item, key, autoRepeat, context, slot ); +} + +template< typename Func1 > +inline int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, + const QObject* context, Func1 slot ) +{ + return addFunctionSlotObject( nullptr, key, autoRepeat, context, slot ); +} + +// -- calling a functor or function pointer + +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickItem* item, const QKeySequence& key, bool autoRepeat, Func1 slot ) +{ + return addFunctionSlotObject( item, key, autoRepeat, nullptr, slot ); +} + +template< typename Func1 > +inline int QskShortcut::addShortcut( + QQuickWindow* window, const QKeySequence& key, bool autoRepeat, Func1 slot ) +{ + auto item = window ? window->contentItem() : nullptr; + return addFunctionSlotObject( item, key, autoRepeat, nullptr, autoRepeat, slot ); } -// 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 ); + return addFunctionSlotObject( nullptr, key, autoRepeat, nullptr, slot ); } #endif