support for shortcut connections on different threads added

This commit is contained in:
Uwe Rathmann 2017-12-05 13:10:50 +01:00
parent 8175719679
commit 85e3af73d9
3 changed files with 233 additions and 135 deletions

View File

@ -4,106 +4,157 @@
*****************************************************************************/ *****************************************************************************/
#include "QskShortcut.h" #include "QskShortcut.h"
#include "QskControl.h"
#include <QObject>
#include <QKeySequence>
#include <QQuickWindow> #include <QQuickWindow>
#include <QMap> #include <QMetaMethod>
#include <QKeySequence>
#include <QGlobalStatic> #include <QGlobalStatic>
#include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qguiapplication_p.h>
#include <map>
class QskShortcutHandler final : public QObject class QskShortcutHandler final : public QObject
{ {
public: public:
int addShortcut( QQuickWindow*, const QKeySequence&, QskShortcutHandler();
const QObject* receiver, const QMetaMethod& );
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* ); const QObject* receiver, QtPrivate::QSlotObjectBase* );
void setEnabled( int id, bool ); void setEnabled( int id, bool );
void setAutoRepeat( int id, bool repeat ); void setAutoRepeat( int id, bool repeat );
protected: virtual bool eventFilter( QObject*, QEvent* ) override final;
virtual bool event( QEvent* event ) override final;
private: private:
int addShortcut( QQuickItem*, const QKeySequence&,
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(); static QShortcutMap& map();
class InvokeData class Data
{ {
public: public:
~InvokeData() Data():
item( nullptr ),
receiver( nullptr ),
slotObj( nullptr )
{
}
~Data()
{ {
if ( slotObj ) if ( slotObj )
slotObj->destroyIfLastRef(); slotObj->destroyIfLastRef();
} }
QQuickItem* item;
const QObject* receiver;
QMetaMethod method; QMetaMethod method;
QtPrivate::QSlotObjectBase* slotObj; 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() inline QShortcutMap& QskShortcutHandler::map()
{ {
return QGuiApplicationPrivate::instance()->shortcutMap; return QGuiApplicationPrivate::instance()->shortcutMap;
} }
int QskShortcutHandler::addShortcut( int QskShortcutHandler::addShortcut(
QQuickWindow* window, const QKeySequence& key, QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const QMetaMethod& method ) const QObject* receiver, const char* method )
{ {
int id; int id = 0;
if ( window ) if ( receiver )
id = map().addShortcut( window, key, Qt::WindowShortcut, contextMatcher ); {
else const QMetaObject* metaObject = receiver->metaObject();
id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher );
InvokeData& data = m_invokeDataMap[ id ]; const int methodIndex = metaObject->indexOfMethod(
data.receiver = receiver; QMetaObject::normalizedSignature( method ).constData() + 1 );
data.method = method;
data.slotObj = nullptr; if ( methodIndex >= 0 )
{
id = addShortcut( item, key,
receiver, metaObject->method( methodIndex ), nullptr );
}
}
return id; 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( int QskShortcutHandler::addShortcut(
QQuickWindow* window, const QKeySequence& key, QQuickItem* item, const QKeySequence& key,
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) const QObject* receiver, const QMetaMethod& method,
QtPrivate::QSlotObjectBase* slotObj )
{ {
int id; if ( receiver == nullptr )
return 0;
if ( window ) #if 1
id = map().addShortcut( window, key, Qt::WindowShortcut, contextMatcher ); // 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 else
{
id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher ); 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.receiver = receiver;
data.slotObj = slotObj;
if ( receiver ) if ( slotObj )
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp ); data.slotObj = slotObj;
else
data.method = method;
return id; 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(); ) 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 ); it = m_invokeDataMap.erase( it );
continue; 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 ) void QskShortcutHandler::setEnabled( int id, bool enabled )
@ -138,32 +209,51 @@ void QskShortcutHandler::setAutoRepeat( int id, bool repeat )
map().setShortcutAutoRepeat( repeat, id, this ); 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() ); Q_ASSERT( data.item == nullptr || data.item == object );
if ( it != m_invokeDataMap.constEnd() )
auto receiver = const_cast< QObject* >( data.receiver );
if ( data.slotObj )
{ {
const InvokeData& data = ( *it ); void* args[] = { 0 };
auto receiver = const_cast< QObject* >( data.receiver );
if ( data.slotObj ) if ( receiver && receiver->thread() != thread() )
{ {
void* args[] = { 0 }; QCoreApplication::postEvent( receiver,
data.slotObj->call( receiver, args ); new QMetaCallEvent( data.slotObj, nullptr, 0, 0, nullptr, args ) );
} }
else else
{ {
data.method.invoke( receiver, Qt::AutoConnection ); data.slotObj->call( receiver, args );
} }
} }
else
{
data.method.invoke( receiver, Qt::AutoConnection );
}
return true; return true;
} }
// seems like someone else is also interested in shortcuts
return false; return false;
} }
@ -172,30 +262,28 @@ Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler )
int QskShortcut::addShortcut( 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 )
{ {
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, int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key,
bool autoRepeat, const QObject* receiver, const char* method ) bool autoRepeat, const QObject* receiver, const char* method )
{ {
int id = 0; auto item = window ? window->contentItem() : nullptr;
if ( receiver == nullptr ) int id = qskShortcutHandler->addShortcut( item, key, receiver, method );
return id; if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
const QMetaObject* metaObject = receiver->metaObject(); return id;
}
const int methodIndex = metaObject->indexOfMethod( int QskShortcut::addShortcut( QQuickItem* item, const QKeySequence& key,
QMetaObject::normalizedSignature( method ).constData() + 1 ); bool autoRepeat, const QObject* receiver, const char* method )
{
if ( methodIndex >= 0 ) int id = qskShortcutHandler->addShortcut( item, key, receiver, method );
{ if ( id && !autoRepeat )
id = qskShortcutHandler->addShortcut( qskShortcutHandler->setAutoRepeat( id, false );
window, key, receiver, metaObject->method( methodIndex ) );
if ( !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
}
return id; return id;
} }
@ -203,18 +291,8 @@ int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key,
int QskShortcut::addShortcutImpl( const QKeySequence& key, int QskShortcut::addShortcutImpl( const QKeySequence& key,
bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj ) bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
{ {
#if 1 int id = qskShortcutHandler->addShortcut( nullptr, key, receiver, slotObj );
if ( receiver ) if ( id && !autoRepeat )
{
// 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 ); qskShortcutHandler->setAutoRepeat( id, false );
return id; return id;

View File

@ -7,71 +7,91 @@
#define QSK_SHORTCUT_H #define QSK_SHORTCUT_H
#include "QskGlobal.h" #include "QskGlobal.h"
#include <QArgument>
#include <QObject> #include <QObject>
class QQuickWindow; class QQuickWindow;
class QQuickItem;
class QKeySequence; class QKeySequence;
class QObject;
namespace QskShortcut class QSK_EXPORT QskShortcut
{ {
QSK_EXPORT void setAutoRepeat( int, bool on ); public:
QSK_EXPORT void setEnabled( int, bool on ); static int addShortcut( const QKeySequence&, bool autoRepeat,
QSK_EXPORT int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method ); 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 ); const QObject* receiver, const char* method );
QSK_EXPORT int addShortcutImpl( const QKeySequence&, bool autoRepeat, static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, QtPrivate::QSlotObjectBase* ); const QObject* receiver, const char* method );
// shortcut calling a QObject method // shortcut calling a QObject method
template< typename Func1 > template< typename Func1 >
inline int addShortcut( const QKeySequence& key, 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 );
{
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 // shortcut calling a functor or function pointer
template< typename Func1 > template< typename Func1 >
int addShortcut( const QKeySequence& key, bool autoRepeat, static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot ) 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 // shortcut calling a functor or function pointer
template< typename Func1 > template< typename Func1 >
int addShortcut( const QKeySequence& key, bool autoRepeat, Func1 slot ) static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot );
{
return addShortcut( key, autoRepeat, nullptr, 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 #endif

View File

@ -32,21 +32,21 @@ void SkinnyShortcut::enable( Types types )
if ( types & RotateSkin ) if ( types & RotateSkin )
{ {
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ), 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; cout << "CTRL-S to change the skin." << endl;
} }
if ( types & DebugBackground ) if ( types & DebugBackground )
{ {
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ), 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; cout << "CTRL-B to enable visual debugging modes." << endl;
} }
if ( types & DebugStatistics ) if ( types & DebugStatistics )
{ {
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ), 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; 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 !! // when not being implemented by the platform !!
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_Q ), 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; cout << "CTRL-Q to terminate the application." << endl;
} }
} }