2017-07-21 16:21:34 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskShortcut.h"
|
2017-12-05 12:10:50 +00:00
|
|
|
#include "QskControl.h"
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
#include <QQuickWindow>
|
2017-12-05 12:10:50 +00:00
|
|
|
#include <QMetaMethod>
|
|
|
|
#include <QKeySequence>
|
2017-12-03 16:58:18 +00:00
|
|
|
#include <QGlobalStatic>
|
|
|
|
#include <QtGui/private/qguiapplication_p.h>
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
#include <map>
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
class QskShortcutHandler final : public QObject
|
|
|
|
{
|
|
|
|
public:
|
2017-12-05 12:10:50 +00:00
|
|
|
QskShortcutHandler();
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
int addShortcut( QQuickItem*, const QKeySequence&,
|
|
|
|
const QObject* receiver, const char* method );
|
|
|
|
|
|
|
|
int addShortcut( QQuickItem*, const QKeySequence&,
|
2017-12-03 16:58:18 +00:00
|
|
|
const QObject* receiver, QtPrivate::QSlotObjectBase* );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
void setEnabled( int id, bool );
|
|
|
|
void setAutoRepeat( int id, bool repeat );
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
virtual bool eventFilter( QObject*, QEvent* ) override final;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
private:
|
2017-12-05 12:10:50 +00:00
|
|
|
int addShortcut( QQuickItem*, const QKeySequence&,
|
|
|
|
const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* );
|
|
|
|
|
2017-12-03 16:58:18 +00:00
|
|
|
void cleanUp( QObject* );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
static bool contextMatcher( QObject*, Qt::ShortcutContext );
|
|
|
|
static QShortcutMap& map();
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
class Data
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-03 16:58:18 +00:00
|
|
|
public:
|
2017-12-05 12:10:50 +00:00
|
|
|
Data():
|
|
|
|
item( nullptr ),
|
|
|
|
receiver( nullptr ),
|
|
|
|
slotObj( nullptr )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~Data()
|
2017-12-03 16:58:18 +00:00
|
|
|
{
|
|
|
|
if ( slotObj )
|
|
|
|
slotObj->destroyIfLastRef();
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
QQuickItem* item;
|
|
|
|
const QObject* receiver;
|
2017-07-21 16:21:34 +00:00
|
|
|
QMetaMethod method;
|
2017-12-03 16:58:18 +00:00
|
|
|
QtPrivate::QSlotObjectBase* slotObj;
|
2017-07-21 16:21:34 +00:00
|
|
|
};
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
std::map< int, Data > m_invokeDataMap;
|
2017-07-21 16:21:34 +00:00
|
|
|
};
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
QskShortcutHandler::QskShortcutHandler()
|
|
|
|
{
|
|
|
|
installEventFilter( this );
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
inline QShortcutMap& QskShortcutHandler::map()
|
|
|
|
{
|
|
|
|
return QGuiApplicationPrivate::instance()->shortcutMap;
|
|
|
|
}
|
|
|
|
|
2017-12-03 16:58:18 +00:00
|
|
|
int QskShortcutHandler::addShortcut(
|
2017-12-05 12:10:50 +00:00
|
|
|
QQuickItem* item, const QKeySequence& key,
|
|
|
|
const QObject* receiver, const char* method )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
int id = 0;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
if ( receiver )
|
|
|
|
{
|
|
|
|
const QMetaObject* metaObject = receiver->metaObject();
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
const int methodIndex = metaObject->indexOfMethod(
|
|
|
|
QMetaObject::normalizedSignature( method ).constData() + 1 );
|
|
|
|
|
|
|
|
if ( methodIndex >= 0 )
|
|
|
|
{
|
|
|
|
id = addShortcut( item, key,
|
|
|
|
receiver, metaObject->method( methodIndex ), nullptr );
|
|
|
|
}
|
|
|
|
}
|
2017-12-03 16:58:18 +00:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
int QskShortcutHandler::addShortcut( QQuickItem* item, const QKeySequence& key,
|
2017-12-03 16:58:18 +00:00
|
|
|
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
|
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
return addShortcut( item, key, receiver, QMetaMethod(), slotObj );
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskShortcutHandler::addShortcut(
|
|
|
|
QQuickItem* item, const QKeySequence& key,
|
|
|
|
const QObject* receiver, const QMetaMethod& method,
|
|
|
|
QtPrivate::QSlotObjectBase* slotObj )
|
|
|
|
{
|
|
|
|
if ( receiver == nullptr )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
// multiple connections ????
|
|
|
|
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int id = 0;
|
2017-12-03 16:58:18 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
item->installEventFilter( this );
|
|
|
|
if ( item != receiver )
|
|
|
|
connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
|
|
|
|
|
|
|
|
id = map().addShortcut( item, key, Qt::WindowShortcut, contextMatcher );
|
|
|
|
}
|
2017-12-03 16:58:18 +00:00
|
|
|
else
|
2017-12-05 12:10:50 +00:00
|
|
|
{
|
2017-12-03 16:58:18 +00:00
|
|
|
id = map().addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher );
|
2017-12-05 12:10:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto& data = m_invokeDataMap[ id ];
|
2017-12-03 16:58:18 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
data.item = item;
|
2017-12-03 16:58:18 +00:00
|
|
|
data.receiver = receiver;
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
if ( slotObj )
|
|
|
|
data.slotObj = slotObj;
|
|
|
|
else
|
|
|
|
data.method = method;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
void QskShortcutHandler::cleanUp( QObject* object )
|
2017-12-03 16:58:18 +00:00
|
|
|
{
|
|
|
|
for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); )
|
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
const auto& data = it->second;
|
|
|
|
|
|
|
|
if ( data.receiver == object || data.item == object )
|
2017-12-03 16:58:18 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
map().removeShortcut( it->first, this );
|
2017-12-03 16:58:18 +00:00
|
|
|
it = m_invokeDataMap.erase( it );
|
2017-12-05 12:10:50 +00:00
|
|
|
|
2017-12-03 16:58:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext context )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
if ( context == Qt::ApplicationShortcut )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( context == Qt::WindowShortcut )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
const auto focusWindow = QGuiApplication::focusWindow();
|
|
|
|
|
|
|
|
if ( auto item = qobject_cast< const QQuickItem* >( object ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
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();
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-05 12:10:50 +00:00
|
|
|
|
|
|
|
return false;
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskShortcutHandler::setEnabled( int id, bool enabled )
|
|
|
|
{
|
|
|
|
map().setShortcutEnabled( enabled, id, this );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskShortcutHandler::setAutoRepeat( int id, bool repeat )
|
|
|
|
{
|
|
|
|
map().setShortcutAutoRepeat( repeat, id, this );
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
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() )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
const Data& data = it->second;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
Q_ASSERT( data.item == nullptr || data.item == object );
|
|
|
|
|
|
|
|
auto receiver = const_cast< QObject* >( data.receiver );
|
|
|
|
|
|
|
|
if ( data.slotObj )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
void* args[] = { 0 };
|
2017-12-03 16:58:18 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
if ( receiver && receiver->thread() != thread() )
|
2017-12-03 16:58:18 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
QCoreApplication::postEvent( receiver,
|
|
|
|
new QMetaCallEvent( data.slotObj, nullptr, 0, 0, nullptr, args ) );
|
2017-12-03 16:58:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
data.slotObj->call( receiver, args );
|
2017-12-03 16:58:18 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
2017-12-05 12:10:50 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
data.method.invoke( receiver, Qt::AutoConnection );
|
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
// seems like someone else is also interested in shortcuts
|
2017-07-21 16:21:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-03 16:58:18 +00:00
|
|
|
Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler )
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
int QskShortcut::addShortcut( const QKeySequence& key,
|
2017-12-03 16:58:18 +00:00
|
|
|
bool autoRepeat, const QObject* receiver, const char* method )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
QQuickWindow* window = nullptr;
|
|
|
|
return addShortcut( window, key, autoRepeat, receiver, method );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int QskShortcut::addShortcut( QQuickWindow* window, const QKeySequence& key,
|
2017-12-03 16:58:18 +00:00
|
|
|
bool autoRepeat, const QObject* receiver, const char* method )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
auto item = window ? window->contentItem() : nullptr;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
int id = qskShortcutHandler->addShortcut( item, key, receiver, method );
|
|
|
|
if ( id && !autoRepeat )
|
|
|
|
qskShortcutHandler->setAutoRepeat( id, false );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
return id;
|
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-12-05 12:10:50 +00:00
|
|
|
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 );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2017-12-03 16:58:18 +00:00
|
|
|
int QskShortcut::addShortcutImpl( const QKeySequence& key,
|
|
|
|
bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
|
|
|
|
{
|
2017-12-05 12:10:50 +00:00
|
|
|
int id = qskShortcutHandler->addShortcut( nullptr, key, receiver, slotObj );
|
|
|
|
if ( id && !autoRepeat )
|
2017-12-03 16:58:18 +00:00
|
|
|
qskShortcutHandler->setAutoRepeat( id, false );
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
void QskShortcut::setAutoRepeat( int id, bool on )
|
|
|
|
{
|
2017-12-03 16:58:18 +00:00
|
|
|
qskShortcutHandler->setAutoRepeat( id, on );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskShortcut::setEnabled( int id, bool on )
|
|
|
|
{
|
|
|
|
qskShortcutHandler->setEnabled( id, on );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|