QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskDirtyItemFilter.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskDirtyItemFilter.h"
7#include "QskItem.h"
8
9QSK_QT_PRIVATE_BEGIN
10#include <private/qquickitem_p.h>
11#include <private/qquickwindow_p.h>
12QSK_QT_PRIVATE_END
13
14static inline bool qskIsUpdateBlocked( const QQuickItem* item )
15{
16 if ( !item->isVisible() )
17 {
18 if ( auto qskItem = qobject_cast< const QskItem* >( item ) )
19 return qskItem->testUpdateFlag( QskItem::DeferredUpdate );
20 }
21
22#if 0
23 /*
24 Blocking items, that are outside the window would be easy,
25 but we have not yet found a performant way to send update notifications
26 when an item enters/leaves the window. TODO ...
27 */
28 else if ( const auto control = qskControlCast( item ) )
29 {
30 const QRectF itemRect( item->mapToScene( QPointF() ), item->size() );
31 const QRectF sceneRect( 0, 0, item->window()->width(), item->window()->height() );
32
33 return !itemRect.intersects( sceneRect );
34 }
35#endif
36
37 return false;
38}
39
40static inline void qskBlockDirty( QQuickItem* item, bool on )
41{
42 if ( qskIsUpdateBlocked( item ) )
43 QQuickItemPrivate::get( item )->componentComplete = !on;
44
45 const auto children = item->childItems();
46 for ( auto child : children )
47 qskBlockDirty( child, on );
48}
49
50namespace
51{
52 class ResetBlockedDirtyJob final : public QRunnable
53 {
54 public:
55 inline ResetBlockedDirtyJob( QQuickWindow* window )
56 : m_window( window )
57 {
58 }
59
60 void run() override
61 {
62 qskBlockDirty( m_window->contentItem(), false );
63 }
64
65 private:
66 const QQuickWindow* m_window;
67 };
68}
69
70QskDirtyItemFilter::QskDirtyItemFilter( QObject* parent )
71 : QObject( parent )
72{
73}
74
75QskDirtyItemFilter::~QskDirtyItemFilter()
76{
77}
78
79void QskDirtyItemFilter::addWindow( QQuickWindow* window )
80{
81 if ( m_windows.contains( window ) )
82 return;
83
84 m_windows.insert( window );
85
86 /*
87 Depending on the configration the scene graph runs on
88 a different thread and we need a direct connection to
89 filter the dirty list in the scene graph thread.
90
91 As QObject::sender() is not valid, when using a direct
92 connection between different threads, we need an
93 extra lambda to pass window as parameter.
94 */
95
96 connect( window, &QQuickWindow::beforeSynchronizing,
97 window, [ this, window ] { beforeSynchronizing( window ); },
98 Qt::DirectConnection );
99
100 connect( window, &QObject::destroyed,
101 this, [ this, window ] { m_windows.remove( window ); } );
102}
103
104void QskDirtyItemFilter::beforeSynchronizing( QQuickWindow* window )
105{
106 filterDirtyList( window, qskIsUpdateBlocked );
107
108 if ( QQuickWindowPrivate::get( window )->renderer == nullptr )
109 {
110 /*
111 In this specific initial situation QQuickWindow updates
112 the dirtyList after emitting "beforeSynchronizing".
113 So we need a special hack setting/resetting componentComplete
114 to avoid having all items in the dirtyList.
115 */
116 qskBlockDirty( window->contentItem(), true );
117
118 window->scheduleRenderJob( new ResetBlockedDirtyJob( window ),
119 QQuickWindow::AfterSynchronizingStage );
120 }
121}
122
123void QskDirtyItemFilter::filterDirtyList(
124 QQuickWindow* window, bool ( *isBlocked )( const QQuickItem* ) )
125{
126 if ( window == nullptr )
127 return;
128
129 auto d = QQuickWindowPrivate::get( window );
130 for ( auto item = d->dirtyItemList; item != nullptr; )
131 {
132 auto nextItem = QQuickItemPrivate::get( item )->nextDirtyItem;
133
134 if ( isBlocked( item ) )
135 QQuickItemPrivate::get( item )->removeFromDirtyList();
136
137 item = nextItem;
138 }
139}
@ DeferredUpdate
Definition QskItem.h:49