qskinny/src/controls/QskShortcut.cpp

356 lines
8.7 KiB
C++
Raw Normal View History

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"
#include "QskControl.h"
2017-07-21 16:21:34 +00:00
#include <QQuickWindow>
#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
#include <map>
2017-12-05 16:40:21 +00:00
static inline QShortcutMap& qskShortcutMap()
{
return QGuiApplicationPrivate::instance()->shortcutMap;
}
2017-07-21 16:21:34 +00:00
class QskShortcutHandler final : public QObject
{
public:
QskShortcutHandler();
2017-07-21 16:21:34 +00:00
2017-12-05 16:40:21 +00:00
int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, const char* method );
2017-12-05 16:40:21 +00:00
int add( QQuickItem*, const QKeySequence&,
2017-12-03 16:58:18 +00:00
const QObject* receiver, QtPrivate::QSlotObjectBase* );
2017-07-21 16:21:34 +00:00
2017-12-05 16:40:21 +00:00
void remove( int id );
2017-07-21 16:21:34 +00:00
void setEnabled( int id, bool );
void setAutoRepeat( int id, bool repeat );
virtual bool eventFilter( QObject*, QEvent* ) override final;
2017-07-21 16:21:34 +00:00
private:
2017-12-05 16:40:21 +00:00
int insert( 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 );
2017-12-05 16:40:21 +00:00
class InvokeData
2017-07-21 16:21:34 +00:00
{
2017-12-03 16:58:18 +00:00
public:
2017-12-05 16:40:21 +00:00
InvokeData():
item( nullptr ),
receiver( nullptr ),
2017-12-05 16:40:21 +00:00
slotObject( nullptr )
{
}
2017-12-05 16:40:21 +00:00
~InvokeData()
2017-12-03 16:58:18 +00:00
{
2017-12-05 16:40:21 +00:00
if ( slotObject )
slotObject->destroyIfLastRef();
2017-12-03 16:58:18 +00:00
}
QQuickItem* item;
const QObject* receiver;
2017-07-21 16:21:34 +00:00
QMetaMethod method;
2017-12-05 16:40:21 +00:00
QtPrivate::QSlotObjectBase* slotObject;
2017-07-21 16:21:34 +00:00
};
2017-12-05 16:40:21 +00:00
std::map< int, InvokeData > m_invokeDataMap;
2017-07-21 16:21:34 +00:00
};
2017-12-05 16:40:21 +00:00
Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler )
QskShortcutHandler::QskShortcutHandler()
{
installEventFilter( this );
}
2017-12-05 16:40:21 +00:00
int QskShortcutHandler::add(
QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const char* method )
2017-07-21 16:21:34 +00:00
{
int id = 0;
2017-07-21 16:21:34 +00:00
if ( receiver )
{
const QMetaObject* metaObject = receiver->metaObject();
2017-07-21 16:21:34 +00:00
const int methodIndex = metaObject->indexOfMethod(
QMetaObject::normalizedSignature( method ).constData() + 1 );
if ( methodIndex >= 0 )
{
2017-12-05 16:40:21 +00:00
id = insert( item, key,
receiver, metaObject->method( methodIndex ), nullptr );
}
}
2017-12-03 16:58:18 +00:00
return id;
}
2017-12-05 16:40:21 +00:00
int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key,
2017-12-03 16:58:18 +00:00
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
{
2017-12-05 16:40:21 +00:00
return insert( item, key, receiver, QMetaMethod(), slotObj );
}
2017-12-05 16:40:21 +00:00
int QskShortcutHandler::insert(
QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const QMetaMethod& method,
2017-12-05 16:40:21 +00:00
QtPrivate::QSlotObjectBase* slotObject )
{
2017-12-05 16:40:21 +00:00
if ( receiver )
{
receiver->disconnect( this );
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
int id = 0;
2017-12-03 16:58:18 +00:00
2017-12-05 16:40:21 +00:00
auto& map = qskShortcutMap();
if ( item )
{
if ( item != receiver )
2017-12-05 16:40:21 +00:00
{
item->disconnect( this );
connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
2017-12-05 16:40:21 +00:00
}
2017-12-05 16:40:21 +00:00
id = map.addShortcut( item, key, Qt::WindowShortcut, contextMatcher );
}
2017-12-03 16:58:18 +00:00
else
{
2017-12-05 16:40:21 +00:00
id = map.addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher );
}
auto& data = m_invokeDataMap[ id ];
2017-12-03 16:58:18 +00:00
data.item = item;
2017-12-03 16:58:18 +00:00
data.receiver = receiver;
2017-12-05 16:40:21 +00:00
if ( slotObject )
data.slotObject = slotObject;
else
data.method = method;
2017-07-21 16:21:34 +00:00
return id;
}
2017-12-05 16:40:21 +00:00
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 )
2017-12-03 16:58:18 +00:00
{
2017-12-05 16:40:21 +00:00
/*
When item != receiver we might remain being connected
to destroyed signals we are not interested in.
*/
auto& map = qskShortcutMap();
2017-12-03 16:58:18 +00:00
for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); )
{
const auto& data = it->second;
2017-12-05 16:40:21 +00:00
if ( data.item == object || data.receiver == object )
2017-12-03 16:58:18 +00:00
{
2017-12-05 16:40:21 +00:00
map.removeShortcut( it->first, nullptr );
2017-12-03 16:58:18 +00:00
it = m_invokeDataMap.erase( it );
2017-12-03 16:58:18 +00:00
continue;
}
++it;
}
}
bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext context )
2017-07-21 16:21:34 +00:00
{
if ( context == Qt::ApplicationShortcut )
return true;
if ( context == Qt::WindowShortcut )
2017-07-21 16:21:34 +00:00
{
const auto focusWindow = QGuiApplication::focusWindow();
if ( auto item = qobject_cast< const QQuickItem* >( object ) )
2017-07-21 16:21:34 +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;
}
2017-12-05 16:40:21 +00:00
item = item->parentItem();
}
2017-12-05 16:40:21 +00:00
// we want to process the following QShortcutEvent
object->installEventFilter( qskShortcutHandler );
return true;
2017-07-21 16:21:34 +00:00
}
}
return false;
2017-07-21 16:21:34 +00:00
}
void QskShortcutHandler::setEnabled( int id, bool enabled )
{
2017-12-05 16:40:21 +00:00
auto& map = qskShortcutMap();
map.setShortcutEnabled( enabled, id, this );
2017-07-21 16:21:34 +00:00
}
void QskShortcutHandler::setAutoRepeat( int id, bool repeat )
{
2017-12-05 16:40:21 +00:00
auto& map = qskShortcutMap();
map.setShortcutAutoRepeat( repeat, id, this );
2017-07-21 16:21:34 +00:00
}
bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
2017-07-21 16:21:34 +00:00
{
if ( event->type() != QEvent::Shortcut )
return false;
2017-12-05 16:40:21 +00:00
if ( object != this )
object->removeEventFilter( this );
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 16:40:21 +00:00
const auto& invokeData = it->second;
2017-07-21 16:21:34 +00:00
2017-12-05 16:40:21 +00:00
Q_ASSERT( invokeData.item == nullptr || invokeData.item == object );
2017-12-05 16:40:21 +00:00
auto receiver = const_cast< QObject* >( invokeData.receiver );
2017-12-05 16:40:21 +00:00
if ( invokeData.slotObject )
2017-07-21 16:21:34 +00:00
{
void* args[] = { 0 };
2017-12-03 16:58:18 +00:00
if ( receiver && receiver->thread() != thread() )
2017-12-03 16:58:18 +00:00
{
QCoreApplication::postEvent( receiver,
2017-12-05 16:40:21 +00:00
new QMetaCallEvent( invokeData.slotObject, nullptr, 0, 0, nullptr, args ) );
2017-12-03 16:58:18 +00:00
}
else
{
2017-12-05 16:40:21 +00:00
invokeData.slotObject->call( receiver, args );
2017-12-03 16:58:18 +00:00
}
2017-07-21 16:21:34 +00:00
}
else
{
2017-12-05 16:40:21 +00:00
invokeData.method.invoke( receiver, Qt::AutoConnection );
}
2017-07-21 16:21:34 +00:00
return true;
}
// seems like someone else is also interested in shortcuts
2017-07-21 16:21:34 +00:00
return false;
}
2017-12-05 16:40:21 +00:00
int QskShortcut::addMethod( QQuickItem* item, 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 16:40:21 +00:00
if ( receiver == nullptr )
{
return 0;
}
2017-07-21 16:21:34 +00:00
2017-12-05 16:40:21 +00:00
int id = qskShortcutHandler->add( item, key, receiver, method );
if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
2017-07-21 16:21:34 +00:00
return id;
}
2017-12-05 16:40:21 +00:00
int QskShortcut::addSlotObject( QQuickItem* item, const QKeySequence& key,
bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObject )
2017-12-03 16:58:18 +00:00
{
2017-12-05 16:40:21 +00:00
int id = qskShortcutHandler->add( item, key, receiver, slotObject );
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
}
2017-12-05 16:40:21 +00:00
void QskShortcut::removeShortcut( int id )
{
qskShortcutHandler->remove( id );
}