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