12#include "QskSkinManager.h"
18#include <private/qquickitem_p.h>
19#include <private/qquickitemchangelistener_p.h>
20#include <private/qquickwindow_p.h>
21#include <private/qsgrenderer_p.h>
24#include <qpa/qwindowsysteminterface.h>
25#include <QGuiApplication>
29#ifdef QSK_DEBUG_RENDER_TIMING
31#include <qelapsedtimer.h>
32#include <qloggingcategory.h>
33Q_LOGGING_CATEGORY( logTiming,
"qsk.window.timing", QtCriticalMsg )
37extern QLocale qskInheritedLocale(
const QObject* );
38extern void qskInheritLocale( QObject*,
const QLocale& );
40static void qskResolveLocale(
QskWindow* );
41static bool qskEnforcedSkin =
false;
43static inline void qskSendEventTo( QObject*
object, QEvent::Type type )
46 QCoreApplication::sendEvent(
object, &event );
49static QQuickItem* qskDefaultFocusItem( QQuickWindow* window )
51 const auto children = qskPaintOrderChildItems( window->contentItem() );
52 for (
auto it = children.crbegin(); it != children.crend(); ++it )
56 if ( child->isFocusScope() && child->isVisible()
57 && child->isEnabled() && child->activeFocusOnTab() )
63 return window->contentItem()->nextItemInFocusChain(
true );
68 class ChildListener final :
public QQuickItemChangeListener
71 void setEnabled( QQuickItem* contentItem,
bool on )
73 m_contentItem = contentItem;
75 const QQuickItemPrivate::ChangeTypes types = QQuickItemPrivate::Children;
77 QQuickItemPrivate* p = QQuickItemPrivate::get( contentItem );
79 p->addItemChangeListener(
this, types );
81 p->removeItemChangeListener(
this, types );
84 void itemChildAdded( QQuickItem*, QQuickItem* )
override
87 if ( window->isExposed() )
90 QCoreApplication::postEvent( window,
new QEvent( QEvent::LayoutRequest ) );
95 QQuickItem* m_contentItem =
nullptr;
99static inline int qskToIntegerConstraint( qreal valueF )
105 if ( valueF >= std::numeric_limits< int >::max() )
106 value = std::numeric_limits< int >::max();
108 value = ( int ) qCeil( valueF );
114static inline void qskSetVisualizationMode(
115 QQuickWindow* window,
const QByteArray& mode )
117 auto d = QQuickWindowPrivate::get( window );
118#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
119 d->visualizationMode = mode;
121 d->customRenderMode = mode;
125static inline const QByteArray& qskVisualizationMode(
const QQuickWindow* window )
127 auto d = QQuickWindowPrivate::get(
const_cast< QQuickWindow*
>( window ) );
128#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
129 return d->visualizationMode;
131 return d->customRenderMode;
135class QskWindowPrivate :
public QQuickWindowPrivate
141 : preferredSize( -1, -1 )
142 , eventAcceptance(
QskWindow::EventProcessed )
143 , explicitLocale( false )
144 , deleteOnClose( false )
145 , autoLayoutChildren( true )
146 , showedOnce( false )
150#ifdef QSK_DEBUG_RENDER_TIMING
151 QElapsedTimer renderInterval;
154 QPointer< QskSkin > skin;
156 ChildListener contentItemListener;
162 QskWindow::EventAcceptance eventAcceptance;
164 bool explicitLocale : 1;
165 bool deleteOnClose : 1;
166 bool autoLayoutChildren : 1;
170QskWindow::QskWindow( QWindow* parent )
171 : Inherited( *( new QskWindowPrivate() ), parent )
173 QSurfaceFormat fmt = format();
177 fmt.setOption( QSurfaceFormat::DebugContext );
185 contentItem()->setProperty(
"locale", locale() );
190 qskResolveLocale(
this );
193 d_func()->contentItemListener.setEnabled( contentItem(),
true );
195 if ( !qskEnforcedSkin )
196 connect(
this, &QQuickWindow::afterAnimating,
this, &QskWindow::enforceSkin );
199QskWindow::QskWindow( QQuickRenderControl* renderControl, QWindow* parent )
204 d->renderControl = renderControl;
205 d->init(
this, renderControl );
208QskWindow::~QskWindow()
212void QskWindow::setScreen(
const QString& name )
214 if ( !name.isEmpty() )
216 const auto screens = QGuiApplication::screens();
217 for (
auto screen : screens )
219 if ( screen->name() == name )
227 setScreen( QGuiApplication::primaryScreen() );
230void QskWindow::resizeF(
const QSizeF& size )
232 const int w =
static_cast< int >( qCeil( size.width() ) );
233 const int h =
static_cast< int >( qCeil( size.height() ) );
238bool QskWindow::deleteOnClose()
const
241 return d->deleteOnClose;
244void QskWindow::setDeleteOnClose(
bool on )
248 if ( on != d->deleteOnClose )
250 d->deleteOnClose = on;
251 Q_EMIT deleteOnCloseChanged();
255void QskWindow::setAutoLayoutChildren(
bool on )
259 if ( on != d->autoLayoutChildren )
261 d->autoLayoutChildren = on;
263 qskSendEventTo(
this, QEvent::LayoutRequest );
265 Q_EMIT autoLayoutChildrenChanged();
269bool QskWindow::autoLayoutChildren()
const
272 return d->autoLayoutChildren;
275void QskWindow::addItem( QQuickItem* item )
277 if ( item ==
nullptr )
280 item->setParent( contentItem() );
281 item->setParentItem( contentItem() );
284void QskWindow::polishItems()
290bool QskWindow::event( QEvent* event )
302 d->eventAcceptance = EventProcessed;
304 switch ( event->type() )
308 if ( !d->showedOnce )
310 d->showedOnce =
true;
325 if ( d->geometry.size().isEmpty() )
327 QSize sz = sizeConstraint();
330 sz = sz.expandedTo( minimumSize() );
331 sz = sz.boundedTo( maximumSize() );
340 case QEvent::LayoutRequest:
346 case QEvent::LocaleChange:
348 Q_EMIT localeChanged( locale() );
353 if ( event->isAccepted() )
355 if ( d->deleteOnClose )
360 case QEvent::UpdateRequest:
362#ifdef QSK_DEBUG_RENDER_TIMING
363 if ( logTiming().isDebugEnabled() )
365 if ( !d->renderInterval.isValid() )
366 d->renderInterval.start();
368 qCDebug( logTiming() ) <<
"update timer - elapsed"
369 << d->renderInterval.restart() << objectName();
379 return Inherited::event( event );
382void QskWindow::keyPressEvent( QKeyEvent* event )
384 if ( qskFocusChainIncrement( event ) != 0 )
386 auto focusItem = activeFocusItem();
387 if ( focusItem ==
nullptr || focusItem == contentItem() )
402 ensureFocus( Qt::TabFocusReason );
409 Inherited::keyPressEvent( event );
412void QskWindow::keyReleaseEvent( QKeyEvent* event )
414 Inherited::keyReleaseEvent( event );
417void QskWindow::exposeEvent( QExposeEvent* event )
419#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
420 if ( qskRenderingHardwareInterface(
this ) )
429 qFatal(
"the experimental Qt5 RHI implementation is not supported:\n"
430 "\tuse Qt6 or run the default OpenGL backend." );
433 ensureFocus( Qt::OtherFocusReason );
436 Inherited::exposeEvent( event );
439void QskWindow::resizeEvent( QResizeEvent* event )
441 auto rootItem = contentItem();
443 const auto oldRect = qskItemGeometry( rootItem );
444 Inherited::resizeEvent( event );
446 const auto newRect = qskItemGeometry( rootItem );
447 if ( newRect != oldRect )
450 QCoreApplication::sendEvent( rootItem, &event );
457QLocale QskWindow::locale()
const
463void QskWindow::setLocale(
const QLocale& locale )
467 d->explicitLocale =
true;
469 if ( d->locale != locale )
472 qskSendEventTo(
this, QEvent::LocaleChange );
473 qskInheritLocale(
this, locale );
477void QskWindow::resetLocale()
481 d->explicitLocale =
false;
482 qskResolveLocale(
this );
485bool qskInheritLocale(
QskWindow* window,
const QLocale& locale )
487 auto d =
static_cast< QskWindowPrivate*
>( QQuickWindowPrivate::get( window ) );
489 if ( d->explicitLocale || d->locale == locale )
493 qskSendEventTo( window, QEvent::LocaleChange );
498static void qskResolveLocale(
QskWindow* window )
500 auto d =
static_cast< QskWindowPrivate*
>( QQuickWindowPrivate::get( window ) );
502 const auto locale = qskInheritedLocale( window );
504 if ( d->locale != locale )
507 qskSendEventTo( window, QEvent::LocaleChange );
509 qskInheritLocale( window, locale );
513void QskWindow::setPreferredSize(
const QSize& size )
516 d->preferredSize = size;
519QSize QskWindow::preferredSize()
const
522 return d->preferredSize;
525QSize QskWindow::sizeConstraint()
const
529 QSizeF constraint = d->preferredSize;
531 if ( !constraint.isValid() )
533 const bool doWidth = constraint.width() < 0;
534 const bool doHeight = constraint.height() < 0;
536 const auto children = contentItem()->childItems();
537 for (
auto child : children )
539 if ( qskIsVisibleToLayout( child ) )
541 const auto size = qskSizeConstraint( child, Qt::PreferredSize );
544 constraint.setWidth( qMax( constraint.width(), size.width() ) );
547 constraint.setHeight( qMax( constraint.height(), size.height() ) );
554 return QSize( qskToIntegerConstraint( constraint.width() ),
555 qskToIntegerConstraint( constraint.height() ) );
558void QskWindow::setFixedSize(
const QSize& size )
561 setMinimumSize( size );
562 setMaximumSize( size );
565void QskWindow::layoutItems()
569 if ( !d->autoLayoutChildren )
572 const QRectF rect = qskItemGeometry( contentItem() );
574 const auto children = contentItem()->childItems();
575 for (
auto child : children )
577 if ( qskIsAdjustableByLayout( child ) )
579 const auto r = qskConstrainedItemRect( child, rect );
580 qskSetItemGeometry( child, r );
585void QskWindow::ensureFocus( Qt::FocusReason reason )
587 auto focusItem = contentItem()->scopedFocusItem();
589 if ( focusItem ==
nullptr )
591 focusItem = qskDefaultFocusItem(
this );
593 focusItem->setFocus(
true, reason );
597void QskWindow::setCustomRenderMode(
const char* mode )
599 class RenderJob final :
public QRunnable
602 RenderJob( QQuickWindow* window,
const QByteArray& mode )
610 qskSetVisualizationMode( m_window, m_mode );
612 auto d = QQuickWindowPrivate::get( m_window );
615 delete d->renderer->rootNode();
617 d->renderer =
nullptr;
619 QMetaObject::invokeMethod( m_window,
"update" );
624 QQuickWindow* m_window;
625 const QByteArray m_mode;
628 const QByteArray newMode( mode );
630 const auto oldMode = qskVisualizationMode(
this );
632 if ( newMode != oldMode )
643 if ( newMode.isEmpty() != oldMode.isEmpty() )
644 scheduleRenderJob(
new RenderJob(
this, newMode ), AfterSwapStage );
646 qskSetVisualizationMode(
this, newMode );
652const char* QskWindow::customRenderMode()
const
654 return qskVisualizationMode(
this );
657void QskWindow::enforceSkin()
659 if ( !qskEnforcedSkin )
665 ( void ) qskSkinManager->skin();
666 qskEnforcedSkin =
true;
669 disconnect(
this, &QQuickWindow::afterAnimating,
this, &QskWindow::enforceSkin );
672void QskWindow::setEventAcceptance( EventAcceptance acceptance )
674 d_func()->eventAcceptance = acceptance;
677QskWindow::EventAcceptance QskWindow::eventAcceptance()
const
679 return d_func()->eventAcceptance;
682void QskWindow::setSkin(
const QString& skinName )
685 auto skin = QskSkinManager::instance()->createSkin( skinName );
689void QskWindow::setSkin(
QskSkin* skin )
693 if ( d->skin == skin )
698 if ( d->skin->parent() ==
this )
702 if ( skin && skin->parent() ==
nullptr )
703 skin->setParent(
this );
708QskSkin* QskWindow::skin()
const
710 return d_func()->skin;
713QskSkin* qskEffectiveSkin(
const QQuickWindow* window )
715 if (
auto w = qobject_cast< const QskWindow* >( window ) )
717 if (
auto skin = w->skin() )
721 return qskSkinManager->skin();
724#include "moc_QskWindow.cpp"