QskShortcut improved

This commit is contained in:
Uwe Rathmann 2017-12-05 17:40:21 +01:00
parent 85e3af73d9
commit c66292f605
3 changed files with 282 additions and 101 deletions

View File

@ -13,8 +13,11 @@
#include <QskGraphicLabel.h> #include <QskGraphicLabel.h>
#include <QskGradient.h> #include <QskGradient.h>
#include <QskAspect.h> #include <QskAspect.h>
#include <QskShortcut.h>
#include <QGuiApplication> #include <QGuiApplication>
#include <QKeySequence>
#include <QDebug>
class SubWindow : public QskSubWindow class SubWindow : public QskSubWindow
{ {
@ -30,6 +33,9 @@ public:
setSizePolicy( QskSizePolicy::MinimumExpanding, setSizePolicy( QskSizePolicy::MinimumExpanding,
QskSizePolicy::MinimumExpanding ); QskSizePolicy::MinimumExpanding );
QskShortcut::addShortcut( this, QKeySequence( Qt::Key_P ), true,
[=] { qDebug() << graphicSource; } );
} }
}; };

View File

@ -14,67 +14,70 @@
#include <map> #include <map>
static inline QShortcutMap& qskShortcutMap()
{
return QGuiApplicationPrivate::instance()->shortcutMap;
}
class QskShortcutHandler final : public QObject class QskShortcutHandler final : public QObject
{ {
public: public:
QskShortcutHandler(); QskShortcutHandler();
int addShortcut( QQuickItem*, const QKeySequence&, int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, const char* method ); const QObject* receiver, const char* method );
int addShortcut( QQuickItem*, const QKeySequence&, int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, QtPrivate::QSlotObjectBase* ); const QObject* receiver, QtPrivate::QSlotObjectBase* );
void remove( int id );
void setEnabled( int id, bool ); void setEnabled( int id, bool );
void setAutoRepeat( int id, bool repeat ); void setAutoRepeat( int id, bool repeat );
virtual bool eventFilter( QObject*, QEvent* ) override final; virtual bool eventFilter( QObject*, QEvent* ) override final;
private: private:
int addShortcut( QQuickItem*, const QKeySequence&, int insert( QQuickItem*, const QKeySequence&,
const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* ); const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* );
void cleanUp( QObject* ); void cleanUp( QObject* );
static bool contextMatcher( QObject*, Qt::ShortcutContext ); static bool contextMatcher( QObject*, Qt::ShortcutContext );
static QShortcutMap& map();
class Data class InvokeData
{ {
public: public:
Data(): InvokeData():
item( nullptr ), item( nullptr ),
receiver( nullptr ), receiver( nullptr ),
slotObj( nullptr ) slotObject( nullptr )
{ {
} }
~Data() ~InvokeData()
{ {
if ( slotObj ) if ( slotObject )
slotObj->destroyIfLastRef(); slotObject->destroyIfLastRef();
} }
QQuickItem* item; QQuickItem* item;
const QObject* receiver; const QObject* receiver;
QMetaMethod method; 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() QskShortcutHandler::QskShortcutHandler()
{ {
installEventFilter( this ); installEventFilter( this );
} }
inline QShortcutMap& QskShortcutHandler::map() int QskShortcutHandler::add(
{
return QGuiApplicationPrivate::instance()->shortcutMap;
}
int QskShortcutHandler::addShortcut(
QQuickItem* item, const QKeySequence& key, QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const char* method ) const QObject* receiver, const char* method )
{ {
@ -89,7 +92,7 @@ int QskShortcutHandler::addShortcut(
if ( methodIndex >= 0 ) if ( methodIndex >= 0 )
{ {
id = addShortcut( item, key, id = insert( item, key,
receiver, metaObject->method( methodIndex ), nullptr ); receiver, metaObject->method( methodIndex ), nullptr );
} }
} }
@ -97,38 +100,40 @@ int QskShortcutHandler::addShortcut(
return id; return id;
} }
int QskShortcutHandler::addShortcut( QQuickItem* item, const QKeySequence& key, int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key,
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) 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, QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const QMetaMethod& method, const QObject* receiver, const QMetaMethod& method,
QtPrivate::QSlotObjectBase* slotObj ) QtPrivate::QSlotObjectBase* slotObject )
{ {
if ( receiver == nullptr ) if ( receiver )
return 0; {
receiver->disconnect( this );
#if 1 connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
// multiple connections ???? }
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
#endif
int id = 0; int id = 0;
auto& map = qskShortcutMap();
if ( item ) if ( item )
{ {
item->installEventFilter( this );
if ( item != receiver ) if ( item != receiver )
{
item->disconnect( this );
connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
id = map().addShortcut( item, key, Qt::WindowShortcut, contextMatcher ); id = map.addShortcut( item, key, Qt::WindowShortcut, contextMatcher );
} }
else else
{ {
id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); id = map.addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher );
} }
auto& data = m_invokeDataMap[ id ]; auto& data = m_invokeDataMap[ id ];
@ -136,23 +141,66 @@ int QskShortcutHandler::addShortcut(
data.item = item; data.item = item;
data.receiver = receiver; data.receiver = receiver;
if ( slotObj ) if ( slotObject )
data.slotObj = slotObj; data.slotObject = slotObject;
else else
data.method = method; data.method = method;
return id; 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 ) 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(); ) for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); )
{ {
const auto& data = it->second; 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 ); it = m_invokeDataMap.erase( it );
continue; continue;
@ -190,9 +238,13 @@ bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext co
if ( !item->hasFocus() ) if ( !item->hasFocus() )
return false; 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 ) 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 ) 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 ) bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
@ -214,6 +268,9 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
if ( event->type() != QEvent::Shortcut ) if ( event->type() != QEvent::Shortcut )
return false; return false;
if ( object != this )
object->removeEventFilter( this );
const QShortcutEvent* se = static_cast< const QShortcutEvent* >( event ); const QShortcutEvent* se = static_cast< const QShortcutEvent* >( event );
#if 0 #if 0
@ -225,29 +282,29 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
const auto it = m_invokeDataMap.find( se->shortcutId() ); const auto it = m_invokeDataMap.find( se->shortcutId() );
if ( it != m_invokeDataMap.end() ) 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 }; void* args[] = { 0 };
if ( receiver && receiver->thread() != thread() ) if ( receiver && receiver->thread() != thread() )
{ {
QCoreApplication::postEvent( receiver, QCoreApplication::postEvent( receiver,
new QMetaCallEvent( data.slotObj, nullptr, 0, 0, nullptr, args ) ); new QMetaCallEvent( invokeData.slotObject, nullptr, 0, 0, nullptr, args ) );
} }
else else
{ {
data.slotObj->call( receiver, args ); invokeData.slotObject->call( receiver, args );
} }
} }
else else
{ {
data.method.invoke( receiver, Qt::AutoConnection ); invokeData.method.invoke( receiver, Qt::AutoConnection );
} }
return true; return true;
@ -257,41 +314,25 @@ bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
return false; return false;
} }
Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler ) int QskShortcut::addMethod( QQuickItem* item, const QKeySequence& key,
int QskShortcut::addShortcut( const QKeySequence& key,
bool autoRepeat, const QObject* receiver, const char* method ) bool autoRepeat, const QObject* receiver, const char* method )
{ {
QQuickWindow* window = nullptr; if ( receiver == nullptr )
return addShortcut( window, key, autoRepeat, receiver, method ); {
} return 0;
}
int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key, int id = qskShortcutHandler->add( item, key, receiver, method );
bool autoRepeat, const QObject* receiver, const char* method )
{
auto item = window ? window->contentItem() : nullptr;
int id = qskShortcutHandler->addShortcut( item, key, receiver, method );
if ( id && !autoRepeat ) if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false ); qskShortcutHandler->setAutoRepeat( id, false );
return id; return id;
} }
int QskShortcut::addShortcut( QQuickItem* item, const QKeySequence& key, int QskShortcut::addSlotObject( QQuickItem* item, const QKeySequence& key,
bool autoRepeat, const QObject* receiver, const char* method ) bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObject )
{ {
int id = qskShortcutHandler->addShortcut( item, key, receiver, method ); int id = qskShortcutHandler->add( item, key, receiver, slotObject );
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 );
if ( id && !autoRepeat ) if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false ); qskShortcutHandler->setAutoRepeat( id, false );
@ -307,3 +348,8 @@ void QskShortcut::setEnabled( int id, bool on )
{ {
qskShortcutHandler->setEnabled( id, on ); qskShortcutHandler->setEnabled( id, on );
} }
void QskShortcut::removeShortcut( int id )
{
qskShortcutHandler->remove( id );
}

View File

@ -7,15 +7,20 @@
#define QSK_SHORTCUT_H #define QSK_SHORTCUT_H
#include "QskGlobal.h" #include "QskGlobal.h"
#include <QObject> #include <QQuickWindow>
class QQuickWindow;
class QQuickItem; class QQuickItem;
class QKeySequence; class QKeySequence;
class QSK_EXPORT QskShortcut class QSK_EXPORT QskShortcut
{ {
public: 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, static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method ); const QObject* receiver, const char* method );
@ -25,50 +30,65 @@ public:
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method ); const QObject* receiver, const char* method );
// shortcut calling a QObject method // -- calling a QObject method
template< typename Func1 > template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat, static int addShortcut( const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ); 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 > template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat, static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot ); 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 > template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot ); static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot );
static void setAutoRepeat( int, bool on ); template< typename Func1 >
static void setEnabled( int, bool on ); static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, Func1 slot );
template< typename Func1 >
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, Func1 slot );
private: private:
QskShortcut() = delete; QskShortcut() = delete;
~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* ); const QObject* receiver, QtPrivate::QSlotObjectBase* );
}; };
// shortcut calling a QObject method
template< typename Func1 > template< typename Func1 >
inline int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, inline int QskShortcut::addFunctionSlotObject(
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot ) QQuickItem* item, const QKeySequence& key, bool autoRepeat,
{
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 ) const QObject* context, Func1 slot )
{ {
using namespace QtPrivate; using namespace QtPrivate;
@ -84,14 +104,123 @@ int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat,
auto slotObj = new QFunctorSlotObject< Func1, 0, auto slotObj = new QFunctorSlotObject< Func1, 0,
typename List_Left< void, 0 >::Value, void >( std::move( slot ) ); 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 > template< typename Func1 >
int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, Func1 slot ) int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, Func1 slot )
{ {
return addShortcut( key, autoRepeat, nullptr, slot ); return addFunctionSlotObject( nullptr, key, autoRepeat, nullptr, slot );
} }
#endif #endif