QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskQuick.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskQuick.h"
7#include "QskControl.h"
8#include "QskFunctions.h"
9#include "QskLayoutElement.h"
10#include "QskPlatform.h"
11#include "QskInternalMacros.h"
12
13#include <qquickitem.h>
14
15QSK_QT_PRIVATE_BEGIN
16#include <private/qquickitem_p.h>
17#include <private/qquickwindow_p.h>
18#include <private/qsgrenderer_p.h>
19QSK_QT_PRIVATE_END
20
21#include <qpa/qplatforminputcontext.h>
22#include <qpa/qplatformintegration.h>
23
24QRhi* qskRenderingHardwareInterface( const QQuickWindow* window )
25{
26 if ( auto w = const_cast< QQuickWindow* >( window ) )
27 return QQuickWindowPrivate::get( w )->rhi;
28
29 return nullptr;
30}
31
32QRectF qskItemRect( const QQuickItem* item )
33{
34 auto d = QQuickItemPrivate::get( item );
35 return QRectF( 0, 0, d->width, d->height );
36}
37
38QRectF qskItemGeometry( const QQuickItem* item )
39{
40 auto d = QQuickItemPrivate::get( item );
41 return QRectF( d->x, d->y, d->width, d->height );
42}
43
44void qskSetItemGeometry( QQuickItem* item, const QRectF& rect )
45{
46 if ( auto control = qskControlCast( item ) )
47 {
48 control->setGeometry( rect );
49 }
50 else if ( item )
51 {
52 item->setPosition( rect.topLeft() );
53 item->setSize( rect.size() );
54 }
55}
56
57bool qskIsItemComplete( const QQuickItem* item )
58{
59 return QQuickItemPrivate::get( item )->componentComplete;
60}
61
62bool qskIsItemInDestructor( const QQuickItem* item )
63{
64 if ( item == nullptr )
65 return false;
66
67 auto d = QQuickItemPrivate::get( item );
68
69#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
70 return d->inDestructor;
71#else
72 /*
73 QskItem sets componentComplete to false in its destructor,
74 but for other items we will will return the wrong information
75 */
76 return !d->componentComplete;
77#endif
78}
79
80bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child )
81{
82 if ( item == nullptr || child == nullptr )
83 return false;
84
85 return item->isAncestorOf( child );
86}
87
88bool qskIsVisibleToParent( const QQuickItem* item )
89{
90 if ( item == nullptr )
91 return false;
92
93 return QQuickItemPrivate::get( item )->explicitVisible;
94}
95
96bool qskIsVisibleTo( const QQuickItem* item, const QQuickItem* ancestor )
97{
98 if ( item == nullptr )
99 return false;
100
101 if ( ancestor == nullptr )
102 return item->isVisible(); // like QWidget::isVisibleTo
103
104 for ( auto it = item->parentItem();
105 it != ancestor; it = it->parentItem() )
106 {
107 if ( it == nullptr )
108 return false; // ancestor is no parent
109
110 if ( !QQuickItemPrivate::get( it )->explicitVisible )
111 return false;
112 }
113
114 return true;
115}
116
117bool qskIsTabFence( const QQuickItem* item )
118{
119 if ( item == nullptr )
120 return false;
121
122 return QQuickItemPrivate::get( item )->isTabFence;
123}
124
125bool qskIsPolishScheduled( const QQuickItem* item )
126{
127 if ( item )
128 return QQuickItemPrivate::get( item )->polishScheduled;
129
130 return false;
131}
132
133bool qskIsShortcutScope( const QQuickItem* item )
134{
135 if ( item == nullptr )
136 return false;
137
138 /*
139 We might have something like CTRL+W to close a "window".
140 But in Qt/Quick a window is not necessarily a QQuickWindow
141 like we have f.e QskSubWindow.
142
143 Maybe it's worth to introduce a shortcutScope flag but for
144 the moment we simply use the isFocusScope/isTabFence combination,
145 that should usually be set for those "windows".
146 */
147
148 return item->isFocusScope() && QQuickItemPrivate::get( item )->isTabFence;
149}
150
151bool qskIsVisibleToLayout( const QQuickItem* item )
152{
153 return qskEffectivePlacementPolicy( item ) != QskPlacementPolicy::Ignore;
154}
155
156bool qskIsAdjustableByLayout( const QQuickItem* item )
157{
158 return qskEffectivePlacementPolicy( item ) == QskPlacementPolicy::Adjust;
159}
160
161QskSizePolicy qskSizePolicy( const QQuickItem* item )
162{
163 if ( auto control = qskControlCast( item ) )
164 return control->sizePolicy();
165
166 if ( item )
167 {
168 const QVariant v = item->property( "sizePolicy" );
169 if ( v.canConvert< QskSizePolicy >() )
170 return qvariant_cast< QskSizePolicy >( v );
171 }
172
173 return QskSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred );
174}
175
176Qt::Alignment qskLayoutAlignmentHint( const QQuickItem* item )
177{
178 if ( auto control = qskControlCast( item ) )
179 return control->layoutAlignmentHint();
180
181 if ( item )
182 {
183 const QVariant v = item->property( "layoutAlignmentHint" );
184 if ( v.canConvert< Qt::Alignment >() )
185 return v.value< Qt::Alignment >();
186 }
187
188 return Qt::Alignment();
189}
190
191void qskSetPlacementPolicy( QQuickItem* item, const QskPlacementPolicy policy )
192{
193 if ( item == nullptr )
194 return;
195
196 if ( auto control = qskControlCast( item ) )
197 {
198 control->setPlacementPolicy( policy );
199 }
200 else
201 {
202 item->setProperty( "layoutPolicy", QVariant::fromValue( policy ) );
203
204 auto d = QQuickItemPrivate::get( item );
205
206 const bool ignore = policy.visiblePolicy() == QskPlacementPolicy::Ignore;
207 if ( ignore != d->isTransparentForPositioner() )
208 d->setTransparentForPositioner( ignore );
209
210 // sending a LayoutRequest ?
211 }
212}
213
214QskPlacementPolicy qskPlacementPolicy( const QQuickItem* item )
215{
216 if ( item == nullptr )
218
219 if ( auto control = qskControlCast( item ) )
220 {
221 return control->placementPolicy();
222 }
223 else
224 {
225 QskPlacementPolicy policy;
226
227 const auto v = item->property( "layoutPolicy" );
228 if ( v.canConvert< QskPlacementPolicy >() )
229 policy = v.value< QskPlacementPolicy >();
230
231 auto d = QQuickItemPrivate::get( item );
232 if ( d->isTransparentForPositioner() )
234
235 return policy;
236 }
237}
238
239QskPlacementPolicy::Policy qskEffectivePlacementPolicy( const QQuickItem* item )
240{
241 if ( item == nullptr )
243
244 const auto policy = qskPlacementPolicy( item );
245
246 if ( qskIsVisibleToParent( item ) )
247 return policy.visiblePolicy();
248 else
249 return policy.hiddenPolicy();
250}
251
252QQuickItem* qskNearestFocusScope( const QQuickItem* item )
253{
254 if ( item )
255 {
256 for ( auto scope = item->parentItem();
257 scope != nullptr; scope = scope->parentItem() )
258 {
259 if ( scope->isFocusScope() )
260 return scope;
261 }
262
263 /*
264 As the default setting of the root item is to be a focus scope
265 we usually never get here - beside the flag has been explicitely
266 disabled in application code.
267 */
268 }
269
270 return nullptr;
271}
272
273void qskForceActiveFocus( QQuickItem* item, Qt::FocusReason reason )
274{
275 /*
276 For unknown reasons Qt::PopupFocusReason is blocked inside of
277 QQuickItem::setFocus and so we can't use QQuickItem::forceActiveFocus
278 */
279
280 if ( item == nullptr || item->window() == nullptr )
281 return;
282
283
284#if QT_VERSION >= QT_VERSION_CHECK( 6, 1, 0 )
285 auto wp = QQuickItemPrivate::get( item )->deliveryAgentPrivate();
286#else
287 auto wp = QQuickWindowPrivate::get( item->window() );
288#endif
289 while ( const auto scope = qskNearestFocusScope( item ) )
290 {
291 wp->setFocusInScope( scope, item, reason );
292 item = scope;
293 }
294}
295
296void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries queries )
297{
298 if ( item == nullptr || !( item->flags() & QQuickItem::ItemAcceptsInputMethod ) )
299 return;
300
301 static QPlatformInputContext* context = nullptr;
302 static int methodId = -1;
303
304 /*
305 We could also get the inputContext from QInputMethodPrivate
306 but for some reason the gcc sanitizer reports errors
307 when using it. So let's go with QGuiApplicationPrivate.
308 */
309
310 auto inputContext = qskPlatformIntegration()->inputContext();
311 if ( inputContext == nullptr )
312 {
313 context = nullptr;
314 methodId = -1;
315
316 return;
317 }
318
319 if ( inputContext != context )
320 {
321 context = inputContext;
322 methodId = inputContext->metaObject()->indexOfMethod(
323 "update(const QQuickItem*,Qt::InputMethodQueries)" );
324 }
325
326 if ( methodId >= 0 )
327 {
328 /*
329 The protocol for input methods does not fit well for a
330 virtual keyboard as it is tied to the focus.
331 So we try to bypass QInputMethod calling the
332 inputContext directly.
333 */
334
335 const auto method = inputContext->metaObject()->method( methodId );
336 method.invoke( inputContext, Qt::DirectConnection,
337 Q_ARG( const QQuickItem*, item ), Q_ARG( Qt::InputMethodQueries, queries ) );
338 }
339 else
340 {
341 QGuiApplication::inputMethod()->update( queries );
342 }
343}
344
345void qskInputMethodSetVisible( const QQuickItem* item, bool on )
346{
347 static QPlatformInputContext* context = nullptr;
348 static int methodId = -1;
349
350 auto inputContext = qskPlatformIntegration()->inputContext();
351 if ( inputContext == nullptr )
352 {
353 context = nullptr;
354 methodId = -1;
355
356 return;
357 }
358
359 if ( inputContext != context )
360 {
361 context = inputContext;
362 methodId = inputContext->metaObject()->indexOfMethod(
363 "setInputPanelVisible(const QQuickItem*,bool)" );
364 }
365
366 if ( methodId >= 0 )
367 {
368 const auto method = inputContext->metaObject()->method( methodId );
369 method.invoke( inputContext, Qt::DirectConnection,
370 Q_ARG( const QQuickItem*, item ), Q_ARG( bool, on ) );
371 }
372 else
373 {
374 if ( on )
375 QGuiApplication::inputMethod()->show();
376 else
377 QGuiApplication::inputMethod()->hide();
378 }
379}
380
381QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* item )
382{
383 if ( item )
384 return QQuickItemPrivate::get( item )->paintOrderChildItems();
385
386 return QList< QQuickItem* >();
387}
388
389const QSGTransformNode* qskItemNode( const QQuickItem* item )
390{
391 if ( item == nullptr )
392 return nullptr;
393
394 return QQuickItemPrivate::get( item )->itemNodeInstance;
395}
396
397const QSGNode* qskPaintNode( const QQuickItem* item )
398{
399 if ( item == nullptr )
400 return nullptr;
401
402 return QQuickItemPrivate::get( item )->paintNode;
403}
404
405const QSGRootNode* qskScenegraphAnchorNode( const QQuickItem* item )
406{
407 if ( item == nullptr )
408 return nullptr;
409
410 return QQuickItemPrivate::get( item )->rootNode();
411}
412
413const QSGRootNode* qskScenegraphAnchorNode( const QQuickWindow* window )
414{
415 if ( auto w = const_cast< QQuickWindow* >( window ) )
416 {
417 if ( auto renderer = QQuickWindowPrivate::get( w )->renderer )
418 return renderer->rootNode();
419 }
420
421 return nullptr;
422}
423
424void qskSetScenegraphAnchor( QQuickItem* item, bool on )
425{
426 /*
427 For setting up a subtree renderer ( f.e in QskSceneTexture ) we need
428 to insert a QSGRootNode above the paintNode.
429
430 ( In Qt this feature is exlusively used in the Qt/Quick Effects module
431 what lead to the not very intuitive name "refFromEffectItem" )
432
433 refFromEffectItem also allows to insert a opacity node of 0 to
434 hide the subtree from the main renderer by setting its parameter to
435 true. We have QskItemNode to achieve the same.
436 */
437 if ( item )
438 {
439 auto d = QQuickItemPrivate::get( item );
440 if ( on )
441 d->refFromEffectItem( false );
442 else
443 d->derefFromEffectItem( false );
444 }
445}
446
447QSizeF qskEffectiveSizeHint( const QQuickItem* item,
448 Qt::SizeHint whichHint, const QSizeF& constraint )
449{
450 if ( auto control = qskControlCast( item ) )
451 return control->effectiveSizeHint( whichHint, constraint );
452
453 if ( constraint.width() >= 0.0 || constraint.height() >= 0.0 )
454 {
455 // QQuickItem does not support dynamic constraints
456 return constraint;
457 }
458
459 if ( item == nullptr )
460 return QSizeF();
461
462 /*
463 Trying to retrieve something useful for non QskControls:
464
465 First are checking some properties, that usually match the
466 names for the explicit hints. For the implicit hints we only
467 have the implicitSize, what is interpreted as the implicit
468 preferred size.
469 */
470 QSizeF hint;
471
472 static const char* properties[] =
473 {
474 "minimumSize",
475 "preferredSize",
476 "maximumSize"
477 };
478
479 const auto v = item->property( properties[ whichHint ] );
480 if ( v.canConvert< QSizeF >() )
481 hint = v.toSizeF();
482
483 if ( whichHint == Qt::PreferredSize )
484 {
485 if ( hint.width() < 0 )
486 hint.setWidth( item->implicitWidth() );
487
488 if ( hint.height() < 0 )
489 hint.setHeight( item->implicitHeight() );
490 }
491
492 return hint;
493}
494
495QSizeF qskSizeConstraint( const QQuickItem* item,
496 Qt::SizeHint which, const QSizeF& constraint )
497{
498 if ( item == nullptr )
499 return QSizeF( 0, 0 );
500
501 const QskItemLayoutElement layoutElement( item );
502 return layoutElement.sizeConstraint( which, constraint );
503}
504
505QSizeF qskConstrainedItemSize( const QQuickItem* item, const QSizeF& size )
506{
507 if ( item == nullptr )
508 return QSizeF( 0.0, 0.0 );
509
510 const QskItemLayoutElement layoutElement( item );
511 return layoutElement.constrainedSize( size );
512}
513
514QRectF qskConstrainedItemRect( const QQuickItem* item,
515 const QRectF& rect, Qt::Alignment alignment )
516{
517 const auto size = qskConstrainedItemSize( item, rect.size() );
518 return qskAlignedRectF( rect, size, alignment );
519}
520
521void qskItemUpdateRecursive( QQuickItem* item )
522{
523 if ( item == nullptr )
524 return;
525
526 if ( item->flags() & QQuickItem::ItemHasContents )
527 item->update();
528
529 const auto& children = QQuickItemPrivate::get( item )->childItems;
530 for ( auto child : children )
531 qskItemUpdateRecursive( child );
532}
533
534#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
535
536static const QQuickPointerTouchEvent* qskPointerPressEvent( const QQuickWindowPrivate* wd )
537{
538 for ( const auto event : std::as_const( wd->pointerEventInstances ) )
539 {
540 if ( auto touchEvent = event->asPointerTouchEvent() )
541 {
542 if ( touchEvent->isPressEvent() )
543 return touchEvent;
544 }
545 }
546
547 return nullptr;
548}
549
550#endif
551
552bool qskGrabMouse( QQuickItem* item )
553{
554 if ( item == nullptr || item->window() == nullptr )
555 return false;
556
557 if ( const auto mouseGrabber = item->window()->mouseGrabberItem() )
558 {
559 if ( mouseGrabber == item )
560 return true;
561
562 if ( mouseGrabber->keepMouseGrab() )
563 {
564 // we respect this
565 return false;
566 }
567 }
568
569 item->setKeepMouseGrab( true );
570
571#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
572
573 auto wd = QQuickWindowPrivate::get( item->window() );
574 if ( wd->touchMouseDevice == nullptr )
575 {
576 /*
577 For synthesized mouse events QQuickWindow sends
578 an initial QEvent::MouseButtonPress before setting
579 touchMouseDevice/touchMouseId. As the mouse grabber is
580 stored depending on these attributes the following
581 mouse event callbacks will look for the grabber at a
582 a different place as it was stored.
583 */
584
585 if ( const auto event = qskPointerPressEvent( wd ) )
586 {
587 if ( const auto p = event->point( 0 ) )
588 {
589 wd->touchMouseDevice = event->device();
590 wd->touchMouseId = p->pointId();
591
592 item->grabMouse();
593
594 wd->touchMouseDevice = nullptr;
595 wd->touchMouseId = -1;
596
597 return true;
598 }
599 }
600 }
601#endif
602
603 item->grabMouse();
604 return true;
605}
606
607void qskUngrabMouse( QQuickItem* item )
608{
609 if ( item )
610 {
611 item->setKeepMouseGrab( false );
612
613 if ( qskIsMouseGrabber( item ) )
614 item->ungrabMouse();
615 }
616}
617
618bool qskIsMouseGrabber( const QQuickItem* item )
619{
620 if ( item )
621 {
622 if ( const auto window = item->window() )
623 return window->mouseGrabberItem() == item;
624 }
625
626 return false;
627}
Policy hiddenPolicy
Policy for the item, when being hidden ( to its parent )
Policy visiblePolicy
Policy for the item, when being visible ( to its parent )
void setVisiblePolicy(Policy) noexcept