8#include "QskInputGrabber.h"
12#include "QskPlatform.h"
13#include "QskHintAnimator.h"
14#include "QskInternalMacros.h"
16#include <qpa/qplatformintegration.h>
19#include <private/qquickwindow_p.h>
20#include <private/qquickitem_p.h>
26static
void qskSetFocus( QQuickItem* item,
bool on )
28 if ( item->window() ==
nullptr )
37 if (
const auto scope = qskNearestFocusScope( item ) )
39#if QT_VERSION >= QT_VERSION_CHECK( 6, 1, 0 )
40 auto dw = QQuickItemPrivate::get( item )->deliveryAgentPrivate();
42 auto dw = QQuickWindowPrivate::get( item->window() );
46 dw->setFocusInScope( scope, item, Qt::PopupFocusReason );
51 if ( QQuickItemPrivate::get( scope )->subFocusItem == item )
52 dw->clearFocusInScope( scope, item, Qt::PopupFocusReason );
57static inline void qskSendPopupEvent(
58 QQuickWindow* window,
QskPopup* popup,
bool on )
62 const auto type = on ? QskEvent::PopupAdded : QskEvent::PopupRemoved;
65 QCoreApplication::sendEvent( window, &event );
69static bool qskReplayMousePress()
71 if (
const auto pf = qskPlatformIntegration() )
73 const auto styleHint = QPlatformIntegration::ReplayMousePressOutsidePopup;
74 return pf->styleHint( styleHint ).toBool();
82 auto aspect = popup->fadingAspect();
88static void qskStartFading(
QskPopup* popup,
bool on )
90 const auto aspect = qskEffectiveFadingAspect( popup );
96 hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode;
98 const qreal from = on ? 0.0 : 1.0;
99 const qreal to = on ? 1.0 : 0.0;
101 popup->startTransition( aspect, hint, from, to );
113 : Inherited( parent )
118 bool event( QEvent* event )
override
120 bool ok = Inherited::event( event );
122 const int eventType =
event->type();
124 if ( eventType == QEvent::MouseButtonPress )
126 if (
auto popup =
static_cast< QskPopup*
>( parentItem() ) )
128 if ( event->isAccepted() &&
129 ( popup->popupFlags() & QskPopup::CloseOnPressOutside ) )
133 if ( qskReplayMousePress() )
140 else if ( eventType == QskEvent::GeometryChange )
142 if (
auto popup =
static_cast< QskPopup*
>( parentItem() ) )
144 if ( popup->hasOverlay() )
154class QskPopup::PrivateData
160 , autoGrabFocus( true )
161 , handoverFocus( true )
165 InputGrabber* inputGrabber =
nullptr;
172 const bool autoGrabFocus : 1;
173 const bool handoverFocus : 1;
176QskPopup::QskPopup( QQuickItem* parent )
177 : Inherited( parent )
178 , m_data( new PrivateData() )
181 Inherited::setVisible(
false );
182 setSkinStateFlag( QskPopup::Closed );
191 setAcceptedMouseButtons( Qt::AllButtons );
192 setWheelEnabled(
true );
197 setFlag( ItemIsFocusScope,
true );
199 setFocusPolicy( Qt::StrongFocus );
205 qskSendPopupEvent( window(),
this,
true );
210 qskSendPopupEvent( window(),
this,
false );
218void QskPopup::close()
223void QskPopup::toggle()
225 setOpen( !isOpen() );
228void QskPopup::setOpen(
bool on )
230 if ( on == isOpen() )
234 QskControl::setVisible( on );
238 Q_EMIT openChanged( on );
248 qskStartFading(
this, on );
252 Q_EMIT fadingChanged(
true );
258 Inherited::setVisible(
false );
260 if ( testPopupFlag( QskPopup::DeleteOnClose ) )
266bool QskPopup::isOpen()
const
268 return !hasSkinState( QskPopup::Closed );
276bool QskPopup::isFading()
const
278 const auto aspect = qskEffectiveFadingAspect(
this );
279 return runningHintAnimator( aspect ) !=
nullptr;
282qreal QskPopup::fadingFactor()
const
284 const auto aspect = qskEffectiveFadingAspect(
this );
285 if (
auto animator = runningHintAnimator( aspect ) )
286 return animator->currentValue().value< qreal >();
288 return isOpen() ? 1.0 : 0.0;
291QRectF QskPopup::overlayRect()
const
293 if ( hasOverlay() && m_data->inputGrabber )
294 return m_data->inputGrabber->grabberRect();
299void QskPopup::updateInputGrabber()
301 if ( parentItem() && isVisible() &&
302 ( isModal() || testPopupFlag( CloseOnPressOutside ) ) )
304 if ( m_data->inputGrabber ==
nullptr )
306 const auto children = childItems();
307 m_data->inputGrabber =
new InputGrabber(
this );
309 if ( !children.isEmpty() )
317 m_data->inputGrabber->stackBefore( children.first() );
323 if ( m_data->inputGrabber )
330 m_data->inputGrabber->setParentItem(
nullptr );
331 m_data->inputGrabber->setParent(
nullptr );
332 m_data->inputGrabber->deleteLater();
334 m_data->inputGrabber =
nullptr;
348 if ( ( aspect.
value() == 0 ) )
351 if ( aspect.
subControl() == qskEffectiveFadingAspect(
this ).subControl() )
361void QskPopup::setPriority( uint priority )
363 if ( m_data->priority != priority )
365 m_data->priority = priority;
366 Q_EMIT priorityChanged( priority );
370uint QskPopup::priority()
const
372 return m_data->priority;
375void QskPopup::setModal(
bool on )
377 if ( on == m_data->isModal )
380 m_data->isModal = on;
381 updateInputGrabber();
383 Q_EMIT modalChanged( on );
386bool QskPopup::isModal()
const
388 return m_data->isModal;
391void QskPopup::setPopupFlags( PopupFlags flags )
393 const auto newFlags =
static_cast< int >( flags );
395 if ( newFlags != m_data->flags )
397 m_data->flags = newFlags;
398 updateInputGrabber();
402QskPopup::PopupFlags QskPopup::popupFlags()
const
404 return static_cast< PopupFlags
>( m_data->flags );
407void QskPopup::setPopupFlag( PopupFlag flag,
bool on )
409 auto flags = m_data->flags;
416 setPopupFlags( PopupFlags( flags ) );
419bool QskPopup::testPopupFlag( PopupFlag flag )
const
421 return m_data->flags & flag;
424void QskPopup::setOverlay(
bool on )
427 Q_EMIT overlayChanged( on );
430void QskPopup::resetOverlay()
433 Q_EMIT overlayChanged( hasOverlay() );
436bool QskPopup::hasOverlay()
const
441void QskPopup::grabFocus(
bool on )
443 if ( on == hasFocus() )
448 qskSetFocus(
this,
true );
452 QQuickItem* successor =
nullptr;
454 if ( m_data->handoverFocus )
461 successor = focusSuccessor();
465 qskSetFocus( successor,
true );
468 qskSetFocus(
this,
false );
472bool QskPopup::event( QEvent* event )
474 bool ok = Inherited::event( event );
476 switch (
static_cast< int >( event->type() ) )
478 case QEvent::KeyPress:
479 case QEvent::KeyRelease:
483 case QEvent::MouseButtonPress:
484 case QEvent::MouseMove:
485 case QEvent::MouseButtonRelease:
487 case QEvent::HoverEnter:
488 case QEvent::HoverLeave:
492 if (
auto w = qobject_cast< QskWindow* >( window() ) )
493 w->setEventAcceptance( QskWindow::EventPropagationStopped );
497 case QskEvent::Animator:
501 if ( ( animatorEvent->state() == QskAnimatorEvent::Terminated )
502 && ( animatorEvent->aspect() == qskEffectiveFadingAspect(
this ) ) )
506 Inherited::setVisible(
false );
508 if ( testPopupFlag( QskPopup::DeleteOnClose ) )
512 Q_EMIT fadingChanged(
false );
534void QskPopup::keyPressEvent( QKeyEvent* event )
536 if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) )
542 return Inherited::keyPressEvent( event );
545void QskPopup::focusInEvent( QFocusEvent* event )
547 Inherited::focusInEvent( event );
549 if ( isFocusScope() &&
isTabFence() && ( scopedFocusItem() ==
nullptr ) )
551 if ( event->reason() == Qt::PopupFocusReason )
563 if (
auto focusItem = nextItemInFocusChain(
true ) )
565 if ( !qskIsItemInDestructor( focusItem )
566 && qskIsAncestorOf(
this, focusItem ) )
568 focusItem->setFocus(
true );
575void QskPopup::focusOutEvent( QFocusEvent* event )
577 Inherited::focusOutEvent( event );
580QQuickItem* QskPopup::focusSuccessor()
const
582 if (
const auto scope = qskNearestFocusScope(
this ) )
584 const auto children = qskPaintOrderChildItems( scope );
585 for (
auto it = children.crbegin(); it != children.crend(); ++it )
589 if ( ( child !=
this ) && child->isFocusScope() &&
590 child->activeFocusOnTab() && child->isVisible() )
602 if ( m_data->autoGrabFocus )
611void QskPopup::itemChange( QQuickItem::ItemChange change,
612 const QQuickItem::ItemChangeData& value )
616 if ( change == QQuickItem::ItemVisibleHasChanged )
618 updateInputGrabber();
620 if ( value.boolValue )
629 else if ( change == QQuickItem::ItemParentHasChanged )
631 delete m_data->inputGrabber;
632 m_data->inputGrabber =
nullptr;
634 updateInputGrabber();
638void QskPopup::geometryChange(
639 const QRectF& newGeometry,
const QRectF& oldGeometry )
646 qskSendPopupEvent( event->oldWindow(),
this,
false );
647 qskSendPopupEvent( event->window(),
this,
true );
652int QskPopup::execPopup()
654 class EventLoop :
public QEventLoop
658 : QEventLoop( popup )
665 connect( popup, &QObject::destroyed,
this, &EventLoop::reject );
666 connect( popup, &QskPopup::fadingChanged,
this, &EventLoop::maybeQuit );
667 connect( popup, &QskPopup::openChanged,
this, &EventLoop::maybeQuit );
673 setParent(
nullptr );
674 QEventLoop::exit( 1 );
679 if (
auto popup = qobject_cast< const QskPopup* >( parent() ) )
681 if ( popup->isOpen() || popup->isFading() )
685 QEventLoop::exit( 0 );
689 if ( isOpen() || isFading() )
691 qWarning() <<
"QskPopup::exec: popup is already opened";
695 if ( priority() > 0 )
697 qWarning(
"QskPopup::exec for a popup with non default priority." );
702 if ( window() ==
nullptr )
704 qWarning(
"trying to exec a popup without window." );
708 if (
auto mouseGrabber = window()->mouseGrabberItem() )
713 if( !qskIsAncestorOf(
this, mouseGrabber ) )
714 qskUngrabMouse( mouseGrabber );
717 return EventLoop(
this ).exec( QEventLoop::DialogExec );
720#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
void geometryChange(const QRectF &, const QRectF &) 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