support for shortcut connections on different threads added
This commit is contained in:
parent
8175719679
commit
85e3af73d9
@ -4,106 +4,157 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskShortcut.h"
|
||||
#include "QskControl.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QKeySequence>
|
||||
#include <QQuickWindow>
|
||||
#include <QMap>
|
||||
#include <QMetaMethod>
|
||||
#include <QKeySequence>
|
||||
#include <QGlobalStatic>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
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;
|
||||
|
@ -7,71 +7,91 @@
|
||||
#define QSK_SHORTCUT_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
|
||||
#include <QArgument>
|
||||
#include <QObject>
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user