2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskPopup.h"
|
|
|
|
#include "QskAspect.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskInputGrabber.h"
|
2018-07-02 08:08:38 +02:00
|
|
|
#include "QskQuick.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskWindow.h"
|
2018-10-05 14:15:17 +02:00
|
|
|
#include "QskEvent.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
2017-11-02 17:42:19 +01:00
|
|
|
#include <private/qquickwindow_p.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
|
|
|
QSK_SUBCONTROL( QskPopup, Overlay )
|
2018-10-10 08:55:03 +02:00
|
|
|
QSK_STATE( QskPopup, Closed, QskAspect::FirstSystemState << 1 )
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-01-21 14:10:12 +01:00
|
|
|
static void qskSetFocus( QQuickItem* item, bool on )
|
2017-11-02 17:42:19 +01:00
|
|
|
{
|
|
|
|
if ( item->window() == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
For unknown reasons Qt::PopupFocusReason is blocked inside of
|
|
|
|
QQuickItem::setFocus. So let's bypass it calling
|
|
|
|
QQuickWindowPrivate::setFocusInScope/clearFocusInScope directly,
|
|
|
|
*/
|
|
|
|
|
2018-01-20 17:21:13 +01:00
|
|
|
if ( const auto scope = qskNearestFocusScope( item ) )
|
2017-11-02 17:42:19 +01:00
|
|
|
{
|
|
|
|
auto dw = QQuickWindowPrivate::get( item->window() );
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
dw->setFocusInScope( scope, item, Qt::PopupFocusReason );
|
|
|
|
else
|
|
|
|
dw->clearFocusInScope( scope, item, Qt::PopupFocusReason );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 14:15:17 +02:00
|
|
|
static inline void qskSendPopupEvent(
|
|
|
|
QQuickWindow* window, QskPopup* popup, bool on )
|
|
|
|
{
|
|
|
|
if ( window )
|
|
|
|
{
|
|
|
|
const auto type = on ? QskEvent::PopupAdded : QskEvent::PopupRemoved;
|
|
|
|
|
|
|
|
QskPopupEvent event( type, popup );
|
|
|
|
QCoreApplication::sendEvent( window, &event );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
namespace
|
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
class InputGrabber final : public QskInputGrabber
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
using Inherited = QskInputGrabber;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
InputGrabber( QskPopup* parent )
|
|
|
|
: Inherited( parent )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
void geometryChanged( const QRectF& newGeometry,
|
|
|
|
const QRectF& oldGeometry ) override
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
Inherited::geometryChanged( newGeometry, oldGeometry );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( auto popup = static_cast< QskPopup* >( parentItem() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( popup->hasOverlay() )
|
|
|
|
popup->update();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-06-26 11:11:37 +02:00
|
|
|
}
|
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
bool event( QEvent* event ) override
|
2018-06-26 11:11:37 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
bool ok = Inherited::event( event );
|
2018-06-26 11:11:37 +02:00
|
|
|
|
2018-07-26 15:45:05 +02:00
|
|
|
if ( event->type() == QEvent::MouseButtonPress )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( auto popup = static_cast< QskPopup* >( parentItem() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( event->isAccepted() &&
|
|
|
|
( popup->popupFlags() & QskPopup::CloseOnPressOutside ) )
|
|
|
|
{
|
|
|
|
popup->close();
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
2018-07-02 08:08:38 +02:00
|
|
|
|
|
|
|
return ok;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskPopup::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
PrivateData()
|
2018-10-05 14:15:17 +02:00
|
|
|
: flags( 0 )
|
2018-08-03 08:15:28 +02:00
|
|
|
, isModal( false )
|
2018-10-10 08:55:03 +02:00
|
|
|
, hasFaderEffect( true )
|
2018-08-03 08:15:28 +02:00
|
|
|
, autoGrabFocus( true )
|
|
|
|
, handoverFocus( true )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-10-05 14:15:17 +02:00
|
|
|
InputGrabber* inputGrabber = nullptr;
|
|
|
|
|
|
|
|
uint priority = 0;
|
2018-10-10 08:55:03 +02:00
|
|
|
QskAspect::Aspect faderAspect;
|
2017-11-02 16:25:15 +01:00
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
int flags : 4;
|
|
|
|
bool isModal : 1;
|
|
|
|
bool hasFaderEffect : 1;
|
2018-06-26 11:11:37 +02:00
|
|
|
|
|
|
|
const bool autoGrabFocus : 1;
|
|
|
|
const bool handoverFocus : 1;
|
2017-07-21 18:21:34 +02:00
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskPopup::QskPopup( QQuickItem* parent )
|
|
|
|
: Inherited( parent )
|
|
|
|
, m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
// initially the popup is closed and invisible
|
|
|
|
Inherited::setVisible( false );
|
|
|
|
setSkinStateFlag( QskPopup::Closed );
|
|
|
|
|
2018-06-25 12:36:15 +02:00
|
|
|
// we need to stop event propagation
|
2017-07-21 18:21:34 +02:00
|
|
|
setAcceptedMouseButtons( Qt::AllButtons );
|
2018-06-25 12:36:15 +02:00
|
|
|
setWheelEnabled( true );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
// we don't want to be resized by layout code
|
|
|
|
setTransparentForPositioner( true );
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
setFlag( ItemIsFocusScope, true );
|
2017-07-21 18:21:34 +02:00
|
|
|
setTabFence( true );
|
2018-01-20 17:21:13 +01:00
|
|
|
setFocusPolicy( Qt::StrongFocus );
|
2018-10-05 14:15:17 +02:00
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
/*
|
|
|
|
sending a notification to the window, that can
|
|
|
|
be used to register popups for some sort of
|
|
|
|
popup/window management
|
|
|
|
*/
|
2018-10-05 14:15:17 +02:00
|
|
|
qskSendPopupEvent( window(), this, true );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskPopup::~QskPopup()
|
|
|
|
{
|
2018-10-05 14:15:17 +02:00
|
|
|
qskSendPopupEvent( window(), this, false );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-07-02 08:08:38 +02:00
|
|
|
void QskPopup::open()
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
setOpen( true );
|
2018-06-26 11:11:37 +02:00
|
|
|
}
|
|
|
|
|
2018-07-02 08:08:38 +02:00
|
|
|
void QskPopup::close()
|
2018-06-26 11:11:37 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
setOpen( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPopup::setOpen( bool on )
|
|
|
|
{
|
|
|
|
if ( on == isOpen() )
|
|
|
|
return;
|
2018-07-02 08:08:38 +02:00
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
if ( on )
|
|
|
|
QskControl::setVisible( on );
|
2018-07-02 08:08:38 +02:00
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
setSkinStateFlag( QskPopup::Closed, !on );
|
|
|
|
|
|
|
|
Q_EMIT openChanged( on );
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
Q_EMIT opened();
|
|
|
|
else
|
2018-07-02 08:08:38 +02:00
|
|
|
Q_EMIT closed();
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
if ( isFading() )
|
|
|
|
{
|
|
|
|
Q_EMIT fadingChanged( true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( !on )
|
|
|
|
{
|
|
|
|
Inherited::setVisible( false );
|
|
|
|
|
|
|
|
if ( testPopupFlag( QskPopup::DeleteOnClose ) )
|
|
|
|
deleteLater();
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-07-02 08:08:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
bool QskPopup::isOpen() const
|
2018-07-02 08:08:38 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
return !( skinState() & QskPopup::Closed );
|
2018-07-02 08:08:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
bool QskPopup::isFading() const
|
2018-07-02 08:08:38 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
if ( m_data->faderAspect.value() == 0 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QskSkinHintStatus status;
|
|
|
|
(void) effectiveHint( m_data->faderAspect, &status );
|
|
|
|
|
|
|
|
return status.source == QskSkinHintStatus::Animator;
|
2018-07-02 08:08:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskPopup::overlayRect() const
|
|
|
|
{
|
|
|
|
if ( hasOverlay() && m_data->inputGrabber )
|
|
|
|
return m_data->inputGrabber->grabberRect();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
return QRectF();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPopup::updateInputGrabber()
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( parentItem() && isVisible() &&
|
|
|
|
( isModal() || testPopupFlag( CloseOnPressOutside ) ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( m_data->inputGrabber == nullptr )
|
2018-02-07 09:26:47 +01:00
|
|
|
{
|
2018-06-26 11:55:57 +02:00
|
|
|
const auto children = childItems();
|
2017-07-21 18:21:34 +02:00
|
|
|
m_data->inputGrabber = new InputGrabber( this );
|
2018-06-26 11:11:37 +02:00
|
|
|
|
2018-02-07 09:26:47 +01:00
|
|
|
if ( !children.isEmpty() )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Even if the input grabber has no content it has an effect
|
|
|
|
on QQuickItem::childAt. Also tools like Squish struggle with
|
|
|
|
sorting out items without content.
|
2018-02-07 15:39:46 +01:00
|
|
|
So let's better move the grabber to the beginning.
|
2018-02-07 09:26:47 +01:00
|
|
|
*/
|
|
|
|
m_data->inputGrabber->stackBefore( children.first() );
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete m_data->inputGrabber;
|
|
|
|
m_data->inputGrabber = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
QskAspect::Aspect QskPopup::faderAspect() const
|
|
|
|
{
|
|
|
|
return m_data->faderAspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPopup::setFaderAspect( QskAspect::Aspect aspect )
|
|
|
|
{
|
|
|
|
auto faderAspect = aspect;
|
|
|
|
faderAspect.clearStates(); // animated values are always stateless
|
|
|
|
|
|
|
|
if ( faderAspect == m_data->faderAspect )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( isFading() )
|
|
|
|
{
|
|
|
|
// stop the running animation TODO ...
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->faderAspect = faderAspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskPopup::isTransitionAccepted( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
if ( isVisible() && m_data->hasFaderEffect )
|
|
|
|
{
|
|
|
|
if ( ( aspect.value() == 0 ) )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
QskAspect::Aspect() is an early check that is used
|
|
|
|
to find out if more detailed checking of aspects
|
|
|
|
is necessary.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( aspect == m_data->faderAspect )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( aspect.type() == QskAspect::Color )
|
|
|
|
{
|
|
|
|
if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::isTransitionAccepted( aspect );
|
|
|
|
}
|
|
|
|
|
2018-10-05 14:15:17 +02:00
|
|
|
void QskPopup::setPriority( uint priority )
|
|
|
|
{
|
|
|
|
if ( m_data->priority != priority )
|
|
|
|
{
|
|
|
|
m_data->priority = priority;
|
|
|
|
Q_EMIT priorityChanged( priority );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint QskPopup::priority() const
|
|
|
|
{
|
|
|
|
return m_data->priority;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskPopup::setModal( bool on )
|
|
|
|
{
|
|
|
|
if ( on == m_data->isModal )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_data->isModal = on;
|
|
|
|
updateInputGrabber();
|
|
|
|
|
2018-06-26 11:11:37 +02:00
|
|
|
Q_EMIT modalChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QskPopup::isModal() const
|
|
|
|
{
|
|
|
|
return m_data->isModal;
|
|
|
|
}
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
void QskPopup::setFaderEffect( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->hasFaderEffect )
|
|
|
|
{
|
|
|
|
m_data->hasFaderEffect = on;
|
|
|
|
Q_EMIT faderEffectChanged( on );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskPopup::hasFaderEffect() const
|
|
|
|
{
|
|
|
|
return m_data->hasFaderEffect;
|
|
|
|
}
|
|
|
|
|
2018-07-02 08:08:38 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskPopup::setOverlay( bool on )
|
|
|
|
{
|
|
|
|
if ( hasOverlay() != on )
|
|
|
|
{
|
|
|
|
const auto subControl = effectiveSubcontrol( QskPopup::Overlay );
|
|
|
|
setFlagHint( subControl | QskAspect::Style, on );
|
|
|
|
|
|
|
|
update();
|
2018-06-26 11:11:37 +02:00
|
|
|
Q_EMIT overlayChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskPopup::hasOverlay() const
|
|
|
|
{
|
|
|
|
return flagHint< bool >( QskPopup::Overlay | QskAspect::Style, true );
|
|
|
|
}
|
|
|
|
|
2017-11-02 16:25:15 +01:00
|
|
|
void QskPopup::grabFocus( bool on )
|
|
|
|
{
|
|
|
|
if ( on == hasFocus() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
{
|
2018-01-21 14:10:12 +01:00
|
|
|
qskSetFocus( this, true );
|
2017-11-02 16:25:15 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-01-21 14:10:12 +01:00
|
|
|
QQuickItem* successor = nullptr;
|
2017-11-02 16:25:15 +01:00
|
|
|
|
2018-01-20 17:21:13 +01:00
|
|
|
if ( m_data->handoverFocus )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Qt/Quick does not handover the focus to another item,
|
|
|
|
when the active focus gets lost. For the situation of
|
|
|
|
a popup being closed we try to do it.
|
|
|
|
*/
|
2018-01-21 14:10:12 +01:00
|
|
|
successor = focusSuccessor();
|
2018-01-20 17:21:13 +01:00
|
|
|
}
|
2017-11-02 16:25:15 +01:00
|
|
|
|
2018-01-21 14:10:12 +01:00
|
|
|
if ( successor )
|
|
|
|
qskSetFocus( successor, true );
|
|
|
|
|
|
|
|
if ( hasFocus() )
|
|
|
|
qskSetFocus( this, false );
|
2017-11-02 16:25:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
bool QskPopup::event( QEvent* event )
|
|
|
|
{
|
|
|
|
bool ok = Inherited::event( event );
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
switch ( static_cast< int >( event->type() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-06-26 11:11:37 +02:00
|
|
|
case QEvent::KeyPress:
|
|
|
|
case QEvent::KeyRelease:
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
case QEvent::Wheel:
|
2018-06-26 11:11:37 +02:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
case QEvent::MouseButtonPress:
|
|
|
|
case QEvent::MouseMove:
|
|
|
|
case QEvent::MouseButtonRelease:
|
2018-06-26 11:11:37 +02:00
|
|
|
|
2018-06-25 12:36:15 +02:00
|
|
|
case QEvent::HoverEnter:
|
|
|
|
case QEvent::HoverLeave:
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
// swallow the event
|
|
|
|
event->accept();
|
2018-06-25 12:36:15 +02:00
|
|
|
if ( auto w = qobject_cast< QskWindow* >( window() ) )
|
|
|
|
w->setEventAcceptance( QskWindow::EventPropagationStopped );
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-10-10 08:55:03 +02:00
|
|
|
case QskEvent::Animator:
|
|
|
|
{
|
|
|
|
const auto animtorEvent = static_cast< QskAnimatorEvent* >( event );
|
|
|
|
|
|
|
|
if ( ( animtorEvent->state() == QskAnimatorEvent::Terminated )
|
|
|
|
&& ( animtorEvent->aspect() == m_data->faderAspect ) )
|
|
|
|
{
|
|
|
|
if ( !isOpen() )
|
|
|
|
{
|
|
|
|
Inherited::setVisible( false );
|
|
|
|
|
|
|
|
if ( testPopupFlag( QskPopup::DeleteOnClose ) )
|
|
|
|
deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_EMIT fadingChanged( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
default:
|
|
|
|
{
|
2018-07-26 15:45:05 +02:00
|
|
|
/*
|
|
|
|
Don't accept touch events otherwise we don't receive the
|
|
|
|
synthesized mouse events and need to handle both type of
|
|
|
|
events from now on.
|
|
|
|
But by accepting the mouse event propagation of the touch
|
|
|
|
events also stops.
|
|
|
|
*/
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2018-02-04 12:28:26 +01:00
|
|
|
void QskPopup::focusInEvent( QFocusEvent* event )
|
|
|
|
{
|
|
|
|
Inherited::focusInEvent( event );
|
|
|
|
|
|
|
|
if ( isFocusScope() && isTabFence() && ( scopedFocusItem() == nullptr ) )
|
|
|
|
{
|
|
|
|
if ( event->reason() == Qt::PopupFocusReason )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
When receiving the focus we need to have a focused
|
|
|
|
item, so that the tab focus chain has a starting point.
|
|
|
|
|
|
|
|
But we only do it when the reason is Qt::PopupFocusReason
|
|
|
|
as we also receive focus events during the process of reparenting
|
|
|
|
children and setting the focus there can leave the item tree
|
|
|
|
in an invalid state.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ( auto focusItem = nextItemInFocusChain( true ) )
|
|
|
|
{
|
|
|
|
if ( qskIsItemComplete( focusItem )
|
|
|
|
&& qskIsAncestorOf( this, focusItem ) )
|
|
|
|
{
|
|
|
|
focusItem->setFocus( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPopup::focusOutEvent( QFocusEvent* event )
|
|
|
|
{
|
|
|
|
Inherited::focusOutEvent( event );
|
|
|
|
}
|
|
|
|
|
2018-01-21 14:10:12 +01:00
|
|
|
QQuickItem* QskPopup::focusSuccessor() const
|
|
|
|
{
|
|
|
|
if ( const auto scope = qskNearestFocusScope( this ) )
|
|
|
|
{
|
|
|
|
const auto children = qskPaintOrderChildItems( scope );
|
2018-08-03 08:15:28 +02:00
|
|
|
for ( auto it = children.crbegin(); it != children.crend(); ++it )
|
2018-01-21 14:10:12 +01:00
|
|
|
{
|
|
|
|
auto child = *it;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( ( child != this ) && child->isFocusScope() &&
|
|
|
|
child->activeFocusOnTab() && child->isVisible() )
|
2018-01-24 10:14:50 +01:00
|
|
|
{
|
2018-01-21 14:10:12 +01:00
|
|
|
return child;
|
2018-01-24 10:14:50 +01:00
|
|
|
}
|
2018-01-21 14:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-26 11:11:37 +02:00
|
|
|
void QskPopup::aboutToShow()
|
2017-11-02 16:25:15 +01:00
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( m_data->autoGrabFocus )
|
|
|
|
{
|
|
|
|
// What to do, when we are hidden below another popup ??
|
|
|
|
grabFocus( true );
|
2017-11-02 16:25:15 +01:00
|
|
|
}
|
|
|
|
|
2018-06-26 11:11:37 +02:00
|
|
|
Inherited::aboutToShow();
|
2017-11-02 16:25:15 +01:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskPopup::itemChange( QQuickItem::ItemChange change,
|
|
|
|
const QQuickItem::ItemChangeData& value )
|
|
|
|
{
|
|
|
|
Inherited::itemChange( change, value );
|
|
|
|
|
2018-07-02 08:08:38 +02:00
|
|
|
if ( change == QQuickItem::ItemVisibleHasChanged )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-10-10 08:55:03 +02:00
|
|
|
updateInputGrabber();
|
2018-06-26 11:11:37 +02:00
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
if ( value.boolValue )
|
|
|
|
{
|
|
|
|
polish(); // so that aboutToShow is called. TODO ...
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-07-02 08:08:38 +02:00
|
|
|
grabFocus( false );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
2018-10-05 14:15:17 +02:00
|
|
|
else if ( change == QQuickItem::ItemParentHasChanged )
|
|
|
|
{
|
|
|
|
delete m_data->inputGrabber;
|
|
|
|
m_data->inputGrabber = nullptr;
|
|
|
|
|
|
|
|
updateInputGrabber();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPopup::windowChangeEvent( QskWindowChangeEvent* event )
|
|
|
|
{
|
|
|
|
qskSendPopupEvent( event->oldWindow(), this, false );
|
|
|
|
qskSendPopupEvent( event->window(), this, true );
|
|
|
|
|
|
|
|
Inherited::windowChangeEvent( event );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskPopup.cpp"
|