Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquicksplitview.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquicksplitview_p.h"
7
8#include <QtCore/qdebug.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/qcborarray.h>
11#include <QtCore/qcbormap.h>
12#include <QtCore/qcborvalue.h>
13#include <QtQml/QQmlInfo>
14
16
21
224Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
225Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
226Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
227
228/*
229 Updates m_fillIndex to be between 0 .. (item count - 1).
230*/
231void QQuickSplitViewPrivate::updateFillIndex()
232{
233 const int count = contentModel->count();
234 const bool horizontal = isHorizontal();
235
236 qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
237
238 int fillIndex = -1;
239 int lastVisibleIndex = -1;
240 for (int i = 0; i < count; ++i) {
241 QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
242 if (!item->isVisible())
243 continue;
244
245 lastVisibleIndex = i;
246
247 const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
248 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
249 if (!attached)
250 continue;
251
252 if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
253 fillIndex = i;
254 qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << fillIndex;
255 break;
256 }
257 }
258
259 if (fillIndex == -1) {
260 // If there was no item with fillWidth/fillHeight set, fillIndex will be -1,
261 // and we'll set m_fillIndex to the last visible item.
262 // If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
263 fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
264 qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << fillIndex;
265 }
266 // Take new fillIndex into use.
267 m_fillIndex = fillIndex;
268}
269
270/*
271 Resizes split items according to their preferred size and any constraints.
272
273 If a split item is being resized due to a split handle being dragged,
274 it will be resized accordingly.
275
276 Items that aren't visible are skipped.
277*/
278void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag)
279{
280 const int count = contentModel->count();
281 const bool horizontal = isHorizontal();
282 for (int index = 0; index < count; ++index) {
284 if (!item->isVisible()) {
285 // The item is not visible, so skip it.
286 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": split item " << item
287 << " at index " << index << " is not visible; skipping it and its handles (if any)";
288 continue;
289 }
290
291 const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
292 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
293 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
294 const auto sizeData = effectiveSizeData(itemPrivate, attached);
295
296 const bool resizeLeftItem = m_fillIndex > m_pressedHandleIndex;
297 // True if any handle is pressed.
298 const bool isAHandlePressed = m_pressedHandleIndex != -1;
299 // True if this particular item is being resized as a result of a handle being dragged.
300 const bool isBeingResized = isAHandlePressed && ((resizeLeftItem && index == m_pressedHandleIndex)
301 || (!resizeLeftItem && index == m_nextVisibleIndexAfterPressedHandle));
302 if (isBeingResized) {
303 indexBeingResizedDueToDrag = index;
304 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": dragging handle for item";
305 }
306
307 const qreal size = horizontal ? width : height;
308 qreal requestedSize = 0;
309 if (isBeingResized) {
310 // Don't let the mouse go past either edge of the SplitView.
311 const qreal clampedMousePos = horizontal
312 ? qBound(qreal(0.0), m_mousePos.x(), qreal(width))
313 : qBound(qreal(0.0), m_mousePos.y(), qreal(height));
314
315 // We also need to ensure that the item's edge doesn't go too far
316 // out and hence give the item more space than is available.
317 const int firstIndex = resizeLeftItem ? m_nextVisibleIndexAfterPressedHandle : 0;
318 const int lastIndex = resizeLeftItem ? contentModel->count() - 1 : m_pressedHandleIndex;
319 const qreal accumulated = accumulatedSize(firstIndex, lastIndex);
320
321 const qreal mousePosRelativeToLeftHandleEdge = horizontal
324
325 const QQuickItem *pressedHandleItem = m_handleItems.at(m_pressedHandleIndex);
326 const qreal pressedHandleSize = horizontal ? pressedHandleItem->width() : pressedHandleItem->height();
327
328 if (resizeLeftItem) {
329 // The handle shouldn't cross other handles, so use the right edge of
330 // the first handle to the left as the left edge.
331 qreal leftEdge = 0;
332 if (m_pressedHandleIndex - 1 >= 0) {
333 const QQuickItem *leftHandle = m_handleItems.at(m_pressedHandleIndex - 1);
334 leftEdge = horizontal
335 ? leftHandle->x() + leftHandle->width()
336 : leftHandle->y() + leftHandle->height();
337 }
338
339 // The mouse can be clicked anywhere in the handle, and if we don't account for
340 // its position within the handle, the handle will jump when dragged.
341 const qreal pressedHandlePos = clampedMousePos - mousePosRelativeToLeftHandleEdge;
342
343 const qreal rightStop = size - accumulated - pressedHandleSize;
344 qreal leftStop = qMax(leftEdge, pressedHandlePos);
345 // qBound() doesn't care if min is greater than max, but we do.
346 if (leftStop > rightStop)
347 leftStop = rightStop;
348 const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
349 const qreal newItemSize = newHandlePos - leftEdge;
350
351 // We still need to use requestedSize in the width/height call below,
352 // because sizeData has already been calculated and now contains an old
353 // effectivePreferredWidth/Height value.
354 requestedSize = newItemSize;
355
356 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
357 << " (clampedMousePos=" << clampedMousePos
358 << " pressedHandlePos=" << pressedHandlePos
359 << " accumulated=" << accumulated
360 << " leftEdge=" << leftEdge
361 << " leftStop=" << leftStop
362 << " rightStop=" << rightStop
363 << " newHandlePos=" << newHandlePos
364 << " newItemSize=" << newItemSize << ")";
365 } else { // Resizing the item on the right.
366 // The handle shouldn't cross other handles, so use the left edge of
367 // the first handle to the right as the right edge.
368 qreal rightEdge = size;
371 rightEdge = horizontal ? rightHandle->x() : rightHandle->y();
372 }
373
374 // The mouse can be clicked anywhere in the handle, and if we don't account for
375 // its position within the handle, the handle will jump when dragged.
376 const qreal pressedHandlePos = clampedMousePos - mousePosRelativeToLeftHandleEdge;
377
378 const qreal leftStop = accumulated - pressedHandleSize;
379 qreal rightStop = qMin(rightEdge - pressedHandleSize, pressedHandlePos);
380 // qBound() doesn't care if min is greater than max, but we do.
381 if (rightStop < leftStop)
382 rightStop = leftStop;
383 const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
384 const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
385
386 // We still need to use requestedSize in the width/height call below,
387 // because sizeData has already been calculated and now contains an old
388 // effectivePreferredWidth/Height value.
389 requestedSize = newItemSize;
390
391 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
392 << " (clampedMousePos=" << clampedMousePos
393 << " pressedHandlePos=" << pressedHandlePos
394 << " accumulated=" << accumulated
395 << " leftEdge=" << rightEdge
396 << " leftStop=" << leftStop
397 << " rightStop=" << rightStop
398 << " newHandlePos=" << newHandlePos
399 << " newItemSize=" << newItemSize << ")";
400 }
401 } else if (index != m_fillIndex) {
402 // No handle is being dragged and we're not the fill item,
403 // so set our preferred size as we normally would.
404 requestedSize = horizontal
405 ? sizeData.effectivePreferredWidth : sizeData.effectivePreferredHeight;
406 }
407
408 if (index != m_fillIndex) {
409 LayoutData layoutData;
410 if (horizontal) {
411 layoutData.width = qBound(
412 sizeData.effectiveMinimumWidth,
413 requestedSize,
414 sizeData.effectiveMaximumWidth);
415 layoutData.height = height;
416 } else {
417 layoutData.width = width;
418 layoutData.height = qBound(
419 sizeData.effectiveMinimumHeight,
420 requestedSize,
421 sizeData.effectiveMaximumHeight);
422 }
423
424 // Mark that this item has been manually resized. After this
425 // we can override the preferredWidth & preferredHeight
426 if (isBeingResized)
427 layoutData.wasResizedByHandle = true;
428
429 m_layoutData.insert(item, layoutData);
430
431 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
432 << " (effective"
433 << " minW=" << sizeData.effectiveMinimumWidth
434 << ", minH=" << sizeData.effectiveMinimumHeight
435 << ", prfW=" << sizeData.effectivePreferredWidth
436 << ", prfH=" << sizeData.effectivePreferredHeight
437 << ", maxW=" << sizeData.effectiveMaximumWidth
438 << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
439
440 // Keep track of how much space has been used so far.
441 if (horizontal)
442 usedWidth += layoutData.width;
443 else
444 usedHeight += layoutData.height;
445 } else if (indexBeingResizedDueToDrag != m_fillIndex) {
446 // The fill item is resized afterwards, outside of the loop.
447 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
448 }
449
450 // Also account for the size of the handle for this item (if any).
451 // We do this for the fill item too, which is why it's outside of the check above.
452 if (index < count - 1 && m_handle) {
453 QQuickItem *handleItem = m_handleItems.at(index);
454 // The handle for an item that's not visible will usually already be skipped
455 // with the item visibility check higher up, but if the view looks like this
456 // [ visible ] | [ visible (fill) ] | [ hidden ]
457 // ^
458 // hidden
459 // and we're iterating over the second item (which is visible but has no handle),
460 // we need to add an extra check for it to avoid it still taking up space.
461 if (handleItem->isVisible()) {
462 if (horizontal) {
463 qCDebug(qlcQQuickSplitView).nospace() << " - " << index
464 << ": handle takes up " << handleItem->width() << " width";
465 usedWidth += handleItem->width();
466 } else {
467 qCDebug(qlcQQuickSplitView).nospace() << " - " << index
468 << ": handle takes up " << handleItem->height() << " height";
469 usedHeight += handleItem->height();
470 }
471 } else {
472 qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": handle is not visible; skipping it";
473 }
474 }
475 }
476}
477
478/*
479 Resizes the fill item by giving it the remaining space
480 after all other items have been resized.
481
482 Items that aren't visible are skipped.
483*/
485 qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag)
486{
487 // Only bother resizing if it it's visible. Also, if it's being resized due to a drag,
488 // then we've already set its size in layoutResizeSplitItems(), so no need to do it here.
489 if (!fillItem->isVisible() || indexBeingResizedDueToDrag == m_fillIndex) {
490 qCDebug(qlcQQuickSplitView).nospace() << m_fillIndex << ": - fill item " << fillItem
491 << " is not visible or was already resized due to a drag;"
492 << " skipping it and its handles (if any)";
493 return;
494 }
495
496 const QQuickItemPrivate *fillItemPrivate = QQuickItemPrivate::get(fillItem);
497 const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
498 qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
499 const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
500
501 LayoutData layoutData;
502 if (isHorizontal()) {
503 layoutData.width = qBound(
504 fillSizeData.effectiveMinimumWidth,
505 width - usedWidth,
506 fillSizeData.effectiveMaximumWidth);
507 layoutData.height = height;
508 usedWidth += layoutData.width;
509 } else {
510 layoutData.width = width;
511 layoutData.height = qBound(
512 fillSizeData.effectiveMinimumHeight,
513 height - usedHeight,
514 fillSizeData.effectiveMaximumHeight);
515 usedHeight += layoutData.height;
516 }
517
518 m_layoutData.insert(fillItem, layoutData);
519
520 qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
521 << ": resized split fill item " << fillItem << " (effective"
522 << " minW=" << fillSizeData.effectiveMinimumWidth
523 << ", minH=" << fillSizeData.effectiveMinimumHeight
524 << ", maxW=" << fillSizeData.effectiveMaximumWidth
525 << ", maxH=" << fillSizeData.effectiveMaximumHeight << ")";
526}
527
528/*
529 Limit the sizes if needed and apply them into items.
530*/
532{
533 const int count = contentModel->count();
534 const bool horizontal = isHorizontal();
535
536 const qreal maxSize = horizontal ? width : height;
537 const qreal usedSize = horizontal ? usedWidth : usedHeight;
538 if (usedSize > maxSize) {
539 // If items don't fit, reduce the size of non-filled items from
540 // right to left / bottom to top. At this point filled item is
541 // already at its minimum size or usedSize wouldn't be > maxSize.
542 qreal delta = usedSize - maxSize;
543 for (int index = count - 1; index >= 0; --index) {
544 if (index == m_fillIndex)
545 continue;
547 if (!item->isVisible())
548 continue;
549
550 const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
551 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
552 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
553 const auto sizeData = effectiveSizeData(itemPrivate, attached);
554 const qreal maxReduce = horizontal ?
555 m_layoutData[item].width - sizeData.effectiveMinimumWidth :
556 m_layoutData[item].height - sizeData.effectiveMinimumHeight;
557
558 const qreal reduce = std::min(maxReduce, delta);
559 if (horizontal)
560 m_layoutData[item].width -= reduce;
561 else
562 m_layoutData[item].height -= reduce;
563
564 delta -= reduce;
565 if (delta <= 0) {
566 // Now all the items fit, so continue
567 break;
568 }
569 }
570 }
571
572 // Apply the new sizes into items
573 for (int index = 0; index < count; ++index) {
575 if (!item->isVisible())
576 continue;
577
578 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
579 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
580 LayoutData layoutData = m_layoutData.value(item);
581 if (layoutData.wasResizedByHandle) {
582 // Modify the preferredWidth/Height, otherwise the original implicit/preferred size
583 // will be used on the next layout (when it's no longer being resized).
584 if (!attached) {
585 // Force the attached object to be created since we rely on it.
586 attached = qobject_cast<QQuickSplitViewAttached*>(
587 qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
588 }
589 /*
590 Users could conceivably respond to size changes in items by setting attached
591 SplitView properties:
592
593 onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
594
595 We handle this by doing another layout after the current layout if the
596 attached/implicit size properties are set during this layout. However, we also
597 need to set preferredWidth/Height here, otherwise the original implicit/preferred sizes
598 will be used on the next layout (when it's no longer being resized).
599 But we don't want this to count as a request for a delayed layout, so we guard against it.
600 */
602 if (horizontal)
603 attached->setPreferredWidth(layoutData.width);
604 else
605 attached->setPreferredHeight(layoutData.height);
606 }
607
608 item->setWidth(layoutData.width);
609 item->setHeight(layoutData.height);
610 }
611}
612
613/*
614 Positions items by laying them out in a row or column.
615
616 Items that aren't visible are skipped.
617*/
619{
620 const bool horizontal = isHorizontal();
621 const int count = contentModel->count();
622 qreal usedWidth = 0;
623 qreal usedHeight = 0;
624
625 for (int i = 0; i < count; ++i) {
627 if (!item->isVisible()) {
628 qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": split item " << item
629 << " is not visible; skipping it and its handles (if any)";
630 continue;
631 }
632
633 // Position the item.
634 if (horizontal) {
635 item->setX(usedWidth);
636 item->setY(0);
637 } else {
638 item->setX(0);
639 item->setY(usedHeight);
640 }
641
642 // Keep track of how much space has been used so far.
643 if (horizontal)
644 usedWidth += item->width();
645 else
646 usedHeight += item->height();
647
648 if (Q_UNLIKELY(qlcQQuickSplitView().isDebugEnabled())) {
649 const QQuickItemPrivate *fillItemPrivate = QQuickItemPrivate::get(fillItem);
650 const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
651 qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
652 const auto sizeData = effectiveSizeData(fillItemPrivate, attached);
653 qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": positioned "
654 << (i == m_fillIndex ? "fill item " : "item ") << item << " (effective"
655 << " minW=" << sizeData.effectiveMinimumWidth
656 << ", minH=" << sizeData.effectiveMinimumHeight
657 << ", prfW=" << sizeData.effectivePreferredWidth
658 << ", prfH=" << sizeData.effectivePreferredHeight
659 << ", maxW=" << sizeData.effectiveMaximumWidth
660 << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
661 }
662
663 // Position the handle for this item (if any).
664 if (i < count - 1 && m_handle) {
665 // Position the handle.
666 QQuickItem *handleItem = m_handleItems.at(i);
667 handleItem->setX(horizontal ? usedWidth : 0);
668 handleItem->setY(horizontal ? 0 : usedHeight);
669
670 if (horizontal)
671 usedWidth += handleItem->width();
672 else
673 usedHeight += handleItem->height();
674
675 qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": positioned handle " << handleItem;
676 }
677 }
678}
679
681{
682 Q_Q(QQuickSplitView);
683 q->polish();
684}
685
686/*
687 Layout steps are (horizontal SplitView as an example):
688 1) layoutResizeSplitItems: Gives each non-filled item its preferredWidth
689 or if not set, implicitWidth. Sizes are kept between effectiveMinimumWidth
690 and effectiveMaximumWidth and stored into layoutData for now.
691 2) layoutResizeFillItem: Gives filled item all the remaining space. Size is
692 kept between effectiveMinimumWidth and effectiveMaximumWidth and stored
693 into layoutData for now.
694 3) limitAndApplySizes: If we have used more space than SplitView item has,
695 start reducing non-filled item sizes from right-to-left. Reduce them up
696 to minimumWidth or until SplitView item width is reached. Finally set the
697 new item sizes from layoutData.
698*/
700{
702 return;
703
704 if (m_layingOut)
705 return;
706
707 const int count = contentModel->count();
708 if (count <= 0)
709 return;
710
712 QString::fromLatin1("m_fillIndex is %1 but our count is %2").arg(m_fillIndex).arg(count)));
713
715 "Expected %1 handle items, but there are %2").arg(count - 1).arg(m_handleItems.size())));
716
717 // We allow mouse events to instantly trigger layouts, whereas with e.g.
718 // attached properties being set, we require a delayed layout.
719 // To prevent recursive calls during mouse events, we need this guard.
720 QBoolBlocker guard(m_layingOut, true);
721
722 const bool horizontal = isHorizontal();
723 qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
724 << (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
725
726 // Total sizes of items used during the layout operation.
727 qreal usedWidth = 0;
728 qreal usedHeight = 0;
729 int indexBeingResizedDueToDrag = -1;
730 m_layoutData.clear();
731
732 qCDebug(qlcQQuickSplitView) << " resizing:";
733
734 // First, resize the non-filled items. We need to do this first because otherwise fill
735 // items would take up all of the remaining space as soon as they are encountered.
736 layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
737
738 qCDebug(qlcQQuickSplitView).nospace()
739 << " - (remaining width=" << width - usedWidth
740 << " remaining height=" << height - usedHeight << ")";
741
742 // Give the fill item the remaining space.
744 layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
745
746 // Reduce the sizes still if needed and apply them into items.
747 limitAndApplySizes(usedWidth, usedHeight);
748
749 qCDebug(qlcQQuickSplitView) << " positioning:";
750
751 // Position the items.
752 layoutPositionItems(fillItem);
753
754 qCDebug(qlcQQuickSplitView).nospace() << "finished layouting";
755}
756
758{
760 // A handle only makes sense if there are two items on either side.
761 if (contentModel->count() <= 1)
762 return;
763
764 // Create new handle items if there aren't enough.
765 const int count = contentModel->count() - 1;
766 qCDebug(qlcQQuickSplitView) << "creating" << count << "handles";
768 for (int i = 0; i < count; ++i)
770}
771
773{
774 Q_Q(QQuickSplitView);
775 if (contentModel->count() <= 1)
776 return;
777
778 qCDebug(qlcQQuickSplitView) << "- creating handle for split item at index" << index
779 << "from handle component" << m_handle;
780
781 // If we don't use the correct context, it won't be possible to refer to
782 // the control's id from within the delegate.
784 // The component might not have been created in QML, in which case
785 // the creation context will be null and we have to create it ourselves.
786 if (!context)
789 if (handleItem) {
790 handleItem->setParent(q);
791 qCDebug(qlcQQuickSplitView) << "- successfully created handle item" << handleItem << "for split item at index" << index;
792
793 // Insert the item to our list of items *before* its parent is set to us,
794 // so that we can avoid it being added as a content item by checking
795 // if it is in the list in isContent().
796 m_handleItems.insert(index, handleItem);
797
798 handleItem->setParentItem(q);
799 // Handles must have priority for press events, so we need to set this.
801 handleItem->setKeepMouseGrab(true);
802#if QT_CONFIG(cursor)
803 updateCursorHandle(handleItem);
804#endif
806 resizeHandle(handleItem);
807 }
808}
809
811{
812 int excess = m_handleItems.size() - qMax(0, contentModel->count() - 1);
813 qCDebug(qlcQQuickSplitView) << "removing" << excess << "excess handles from the end of our list";
814 for (; excess > 0; --excess) {
815 QQuickItem *handleItem = m_handleItems.takeLast();
816 delete handleItem;
817 }
818}
819
820qreal QQuickSplitViewPrivate::accumulatedSize(int firstIndex, int lastIndex) const
821{
822 qreal size = 0.0;
823 const bool horizontal = isHorizontal();
824 for (int i = firstIndex; i <= lastIndex; ++i) {
826 if (item->isVisible()) {
827 if (i != m_fillIndex) {
828 size += horizontal ? item->width() : item->height();
829 } else {
830 // If the fill item has a minimum size specified, we must respect it.
831 const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
832 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
833 if (attached) {
834 const QQuickSplitViewAttachedPrivate *attachedPrivate
836 if (horizontal && attachedPrivate->m_isMinimumWidthSet)
837 size += attachedPrivate->m_minimumWidth;
838 else if (!horizontal && attachedPrivate->m_isMinimumHeightSet)
839 size += attachedPrivate->m_minimumHeight;
840 }
841 }
842 }
843
844 // Only add the handle's width if there's actually a handle for this split item index.
845 if (i < lastIndex || lastIndex < contentModel->count() - 1) {
846 const QQuickItem *handleItem = m_handleItems.at(i);
847 if (handleItem->isVisible())
848 size += horizontal ? handleItem->width() : handleItem->height();
849 }
850 }
851 return size;
852}
853
855{
856 return attachedPrivate && attachedPrivate->m_isMinimumWidthSet ? attachedPrivate->m_minimumWidth : 0;
857}
858
860{
861 return attachedPrivate && attachedPrivate->m_isMinimumHeightSet ? attachedPrivate->m_minimumHeight : 0;
862}
863
865 const QQuickItemPrivate *itemPrivate)
866{
867 return attachedPrivate && attachedPrivate->m_isPreferredWidthSet
868 ? attachedPrivate->m_preferredWidth : itemPrivate->implicitWidth;
869}
870
872 const QQuickItemPrivate *itemPrivate)
873{
874 return attachedPrivate && attachedPrivate->m_isPreferredHeightSet
875 ? attachedPrivate->m_preferredHeight : itemPrivate->implicitHeight;
876}
877
879{
880 return attachedPrivate && attachedPrivate->m_isMaximumWidthSet
881 ? attachedPrivate->m_maximumWidth : std::numeric_limits<qreal>::infinity();
882}
883
885{
886 return attachedPrivate && attachedPrivate->m_isMaximumHeightSet
887 ? attachedPrivate->m_maximumHeight : std::numeric_limits<qreal>::infinity();
888}
889
890// We don't just take an index, because the item and attached properties object
891// will both be used outside of this function by calling code, so save some
892// time by not accessing them twice.
894 const QQuickItemPrivate *itemPrivate, const QQuickSplitViewAttached *attached) const
895{
897 const QQuickSplitViewAttachedPrivate *attachedPrivate = attached ? QQuickSplitViewAttachedPrivate::get(attached) : nullptr;
898 data.effectiveMinimumWidth = effectiveMinimumWidth(attachedPrivate);
899 data.effectiveMinimumHeight = effectiveMinimumHeight(attachedPrivate);
900 data.effectivePreferredWidth = effectivePreferredWidth(attachedPrivate, itemPrivate);
901 data.effectivePreferredHeight = effectivePreferredHeight(attachedPrivate, itemPrivate);
902 data.effectiveMaximumWidth = effectiveMaximumWidth(attachedPrivate);
903 data.effectiveMaximumHeight = effectiveMaximumHeight(attachedPrivate);
904 return data;
905}
906
908{
909 // If it's the first and only item in the view, it doesn't have a handle,
910 // so return -1: splitIndex (0) - 1.
911 // If it's the last item in the view, it doesn't have a handle, so use
912 // the handle for the previous item.
913 return splitIndex == contentModel->count() - 1 ? splitIndex - 1 : splitIndex;
914}
915
917{
918 qCDebug(qlcQQuickSplitView) << "destroying" << m_handleItems.size() << "handles";
921}
922
924{
925 const bool horizontal = isHorizontal();
926 handleItem->setWidth(horizontal ? handleItem->implicitWidth() : width);
927 handleItem->setHeight(horizontal ? height : handleItem->implicitHeight());
928}
929
931{
932 for (QQuickItem *handleItem : m_handleItems)
933 resizeHandle(handleItem);
934}
935
936#if QT_CONFIG(cursor)
937void QQuickSplitViewPrivate::updateCursorHandle(QQuickItem *handleItem)
938{
939 handleItem->setCursor(isHorizontal() ? Qt::SplitHCursor : Qt::SplitVCursor);
940}
941#endif
942
944{
945 // If this is the first item that is visible, we won't have any
946 // handles yet, because we don't create a handle if we only have one item.
948 return;
949
950 // If the visibility/children change makes any item the last (right/bottom-most)
951 // visible item, we don't want to display a handle for it either:
952 // [ visible (fill) ] | [ hidden ] | [ hidden ]
953 // ^ ^
954 // hidden hidden
955 const int count = contentModel->count();
956 int lastVisibleItemIndex = -1;
957 for (int i = count - 1; i >= 0; --i) {
959 if (item->isVisible()) {
960 lastVisibleItemIndex = i;
961 break;
962 }
963 }
964
965 for (int i = 0; i < count - 1; ++i) {
967 QQuickItem *handleItem = m_handleItems.at(i);
968 if (i != lastVisibleItemIndex)
969 handleItem->setVisible(item->isVisible());
970 else
971 handleItem->setVisible(false);
972 qCDebug(qlcQQuickSplitView) << "set visible property of handle" << handleItem << "at index"
973 << i << "to" << handleItem->isVisible();
974 }
975}
976
978{
979 qCDebug(qlcQQuickSplitViewPointer) << "updating hovered handle after" << hoveredItem << "was hovered";
980
981 const int oldHoveredHandleIndex = m_hoveredHandleIndex;
983 if (m_hoveredHandleIndex == oldHoveredHandleIndex)
984 return;
985
986 // First, clear the hovered flag of any previously-hovered handle.
987 if (oldHoveredHandleIndex != -1) {
988 QQuickItem *oldHoveredHandle = m_handleItems.at(oldHoveredHandleIndex);
989 QQuickSplitHandleAttached *oldHoveredHandleAttached = qobject_cast<QQuickSplitHandleAttached*>(
990 qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(oldHoveredHandle, true));
991 QQuickSplitHandleAttachedPrivate::get(oldHoveredHandleAttached)->setHovered(false);
992 qCDebug(qlcQQuickSplitViewPointer) << "handle item at index" << oldHoveredHandleIndex << "is no longer hovered";
993 }
994
995 if (m_hoveredHandleIndex != -1) {
996 QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
997 qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(hoveredItem, true));
999 qCDebug(qlcQQuickSplitViewPointer) << "handle item at index" << m_hoveredHandleIndex << "is now hovered";
1000 } else {
1001 qCDebug(qlcQQuickSplitViewPointer) << "either there is no hovered item or" << hoveredItem << "is not a handle";
1002 }
1003}
1004
1006{
1007 Q_Q(QQuickSplitView);
1008 if (resizing == m_resizing)
1009 return;
1010
1011 m_resizing = resizing;
1012 emit q->resizingChanged();
1013}
1014
1016{
1017 return m_orientation == Qt::Horizontal;
1018}
1019
1021{
1022 Q_Q(QQuickSplitView);
1024 return item;
1025
1026 return new QQuickContentItem(q);
1027}
1028
1030{
1031 Q_Q(QQuickSplitView);
1032 QQuickContainerPrivate::handlePress(point, timestamp);
1033
1034 QQuickItem *pressedItem = q->childAt(point.x(), point.y());
1035 const int pressedHandleIndex = m_handleItems.indexOf(pressedItem);
1036 if (pressedHandleIndex != -1) {
1037 m_pressedHandleIndex = pressedHandleIndex;
1038 m_pressPos = point;
1039 m_mousePos = point;
1040
1042 // Find the first item to the right/bottom of this one that is visible.
1043 QQuickItem *rightOrBottomItem = nullptr;
1045 for (int i = m_pressedHandleIndex + 1; i < contentModel->count(); ++i) {
1047 if (nextItem->isVisible()) {
1048 rightOrBottomItem = nextItem;
1050 break;
1051 }
1052 }
1054 "Failed to find a visible item to the right/bottom of the one that was pressed at index %1; this shouldn't happen")
1056
1058 m_leftOrTopItemSizeBeforePress = isHorizontal ? leftOrTopItem->width() : leftOrTopItem->height();
1059 m_rightOrBottomItemSizeBeforePress = isHorizontal ? rightOrBottomItem->width() : rightOrBottomItem->height();
1060 m_handlePosBeforePress = pressedItem->position();
1061
1062
1063 // Force the attached object to be created since we rely on it.
1064 QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
1065 qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(pressedItem, true));
1067
1068 setResizing(true);
1069
1070 qCDebug(qlcQQuickSplitViewPointer).nospace() << "handled press -"
1071 << " left/top index=" << m_pressedHandleIndex << ","
1072 << " size before press=" << m_leftOrTopItemSizeBeforePress << ","
1073 << " item=" << leftOrTopItem
1074 << " right/bottom index=" << m_nextVisibleIndexAfterPressedHandle << ","
1075 << " size before press=" << m_rightOrBottomItemSizeBeforePress
1076 << " item=" << rightOrBottomItem;
1077 }
1078 return true;
1079}
1080
1082{
1083 QQuickContainerPrivate::handleMove(point, timestamp);
1084
1085 if (m_pressedHandleIndex != -1) {
1086 m_mousePos = point;
1087 // Don't request layouts for input events because we want
1088 // resizing to be as responsive and smooth as possible.
1089 updatePolish();
1090 }
1091 return true;
1092}
1093
1095{
1096 QQuickContainerPrivate::handleRelease(point, timestamp);
1097
1098 if (m_pressedHandleIndex != -1) {
1100 QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
1101 qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(pressedHandle, true));
1102 QQuickSplitHandleAttachedPrivate::get(handleAttached)->setPressed(false);
1103 }
1104
1105 setResizing(false);
1106
1108 m_pressPos = QPointF();
1109 m_mousePos = QPointF();
1113 return true;
1114}
1115
1117{
1118 const int itemIndex = contentModel->indexOf(item, nullptr);
1119 Q_ASSERT(itemIndex != -1);
1120
1121 qCDebug(qlcQQuickSplitView) << "visible property of split item"
1122 << item << "at index" << itemIndex << "changed to" << item->isVisible();
1123
1124 // The visibility of an item just changed, so we need to update the visibility
1125 // of the corresponding handle (if one exists).
1126
1127 const int handleIndex = handleIndexForSplitIndex(itemIndex);
1128 if (handleIndex != -1) {
1129 QQuickItem *handleItem = m_handleItems.at(handleIndex);
1130 handleItem->setVisible(item->isVisible());
1131
1132 qCDebug(qlcQQuickSplitView) << "set visible property of handle item"
1133 << handleItem << "at index" << handleIndex << "to" << item->isVisible();
1134 }
1135
1138 requestLayout();
1139}
1140
1142{
1143 requestLayout();
1144}
1145
1147{
1148 requestLayout();
1149}
1150
1152{
1153 layout();
1154}
1155
1157{
1158 return splitView->d_func();
1159}
1160
1163{
1164 Q_D(QQuickSplitView);
1165 d->changeTypes |= QQuickItemPrivate::Visibility;
1166
1168}
1169
1171 : QQuickContainer(dd, parent)
1172{
1173 Q_D(QQuickSplitView);
1174 d->changeTypes |= QQuickItemPrivate::Visibility;
1175
1177}
1178
1180{
1181 Q_D(QQuickSplitView);
1182 for (int i = 0; i < d->contentModel->count(); ++i) {
1183 QQuickItem *item = qobject_cast<QQuickItem*>(d->contentModel->object(i));
1184 d->removeImplicitSizeListener(item);
1185 }
1186}
1187
1200{
1201 Q_D(const QQuickSplitView);
1202 return d->m_orientation;
1203}
1204
1206{
1207 Q_D(QQuickSplitView);
1208 if (orientation == d->m_orientation)
1209 return;
1210
1211 d->m_orientation = orientation;
1212
1213#if QT_CONFIG(cursor)
1214 for (QQuickItem *handleItem : d->m_handleItems)
1215 d->updateCursorHandle(handleItem);
1216#endif
1218
1219 // Do this after emitting orientationChanged so that the bindings in QML
1220 // update the implicit size in time.
1221 d->resizeHandles();
1222 // This is queued (via polish) anyway, but to make our intentions clear,
1223 // do it afterwards too.
1224 d->requestLayout();
1225}
1226
1235{
1236 Q_D(const QQuickSplitView);
1237 return d->m_resizing;
1238}
1239
1274{
1275 Q_D(const QQuickSplitView);
1276 return d->m_handle;
1277}
1278
1280{
1281 Q_D(QQuickSplitView);
1282 if (handle == d->m_handle)
1283 return;
1284
1285 qCDebug(qlcQQuickSplitView) << "setting handle" << handle;
1286
1287 if (d->m_handle)
1288 d->destroyHandles();
1289
1290 d->m_handle = handle;
1291
1292 if (d->m_handle) {
1293 d->createHandles();
1294 d->updateHandleVisibilities();
1295 }
1296
1297 d->requestLayout();
1298
1300}
1301
1303{
1304 Q_D(const QQuickSplitView);
1305 if (!qmlContext(item))
1306 return false;
1307
1308 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
1309 return false;
1310
1311 return !d->m_handleItems.contains(item);
1312}
1313
1315{
1316 return new QQuickSplitViewAttached(object);
1317}
1318
1327{
1328#if QT_CONFIG(cborstreamwriter)
1329 Q_D(QQuickSplitView);
1330 qCDebug(qlcQQuickSplitViewState) << "saving state for split items in" << this;
1331
1332 // Save the preferred sizes of each split item.
1333 QCborArray cborArray;
1334 for (int i = 0; i < d->contentModel->count(); ++i) {
1335 const QQuickItem *item = qobject_cast<QQuickItem*>(d->contentModel->object(i));
1336 const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
1337 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
1338 // Don't serialise stuff if we don't need to. If a split item was given a preferred
1339 // size in QML or it was dragged, it will have an attached object and either
1340 // m_isPreferredWidthSet or m_isPreferredHeightSet (or both) will be true,
1341 // so items without these can be skipped. We write the index of each item
1342 // that has data so that we know which item to set it on when restoring.
1343 if (!attached)
1344 continue;
1345
1347 if (!attachedPrivate->m_isPreferredWidthSet && !attachedPrivate->m_isPreferredHeightSet)
1348 continue;
1349
1350 QCborMap cborMap;
1351 cborMap[QLatin1String("index")] = i;
1352 if (attachedPrivate->m_isPreferredWidthSet) {
1353 cborMap[QLatin1String("preferredWidth")] = static_cast<double>(attachedPrivate->m_preferredWidth);
1354
1355 qCDebug(qlcQQuickSplitViewState).nospace() << "- wrote preferredWidth of "
1356 << attachedPrivate->m_preferredWidth << " for split item " << item << " at index " << i;
1357 }
1358 if (attachedPrivate->m_isPreferredHeightSet) {
1359 cborMap[QLatin1String("preferredHeight")] = static_cast<double>(attachedPrivate->m_preferredHeight);
1360
1361 qCDebug(qlcQQuickSplitViewState).nospace() << "- wrote preferredHeight of "
1362 << attachedPrivate->m_preferredHeight << " for split item " << item << " at index " << i;
1363 }
1364
1365 cborArray.append(cborMap);
1366 }
1367
1368 const QByteArray byteArray = cborArray.toCborValue().toCbor();
1369 qCDebug(qlcQQuickSplitViewState) << "the resulting byte array is:" << byteArray;
1370 return QVariant(byteArray);
1371#else
1372 return QVariant();
1373#endif
1374}
1375
1386{
1387 const QByteArray cborByteArray = state.toByteArray();
1388 Q_D(QQuickSplitView);
1389 if (cborByteArray.isEmpty())
1390 return false;
1391
1392 QCborParserError parserError;
1393 const QCborValue cborValue(QCborValue::fromCbor(cborByteArray, &parserError));
1394 if (parserError.error != QCborError::NoError) {
1395 qmlWarning(this) << "Error reading SplitView state:" << parserError.errorString();
1396 return false;
1397 }
1398
1399 qCDebug(qlcQQuickSplitViewState) << "restoring state for split items of" << this
1400 << "from the following string:" << state;
1401
1402 const QCborArray cborArray(cborValue.toArray());
1403 const int ourCount = d->contentModel->count();
1404 // This could conceivably happen if items were removed from the SplitView since the state was last saved.
1405 if (cborArray.size() > ourCount) {
1406 qmlWarning(this) << "Error reading SplitView state: expected "
1407 << ourCount << " or less split items but got " << cborArray.size();
1408 return false;
1409 }
1410
1411 for (auto it = cborArray.constBegin(); it != cborArray.constEnd(); ++it) {
1412 QCborMap cborMap(it->toMap());
1413 const int splitItemIndex = cborMap.value(QLatin1String("index")).toInteger();
1414 const bool isPreferredWidthSet = cborMap.contains(QLatin1String("preferredWidth"));
1415 const bool isPreferredHeightSet = cborMap.contains(QLatin1String("preferredHeight"));
1416
1417 QQuickItem *item = qobject_cast<QQuickItem*>(d->contentModel->object(splitItemIndex));
1418 // If the split item does not have a preferred size specified in QML, it could still have
1419 // been resized via dragging before it was saved. In this case, it won't have an
1420 // attached object upon application startup, so we create it.
1421 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
1422 qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
1423 if (isPreferredWidthSet) {
1424 const qreal preferredWidth = cborMap.value(QLatin1String("preferredWidth")).toDouble();
1425 attached->setPreferredWidth(preferredWidth);
1426 }
1427 if (isPreferredHeightSet) {
1428 const qreal preferredHeight = cborMap.value(QLatin1String("preferredHeight")).toDouble();
1429 attached->setPreferredHeight(preferredHeight);
1430 }
1431
1433 qCDebug(qlcQQuickSplitViewState).nospace()
1434 << "- restored the following state for split item " << item << " at index " << splitItemIndex
1435 << ": preferredWidthSet=" << attachedPrivate->m_isPreferredWidthSet
1436 << " preferredWidth=" << attachedPrivate->m_preferredWidth
1437 << " preferredHeightSet=" << attachedPrivate->m_isPreferredHeightSet
1438 << " preferredHeight=" << attachedPrivate->m_preferredHeight;
1439 }
1440
1441 return true;
1442}
1443
1445{
1446 Q_D(QQuickSplitView);
1448 d->updateFillIndex();
1449 d->updatePolish();
1450}
1451
1453{
1454 Q_D(QQuickSplitView);
1456
1457 QQuickItem *hoveredItem = childAt(event->position().toPoint().x(), event->position().toPoint().y());
1458 d->updateHoveredHandle(hoveredItem);
1459}
1460
1462{
1463 Q_UNUSED(event);
1464 Q_D(QQuickSplitView);
1465 // If SplitView is no longer hovered (e.g. visible set to false), clear handle hovered value
1466 d->updateHoveredHandle(nullptr);
1467}
1468
1470{
1471 Q_D(QQuickSplitView);
1472 qCDebug(qlcQQuickSplitViewPointer) << "childMouseEventFilter called with" << item << event;
1473
1474 if (Q_LIKELY(event->isPointerEvent())) {
1475 auto *pointerEvent = static_cast<QPointerEvent *>(event);
1476 const auto &eventPoint = pointerEvent->points().first();
1477 const QPointF point = mapFromItem(item, eventPoint.position());
1478 const auto timestamp = pointerEvent->timestamp();
1479
1480 switch (event->type()) {
1482 d->handlePress(point, timestamp);
1483 // Keep the mouse grab if this item belongs to the handle,
1484 // otherwise this event can be stolen e.g. Flickable if we're inside it.
1485 if (d->m_pressedHandleIndex != -1)
1486 item->setKeepMouseGrab(true);
1487 break;
1489 d->handleRelease(point, timestamp);
1490 break;
1491 case QEvent::MouseMove:
1492 d->handleMove(point, timestamp);
1493 break;
1494 case QEvent::TouchBegin:
1495 if (pointerEvent->pointCount() == 1) {
1496 d->handlePress(point, timestamp);
1497 // We filter the event on behalf of item, but we want the item
1498 // to be the exclusive grabber so that we can continue to filter
1499 // touch events for it.
1500 if (d->m_pressedHandleIndex != -1) {
1501 item->setKeepTouchGrab(true);
1502 pointerEvent->setExclusiveGrabber(eventPoint, item);
1503 }
1504 }
1505 break;
1506 case QEvent::TouchEnd:
1507 if (pointerEvent->pointCount() == 1)
1508 d->handleRelease(point, timestamp);
1509 break;
1511 if (pointerEvent->pointCount() == 1)
1512 d->handleMove(point, timestamp);
1513 break;
1514 default:
1515 break;
1516 }
1517 }
1518
1519 // If this event belongs to the handle, filter it. (d->m_pressedHandleIndex != -1) means that
1520 // we press or move the handle, so we don't need to propagate it further.
1521 if (d->m_pressedHandleIndex != -1)
1522 return true;
1523
1525}
1526
1527void QQuickSplitView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1528{
1529 Q_D(QQuickSplitView);
1530 QQuickControl::geometryChange(newGeometry, oldGeometry);
1531 d->resizeHandles();
1532 d->requestLayout();
1533}
1534
1536{
1537 Q_D(QQuickSplitView);
1538 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
1539 return;
1540
1541 const int count = d->contentModel->count();
1542 qCDebug(qlcQQuickSplitView).nospace() << "split item " << item << " added at index " << index
1543 << "; there are now " << count << " items";
1544
1545 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
1546 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
1547 if (attached)
1549
1550 // Only need to add handles if we have more than one split item.
1551 if (count > 1) {
1552 // If the item was added at the end, it shouldn't get a handle;
1553 // the handle always goes to the split item on the left.
1554 d->createHandleItem(index < count - 1 ? index : index - 1);
1555 }
1556
1557 d->addImplicitSizeListener(item);
1558
1559 d->updateHandleVisibilities();
1560 d->updateFillIndex();
1561 d->requestLayout();
1562}
1563
1565{
1566 Q_D(QQuickSplitView);
1567 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
1568 return;
1569
1570 qCDebug(qlcQQuickSplitView) << "split item" << item << "moved to index" << index;
1571
1572 d->updateHandleVisibilities();
1573 d->updateFillIndex();
1574 d->requestLayout();
1575}
1576
1578{
1579 Q_D(QQuickSplitView);
1580 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
1581 return;
1582
1583 qCDebug(qlcQQuickSplitView).nospace() << "split item " << item << " removed from index " << index
1584 << "; there are now " << d->contentModel->count() << " items";
1585
1586 // Clear hovered/pressed handle if there are any.
1587 if (d->m_hoveredHandleIndex != -1 || d->m_pressedHandleIndex != -1) {
1588 const int handleIndex = d->m_hoveredHandleIndex != -1 ? d->m_hoveredHandleIndex : d->m_pressedHandleIndex;
1589 QQuickItem *itemHandle = d->m_handleItems.at(handleIndex);
1590 QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
1591 qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(itemHandle, false));
1592 if (handleAttached) {
1593 auto handleAttachedPrivate = QQuickSplitHandleAttachedPrivate::get(handleAttached);
1594 handleAttachedPrivate->setHovered(false);
1595 handleAttachedPrivate->setPressed(false);
1596 }
1597
1598 d->m_hoveredHandleIndex = -1;
1599 d->m_pressedHandleIndex = -1;
1600 }
1601
1602 // Unset any attached properties since the item is no longer owned by us.
1603 QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
1604 qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
1605 if (attached)
1607
1608 d->removeImplicitSizeListener(item);
1609
1610 d->removeExcessHandles();
1611 d->updateHandleVisibilities();
1612 d->updateFillIndex();
1613 d->requestLayout();
1614}
1615
1616#if QT_CONFIG(accessibility)
1617QAccessible::Role QQuickSplitView::accessibleRole() const
1618{
1619 return QAccessible::Pane;
1620}
1621#endif
1622
1625{
1628 if (!item) {
1629 qmlWarning(parent) << "SplitView: attached properties can only be used on Items";
1630 return;
1631 }
1632
1633 if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
1634 return;
1635
1636 d->m_splitItem = item;
1637
1638 // Child items get added to SplitView's contentItem, so we have to ensure
1639 // that exists first before trying to set m_splitView.
1640 // Apparently, in some cases it's normal for the parent item
1641 // to not exist until shortly after this constructor has run.
1642 if (!item->parentItem())
1643 return;
1644
1645 // This will get hit when attached SplitView properties are imperatively set
1646 // on an item that previously had none set, for example.
1647 QQuickSplitView *splitView = qobject_cast<QQuickSplitView*>(item->parentItem()->parentItem());
1648 if (!splitView) {
1649 qmlWarning(parent) << "SplitView: attached properties must be accessed through a direct child of SplitView";
1650 return;
1651 }
1652
1653 d->setView(splitView);
1654}
1655
1663{
1664 Q_D(const QQuickSplitViewAttached);
1665 return d->m_splitView;
1666}
1667
1682{
1683 Q_D(const QQuickSplitViewAttached);
1684 return d->m_minimumWidth;
1685}
1686
1688{
1690 d->m_isMinimumWidthSet = true;
1691 if (qFuzzyCompare(width, d->m_minimumWidth))
1692 return;
1693
1694 d->m_minimumWidth = width;
1695 d->requestLayoutView();
1697}
1698
1700{
1702 const qreal oldEffectiveMinimumWidth = effectiveMinimumWidth(d);
1703
1704 d->m_isMinimumWidthSet = false;
1705 d->m_minimumWidth = -1;
1706
1707 const qreal newEffectiveMinimumWidth = effectiveMinimumWidth(d);
1708 if (qFuzzyCompare(newEffectiveMinimumWidth, oldEffectiveMinimumWidth))
1709 return;
1710
1711 d->requestLayoutView();
1713}
1714
1729{
1730 Q_D(const QQuickSplitViewAttached);
1731 return d->m_minimumHeight;
1732}
1733
1735{
1737 d->m_isMinimumHeightSet = true;
1738 if (qFuzzyCompare(height, d->m_minimumHeight))
1739 return;
1740
1741 d->m_minimumHeight = height;
1742 d->requestLayoutView();
1744}
1745
1747{
1749 const qreal oldEffectiveMinimumHeight = effectiveMinimumHeight(d);
1750
1751 d->m_isMinimumHeightSet = false;
1752 d->m_minimumHeight = -1;
1753
1754 const qreal newEffectiveMinimumHeight = effectiveMinimumHeight(d);
1755 if (qFuzzyCompare(newEffectiveMinimumHeight, oldEffectiveMinimumHeight))
1756 return;
1757
1758 d->requestLayoutView();
1760}
1761
1783{
1784 Q_D(const QQuickSplitViewAttached);
1785 return d->m_preferredWidth;
1786}
1787
1789{
1791 d->m_isPreferredWidthSet = true;
1792 // Make sure that we clear this flag now, before we emit the change signals
1793 // which could cause another setter to be called.
1794 auto splitViewPrivate = d->m_splitView ? QQuickSplitViewPrivate::get(d->m_splitView) : nullptr;
1795 const bool ignoreNextLayoutRequest = splitViewPrivate && splitViewPrivate->m_ignoreNextLayoutRequest;
1796 if (splitViewPrivate)
1797 splitViewPrivate->m_ignoreNextLayoutRequest = false;
1798
1799 if (qFuzzyCompare(width, d->m_preferredWidth))
1800 return;
1801
1802 d->m_preferredWidth = width;
1803
1804 if (!ignoreNextLayoutRequest) {
1805 // We are currently in the middle of performing a layout, and the user (not our internal code)
1806 // changed the preferred width of one of the split items, so request another layout.
1807 d->requestLayoutView();
1808 }
1809
1811}
1812
1814{
1816 const qreal oldEffectivePreferredWidth = effectivePreferredWidth(
1817 d, QQuickItemPrivate::get(d->m_splitItem));
1818
1819 d->m_isPreferredWidthSet = false;
1820 d->m_preferredWidth = -1;
1821
1822 const qreal newEffectivePreferredWidth = effectivePreferredWidth(
1823 d, QQuickItemPrivate::get(d->m_splitItem));
1824 if (qFuzzyCompare(newEffectivePreferredWidth, oldEffectivePreferredWidth))
1825 return;
1826
1827 d->requestLayoutView();
1829}
1830
1852{
1853 Q_D(const QQuickSplitViewAttached);
1854 return d->m_preferredHeight;
1855}
1856
1858{
1860 d->m_isPreferredHeightSet = true;
1861 // Make sure that we clear this flag now, before we emit the change signals
1862 // which could cause another setter to be called.
1863 auto splitViewPrivate = d->m_splitView ? QQuickSplitViewPrivate::get(d->m_splitView) : nullptr;
1864 const bool ignoreNextLayoutRequest = splitViewPrivate && splitViewPrivate->m_ignoreNextLayoutRequest;
1865 if (splitViewPrivate)
1866 splitViewPrivate->m_ignoreNextLayoutRequest = false;
1867
1868 if (qFuzzyCompare(height, d->m_preferredHeight))
1869 return;
1870
1871 d->m_preferredHeight = height;
1872
1873 if (!ignoreNextLayoutRequest) {
1874 // We are currently in the middle of performing a layout, and the user (not our internal code)
1875 // changed the preferred height of one of the split items, so request another layout.
1876 d->requestLayoutView();
1877 }
1878
1880}
1881
1883{
1885 const qreal oldEffectivePreferredHeight = effectivePreferredHeight(
1886 d, QQuickItemPrivate::get(d->m_splitItem));
1887
1888 d->m_isPreferredHeightSet = false;
1889 d->m_preferredHeight = -1;
1890
1891 const qreal newEffectivePreferredHeight = effectivePreferredHeight(
1892 d, QQuickItemPrivate::get(d->m_splitItem));
1893 if (qFuzzyCompare(newEffectivePreferredHeight, oldEffectivePreferredHeight))
1894 return;
1895
1896 d->requestLayoutView();
1898}
1899
1914{
1915 Q_D(const QQuickSplitViewAttached);
1916 return d->m_maximumWidth;
1917}
1918
1920{
1922 d->m_isMaximumWidthSet = true;
1923 if (qFuzzyCompare(width, d->m_maximumWidth))
1924 return;
1925
1926 d->m_maximumWidth = width;
1927 d->requestLayoutView();
1929}
1930
1932{
1934 const qreal oldEffectiveMaximumWidth = effectiveMaximumWidth(d);
1935
1936 d->m_isMaximumWidthSet = false;
1937 d->m_maximumWidth = -1;
1938
1939 const qreal newEffectiveMaximumWidth = effectiveMaximumWidth(d);
1940 if (qFuzzyCompare(newEffectiveMaximumWidth, oldEffectiveMaximumWidth))
1941 return;
1942
1943 d->requestLayoutView();
1945}
1946
1961{
1962 Q_D(const QQuickSplitViewAttached);
1963 return d->m_maximumHeight;
1964}
1965
1967{
1969 d->m_isMaximumHeightSet = true;
1970 if (qFuzzyCompare(height, d->m_maximumHeight))
1971 return;
1972
1973 d->m_maximumHeight = height;
1974 d->requestLayoutView();
1976}
1977
1979{
1981 const qreal oldEffectiveMaximumHeight = effectiveMaximumHeight(d);
1982
1983 d->m_isMaximumHeightSet = false;
1984 d->m_maximumHeight = -1;
1985
1986 const qreal newEffectiveMaximumHeight = effectiveMaximumHeight(d);
1987 if (qFuzzyCompare(newEffectiveMaximumHeight, oldEffectiveMaximumHeight))
1988 return;
1989
1990 d->requestLayoutView();
1992}
1993
2011{
2012 Q_D(const QQuickSplitViewAttached);
2013 return d->m_fillWidth;
2014}
2015
2017{
2019 d->m_isFillWidthSet = true;
2020 if (fill == d->m_fillWidth)
2021 return;
2022
2023 d->m_fillWidth = fill;
2024 if (d->m_splitView && d->m_splitView->orientation() == Qt::Horizontal)
2026 d->requestLayoutView();
2028}
2029
2047{
2048 Q_D(const QQuickSplitViewAttached);
2049 return d->m_fillHeight;
2050}
2051
2053{
2055 d->m_isFillHeightSet = true;
2056 if (fill == d->m_fillHeight)
2057 return;
2058
2059 d->m_fillHeight = fill;
2060 if (d->m_splitView && d->m_splitView->orientation() == Qt::Vertical)
2062 d->requestLayoutView();
2064}
2065
2067 : m_fillWidth(false)
2068 , m_fillHeight(false)
2069 , m_isFillWidthSet(false)
2070 , m_isFillHeightSet(false)
2071 , m_isMinimumWidthSet(false)
2072 , m_isMinimumHeightSet(false)
2073 , m_isPreferredWidthSet(false)
2074 , m_isPreferredHeightSet(false)
2075 , m_isMaximumWidthSet(false)
2076 , m_isMaximumHeightSet(false)
2077 , m_minimumWidth(0)
2078 , m_minimumHeight(0)
2079 , m_preferredWidth(-1)
2080 , m_preferredHeight(-1)
2081 , m_maximumWidth(std::numeric_limits<qreal>::infinity())
2082 , m_maximumHeight(std::numeric_limits<qreal>::infinity())
2083{
2084}
2085
2087{
2089 if (newView == m_splitView)
2090 return;
2091
2092 m_splitView = newView;
2093 qCDebug(qlcQQuickSplitView) << "set SplitView" << newView << "on attached object" << this;
2094 emit q->viewChanged();
2095}
2096
2098{
2099 if (m_splitView)
2101}
2102
2104{
2105 return attached->d_func();
2106}
2107
2109{
2110 return attached->d_func();
2111}
2112
2114 : m_hovered(false)
2115 , m_pressed(false)
2116{
2117}
2118
2120{
2122 if (hovered == m_hovered)
2123 return;
2124
2125 m_hovered = hovered;
2126 emit q->hoveredChanged();
2127}
2128
2130{
2132 if (pressed == m_pressed)
2133 return;
2134
2135 m_pressed = pressed;
2136 emit q->pressedChanged();
2137}
2138
2140{
2141 return attached->d_func();
2142}
2143
2145{
2146 return attached->d_func();
2147}
2148
2151{
2152}
2153
2158
2177{
2178 Q_D(const QQuickSplitHandleAttached);
2179 return d->m_hovered;
2180}
2181
2190{
2191 Q_D(const QQuickSplitHandleAttached);
2192 return d->m_pressed;
2193}
2194
2196{
2197 return new QQuickSplitHandleAttached(object);
2198}
2199
2201
2202#include "moc_qquicksplitview_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
\inmodule QtCore\reentrant
Definition qcborarray.h:20
const_iterator constEnd() const
Returns an array iterator pointing to just after the last element in this array.
Definition qcborarray.h:208
const_iterator constBegin() const
Returns an array iterator pointing to the first item in this array.
Definition qcborarray.h:204
void append(const QCborValue &value)
Definition qcborarray.h:170
QCborValue toCborValue() const
Explicitly construcuts a \l QCborValue object that represents this array.
Definition qcborarray.h:147
qsizetype size() const noexcept
Returns the size of this array.
\inmodule QtCore\reentrant
Definition qcbormap.h:21
QCborValue value(qint64 key) const
Returns the QCborValue element in this map that corresponds to key key, if there is one.
Definition qcbormap.h:169
bool contains(qint64 key) const
Returns true if this map contains a key-value pair identified by key key.
Definition qcbormap.h:214
\inmodule QtCore\reentrant
Definition qcborvalue.h:50
QCborArray toArray() const
qint64 toInteger(qint64 defaultValue=0) const
Returns the integer value stored in this QCborValue, if it is of the integer type.
Definition qcborvalue.h:190
double toDouble(double defaultValue=0) const
Returns the floating point value stored in this QCborValue, if it is of the Double type.
Definition qcborvalue.h:194
\inmodule QtCore
Definition qcoreevent.h:45
@ MouseMove
Definition qcoreevent.h:63
@ MouseButtonPress
Definition qcoreevent.h:60
@ TouchUpdate
Definition qcoreevent.h:242
@ TouchBegin
Definition qcoreevent.h:241
@ MouseButtonRelease
Definition qcoreevent.h:61
void setX(qreal x)
QGraphicsItem * parentItem() const
Returns a pointer to this item's parent item.
bool isVisible() const
Returns true if the item is visible; otherwise, false is returned.
void setY(qreal y)
\inmodule QtGui
Definition qevent.h:245
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & first()
Definition qlist.h:628
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
value_type takeLast()
Definition qlist.h:550
void reserve(qsizetype size)
Definition qlist.h:746
void clear()
Definition qlist.h:417
\inmodule QtCore
Definition qobject.h:90
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2142
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
A base class for pointer events.
Definition qevent.h:73
const QList< QEventPoint > & points() const
Returns a list of points in this pointer event.
Definition qevent.h:86
The QQmlComponent class encapsulates a QML component definition.
virtual QObject * beginCreate(QQmlContext *)
Create an object instance from this component, within the specified context.
virtual void completeCreate()
This method provides advanced control over component instance creation.
QQmlContext * creationContext() const
Returns the QQmlContext the component was created in.
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
int count() const override
\qmlproperty int QtQml.Models::ObjectModel::count
int indexOf(QObject *object, QObject *objectContext) const override
QQmlObjectModel * contentModel
virtual bool handlePress(const QPointF &point, ulong timestamp)
virtual QQuickItem * getContentItem()
virtual bool handleRelease(const QPointF &point, ulong timestamp)
virtual bool handleMove(const QPointF &point, ulong timestamp)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
quint32 componentComplete
QQmlListProperty< QObject > data()
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
void setFiltersChildMouseEvents(bool filter)
Sets whether pointer events intended for this item's children should be filtered through this item.
Q_INVOKABLE QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in item's coordinate system to the equivalent point within this item's coordinat...
qreal implicitWidth
Definition qquickitem.h:113
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:73
void setParentItem(QQuickItem *parent)
QString state() const
\qmlproperty string QtQuick::Item::state
virtual void hoverMoveEvent(QHoverEvent *event)
This event handler can be reimplemented in a subclass to receive hover-move events for an item.
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:74
bool isVisible() const
Q_INVOKABLE QQuickItem * childAt(qreal x, qreal y) const
\qmlmethod QtQuick::Item::childAt(real x, real y)
void setHeight(qreal)
void setAcceptedMouseButtons(Qt::MouseButtons buttons)
Sets the mouse buttons accepted by this item to buttons.
qreal width
This property holds the width of this item.
Definition qquickitem.h:76
void setVisible(bool)
qreal implicitHeight
Definition qquickitem.h:114
QPointF position() const
void setKeepMouseGrab(bool)
Sets whether the mouse input should remain exclusively with this item.
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
virtual bool childMouseEventFilter(QQuickItem *, QEvent *)
Reimplement this method to filter the pointer events that are received by this item's children.
void setWidth(qreal)
void setX(qreal)
void setY(qreal)
static QQuickSplitHandleAttachedPrivate * get(QQuickSplitHandleAttached *attached)
bool isHovered() const
Provides attached properties for SplitView handles.
QQuickSplitHandleAttached(QObject *parent=nullptr)
static QQuickSplitHandleAttached * qmlAttachedProperties(QObject *object)
bool isPressed() const
\qmlattachedproperty bool QtQuick.Controls::SplitHandle::pressed
void setView(QQuickSplitView *newView)
static QQuickSplitViewAttachedPrivate * get(QQuickSplitViewAttached *attached)
void setMinimumHeight(qreal height)
void setMinimumWidth(qreal width)
void setMaximumHeight(qreal height)
QQuickSplitViewAttached(QObject *parent=nullptr)
void setPreferredWidth(qreal width)
void setPreferredHeight(qreal height)
void setMaximumWidth(qreal width)
void resizeHandle(QQuickItem *handleItem)
void setResizing(bool resizing)
bool handlePress(const QPointF &point, ulong timestamp) override
qreal accumulatedSize(int firstIndex, int lastIndex) const
void itemImplicitHeightChanged(QQuickItem *item) override
void itemVisibilityChanged(QQuickItem *item) override
bool handleRelease(const QPointF &point, ulong timestamp) override
void itemImplicitWidthChanged(QQuickItem *item) override
QHash< QQuickItem *, LayoutData > m_layoutData
void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag)
int handleIndexForSplitIndex(int splitIndex) const
bool handleMove(const QPointF &point, ulong timestamp) override
void limitAndApplySizes(qreal usedWidth, qreal usedHeight)
void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag)
EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate, const QQuickSplitViewAttached *attached) const
void layoutPositionItems(const QQuickItem *fillItem)
void updateHoveredHandle(QQuickItem *hoveredItem)
QList< QQuickItem * > m_handleItems
static QQuickSplitViewPrivate * get(QQuickSplitView *splitView)
void createHandleItem(int index)
QQuickItem * getContentItem() override
void updateFillIndex()
Lays out items with a draggable splitter between each item.
static QQuickSplitViewAttached * qmlAttachedProperties(QObject *object)
void handleChanged()
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setOrientation(Qt::Orientation orientation)
~QQuickSplitView() override
void setHandle(QQmlComponent *handle)
void itemAdded(int index, QQuickItem *item) override
void hoverLeaveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-leave events for an item.
void hoverMoveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-move events for an item.
Q_INVOKABLE QVariant saveState()
\qmlmethod var QtQuick.Controls::SplitView::saveState()
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
bool isContent(QQuickItem *item) const override
void orientationChanged()
QQmlComponent * handle
\qmlproperty Component QtQuick.Controls::SplitView::handle
Qt::Orientation orientation
void itemRemoved(int index, QQuickItem *item) override
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override
Reimplement this method to filter the pointer events that are received by this item's children.
Q_INVOKABLE bool restoreState(const QVariant &state)
\qmlmethod bool QtQuick.Controls::SplitView::restoreState(state)
bool isResizing() const
\qmlproperty bool QtQuick.Controls::SplitView::resizing \readonly
QQuickSplitView(QQuickItem *parent=nullptr)
void itemMoved(int index, QQuickItem *item) override
\inmodule QtCore\reentrant
Definition qrect.h:483
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
\inmodule QtCore
Definition qvariant.h:64
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
@ LeftButton
Definition qnamespace.h:57
Orientation
Definition qnamespace.h:97
@ Horizontal
Definition qnamespace.h:98
@ Vertical
Definition qnamespace.h:99
@ SplitHCursor
static void * context
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
#define Q_FUNC_INFO
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint64 GLenum void * handle
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLenum GLsizei count
GLint GLsizei width
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
struct _cl_event * event
GLsizei const void * pointer
Definition qopenglext.h:384
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
qreal effectiveMaximumHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate)
qreal effectivePreferredHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate, const QQuickItemPrivate *itemPrivate)
qreal effectivePreferredWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate, const QQuickItemPrivate *itemPrivate)
qreal effectiveMinimumWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate)
qreal effectiveMaximumWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate)
qreal effectiveMinimumHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define emit
#define Q_UNUSED(x)
unsigned long ulong
Definition qtypes.h:30
double qreal
Definition qtypes.h:92
ba fill(true)
QGraphicsItem * item
\inmodule QtCore\reentrant
Definition qcborvalue.h:40
QString errorString() const
\variable QCborParserError::offset
Definition qcborvalue.h:44
QCborError error
Definition qcborvalue.h:42
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent