QskShortcut moved to QskShortcutMap, new class QskShortcut for the QML

use case ( makes sense from C++ as well, but is less efficient as it needs one QObject per
shortcut )
This commit is contained in:
Uwe Rathmann 2017-12-06 17:01:10 +01:00
parent ccf1834a9c
commit c1e7571e10
10 changed files with 908 additions and 510 deletions

View File

@ -1,7 +1,7 @@
#include "MainWindow.h"
#include "SkinFactory.h"
#include <QskShortcut.h>
#include <QskShortcutMap.h>
#include <QskSetup.h>
#include <SkinnyShortcut.h>
@ -35,10 +35,10 @@ int main( int argc, char** argv )
cout << "CTRL-S to change the skin." << endl;
cout << "CTRL-T to change the color scheme, when the \"Default\" skin is active." << endl;
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ),
false, &skinFactory, SLOT(toggleScheme()) );
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ),
false, &skinFactory, SLOT(rotateSkin()) );
// With CTRL-B you can rotate a couple of visual debug modes

View File

@ -12,6 +12,12 @@ Qsk.Window
height: 600
color: "Beige"
Qsk.Shortcut
{
sequence : "Ctrl+X"
onActivated: console.log( "Ctrl+X" )
}
Qsk.LinearBox
{
orientation: Qt.Horizontal

View File

@ -13,7 +13,7 @@
#include <QskGraphicLabel.h>
#include <QskGradient.h>
#include <QskAspect.h>
#include <QskShortcut.h>
#include <QskShortcutMap.h>
#include <QGuiApplication>
#include <QKeySequence>
@ -34,7 +34,7 @@ public:
setSizePolicy( QskSizePolicy::MinimumExpanding,
QskSizePolicy::MinimumExpanding );
QskShortcut::addShortcut( this, QKeySequence( Qt::Key_P ), true,
QskShortcutMap::addShortcut( this, QKeySequence( Qt::Key_P ), true,
[=] { qDebug() << graphicSource; } );
}
};

View File

@ -24,6 +24,7 @@
#include "QskScrollArea.h"
#include "QskSeparator.h"
#include "QskSimpleListBox.h"
#include "QskShortcut.h"
#include "QskSlider.h"
#include "QskStackBox.h"
#include "QskStandardSymbol.h"
@ -239,6 +240,7 @@ void QskModule::registerTypes()
qRegisterMetaType< QskSkin* >();
QSK_REGISTER( QskMain, "Main" );
QSK_REGISTER( QskShortcut, "Shortcut" );
QSK_REGISTER( QskWindow, "Window" );

View File

@ -4,245 +4,221 @@
*****************************************************************************/
#include "QskShortcut.h"
#include "QskControl.h"
#include "QskShortcutMap.h"
#include <QQuickItem>
#include <QQuickWindow>
#include <QMetaMethod>
#include <QKeySequence>
#include <QGlobalStatic>
#include <QtGui/private/qguiapplication_p.h>
#include <map>
static inline QShortcutMap& qskShortcutMap()
{
return QGuiApplicationPrivate::instance()->shortcutMap;
}
class QskShortcutHandler final : public QObject
{
public:
QskShortcutHandler();
int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, const char* method );
int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, QtPrivate::QSlotObjectBase* );
void remove( int id );
void setEnabled( int id, bool );
void setAutoRepeat( int id, bool repeat );
virtual bool eventFilter( QObject*, QEvent* ) override final;
private:
int insert( QQuickItem*, const QKeySequence&,
const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* );
void cleanUp( QObject* );
static bool contextMatcher( QObject*, Qt::ShortcutContext );
class InvokeData
{
public:
InvokeData():
item( nullptr ),
receiver( nullptr ),
slotObject( nullptr )
{
}
~InvokeData()
{
if ( slotObject )
slotObject->destroyIfLastRef();
}
QQuickItem* item;
const QObject* receiver;
QMetaMethod method;
QtPrivate::QSlotObjectBase* slotObject;
};
std::map< int, InvokeData > m_invokeDataMap;
};
Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler )
QskShortcutHandler::QskShortcutHandler()
{
installEventFilter( this );
}
int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const char* method )
{
int id = 0;
if ( receiver )
{
const QMetaObject* metaObject = receiver->metaObject();
const int methodIndex = metaObject->indexOfMethod(
QMetaObject::normalizedSignature( method ).constData() + 1 );
if ( methodIndex >= 0 )
{
id = insert( item, key,
receiver, metaObject->method( methodIndex ), nullptr );
}
}
return id;
}
int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& key,
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
{
return insert( item, key, receiver, QMetaMethod(), slotObj );
}
int QskShortcutHandler::insert(
QQuickItem* item, const QKeySequence& key,
const QObject* receiver, const QMetaMethod& method,
QtPrivate::QSlotObjectBase* slotObject )
{
if ( receiver )
{
receiver->disconnect( this );
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
int id = 0;
auto& map = qskShortcutMap();
if ( item )
{
if ( item != receiver )
{
item->disconnect( this );
connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
id = map.addShortcut( item, key, Qt::WindowShortcut, contextMatcher );
}
else
{
id = map.addShortcut( this, key, Qt::ApplicationShortcut, contextMatcher );
}
auto& data = m_invokeDataMap[ id ];
data.item = item;
data.receiver = receiver;
if ( slotObject )
data.slotObject = slotObject;
else
data.method = method;
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 )
{
/*
When item != receiver we might remain being connected
to destroyed signals we are not interested in anymore. TODO ...
*/
auto& map = qskShortcutMap();
for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); )
{
const auto& data = it->second;
if ( data.item == object || data.receiver == object )
{
map.removeShortcut( it->first, nullptr );
it = m_invokeDataMap.erase( it );
continue;
}
++it;
}
}
bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext context )
static bool qskContextMatcher( QObject* object, Qt::ShortcutContext context )
{
if ( context == Qt::ApplicationShortcut )
return true;
if ( context == Qt::WindowShortcut )
{
const auto focusWindow = QGuiApplication::focusWindow();
if ( const auto shortcut = qobject_cast< const QskShortcut* >( object ) )
return shortcut->isFocusInScope();
}
if ( auto item = qobject_cast< const QQuickItem* >( object ) )
return false;
}
class QskShortcut::PrivateData
{
public:
PrivateData():
id( 0 ),
autoRepeat( true ),
enabled( true ),
isWindowContext( true ),
isComplete( true )
{
}
~PrivateData()
{
if ( id != 0 )
qskShortcutMap().removeShortcut( id, nullptr );
}
void resetShortcut( QskShortcut* shortcut )
{
if ( !isComplete )
return;
auto& map = qskShortcutMap();
const int oldId = id;
if ( id != 0 )
{
const auto window = item->window();
if ( window == nullptr || window != focusWindow )
{
return false;
}
map.removeShortcut( id, nullptr );
id = 0;
}
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;
}
if ( !sequence.isEmpty() )
{
id = map.addShortcut( shortcut, sequence,
shortcut->context(), qskContextMatcher );
item = item->parentItem();
}
if ( !autoRepeat )
map.setShortcutAutoRepeat( false, id, shortcut );
if ( !enabled )
map.setShortcutEnabled( false, id, shortcut );
}
if ( oldId != id )
shortcut->Q_EMIT shortcutIdChanged( id );
}
public:
QKeySequence sequence;
int id;
bool autoRepeat : 1;
bool enabled : 1;
bool isWindowContext : 1;
bool isComplete : 1;
};
QskShortcut::QskShortcut( QObject* parent ):
Inherited( parent ),
m_data( new PrivateData )
{
}
QskShortcut::QskShortcut( const QKeySequence& sequence, QObject* parent ):
QskShortcut( sequence, Qt::WindowShortcut, parent )
{
}
QskShortcut::QskShortcut( const QKeySequence& sequence,
Qt::ShortcutContext context, QObject* parent ):
Inherited( parent ),
m_data( new PrivateData )
{
m_data->sequence = sequence;
m_data->isWindowContext = ( context == Qt::WindowShortcut );
m_data->resetShortcut( this );
}
QskShortcut::~QskShortcut()
{
}
int QskShortcut::shortcutId() const
{
return m_data->id;
}
Qt::ShortcutContext QskShortcut::context() const
{
return m_data->isWindowContext
? Qt::WindowShortcut : Qt::ApplicationShortcut;
}
void QskShortcut::setContext( Qt::ShortcutContext context )
{
if ( context == Qt::ApplicationShortcut
|| context == Qt::WindowShortcut )
{
const bool isWindowContext = ( context == Qt::WindowShortcut );
if ( isWindowContext != m_data->isWindowContext )
{
m_data->isWindowContext = isWindowContext;
m_data->resetShortcut( this );
Q_EMIT contextChanged();
}
}
}
void QskShortcut::setSequence( const QKeySequence& sequence )
{
if ( sequence != m_data->sequence )
{
m_data->sequence = sequence;
m_data->resetShortcut( this );
Q_EMIT sequenceChanged();
}
}
QKeySequence QskShortcut::sequence() const
{
return m_data->sequence;
}
void QskShortcut::setSequenceVariant( const QVariant& sequence )
{
if ( sequence.type() == QVariant::Int )
setSequence( static_cast<QKeySequence::StandardKey>( sequence.toInt() ) );
else
setSequence( QKeySequence::fromString( sequence.toString() ) );
}
QVariant QskShortcut::sequenceVariant() const
{
return m_data->sequence.toString();
}
void QskShortcut::setEnabled( bool on )
{
if ( on != m_data->enabled )
{
m_data->enabled = on;
if ( m_data->id != 0 )
qskShortcutMap().setShortcutEnabled( on, m_data->id, this );
Q_EMIT enabledChanged();
}
}
bool QskShortcut::isEnabled() const
{
return m_data->enabled;
}
void QskShortcut::setAutoRepeat( bool on )
{
if ( on != m_data->autoRepeat )
{
m_data->autoRepeat = on;
if ( m_data->id != 0 )
qskShortcutMap().setShortcutEnabled( on, m_data->id, this );
Q_EMIT autoRepeatChanged();
}
}
bool QskShortcut::autoRepeat() const
{
return m_data->autoRepeat;
}
bool QskShortcut::event( QEvent* event )
{
if ( event->type() == QEvent::Shortcut )
{
auto* shortcutEvent = static_cast< QShortcutEvent* >( event );
if ( shortcutEvent->shortcutId() == m_data->id )
{
if ( shortcutEvent->isAmbiguous() )
Q_EMIT activatedAmbiguously();
else
Q_EMIT activated();
// we want to process the following QShortcutEvent
object->installEventFilter( qskShortcutHandler );
return true;
}
}
@ -250,105 +226,46 @@ bool QskShortcutHandler::contextMatcher( QObject* object, Qt::ShortcutContext co
return false;
}
void QskShortcutHandler::setEnabled( int id, bool enabled )
bool QskShortcut::isFocusInScope() const
{
auto& map = qskShortcutMap();
map.setShortcutEnabled( enabled, id, this );
}
void QskShortcutHandler::setAutoRepeat( int id, bool repeat )
{
auto& map = qskShortcutMap();
map.setShortcutAutoRepeat( repeat, id, this );
}
bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() != QEvent::Shortcut )
return false;
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() )
{
const auto& invokeData = it->second;
Q_ASSERT( invokeData.item == nullptr || invokeData.item == object );
auto receiver = const_cast< QObject* >( invokeData.receiver );
if ( invokeData.slotObject )
{
void* args[] = { 0 };
if ( receiver && receiver->thread() != thread() )
{
QCoreApplication::postEvent( receiver,
new QMetaCallEvent( invokeData.slotObject, nullptr, 0, 0, nullptr, args ) );
}
else
{
invokeData.slotObject->call( receiver, args );
}
}
else
{
invokeData.method.invoke( receiver, Qt::AutoConnection );
}
if ( !m_data->isWindowContext )
return true;
}
// seems like someone else is also interested in shortcuts
return false;
}
const QQuickItem* contextItem = nullptr;
int QskShortcut::addMethod( QQuickItem* item, const QKeySequence& key,
bool autoRepeat, const QObject* receiver, const char* method )
{
if ( receiver == nullptr )
if ( parent()->isWindowType() )
{
return 0;
if ( auto window = qobject_cast< const QQuickWindow* >( parent() ) )
contextItem = window->contentItem();
}
else
{
contextItem = qobject_cast< const QQuickItem* >( parent() );
}
int id = qskShortcutHandler->add( item, key, receiver, method );
if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
return id;
if ( contextItem )
{
return QskShortcutMap::contextMatcher( contextItem, Qt::WindowShortcut );
}
else
{
qWarning( "QskShortcut has no valid parent for Qt::WindowShortcut" );
return false;
}
}
int QskShortcut::addSlotObject( QQuickItem* item, const QKeySequence& key,
bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObject )
void QskShortcut::classBegin()
{
int id = qskShortcutHandler->add( item, key, receiver, slotObject );
if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
return id;
m_data->isComplete = false;
}
void QskShortcut::setAutoRepeat( int id, bool on )
void QskShortcut::componentComplete()
{
qskShortcutHandler->setAutoRepeat( id, on );
if ( m_data->isComplete == false )
{
m_data->isComplete = true;
m_data->resetShortcut( this );
}
}
void QskShortcut::setEnabled( int id, bool on )
{
qskShortcutHandler->setEnabled( id, on );
}
void QskShortcut::removeShortcut( int id )
{
qskShortcutHandler->remove( id );
}
#include "moc_QskShortcut.cpp"

View File

@ -7,219 +7,87 @@
#define QSK_SHORTCUT_H
#include "QskGlobal.h"
#include <QQuickWindow>
class QQuickItem;
#include <QObject>
#include <QQmlParserStatus>
#include <Qt>
#include <memory>
class QKeySequence;
class QSK_EXPORT QskShortcut
/*
For QML, with C++ there is also QskShortcutMap that does
not need to create QObjects per shortcut
*/
class QSK_EXPORT QskShortcut : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES( QQmlParserStatus )
Q_PROPERTY( QVariant sequence READ sequenceVariant
WRITE setSequenceVariant NOTIFY sequenceChanged FINAL)
Q_PROPERTY( Qt::ShortcutContext context READ context
WRITE setContext NOTIFY contextChanged )
Q_PROPERTY( bool enabled READ isEnabled
WRITE setEnabled NOTIFY enabledChanged )
Q_PROPERTY( bool autoRepeat READ autoRepeat
WRITE setAutoRepeat NOTIFY autoRepeatChanged )
Q_PROPERTY( int shortcutId READ shortcutId
NOTIFY shortcutIdChanged )
using Inherited = QObject;
public:
static void setAutoRepeat( int, bool on );
static void setEnabled( int, bool on );
QskShortcut( QObject* parent = nullptr );
static void removeShortcut( int );
QskShortcut( const QKeySequence&, QObject* = nullptr );
QskShortcut( const QKeySequence&, Qt::ShortcutContext, QObject* = nullptr );
// -- traditional slots
static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
virtual ~QskShortcut();
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
int shortcutId() const;
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
void setSequence( const QKeySequence& );
QKeySequence sequence() const;
// -- calling a QObject method
template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
// for QML
void setSequenceVariant( const QVariant& );
QVariant sequenceVariant() const;
template< typename Func1 >
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
Qt::ShortcutContext context() const;
void setContext(Qt::ShortcutContext context);
template< typename Func1 >
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
void setEnabled( bool );
bool isEnabled() const;
// -- calling a functor or function pointer inside a thread context
template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot );
void setAutoRepeat( bool );
bool autoRepeat() const;
template< typename Func1 >
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot );
virtual bool isFocusInScope() const;
template< typename Func1 >
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot );
Q_SIGNALS:
void sequenceChanged();
void contextChanged();
void enabledChanged();
void autoRepeatChanged();
// -- calling a functor or function pointer
template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot );
void activated();
void activatedAmbiguously();
template< typename Func1 >
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, Func1 slot );
int shortcutIdChanged( int ) const;
template< typename Func1 >
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, Func1 slot );
protected:
virtual bool event( QEvent* ) override;
virtual void classBegin() override;
virtual void componentComplete() override;
private:
QskShortcut() = delete;
~QskShortcut() = delete;
static int addMethod( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
template< typename Func1 >
static int addMemberSlot( QQuickItem*, const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
template< typename Func1 >
static int addFunctorSlot( QQuickItem*, const QKeySequence&,
bool autoRepeat, const QObject* context, Func1 slot );
static int addSlotObject(
QQuickItem* item, const QKeySequence&, bool autoRepeat,
const QObject* receiver, QtPrivate::QSlotObjectBase* );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
template< typename Func1 >
inline int QskShortcut::addMemberSlot(
QQuickItem* item, const QKeySequence& key, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
using namespace QtPrivate;
typedef FunctionPointer< Func1 > SlotType;
Q_STATIC_ASSERT_X( int( SlotType::ArgumentCount ) == 0,
"The slot must not have any arguments.");
return addSlotObject( item, key, autoRepeat, receiver,
new QSlotObject< Func1, typename SlotType::Arguments, void >( slot ) );
}
template< typename Func1 >
inline int QskShortcut::addFunctorSlot(
QQuickItem* item, 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." );
using Args = List_Left< void, 0 >::Value;
return addSlotObject( item, key, autoRepeat, context,
new QFunctorSlotObject< Func1, 0, Args, void >( slot ) );
}
// -- 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::addShortcut(
QQuickItem* item, const QKeySequence& key, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
return addMemberSlot( 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 addMemberSlot( 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 addMemberSlot( 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 addFunctorSlot( 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 addFunctorSlot( item, key, autoRepeat, context, slot );
}
template< typename Func1 >
inline int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat,
const QObject* context, Func1 slot )
{
return addFunctorSlot( 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 addFunctorSlot( 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 addFunctorSlot( item, key, autoRepeat, nullptr, autoRepeat, slot );
}
template< typename Func1 >
int QskShortcut::addShortcut( const QKeySequence& key, bool autoRepeat, Func1 slot )
{
return addFunctorSlot( nullptr, key, autoRepeat, nullptr, slot );
}
#endif

View File

@ -0,0 +1,375 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskShortcutMap.h"
#include "QskControl.h"
#include <QQuickWindow>
#include <QMetaMethod>
#include <QKeySequence>
#include <QGlobalStatic>
#include <QtGui/private/qguiapplication_p.h>
#include <map>
static inline QShortcutMap& qskShortcutMap()
{
return QGuiApplicationPrivate::instance()->shortcutMap;
}
class QskShortcutHandler final : public QObject
{
public:
QskShortcutHandler();
int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, const char* method );
int add( QQuickItem*, const QKeySequence&,
const QObject* receiver, QtPrivate::QSlotObjectBase* );
void remove( int id );
void setEnabled( int id, bool );
void setAutoRepeat( int id, bool repeat );
virtual bool eventFilter( QObject*, QEvent* ) override final;
private:
int insert( QQuickItem*, const QKeySequence&,
const QObject* receiver, const QMetaMethod&, QtPrivate::QSlotObjectBase* );
void cleanUp( QObject* );
class InvokeData
{
public:
InvokeData():
item( nullptr ),
receiver( nullptr ),
slotObject( nullptr )
{
}
~InvokeData()
{
if ( slotObject )
slotObject->destroyIfLastRef();
}
QQuickItem* item;
const QObject* receiver;
QMetaMethod method;
QtPrivate::QSlotObjectBase* slotObject;
};
std::map< int, InvokeData > m_invokeDataMap;
};
Q_GLOBAL_STATIC( QskShortcutHandler, qskShortcutHandler )
static bool qskContextMatcher( QObject* object, Qt::ShortcutContext context )
{
if ( context == Qt::ApplicationShortcut )
return true;
auto item = qobject_cast< QQuickItem* >( object );
if ( item && context == Qt::WindowShortcut )
{
if ( QskShortcutMap::contextMatcher( item, context ) )
{
/*
Unfortunatley there is no way to have to know about
the contextItem without making it the receiver of
the following QShortcutEvent. So we have to install
an event handler to process and swallow it in QskShortcutHandler.
*/
item->installEventFilter( qskShortcutHandler );
return true;
}
}
return false;
}
QskShortcutHandler::QskShortcutHandler()
{
// to process all sort of shortcut events at the same place
installEventFilter( this );
}
int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& sequence,
const QObject* receiver, const char* method )
{
int id = 0;
if ( receiver )
{
const QMetaObject* metaObject = receiver->metaObject();
const int methodIndex = metaObject->indexOfMethod(
QMetaObject::normalizedSignature( method ).constData() + 1 );
if ( methodIndex >= 0 )
{
id = insert( item, sequence,
receiver, metaObject->method( methodIndex ), nullptr );
}
}
return id;
}
int QskShortcutHandler::add( QQuickItem* item, const QKeySequence& sequence,
const QObject* receiver, QtPrivate::QSlotObjectBase* slotObj )
{
return insert( item, sequence, receiver, QMetaMethod(), slotObj );
}
int QskShortcutHandler::insert(
QQuickItem* item, const QKeySequence& sequence,
const QObject* receiver, const QMetaMethod& method,
QtPrivate::QSlotObjectBase* slotObject )
{
if ( receiver )
{
receiver->disconnect( this );
connect( receiver, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
int id = 0;
auto& map = qskShortcutMap();
if ( item )
{
if ( item != receiver )
{
item->disconnect( this );
connect( item, &QObject::destroyed, this, &QskShortcutHandler::cleanUp );
}
id = map.addShortcut( item, sequence, Qt::WindowShortcut, qskContextMatcher );
}
else
{
id = map.addShortcut( this, sequence, Qt::ApplicationShortcut, qskContextMatcher );
}
auto& data = m_invokeDataMap[ id ];
data.item = item;
data.receiver = receiver;
if ( slotObject )
data.slotObject = slotObject;
else
data.method = method;
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 )
{
/*
When item != receiver we might remain being connected
to destroyed signals we are not interested in anymore. TODO ...
*/
auto& map = qskShortcutMap();
for ( auto it = m_invokeDataMap.begin(); it != m_invokeDataMap.end(); )
{
const auto& data = it->second;
if ( data.item == object || data.receiver == object )
{
map.removeShortcut( it->first, nullptr );
it = m_invokeDataMap.erase( it );
continue;
}
++it;
}
}
void QskShortcutHandler::setEnabled( int id, bool enabled )
{
auto& map = qskShortcutMap();
map.setShortcutEnabled( enabled, id, this );
}
void QskShortcutHandler::setAutoRepeat( int id, bool repeat )
{
auto& map = qskShortcutMap();
map.setShortcutAutoRepeat( repeat, id, this );
}
bool QskShortcutHandler::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() != QEvent::Shortcut )
return false;
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() )
{
const auto& invokeData = it->second;
Q_ASSERT( invokeData.item == nullptr || invokeData.item == object );
auto receiver = const_cast< QObject* >( invokeData.receiver );
if ( invokeData.slotObject )
{
void* args[] = { 0 };
if ( receiver && receiver->thread() != thread() )
{
QCoreApplication::postEvent( receiver,
new QMetaCallEvent( invokeData.slotObject, nullptr, 0, 0, nullptr, args ) );
}
else
{
invokeData.slotObject->call( receiver, args );
}
}
else
{
invokeData.method.invoke( receiver, Qt::AutoConnection );
}
return true;
}
// seems like someone else is also interested in shortcuts
return false;
}
int QskShortcutMap::addMethod( QQuickItem* item, const QKeySequence& sequence,
bool autoRepeat, const QObject* receiver, const char* method )
{
if ( receiver == nullptr )
{
return 0;
}
int id = qskShortcutHandler->add( item, sequence, receiver, method );
if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
return id;
}
int QskShortcutMap::addSlotObject( QQuickItem* item, const QKeySequence& sequence,
bool autoRepeat, const QObject* receiver, QtPrivate::QSlotObjectBase* slotObject )
{
int id = qskShortcutHandler->add( item, sequence, receiver, slotObject );
if ( id && !autoRepeat )
qskShortcutHandler->setAutoRepeat( id, false );
return id;
}
void QskShortcutMap::setAutoRepeat( int id, bool on )
{
qskShortcutHandler->setAutoRepeat( id, on );
}
void QskShortcutMap::setEnabled( int id, bool on )
{
qskShortcutHandler->setEnabled( id, on );
}
void QskShortcutMap::removeShortcut( int id )
{
qskShortcutHandler->remove( id );
}
bool QskShortcutMap::contextMatcher(
const QQuickItem* item, Qt::ShortcutContext context )
{
if ( context == Qt::ApplicationShortcut )
return true;
if ( item && context == Qt::WindowShortcut )
{
const auto focusWindow = QGuiApplication::focusWindow();
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();
}
return true;
}
return false;
}

View File

@ -0,0 +1,228 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_SHORTCUT_MAP_H
#define QSK_SHORTCUT_MAP_H
#include "QskGlobal.h"
#include <QQuickWindow>
class QQuickItem;
class QKeySequence;
class QSK_EXPORT QskShortcutMap
{
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,
const QObject* receiver, const char* method );
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
// -- calling a QObject method
template< typename Func1 >
static int addShortcut( const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
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 >
static int addShortcut( const QKeySequence&, bool autoRepeat,
const QObject* context, Func1 slot );
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 >
static int addShortcut( const QKeySequence&, bool autoRepeat, Func1 slot );
template< typename Func1 >
static int addShortcut( QQuickWindow*, const QKeySequence&, bool autoRepeat, Func1 slot );
template< typename Func1 >
static int addShortcut( QQuickItem*, const QKeySequence&, bool autoRepeat, Func1 slot );
static bool contextMatcher( const QQuickItem*, Qt::ShortcutContext );
private:
QskShortcutMap() = delete;
~QskShortcutMap() = delete;
static int addMethod( QQuickItem*, const QKeySequence&, bool autoRepeat,
const QObject* receiver, const char* method );
template< typename Func1 >
static int addMemberSlot( QQuickItem*, const QKeySequence&, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot );
template< typename Func1 >
static int addFunctorSlot( QQuickItem*, const QKeySequence&,
bool autoRepeat, const QObject* context, Func1 slot );
static int addSlotObject(
QQuickItem* item, const QKeySequence&, bool autoRepeat,
const QObject* receiver, QtPrivate::QSlotObjectBase* );
};
template< typename Func1 >
inline int QskShortcutMap::addMemberSlot(
QQuickItem* item, const QKeySequence& sequence, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
using namespace QtPrivate;
typedef FunctionPointer< Func1 > SlotType;
Q_STATIC_ASSERT_X( int( SlotType::ArgumentCount ) == 0,
"The slot must not have any arguments.");
return addSlotObject( item, sequence, autoRepeat, receiver,
new QSlotObject< Func1, typename SlotType::Arguments, void >( slot ) );
}
template< typename Func1 >
inline int QskShortcutMap::addFunctorSlot(
QQuickItem* item, const QKeySequence& sequence, 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." );
using Args = List_Left< void, 0 >::Value;
return addSlotObject( item, sequence, autoRepeat, context,
new QFunctorSlotObject< Func1, 0, Args, void >( slot ) );
}
// -- traditional slots
inline int QskShortcutMap::addShortcut(
QQuickItem* item, const QKeySequence& sequence, bool autoRepeat,
const QObject* receiver, const char* method )
{
return addMethod( item, sequence, autoRepeat, receiver, method );
}
inline int QskShortcutMap::addShortcut(
const QKeySequence& sequence, bool autoRepeat,
const QObject* receiver, const char* method )
{
return addMethod( nullptr, sequence, autoRepeat, receiver, method );
}
inline int QskShortcutMap::addShortcut(
QQuickWindow* window, const QKeySequence& sequence, bool autoRepeat,
const QObject* receiver, const char* method )
{
auto item = window ? window->contentItem() : nullptr;
return addMethod( item, sequence, autoRepeat, receiver, method );
}
// -- calling a QObject method
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickItem* item, const QKeySequence& sequence, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
return addMemberSlot( item, sequence, autoRepeat, receiver, slot );
}
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickWindow* window, const QKeySequence& sequence, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
auto item = window ? window->contentItem() : nullptr;
return addMemberSlot( item, sequence, autoRepeat, receiver, slot );
}
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
const QKeySequence& sequence, bool autoRepeat,
const typename QtPrivate::FunctionPointer< Func1 >::Object* receiver, Func1 slot )
{
return addMemberSlot( nullptr, sequence, autoRepeat, receiver, slot );
}
// -- calling a functor or function pointer with context
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickItem* item, const QKeySequence& sequence, bool autoRepeat,
const QObject* context, Func1 slot )
{
return addFunctorSlot( item, sequence, autoRepeat, context, slot );
}
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickWindow* window, const QKeySequence& sequence, bool autoRepeat,
const QObject* context, Func1 slot )
{
auto item = window ? window->contentItem() : nullptr;
return addFunctorSlot( item, sequence, autoRepeat, context, slot );
}
template< typename Func1 >
inline int QskShortcutMap::addShortcut( const QKeySequence& sequence, bool autoRepeat,
const QObject* context, Func1 slot )
{
return addFunctorSlot( nullptr, sequence, autoRepeat, context, slot );
}
// -- calling a functor or function pointer
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickItem* item, const QKeySequence& sequence, bool autoRepeat, Func1 slot )
{
return addFunctorSlot( item, sequence, autoRepeat, nullptr, slot );
}
template< typename Func1 >
inline int QskShortcutMap::addShortcut(
QQuickWindow* window, const QKeySequence& sequence, bool autoRepeat, Func1 slot )
{
auto item = window ? window->contentItem() : nullptr;
return addFunctorSlot( item, sequence, autoRepeat, nullptr, autoRepeat, slot );
}
template< typename Func1 >
int QskShortcutMap::addShortcut(
const QKeySequence& sequence, bool autoRepeat, Func1 slot )
{
return addFunctorSlot( nullptr, sequence, autoRepeat, nullptr, slot );
}
#endif

View File

@ -152,6 +152,7 @@ HEADERS += \
controls/QskSeparatorSkinlet.h \
controls/QskSetup.h \
controls/QskShortcut.h \
controls/QskShortcutMap.h \
controls/QskSimpleListBox.h \
controls/QskSkinFactory.h \
controls/QskSkin.h \
@ -215,6 +216,7 @@ SOURCES += \
controls/QskSeparatorSkinlet.cpp \
controls/QskSetup.cpp \
controls/QskShortcut.cpp \
controls/QskShortcutMap.cpp \
controls/QskSimpleListBox.cpp \
controls/QskSkin.cpp \
controls/QskSkinHintTable.cpp \

View File

@ -1,7 +1,7 @@
#include "SkinnyShortcut.h"
#include <QskSkinFactory.h>
#include <QskShortcut.h>
#include <QskShortcutMap.h>
#include <QskSetup.h>
#include <QskWindow.h>
#include <QskAspect.h>
@ -31,21 +31,21 @@ void SkinnyShortcut::enable( Types types )
if ( types & RotateSkin )
{
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ),
false, &s_shortcut, &SkinnyShortcut::rotateSkin );
cout << "CTRL-S to change the skin." << endl;
}
if ( types & DebugBackground )
{
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_B ),
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 ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_K ),
false, &s_shortcut, &SkinnyShortcut::debugStatistics );
cout << "CTRL-K to dump statistics about the items/nodes being currently used." << endl;
}
@ -55,7 +55,7 @@ void SkinnyShortcut::enable( Types types )
// QKeySequence::Quit crashes the application
// when not being implemented by the platform !!
QskShortcut::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_Q ),
QskShortcutMap::addShortcut( QKeySequence( Qt::CTRL + Qt::Key_Q ),
false, QGuiApplication::instance(), &QGuiApplication::quit );
cout << "CTRL-Q to terminate the application." << endl;
}