8#include "QskInputGrabber.h"
12#include "QskPlatform.h"
13#include "QskHintAnimator.h"
15#include <qpa/qplatformintegration.h>
18#include <private/qquickwindow_p.h>
19#include <private/qquickitem_p.h>
25static
void qskSetFocus( QQuickItem* item,
bool on )
27 if ( item->window() ==
nullptr )
36 if (
const auto scope = qskNearestFocusScope( item ) )
38#if QT_VERSION >= QT_VERSION_CHECK( 6, 1, 0 )
39 auto dw = QQuickItemPrivate::get( item )->deliveryAgentPrivate();
41 auto dw = QQuickWindowPrivate::get( item->window() );
45 dw->setFocusInScope( scope, item, Qt::PopupFocusReason );
50 if ( QQuickItemPrivate::get( scope )->subFocusItem == item )
51 dw->clearFocusInScope( scope, item, Qt::PopupFocusReason );
56static inline void qskSendPopupEvent(
57 QQuickWindow* window,
QskPopup* popup,
bool on )
61 const auto type = on ? QskEvent::PopupAdded : QskEvent::PopupRemoved;
64 QCoreApplication::sendEvent( window, &event );
68static bool qskReplayMousePress()
70 if (
const auto pf = qskPlatformIntegration() )
72 const auto styleHint = QPlatformIntegration::ReplayMousePressOutsidePopup;
73 return pf->styleHint( styleHint ).toBool();
81 auto aspect = popup->fadingAspect();
87static void qskStartFading(
QskPopup* popup,
bool on )
89 const auto aspect = qskEffectiveFadingAspect( popup );
95 hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode;
97 const qreal from = on ? 0.0 : 1.0;
98 const qreal to = on ? 1.0 : 0.0;
100 popup->startTransition( aspect, hint, from, to );
112 : Inherited( parent )
117 bool event( QEvent* event )
override
119 bool ok = Inherited::event( event );
121 const int eventType =
event->type();
123 if ( eventType == QEvent::MouseButtonPress )
125 if (
auto popup =
static_cast< QskPopup*
>( parentItem() ) )
127 if ( event->isAccepted() &&
128 ( popup->popupFlags() & QskPopup::CloseOnPressOutside ) )
132 if ( qskReplayMousePress() )
139 else if ( eventType == QskEvent::GeometryChange )
141 if (
auto popup =
static_cast< QskPopup*
>( parentItem() ) )
143 if ( popup->hasOverlay() )
153class QskPopup::PrivateData
159 , autoGrabFocus( true )
160 , handoverFocus( true )
164 InputGrabber* inputGrabber =
nullptr;
171 const bool autoGrabFocus : 1;
172 const bool handoverFocus : 1;
175QskPopup::QskPopup( QQuickItem* parent )
176 : Inherited( parent )
177 , m_data( new PrivateData() )
180 Inherited::setVisible(
false );
181 setSkinStateFlag( QskPopup::Closed );
190 setAcceptedMouseButtons( Qt::AllButtons );
191 setWheelEnabled(
true );
196 setFlag( ItemIsFocusScope,
true );
198 setFocusPolicy( Qt::StrongFocus );
204 qskSendPopupEvent( window(),
this,
true );
209 qskSendPopupEvent( window(),
this,
false );
217void QskPopup::close()
222void QskPopup::toggle()
224 setOpen( !isOpen() );
227void QskPopup::setOpen(
bool on )
229 if ( on == isOpen() )
233 QskControl::setVisible( on );
237 Q_EMIT openChanged( on );
247 qskStartFading(
this, on );
251 Q_EMIT fadingChanged(
true );
257 Inherited::setVisible(
false );
259 if ( testPopupFlag( QskPopup::DeleteOnClose ) )
265bool QskPopup::isOpen()
const
267 return !hasSkinState( QskPopup::Closed );
275bool QskPopup::isFading()
const
277 const auto aspect = qskEffectiveFadingAspect(
this );
278 return runningHintAnimator( aspect ) !=
nullptr;
281qreal QskPopup::fadingFactor()
const
283 const auto aspect = qskEffectiveFadingAspect(
this );
284 if (
auto animator = runningHintAnimator( aspect ) )
285 return animator->currentValue().value< qreal >();
287 return isOpen() ? 1.0 : 0.0;
290QRectF QskPopup::overlayRect()
const
292 if ( hasOverlay() && m_data->inputGrabber )
293 return m_data->inputGrabber->grabberRect();
298void QskPopup::updateInputGrabber()
300 if ( parentItem() && isVisible() &&
301 ( isModal() || testPopupFlag( CloseOnPressOutside ) ) )
303 if ( m_data->inputGrabber ==
nullptr )
305 const auto children = childItems();
306 m_data->inputGrabber =
new InputGrabber(
this );
308 if ( !children.isEmpty() )
316 m_data->inputGrabber->stackBefore( children.first() );
322 if ( m_data->inputGrabber )
329 m_data->inputGrabber->setParentItem(
nullptr );
330 m_data->inputGrabber->setParent(
nullptr );
331 m_data->inputGrabber->deleteLater();
333 m_data->inputGrabber =
nullptr;
347 if ( ( aspect.
value() == 0 ) )
350 if ( aspect.
subControl() == qskEffectiveFadingAspect(
this ).subControl() )
360void QskPopup::setPriority( uint priority )
362 if ( m_data->priority != priority )
364 m_data->priority = priority;
365 Q_EMIT priorityChanged( priority );
369uint QskPopup::priority()
const
371 return m_data->priority;
374void QskPopup::setModal(
bool on )
376 if ( on == m_data->isModal )
379 m_data->isModal = on;
380 updateInputGrabber();
382 Q_EMIT modalChanged( on );
385bool QskPopup::isModal()
const
387 return m_data->isModal;
390void QskPopup::setPopupFlags( PopupFlags flags )
392 const auto newFlags =
static_cast< int >( flags );
394 if ( newFlags != m_data->flags )
396 m_data->flags = newFlags;
397 updateInputGrabber();
401QskPopup::PopupFlags QskPopup::popupFlags()
const
403 return static_cast< PopupFlags
>( m_data->flags );
406void QskPopup::setPopupFlag( PopupFlag flag,
bool on )
408 auto flags = m_data->flags;
415 setPopupFlags( PopupFlags( flags ) );
418bool QskPopup::testPopupFlag( PopupFlag flag )
const
420 return m_data->flags & flag;
423void QskPopup::setOverlay(
bool on )
426 Q_EMIT overlayChanged( on );
429void QskPopup::resetOverlay()
432 Q_EMIT overlayChanged( hasOverlay() );
435bool QskPopup::hasOverlay()
const
440void QskPopup::grabFocus(
bool on )
442 if ( on == hasFocus() )
447 qskSetFocus(
this,
true );
451 QQuickItem* successor =
nullptr;
453 if ( m_data->handoverFocus )
460 successor = focusSuccessor();
464 qskSetFocus( successor,
true );
467 qskSetFocus(
this,
false );
471bool QskPopup::event( QEvent* event )
473 bool ok = Inherited::event( event );
475 switch (
static_cast< int >( event->type() ) )
477 case QEvent::KeyPress:
478 case QEvent::KeyRelease:
482 case QEvent::MouseButtonPress:
483 case QEvent::MouseMove:
484 case QEvent::MouseButtonRelease:
486 case QEvent::HoverEnter:
487 case QEvent::HoverLeave:
491 if (
auto w = qobject_cast< QskWindow* >( window() ) )
492 w->setEventAcceptance( QskWindow::EventPropagationStopped );
496 case QskEvent::Animator:
500 if ( ( animatorEvent->state() == QskAnimatorEvent::Terminated )
501 && ( animatorEvent->aspect() == qskEffectiveFadingAspect(
this ) ) )
505 Inherited::setVisible(
false );
507 if ( testPopupFlag( QskPopup::DeleteOnClose ) )
511 Q_EMIT fadingChanged(
false );
533void QskPopup::keyPressEvent( QKeyEvent* event )
535 if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) )
541 return Inherited::keyPressEvent( event );
544void QskPopup::focusInEvent( QFocusEvent* event )
546 Inherited::focusInEvent( event );
548 if ( isFocusScope() &&
isTabFence() && ( scopedFocusItem() ==
nullptr ) )
550 if ( event->reason() == Qt::PopupFocusReason )
562 if (
auto focusItem = nextItemInFocusChain(
true ) )
564 if ( !qskIsItemInDestructor( focusItem )
565 && qskIsAncestorOf(
this, focusItem ) )
567 focusItem->setFocus(
true );
574void QskPopup::focusOutEvent( QFocusEvent* event )
576 Inherited::focusOutEvent( event );
579QQuickItem* QskPopup::focusSuccessor()
const
581 if (
const auto scope = qskNearestFocusScope(
this ) )
583 const auto children = qskPaintOrderChildItems( scope );
584 for (
auto it = children.crbegin(); it != children.crend(); ++it )
588 if ( ( child !=
this ) && child->isFocusScope() &&
589 child->activeFocusOnTab() && child->isVisible() )
601 if ( m_data->autoGrabFocus )
610void QskPopup::itemChange( QQuickItem::ItemChange change,
611 const QQuickItem::ItemChangeData& value )
615 if ( change == QQuickItem::ItemVisibleHasChanged )
617 updateInputGrabber();
619 if ( value.boolValue )
628 else if ( change == QQuickItem::ItemParentHasChanged )
630 delete m_data->inputGrabber;
631 m_data->inputGrabber =
nullptr;
633 updateInputGrabber();
639 qskSendPopupEvent( event->oldWindow(),
this,
false );
640 qskSendPopupEvent( event->window(),
this,
true );
645int QskPopup::execPopup()
647 class EventLoop :
public QEventLoop
651 : QEventLoop( popup )
658 connect( popup, &QObject::destroyed,
this, &EventLoop::reject );
659 connect( popup, &QskPopup::fadingChanged,
this, &EventLoop::maybeQuit );
660 connect( popup, &QskPopup::openChanged,
this, &EventLoop::maybeQuit );
666 setParent(
nullptr );
667 QEventLoop::exit( 1 );
672 if (
auto popup = qobject_cast< const QskPopup* >( parent() ) )
674 if ( popup->isOpen() || popup->isFading() )
678 QEventLoop::exit( 0 );
682 if ( isOpen() || isFading() )
684 qWarning() <<
"QskPopup::exec: popup is already opened";
688 if ( priority() > 0 )
690 qWarning(
"QskPopup::exec for a popup with non default priority." );
695 if ( window() ==
nullptr )
697 qWarning(
"trying to exec a popup without window." );
701 if (
auto mouseGrabber = window()->mouseGrabberItem() )
706 if( !qskIsAncestorOf(
this, mouseGrabber ) )
707 qskUngrabMouse( mouseGrabber );
710 return EventLoop(
this ).exec( QEventLoop::DialogExec );
713#include "moc_QskPopup.cpp"
Lookup key for a QskSkinHintTable.
constexpr quint64 value() const noexcept
constexpr Subcontrol subControl() const noexcept
void itemChange(ItemChange, const ItemChangeData &) override
virtual void aboutToShow()
bool isInitiallyPainted() const
virtual void windowChangeEvent(QskWindowChangeEvent *)
bool resetSkinHint(QskAspect)
Remove a hint from the local hint table.
virtual bool isTransitionAccepted(QskAspect) const
Additional check if an transition should be started.
QskAnimationHint animationHint(QskAspect, QskSkinHintStatus *=nullptr) const
void setSkinStateFlag(QskAspect::State, bool on=true)
bool setFlagHint(QskAspect, int flag)
Sets a flag hint.
QskAspect::Subcontrol effectiveSubcontrol(QskAspect::Subcontrol) const