QskInputGrabber added

This commit is contained in:
Uwe Rathmann 2018-07-02 08:08:38 +02:00
parent 7ee3ff436d
commit 73d911cee2
7 changed files with 403 additions and 186 deletions

View File

@ -0,0 +1,218 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskInputGrabber.h"
#include "QskWindow.h"
#include "QskQuick.h"
#include <QPointer>
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
#include <private/qquickitemchangelistener_p.h>
QSK_QT_PRIVATE_END
class QskInputGrabber::PrivateData : public QQuickItemChangeListener
{
public:
PrivateData( QskInputGrabber* grabber ):
m_grabber( grabber )
{
}
inline QRectF grabberRect() const
{
if ( itemBelow && itemAbove )
{
const auto pos = itemBelow->mapToItem( itemAbove, QPointF() );
return QRectF( pos.x(), pos.y(), itemBelow->width(), itemBelow->height() );
}
return QRectF();
}
void enableListener( QQuickItem* item,
QQuickItemPrivate::ChangeTypes types, bool on )
{
if ( item )
{
auto d = QQuickItemPrivate::get( item );
if ( on )
d->addItemChangeListener( this, types );
else
d->removeItemChangeListener( this, types );
}
}
void setup( QQuickItem* item )
{
QQuickItem* newItemBelow = item ? item->parentItem() : nullptr;
QQuickItem* newItemAbove = item;
if ( newItemBelow != itemBelow )
{
const auto changeTypes = QQuickItemPrivate::Geometry;
enableListener( itemBelow, changeTypes, false );
enableListener( newItemBelow, changeTypes, true );
itemBelow = newItemBelow;
}
if ( newItemAbove != itemAbove )
{
const auto changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Parent;
enableListener( itemAbove, changeTypes, false );
enableListener( newItemAbove, changeTypes, true );
itemAbove = newItemAbove;
}
}
private:
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
virtual void itemGeometryChanged( QQuickItem* item,
QQuickGeometryChange change, const QRectF& ) override final
{
bool doUpdate = false;
if ( item == itemAbove )
doUpdate = change.positionChange();
else
doUpdate = change.sizeChange();
if ( doUpdate )
m_grabber->updateGeometry();
}
#else
virtual void itemGeometryChanged(
QQuickItem* item, const QRectF& newRect, const QRectF& oldRect ) override
{
bool doUpdate = false;
if ( item == itemAbove )
doUpdate = newRect.size() != oldRect.size();
else
doUpdate = newRect.topLeft() != oldRect.topLeft();
if ( doUpdate )
m_grabber->updateGeometry();
}
#endif
virtual void itemParentChanged( QQuickItem* item, QQuickItem* parentItem ) override
{
if ( item == m_grabber && parentItem )
{
setup( parentItem );
m_grabber->updateGeometry();
}
}
QskInputGrabber* m_grabber;
public:
QPointer< QQuickItem > itemAbove;
QPointer< QQuickItem > itemBelow;
};
QskInputGrabber::QskInputGrabber( QQuickItem* parent ) :
Inherited( parent ),
m_data( new PrivateData( this ) )
{
setAcceptedMouseButtons( Qt::AllButtons );
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
setAcceptTouchEvents( true );
#endif
setAcceptHoverEvents( true );
setTransparentForPositioner( true );
setFlag( QQuickItem::ItemHasContents, false );
m_data->setup( parent );
updateGeometry();
}
QskInputGrabber::~QskInputGrabber()
{
m_data->setup( nullptr );
}
bool QskInputGrabber::event( QEvent* event )
{
bool doBlock = false;
switch ( static_cast< int >( event->type() ) )
{
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
{
const auto ev = static_cast< QMouseEvent* > ( event );
doBlock = isBlocking( ev->localPos() );
break;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchCancel:
{
#if 1
// TODO
doBlock = true;
#endif
break;
}
case QEvent::Wheel:
{
const auto ev = static_cast< QWheelEvent* > ( event );
doBlock = isBlocking( ev->posF() );
break;
}
case QEvent::HoverEnter:
case QEvent::HoverLeave:
{
const auto ev = static_cast< QHoverEvent* > ( event );
doBlock = isBlocking( ev->posF() );
break;
}
}
if ( doBlock )
{
event->accept();
if ( auto w = qobject_cast< QskWindow* >( window() ) )
w->setEventAcceptance( QskWindow::EventPropagationStopped );
return true;
}
return Inherited::event( event );
}
bool QskInputGrabber::isBlocking( const QPointF& pos ) const
{
if ( const auto item = parentItem() )
return !item->contains( position() + pos );
return true;
}
QRectF QskInputGrabber::grabberRect() const
{
return m_data->grabberRect();
}
void QskInputGrabber::updateGeometry()
{
const QRectF rect = grabberRect();
if ( rect != geometry() )
setGeometry( rect );
}
#include "moc_QskInputGrabber.cpp"

View File

@ -0,0 +1,36 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_INPUT_GRABBER_H
#define QSK_INPUT_GRABBER_H 1
#include "QskGlobal.h"
#include "QskControl.h"
class QskInputGrabber: public QskControl
{
Q_OBJECT
using Inherited = QskControl;
public:
QskInputGrabber( QQuickItem* parent = nullptr );
virtual ~QskInputGrabber();
virtual QRectF grabberRect() const;
virtual bool isBlocking( const QPointF& ) const;
public Q_SLOTS:
void updateGeometry();
protected:
virtual bool event( QEvent* ) override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -4,9 +4,10 @@
*****************************************************************************/ *****************************************************************************/
#include "QskPopup.h" #include "QskPopup.h"
#include "QskQuick.h" #include "QskInputGrabber.h"
#include "QskAspect.h" #include "QskAspect.h"
#include "QskWindow.h" #include "QskWindow.h"
#include "QskQuick.h"
#include <QtMath> #include <QtMath>
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
@ -40,147 +41,45 @@ static void qskSetFocus( QQuickItem* item, bool on )
namespace namespace
{ {
class InputGrabber final : public QQuickItem class InputGrabber final : public QskInputGrabber
{ {
using Inherited = QQuickItem; using Inherited = QskInputGrabber;
public: public:
InputGrabber( QskPopup* popup ): InputGrabber( QskPopup* parent ):
Inherited( popup ) Inherited( parent )
{ {
setObjectName( QStringLiteral( "QskPopupInputGrabber" ) );
/*
We want to receive those events to stop them
from being propagated any further
*/
setAcceptedMouseButtons( Qt::AllButtons );
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
setAcceptTouchEvents( true );
#endif
setAcceptHoverEvents( true );
QQuickItemPrivate::get( this )->setTransparentForPositioner( true );
setFlag( QQuickItem::ItemHasContents, false );
/*
The grabber has to be adjusted to the geometry of
the parent of the popup, but being a child of the popup its
coordinate system is relative to it and needs to be adjusted
when the position of the popup changes too.
Using a QQuickItemChangeListener instead of connections
doesn't make the code much better as we need to deal with
how to remove the listener when the grand parent has changed then.
*/
auto method = &InputGrabber::updateGeometry;
connect( popup, &QQuickItem::xChanged, this, method );
connect( popup, &QQuickItem::yChanged, this, method );
if ( QQuickItem* item = parentItem()->parentItem() )
{
connect( item, &QQuickItem::xChanged, this, method );
connect( item, &QQuickItem::yChanged, this, method );
connect( item, &QQuickItem::widthChanged, this, method );
connect( item, &QQuickItem::heightChanged, this, method );
} }
updateGeometry(); virtual void geometryChanged(
} const QRectF& newGeometry, const QRectF& oldGeometry ) override final
protected:
virtual bool event( QEvent* event ) override final
{ {
bool doSwallow = false; Inherited::geometryChanged( newGeometry, oldGeometry );
switch ( static_cast<int>( event->type() ) )
{
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
{
doSwallow = handleMouseEvent( static_cast< QMouseEvent* > ( event ) );
break;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchCancel:
{
doSwallow = handleTouchEvent( static_cast< QTouchEvent* > ( event ) );
break;
}
case QEvent::Wheel:
{
doSwallow = handleWheelEvent( static_cast< QWheelEvent* > ( event ) );
break;
}
case QEvent::HoverEnter:
case QEvent::HoverLeave:
{
doSwallow = handleHoverEvent( static_cast< QHoverEvent* > ( event ) );
break;
}
}
if ( doSwallow )
{
event->accept();
if ( auto w = qobject_cast< QskWindow* >( window() ) )
w->setEventAcceptance( QskWindow::EventPropagationStopped );
return true;
}
return Inherited::event( event );
}
private:
inline bool handleMouseEvent( const QMouseEvent* event )
{
return !isInsidePopup( event->localPos() );
}
inline bool handleWheelEvent( const QWheelEvent* event )
{
return !isInsidePopup( event->posF() );
}
inline bool handleTouchEvent( const QTouchEvent* )
{
#if 1
return true; // TODO
#endif
}
inline bool handleHoverEvent( const QHoverEvent* event )
{
return !isInsidePopup( event->posF() );
}
inline bool isInsidePopup( const QPointF& pos ) const
{
if ( const auto item = parentItem() )
return item->contains( position() + pos );
return false;
}
void updateGeometry()
{
if ( auto popup = static_cast< QskPopup* >( parentItem() ) ) if ( auto popup = static_cast< QskPopup* >( parentItem() ) )
{ {
const QRectF rect = popup->grabberRect();
if ( rect != qskItemGeometry( this ) )
{
qskSetItemGeometry( this, rect );
if ( popup->hasOverlay() ) if ( popup->hasOverlay() )
popup->update(); popup->update();
} }
} }
virtual bool event( QEvent* event ) override final
{
bool ok = Inherited::event( event );
if ( event->type() == QEvent::MouseButtonPress )
{
if ( auto popup = static_cast< QskPopup* >( parentItem() ) )
{
if ( event->isAccepted() &&
( popup->popupFlags() & QskPopup::CloseOnPressOutside ) )
{
popup->close();
}
}
}
return ok;
} }
}; };
} }
@ -190,6 +89,7 @@ class QskPopup::PrivateData
public: public:
PrivateData(): PrivateData():
inputGrabber( nullptr ), inputGrabber( nullptr ),
flags( 0 ),
isModal( false ), isModal( false ),
isOpen( false ), isOpen( false ),
autoGrabFocus( true ), autoGrabFocus( true ),
@ -199,6 +99,7 @@ public:
InputGrabber* inputGrabber; InputGrabber* inputGrabber;
int flags : 4;
bool isModal : 1; bool isModal : 1;
bool isOpen : 1; bool isOpen : 1;
@ -226,28 +127,48 @@ QskPopup::~QskPopup()
{ {
} }
void QskPopup::open()
{
setFading( true );
}
void QskPopup::close()
{
const bool wasOpen = m_data->isOpen;
m_data->isOpen = false;
setFading( false );
if ( wasOpen )
{
Q_EMIT closed();
if ( testPopupFlag( DeleteOnClose ) )
deleteLater();
}
}
void QskPopup::setFading( bool on )
{
setVisible( on );
}
bool QskPopup::isOpen() const
{
return m_data->isOpen;
}
QRectF QskPopup::overlayRect() const QRectF QskPopup::overlayRect() const
{ {
if ( hasOverlay() ) if ( hasOverlay() && m_data->inputGrabber )
return grabberRect(); return m_data->inputGrabber->grabberRect();
return QRectF();
}
QRectF QskPopup::grabberRect() const
{
if ( const auto item = parentItem() )
{
if ( isVisible() && m_data->isModal )
return QRectF( -position(), QSizeF( item->width(), item->height() ) );
}
return QRectF(); return QRectF();
} }
void QskPopup::updateInputGrabber() void QskPopup::updateInputGrabber()
{ {
if ( window() && parentItem() && isVisible() && isModal() ) if ( isVisible() && ( isModal() || testPopupFlag( CloseOnPressOutside ) ) )
{ {
if ( m_data->inputGrabber == nullptr ) if ( m_data->inputGrabber == nullptr )
{ {
@ -289,6 +210,37 @@ bool QskPopup::isModal() const
return m_data->isModal; return m_data->isModal;
} }
void QskPopup::setPopupFlags( PopupFlags flags )
{
m_data->flags = flags;
}
QskPopup::PopupFlags QskPopup::popupFlags() const
{
return static_cast< PopupFlags >( m_data->flags );
}
void QskPopup::setPopupFlag( PopupFlag flag, bool on )
{
auto flags = m_data->flags;
if ( on )
flags |= flag;
else
flags &= ~flag;
if ( flags != m_data->flags )
{
m_data->flags = flags;
updateInputGrabber();
}
}
bool QskPopup::testPopupFlag( PopupFlag flag ) const
{
return m_data->flags & flag;
}
void QskPopup::setOverlay( bool on ) void QskPopup::setOverlay( bool on )
{ {
if ( hasOverlay() != on ) if ( hasOverlay() != on )
@ -432,12 +384,12 @@ QQuickItem* QskPopup::focusSuccessor() const
void QskPopup::aboutToShow() void QskPopup::aboutToShow()
{ {
if ( !m_data->isOpen )
{
if ( m_data->autoGrabFocus )
grabFocus( true );
m_data->isOpen = true; m_data->isOpen = true;
if ( m_data->autoGrabFocus )
{
// What to do, when we are hidden below another popup ??
grabFocus( true );
} }
Inherited::aboutToShow(); Inherited::aboutToShow();
@ -448,33 +400,19 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
{ {
Inherited::itemChange( change, value ); Inherited::itemChange( change, value );
switch ( change ) if ( change == QQuickItem::ItemVisibleHasChanged )
{ {
case QQuickItem::ItemVisibleHasChanged:
{
updateInputGrabber();
if ( !value.boolValue ) if ( !value.boolValue )
{ {
m_data->isOpen = false;
grabFocus( false );
}
break;
}
case QQuickItem::ItemParentHasChanged:
case QQuickItem::ItemSceneChange:
{
delete m_data->inputGrabber;
m_data->inputGrabber = nullptr;
updateInputGrabber(); updateInputGrabber();
break; grabFocus( false );
if ( m_data->isOpen )
{
if ( testPopupFlag( CloseOnHide ) )
close();
}
} }
default:
;
} }
} }

View File

@ -21,9 +21,25 @@ class QSK_EXPORT QskPopup : public QskControl
public: public:
QSK_SUBCONTROLS( Overlay ) QSK_SUBCONTROLS( Overlay )
enum PopupFlag
{
CloseOnHide = 1 << 0,
DeleteOnClose = 1 << 1,
CloseOnPressOutside = 1 << 2
};
Q_ENUM( PopupFlag )
Q_DECLARE_FLAGS( PopupFlags, PopupFlag )
QskPopup( QQuickItem* parent = nullptr ); QskPopup( QQuickItem* parent = nullptr );
virtual ~QskPopup(); virtual ~QskPopup();
void setPopupFlags( PopupFlags );
PopupFlags popupFlags() const;
void setPopupFlag( PopupFlag, bool on = true );
bool testPopupFlag( PopupFlag ) const;
void setModal( bool on = true ); void setModal( bool on = true );
bool isModal() const; bool isModal() const;
@ -31,15 +47,21 @@ public:
bool hasOverlay() const; bool hasOverlay() const;
virtual QRectF overlayRect() const; virtual QRectF overlayRect() const;
virtual QRectF grabberRect() const;
bool isOpen() const;
public Q_SLOTS:
void open();
void close();
Q_SIGNALS: Q_SIGNALS:
void closed();
void modalChanged( bool ); void modalChanged( bool );
void overlayChanged( bool ); void overlayChanged( bool );
protected: protected:
virtual void aboutToShow() override; virtual void aboutToShow() override;
virtual void setFading( bool on );
virtual bool event( QEvent* ) override; virtual bool event( QEvent* ) override;
virtual void focusInEvent( QFocusEvent * ) override; virtual void focusInEvent( QFocusEvent * ) override;

View File

@ -7,27 +7,31 @@
#include <QEventLoop> #include <QEventLoop>
#include <QQuickWindow> #include <QQuickWindow>
static inline void qskSetRejectOnClose( QskDialogSubWindow* subWindow, bool on )
{
if ( on )
{
QObject::connect( subWindow, &QskPopup::closed,
subWindow, &QskDialogSubWindow::reject );
}
else
{
QObject::disconnect( subWindow, &QskPopup::closed,
subWindow, &QskDialogSubWindow::reject );
}
}
QskDialogSubWindow::QskDialogSubWindow( QQuickItem* parent ): QskDialogSubWindow::QskDialogSubWindow( QQuickItem* parent ):
Inherited( parent ), Inherited( parent ),
m_result( QskDialog::Rejected ), m_result( QskDialog::Rejected )
m_deleteOnDone( false )
{ {
qskSetRejectOnClose( this, true );
} }
QskDialogSubWindow::~QskDialogSubWindow() QskDialogSubWindow::~QskDialogSubWindow()
{ {
} }
void QskDialogSubWindow::setDeleteOnDone( bool on )
{
m_deleteOnDone = on;
}
bool QskDialogSubWindow::deleteOnDone() const
{
return m_deleteOnDone;
}
void QskDialogSubWindow::setResult( QskDialog::DialogCode result ) void QskDialogSubWindow::setResult( QskDialog::DialogCode result )
{ {
m_result = result; m_result = result;
@ -68,17 +72,18 @@ void QskDialogSubWindow::done( QskDialog::DialogCode result )
{ {
m_result = result; m_result = result;
if ( !isOpen() )
return;
qskSetRejectOnClose( this, false );
close();
Q_EMIT finished( result ); Q_EMIT finished( result );
if ( result == QskDialog::Accepted ) if ( result == QskDialog::Accepted )
Q_EMIT accepted(); Q_EMIT accepted();
else else
Q_EMIT rejected(); Q_EMIT rejected();
hide();
if ( m_deleteOnDone )
deleteLater();
} }
void QskDialogSubWindow::accept() void QskDialogSubWindow::accept()

View File

@ -20,9 +20,6 @@ public:
QskDialogSubWindow( QQuickItem* parent = nullptr ); QskDialogSubWindow( QQuickItem* parent = nullptr );
virtual ~QskDialogSubWindow(); virtual ~QskDialogSubWindow();
void setDeleteOnDone( bool on );
bool deleteOnDone() const;
Q_INVOKABLE QskDialog::DialogCode result() const; Q_INVOKABLE QskDialog::DialogCode result() const;
Q_INVOKABLE QskDialog::DialogCode exec(); Q_INVOKABLE QskDialog::DialogCode exec();
@ -44,7 +41,6 @@ protected:
private: private:
QskDialog::DialogCode m_result; QskDialog::DialogCode m_result;
bool m_deleteOnDone : 1;
}; };
#endif #endif

View File

@ -135,6 +135,7 @@ HEADERS += \
controls/QskGraphicLabel.h \ controls/QskGraphicLabel.h \
controls/QskGraphicLabelSkinlet.h \ controls/QskGraphicLabelSkinlet.h \
controls/QskHintAnimator.h \ controls/QskHintAnimator.h \
controls/QskInputGrabber.h \
controls/QskListView.h \ controls/QskListView.h \
controls/QskListViewSkinlet.h \ controls/QskListViewSkinlet.h \
controls/QskObjectTree.h \ controls/QskObjectTree.h \
@ -200,6 +201,7 @@ SOURCES += \
controls/QskGraphicLabel.cpp \ controls/QskGraphicLabel.cpp \
controls/QskGraphicLabelSkinlet.cpp \ controls/QskGraphicLabelSkinlet.cpp \
controls/QskHintAnimator.cpp \ controls/QskHintAnimator.cpp \
controls/QskInputGrabber.cpp \
controls/QskListView.cpp \ controls/QskListView.cpp \
controls/QskListViewSkinlet.cpp \ controls/QskListViewSkinlet.cpp \
controls/QskObjectTree.cpp \ controls/QskObjectTree.cpp \