8#include "columnview_p.h"
10#include "loggingcategory.h"
11#include <QAbstractItemModel>
12#include <QGuiApplication>
13#include <QPropertyAnimation>
14#include <QQmlComponent>
19#include "platform/units.h"
21class QmlComponentsPoolSingleton
24 QmlComponentsPoolSingleton()
27 static QmlComponentsPool *instance(QQmlEngine *engine);
30 QHash<QQmlEngine *, QmlComponentsPool *> m_instances;
33Q_GLOBAL_STATIC(QmlComponentsPoolSingleton, privateQmlComponentsPoolSelf)
35QmlComponentsPool *QmlComponentsPoolSingleton::instance(
QQmlEngine *engine)
38 auto componentPool = privateQmlComponentsPoolSelf->m_instances.value(engine);
44 componentPool =
new QmlComponentsPool(engine);
46 const auto removePool = [engine]() {
48 if (privateQmlComponentsPoolSelf) {
49 privateQmlComponentsPoolSelf->m_instances.remove(engine);
55 privateQmlComponentsPoolSelf->m_instances[engine] = componentPool;
59QmlComponentsPool::QmlComponentsPool(
QQmlEngine *engine)
63 component.loadFromModule(
"org.kde.kirigami.layouts.private",
"ColumnViewSeparator");
65 m_instance = component.create();
67 if (component.isError()) {
68 qCWarning(KirigamiLayoutsLog) << component.errors();
71 m_instance->setParent(
this);
73 m_leadingSeparatorComponent = m_instance->property(
"leadingSeparator").value<
QQmlComponent *>();
74 Q_ASSERT(m_leadingSeparatorComponent);
76 m_trailingSeparatorComponent = m_instance->property(
"trailingSeparator").value<
QQmlComponent *>();
77 Q_ASSERT(m_trailingSeparatorComponent);
82 connect(m_units, &Kirigami::Platform::Units::gridUnitChanged,
this, &QmlComponentsPool::gridUnitChanged);
83 connect(m_units, &Kirigami::Platform::Units::longDurationChanged,
this, &QmlComponentsPool::longDurationChanged);
86QmlComponentsPool::~QmlComponentsPool()
92ColumnViewAttached::ColumnViewAttached(
QObject *parent)
97ColumnViewAttached::~ColumnViewAttached()
101void ColumnViewAttached::setIndex(
int index)
103 if (!m_customFillWidth && m_view) {
104 const bool oldFillWidth = m_fillWidth;
105 m_fillWidth =
index == m_view->count() - 1;
106 if (oldFillWidth != m_fillWidth) {
107 Q_EMIT fillWidthChanged();
111 if (
index == m_index) {
124void ColumnViewAttached::setFillWidth(
bool fill)
127 disconnect(m_view.data(), &ColumnView::countChanged,
this,
nullptr);
129 m_customFillWidth =
true;
131 if (fill == m_fillWidth) {
136 Q_EMIT fillWidthChanged();
150 return m_reservedSpace;
153void ColumnViewAttached::setReservedSpace(qreal space)
156 disconnect(m_view.data(), &ColumnView::columnWidthChanged,
this,
nullptr);
158 m_customReservedSpace =
true;
160 if (qFuzzyCompare(space, m_reservedSpace)) {
164 m_reservedSpace = space;
165 Q_EMIT reservedSpaceChanged();
177void ColumnViewAttached::setView(
ColumnView *view)
179 if (
view == m_view) {
184 disconnect(m_view.data(),
nullptr,
this,
nullptr);
188 if (!m_customFillWidth && m_view) {
189 m_fillWidth = m_index == m_view->count() - 1;
190 connect(m_view.data(), &ColumnView::countChanged,
this, [
this]() {
191 m_fillWidth = m_index == m_view->count() - 1;
192 Q_EMIT fillWidthChanged();
195 if (!m_customReservedSpace && m_view) {
196 m_reservedSpace = m_view->columnWidth();
197 connect(m_view.data(), &ColumnView::columnWidthChanged,
this, [
this]() {
198 m_reservedSpace = m_view->columnWidth();
199 Q_EMIT reservedSpaceChanged();
206QQuickItem *ColumnViewAttached::originalParent()
const
208 return m_originalParent;
211void ColumnViewAttached::setOriginalParent(
QQuickItem *parent)
213 m_originalParent =
parent;
216bool ColumnViewAttached::shouldDeleteOnRemove()
const
218 return m_shouldDeleteOnRemove;
221void ColumnViewAttached::setShouldDeleteOnRemove(
bool del)
223 m_shouldDeleteOnRemove =
del;
228 return m_preventStealing;
231void ColumnViewAttached::setPreventStealing(
bool prevent)
233 if (prevent == m_preventStealing) {
237 m_preventStealing = prevent;
238 Q_EMIT preventStealingChanged();
241bool ColumnViewAttached::isPinned()
const
246void ColumnViewAttached::setPinned(
bool pinned)
266void ColumnViewAttached::setInViewport(
bool inViewport)
274 Q_EMIT inViewportChanged();
277QQuickItem *ColumnViewAttached::globalHeader()
const
279 return m_globalHeader;
282void ColumnViewAttached::setGlobalHeader(
QQuickItem *header)
284 if (header == m_globalHeader) {
288 QQuickItem *oldHeader = m_globalHeader;
289 if (m_globalHeader) {
290 disconnect(m_globalHeader,
nullptr,
this,
nullptr);
293 m_globalHeader = header;
296 globalHeaderChanged(header,
nullptr);
299 Q_EMIT globalHeaderChanged(oldHeader, header);
302QQuickItem *ColumnViewAttached::globalFooter()
const
304 return m_globalFooter;
307void ColumnViewAttached::setGlobalFooter(
QQuickItem *footer)
309 if (footer == m_globalFooter) {
313 QQuickItem *oldFooter = m_globalFooter;
314 if (m_globalFooter) {
315 disconnect(m_globalFooter,
nullptr,
this,
nullptr);
318 m_globalFooter = footer;
321 globalFooterChanged(footer,
nullptr);
324 Q_EMIT globalFooterChanged(oldFooter, footer);
336 setFlags(flags() | ItemIsFocusScope);
338 m_slideAnim->setTargetObject(
this);
339 m_slideAnim->setPropertyName(
"x");
341 m_slideAnim->setDuration(0);
344 if (!m_view->currentItem()) {
345 m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem));
349 m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem));
355 m_creationInProgress =
false;
358ContentItem::~ContentItem()
362void ContentItem::setBoundedX(qreal x)
368 setX(qRound(qBound(qMin(0.0, -width() + parentItem()->width()), x, 0.0)));
371void ContentItem::animateX(qreal newX)
377 const qreal to = qRound(qBound(qMin(0.0, -width() + parentItem()->width()), newX, 0.0));
380 m_slideAnim->setStartValue(x());
381 m_slideAnim->setEndValue(to);
382 m_slideAnim->start();
385void ContentItem::snapToItem()
387 QQuickItem *firstItem = childAt(viewportLeft(), height() / 2);
391 QQuickItem *nextItem = childAt(firstItem->
x() + firstItem->
width() + 1, height() / 2);
395 ((m_view->dragging() && m_lastDragDelta < 0)
396 || (!m_view->dragging()
397 && (width() - viewportRight()) < (viewportLeft() - firstItem->
x())))) {
398 m_viewAnchorItem = nextItem;
399 animateX(-nextItem->
x() + m_leftPinnedSpace);
402 }
else if ((m_view->dragging() && m_lastDragDelta >= 0)
403 || (!m_view->dragging() && (viewportLeft() <= (firstItem->
x() + (firstItem->
width() / 2))))
405 m_viewAnchorItem = firstItem;
406 animateX(-firstItem->
x() + m_leftPinnedSpace);
410 m_viewAnchorItem = nextItem;
411 animateX(-nextItem->
x() + m_leftPinnedSpace);
415qreal ContentItem::viewportLeft()
const
417 return -x() + m_leftPinnedSpace;
420qreal ContentItem::viewportRight()
const
422 return -x() + m_view->width() - m_rightPinnedSpace;
425qreal ContentItem::childWidth(
QQuickItem *child)
431 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
433 if (m_columnResizeMode == ColumnView::SingleColumn) {
434 return qRound(parentItem()->width());
437 if (m_view->count() == 1) {
439 return qRound(parentItem()->width());
442 return qRound(qBound(m_columnWidth, (parentItem()->width() - attached->
reservedSpace()), std::max(m_columnWidth, parentItem()->width())));
444 }
else if (m_columnResizeMode == ColumnView::FixedColumns) {
445 return qRound(qMin(parentItem()->width(), m_columnWidth));
453 width = m_columnWidth;
456 return qRound(qMin(m_view->width(), width));
460void ContentItem::layoutItems()
462 setY(m_view->topPadding());
463 setHeight(m_view->height() - m_view->topPadding() - m_view->bottomPadding());
465 qreal implicitWidth = 0;
466 qreal implicitHeight = 0;
467 qreal partialWidth = 0;
469 m_leftPinnedSpace = 0;
470 m_rightPinnedSpace = 0;
473 auto it = !reverse ? m_items.begin() : m_items.end();
474 int increment = reverse ? -1 : +1;
475 auto lastPos = reverse ? m_items.begin() : m_items.end();
477 for (; it != lastPos; it += increment) {
479 QQuickItem *child = reverse ? *(it - 1) : *it;
480 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
481 if (child == m_globalHeaderParent || child == m_globalFooterParent) {
486 if (attached->isPinned() && m_view->columnResizeMode() != ColumnView::SingleColumn) {
489 if (m_view->separatorVisible()) {
490 sep = ensureTrailingSeparator(child);
491 sepWidth = (sep ? sep->
width() : 0);
493 const qreal width = childWidth(child);
494 const qreal widthDiff = std::max(0.0, m_view->width() - child->
width());
495 const qreal pageX = std::clamp(partialWidth, -x(), -x() + widthDiff);
496 qreal headerHeight = .0;
497 qreal footerHeight = .0;
498 if (
QQuickItem *header = attached->globalHeader()) {
501 header->setPosition(
QPointF(pageX, .0));
503 if (m_view->separatorVisible()) {
504 QQuickItem *sep = ensureTrailingSeparator(header);
508 if (
QQuickItem *footer = attached->globalFooter()) {
511 footer->setPosition(
QPointF(pageX, height() - footerHeight));
513 if (m_view->separatorVisible()) {
514 QQuickItem *sep = ensureTrailingSeparator(footer);
519 child->
setSize(
QSizeF(width + sepWidth, height() - headerHeight - footerHeight));
520 child->setPosition(
QPointF(pageX, headerHeight));
523 if (partialWidth <= -x()) {
524 m_leftPinnedSpace = qMax(m_leftPinnedSpace, width);
525 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
526 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
529 partialWidth += width;
532 const qreal width = childWidth(child);
533 qreal headerHeight = .0;
534 qreal footerHeight = .0;
535 if (
QQuickItem *header = attached->globalHeader(); header && qmlEngine(header)) {
536 if (m_view->separatorVisible()) {
537 QQuickItem *sep = ensureLeadingSeparator(header);
542 header->setPosition(
QPointF(partialWidth, .0));
544 auto it = m_trailingSeparators.find(header);
545 if (it != m_trailingSeparators.end()) {
546 it.value()->deleteLater();
547 m_trailingSeparators.erase(it);
550 if (
QQuickItem *footer = attached->globalFooter(); footer && qmlEngine(footer)) {
551 if (m_view->separatorVisible()) {
552 QQuickItem *sep = ensureLeadingSeparator(footer);
557 footer->setPosition(
QPointF(partialWidth, height() - footerHeight));
559 auto it = m_trailingSeparators.find(footer);
560 if (it != m_trailingSeparators.end()) {
561 it.value()->deleteLater();
562 m_trailingSeparators.erase(it);
566 child->
setSize(
QSizeF(width, height() - headerHeight - footerHeight));
568 auto it = m_trailingSeparators.find(child);
569 if (it != m_trailingSeparators.end()) {
570 it.value()->deleteLater();
571 m_trailingSeparators.erase(it);
573 child->setPosition(
QPointF(partialWidth, headerHeight));
576 partialWidth += child->
width();
581 attached->setIndex(m_items.count() - (++i));
583 attached->setIndex(i++);
591 setWidth(partialWidth);
593 setImplicitWidth(implicitWidth);
594 setImplicitHeight(implicitHeight);
596 m_view->setImplicitWidth(implicitWidth);
597 m_view->setImplicitHeight(implicitHeight + m_view->topPadding() + m_view->bottomPadding());
599 const qreal newContentX = m_viewAnchorItem ? -m_viewAnchorItem->x() : 0.0;
600 if (m_shouldAnimate) {
601 animateX(newContentX);
603 setBoundedX(newContentX);
606 updateVisibleItems();
609void ContentItem::layoutPinnedItems()
611 if (m_view->columnResizeMode() == ColumnView::SingleColumn) {
615 qreal partialWidth = 0;
616 m_leftPinnedSpace = 0;
617 m_rightPinnedSpace = 0;
619 for (
QQuickItem *child : std::as_const(m_items)) {
620 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
623 if (attached->isPinned()) {
626 if (m_view->separatorVisible()) {
627 sep = ensureTrailingSeparator(child);
628 sepWidth = (sep ? sep->
width() : 0);
631 const qreal pageX = qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->
width() + sepWidth);
632 qreal headerHeight = .0;
633 qreal footerHeight = .0;
634 if (
QQuickItem *header = attached->globalHeader()) {
636 header->setPosition(
QPointF(pageX, .0));
637 if (m_view->separatorVisible()) {
638 QQuickItem *sep = ensureTrailingSeparator(header);
642 if (
QQuickItem *footer = attached->globalFooter()) {
644 footer->setPosition(
QPointF(pageX, height() - footerHeight));
645 if (m_view->separatorVisible()) {
646 QQuickItem *sep = ensureTrailingSeparator(footer);
650 child->setPosition(
QPointF(pageX, headerHeight));
652 if (partialWidth <= -x()) {
653 m_leftPinnedSpace = qMax(m_leftPinnedSpace, child->
width() - sepWidth);
654 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
655 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
659 partialWidth += child->
width();
664void ContentItem::updateVisibleItems()
668 for (
auto *item : std::as_const(m_items)) {
669 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
671 if (item->isVisible() && item->x() + x() < m_view->width() && item->x() + item->width() + x() > 0) {
674 m_visibleItems.removeAll(item);
676 attached->setInViewport(
true);
677 item->setEnabled(
true);
678 if (attached->globalHeader()) {
681 if (attached->globalFooter()) {
685 attached->setInViewport(
false);
686 item->setEnabled(
false);
687 if (attached->globalHeader()) {
690 if (attached->globalFooter()) {
696 for (
auto *item : std::as_const(m_visibleItems)) {
700 const QQuickItem *oldLeadingVisibleItem = m_view->leadingVisibleItem();
701 const QQuickItem *oldTrailingVisibleItem = m_view->trailingVisibleItem();
703 if (newItems != m_visibleItems) {
704 m_visibleItems = newItems;
705 Q_EMIT m_view->visibleItemsChanged();
706 if (!m_visibleItems.isEmpty() && m_visibleItems.first() != oldLeadingVisibleItem) {
707 Q_EMIT m_view->leadingVisibleItemChanged();
709 if (!m_visibleItems.isEmpty() && m_visibleItems.last() != oldTrailingVisibleItem) {
710 Q_EMIT m_view->trailingVisibleItemChanged();
717 if (!m_items.contains(item)) {
721 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
722 attached->setView(
nullptr);
723 attached->setIndex(-1);
725 disconnect(attached,
nullptr,
this,
nullptr);
726 disconnect(item,
nullptr,
this,
nullptr);
727 disconnect(item,
nullptr, m_view,
nullptr);
729 QQuickItem *separatorItem = m_leadingSeparators.take(item);
733 separatorItem = m_trailingSeparators.take(item);
738 if (
QQuickItem *header = attached->globalHeader()) {
741 separatorItem = m_leadingSeparators.take(header);
745 separatorItem = m_trailingSeparators.take(header);
750 if (
QQuickItem *footer = attached->globalFooter()) {
753 separatorItem = m_leadingSeparators.take(footer);
757 separatorItem = m_trailingSeparators.take(footer);
763 const int index = m_items.indexOf(item);
764 m_items.removeAll(item);
766 disconnect(item,
nullptr,
this,
nullptr);
767 updateVisibleItems();
768 m_shouldAnimate =
true;
771 if (index <= m_view->currentIndex()) {
772 m_view->setCurrentIndex(m_items.isEmpty() ? 0 : qBound(0, index - 1, m_items.count() - 1));
774 Q_EMIT m_view->countChanged();
779 QQuickItem *separatorItem = m_leadingSeparators.value(item);
781 if (!separatorItem) {
782 separatorItem = qobject_cast<QQuickItem *>(
787 separatorItem->
setZ(9999);
790 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_leadingSeparatorComponent->completeCreate();
791 m_leadingSeparators[item] = separatorItem;
795 return separatorItem;
800 QQuickItem *separatorItem = m_trailingSeparators.value(item);
802 if (!separatorItem) {
803 separatorItem = qobject_cast<QQuickItem *>(
808 separatorItem->
setZ(9999);
810 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_trailingSeparatorComponent->completeCreate();
811 m_trailingSeparators[item] = separatorItem;
815 return separatorItem;
820 if (m_creationInProgress) {
826 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(value.item,
true));
827 attached->setView(m_view);
830 connect(attached, &ColumnViewAttached::fillWidthChanged,
this, [
this] {
835 value.item->setVisible(
true);
837 if (!m_items.contains(value.item)) {
842 m_view->removeItem(item);
846 if (m_view->separatorVisible()) {
847 ensureLeadingSeparator(value.item);
850 m_shouldAnimate =
true;
852 Q_EMIT m_view->countChanged();
856 forgetItem(value.item);
860 updateVisibleItems();
861 if (value.boolValue) {
871void ContentItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
873 updateVisibleItems();
877void ContentItem::syncItemsOrder()
879 if (m_items == childItems()) {
883 m_items = childItems();
888void ContentItem::updateRepeaterModel()
897 m_models.remove(sender());
901 if (m_models[sender()]) {
902 disconnect(m_models[sender()],
nullptr,
this,
nullptr);
905 m_models[sender()] = modelObj;
913 connect(modelObj, SIGNAL(childrenChanged()),
this, SLOT(syncItemsOrder()));
920 disconnect(oldHeader,
nullptr,
this,
nullptr);
933 disconnect(oldFooter,
nullptr,
this,
nullptr);
945 , m_contentItem(nullptr)
948 m_contentItem =
new ContentItem(
this);
952 setAcceptTouchEvents(
false);
953 setFiltersChildMouseEvents(
true);
957 Q_EMIT movingChanged();
959 connect(m_contentItem, &ContentItem::widthChanged,
this, &ColumnView::contentWidthChanged);
960 connect(m_contentItem, &ContentItem::xChanged,
this, &ColumnView::contentXChanged);
963 if (hasActiveFocus() && m_currentItem) {
964 m_currentItem->forceActiveFocus();
967 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(
this,
true));
968 attached->setView(
this);
969 attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(m_contentItem,
true));
970 attached->setView(
this);
973ColumnView::~ColumnView()
979 return m_contentItem->m_columnResizeMode;
982void ColumnView::setColumnResizeMode(ColumnResizeMode mode)
984 if (m_contentItem->m_columnResizeMode == mode) {
988 m_contentItem->m_columnResizeMode = mode;
989 if (mode == SingleColumn && m_currentItem) {
990 m_contentItem->m_viewAnchorItem = m_currentItem;
992 m_contentItem->m_shouldAnimate =
false;
994 Q_EMIT columnResizeModeChanged();
999 return m_contentItem->m_columnWidth;
1002void ColumnView::setColumnWidth(qreal width)
1005 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this,
nullptr);
1007 if (m_contentItem->m_columnWidth ==
width) {
1011 m_contentItem->m_columnWidth =
width;
1012 m_contentItem->m_shouldAnimate =
false;
1014 Q_EMIT columnWidthChanged();
1019 return m_currentIndex;
1022void ColumnView::setCurrentIndex(
int index)
1024 if (m_currentIndex == index || index < -1 || index >= m_contentItem->m_items.count()) {
1028 m_currentIndex = index;
1031 m_currentItem.clear();
1034 m_currentItem = m_contentItem->m_items[index];
1035 Q_ASSERT(m_currentItem);
1036 m_currentItem->forceActiveFocus();
1039 QRectF mappedCurrent = m_currentItem->mapRectToItem(
this, QRectF(QPointF(0, 0), m_currentItem->size()));
1042 mappedCurrent.
moveLeft(mappedCurrent.
left() + m_contentItem->x() + m_contentItem->m_slideAnim->endValue().toInt());
1047 QRectF contentsRect(m_contentItem->m_leftPinnedSpace,
1049 width() - m_contentItem->m_rightPinnedSpace - m_contentItem->m_leftPinnedSpace,
1053 if (!contentsRect.contains(mappedCurrent)) {
1054 m_contentItem->m_viewAnchorItem = m_currentItem;
1056 m_contentItem->animateX(-m_currentItem->x() - m_currentItem->width() +
width());
1058 m_contentItem->animateX(-m_currentItem->x() + m_contentItem->m_leftPinnedSpace);
1061 m_contentItem->snapToItem();
1066 Q_EMIT currentIndexChanged();
1067 Q_EMIT currentItemChanged();
1072 return m_currentItem;
1077 return m_contentItem->m_visibleItems;
1082 if (m_contentItem->m_visibleItems.isEmpty()) {
1086 return m_contentItem->m_visibleItems.first();
1091 if (m_contentItem->m_visibleItems.isEmpty()) {
1100 return m_contentItem->m_items.count();
1105 return m_topPadding;
1108void ColumnView::setTopPadding(qreal padding)
1110 if (padding == m_topPadding) {
1114 m_topPadding = padding;
1116 Q_EMIT topPaddingChanged();
1121 return m_bottomPadding;
1124void ColumnView::setBottomPadding(qreal padding)
1126 if (padding == m_bottomPadding) {
1130 m_bottomPadding = padding;
1132 Q_EMIT bottomPaddingChanged();
1137 return m_contentItem;
1142 return m_contentItem->m_slideAnim->duration();
1145void ColumnView::setScrollDuration(
int duration)
1147 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this,
nullptr);
1149 if (m_contentItem->m_slideAnim->duration() == duration) {
1153 m_contentItem->m_slideAnim->setDuration(duration);
1154 Q_EMIT scrollDurationChanged();
1159 return m_separatorVisible;
1162void ColumnView::setSeparatorVisible(
bool visible)
1164 if (
visible == m_separatorVisible) {
1170 Q_EMIT separatorVisibleChanged();
1185 return m_contentItem->width();
1190 return -m_contentItem->x();
1193void ColumnView::setContentX(qreal x)
const
1195 m_contentItem->setX(qRound(-
x));
1200 return m_interactive;
1203void ColumnView::setInteractive(
bool interactive)
1211 if (!m_interactive) {
1214 Q_EMIT draggingChanged();
1217 m_contentItem->snapToItem();
1221 Q_EMIT interactiveChanged();
1226 return m_acceptsMouse;
1229void ColumnView::setAcceptsMouse(
bool accepts)
1231 if (m_acceptsMouse == accepts) {
1235 m_acceptsMouse = accepts;
1237 if (!m_acceptsMouse) {
1240 Q_EMIT draggingChanged();
1243 m_contentItem->snapToItem();
1247 Q_EMIT acceptsMouseChanged();
1252 insertItem(m_contentItem->m_items.length(), item);
1257 if (!item || m_contentItem->m_items.
contains(item)) {
1261 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1267 attached->setOriginalParent(item->
parentItem());
1274 if (attached->globalHeader()) {
1275 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1277 if (attached->globalFooter()) {
1278 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1280 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1281 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1284 m_contentItem->m_shouldAnimate =
true;
1285 m_contentItem->layoutItems();
1286 Q_EMIT contentChildrenChanged();
1290 if (m_currentIndex >= pos) {
1292 Q_EMIT currentIndexChanged();
1300 if (pos < 0 || pos >= m_contentItem->m_items.length()) {
1301 qCWarning(KirigamiLayoutsLog) <<
"Position" << pos <<
"passed to ColumnView::replaceItem is out of range.";
1306 qCWarning(KirigamiLayoutsLog) <<
"Null item passed to ColumnView::replaceItem.";
1310 QQuickItem *oldItem = m_contentItem->m_items[pos];
1313 if (m_currentIndex >= pos) {
1314 setCurrentIndex(m_currentIndex - 1);
1317 m_contentItem->forgetItem(oldItem);
1322 if (attached && attached->shouldDeleteOnRemove()) {
1325 oldItem->
setParentItem(attached ? attached->originalParent() :
nullptr);
1330 if (!m_contentItem->m_items.contains(item)) {
1331 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1337 attached->setOriginalParent(item->
parentItem());
1342 if (attached->globalHeader()) {
1343 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1345 if (attached->globalFooter()) {
1346 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1348 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1349 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1351 if (m_currentIndex >= pos) {
1353 Q_EMIT currentIndexChanged();
1360 m_contentItem->m_shouldAnimate =
false;
1361 m_contentItem->layoutItems();
1362 Q_EMIT contentChildrenChanged();
1367 if (m_contentItem->m_items.isEmpty()
1368 || from < 0 || from >= m_contentItem->m_items.length()
1369 || to < 0 || to >= m_contentItem->m_items.length()) {
1373 m_contentItem->m_items.move(from, to);
1374 m_contentItem->m_shouldAnimate =
true;
1376 if (from == m_currentIndex) {
1377 m_currentIndex = to;
1378 Q_EMIT currentIndexChanged();
1379 }
else if (from < m_currentIndex && to > m_currentIndex) {
1381 Q_EMIT currentIndexChanged();
1382 }
else if (from > m_currentIndex && to <= m_currentIndex) {
1384 Q_EMIT currentIndexChanged();
1392 if (m_contentItem->m_items.isEmpty() || !m_contentItem->m_items.contains(item)) {
1396 const int index = m_contentItem->m_items.indexOf(item);
1399 if (m_currentIndex >= index) {
1400 setCurrentIndex(m_currentIndex - 1);
1403 m_contentItem->forgetItem(item);
1408 if (attached && attached->shouldDeleteOnRemove()) {
1411 item->
setParentItem(attached ? attached->originalParent() :
nullptr);
1414 Q_EMIT contentChildrenChanged();
1422 if (m_contentItem->m_items.isEmpty() || index < 0 || index >=
count()) {
1425 return removeItem(m_contentItem->m_items[index]);
1446 }
else if (item.
isNull()) {
1455 while (!m_contentItem->m_items.isEmpty() && m_contentItem->m_items.last() != item) {
1456 removed =
removeItem(m_contentItem->m_items.last());
1463 if (index >= 0 && index <
count() - 1) {
1464 return pop(m_contentItem->m_items.at(index));
1465 }
else if (index == -1) {
1466 return pop(
nullptr);
1482 while (!m_contentItem->m_items.isEmpty()) {
1483 QQuickItem *item = m_contentItem->m_items.first();
1487 m_contentItem->m_items.clear();
1488 Q_EMIT contentChildrenChanged();
1493 return m_contentItem->m_items.contains(item);
1506void ColumnView::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1508 m_contentItem->setY(m_topPadding);
1509 m_contentItem->setHeight(newGeometry.
height() - m_topPadding - m_bottomPadding);
1510 m_contentItem->m_shouldAnimate =
false;
1513 m_contentItem->updateVisibleItems();
1519 if (!m_interactive || item == m_contentItem) {
1523 switch (
event->type()) {
1525 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1536 if (
int idx = m_contentItem->m_items.indexOf(candidateItem); idx >= 0 && candidateItem->
parentItem() == m_contentItem) {
1537 setCurrentIndex(idx);
1542 event->setAccepted(
false);
1546 m_contentItem->m_slideAnim->stop();
1548 m_contentItem->snapToItem();
1561 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1583 bool verticalScrollIntercepted =
false;
1589 if (candidateItem->
parentItem() == m_contentItem) {
1599 ScrollIntentionEvent scrollIntentionEvent;
1600 scrollIntentionEvent.delta = QPointF(pos.
x() - m_oldMouseX, pos.
y() - m_oldMouseY);
1602 Q_EMIT attached->scrollIntention(&scrollIntentionEvent);
1604 if (scrollIntentionEvent.accepted) {
1605 verticalScrollIntercepted =
true;
1606 event->setAccepted(
true);
1611 m_contentItem->snapToItem();
1612 m_oldMouseX = pos.
x();
1613 m_oldMouseY = pos.
y();
1617 const bool wasDragging = m_dragging;
1621 if (m_dragging != wasDragging) {
1624 Q_EMIT draggingChanged();
1628 m_contentItem->setBoundedX(m_contentItem->x() + pos.
x() - m_oldMouseX);
1631 m_contentItem->m_lastDragDelta = pos.
x() - m_oldMouseX;
1632 m_oldMouseX = pos.
x();
1633 m_oldMouseY = pos.
y();
1638 return m_dragging && !verticalScrollIntercepted;
1641 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1647 setCurrentIndex(m_currentIndex - 1);
1651 setCurrentIndex(m_currentIndex + 1);
1664 m_mouseDown =
false;
1667 m_contentItem->snapToItem();
1668 m_contentItem->m_lastDragDelta = 0;
1670 Q_EMIT draggingChanged();
1689void ColumnView::mousePressEvent(
QMouseEvent *event)
1692 event->setAccepted(
false);
1701 if (!m_interactive) {
1705 m_contentItem->snapToItem();
1706 m_oldMouseX =
event->position().x();
1707 m_startMouseX =
event->position().x();
1713void ColumnView::mouseMoveEvent(
QMouseEvent *event)
1720 if (!m_interactive) {
1724 const bool wasDragging = m_dragging;
1726 m_dragging =
keepMouseGrab() || qAbs(
event->position().x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 2;
1727 if (m_dragging != wasDragging) {
1730 Q_EMIT draggingChanged();
1736 m_contentItem->setBoundedX(m_contentItem->x() +
event->pos().x() - m_oldMouseX);
1739 m_contentItem->m_lastDragDelta =
event->pos().x() - m_oldMouseX;
1740 m_oldMouseX =
event->pos().x();
1744void ColumnView::mouseReleaseEvent(
QMouseEvent *event)
1747 setCurrentIndex(m_currentIndex - 1);
1751 setCurrentIndex(m_currentIndex + 1);
1756 m_mouseDown =
false;
1758 if (!m_interactive) {
1762 m_contentItem->snapToItem();
1763 m_contentItem->m_lastDragDelta = 0;
1767 Q_EMIT draggingChanged();
1774void ColumnView::mouseUngrabEvent()
1776 m_mouseDown =
false;
1779 m_contentItem->snapToItem();
1781 m_contentItem->m_lastDragDelta = 0;
1785 Q_EMIT draggingChanged();
1791void ColumnView::classBegin()
1793 auto syncColumnWidth = [
this]() {
1794 m_contentItem->m_columnWidth = privateQmlComponentsPoolSelf->instance(qmlEngine(
this))->m_units->gridUnit() * 20;
1795 Q_EMIT columnWidthChanged();
1798 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this, syncColumnWidth);
1801 auto syncDuration = [
this]() {
1802 m_contentItem->m_slideAnim->setDuration(QmlComponentsPoolSingleton::instance(qmlEngine(
this))->m_units->veryLongDuration());
1803 Q_EMIT scrollDurationChanged();
1806 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this, syncDuration);
1812void ColumnView::componentComplete()
1818void ColumnView::updatePolish()
1820 m_contentItem->layoutItems();
1827 if (m_contentItem && value.item != m_contentItem && !value.item->inherits(
"QQuickRepeater")) {
1840 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1845 view->m_contentItem->m_items.append(item);
1847 view->removeItem(item);
1851 attached->setOriginalParent(item->
parentItem());
1859 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1864 return view->m_contentItem->m_items.count();
1869 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1874 if (index < 0 || index >= view->m_contentItem->m_items.count()) {
1877 return view->m_contentItem->m_items.value(index);
1882 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1887 return view->m_contentItem->m_items.clear();
1892 return QQmlListProperty<QQuickItem>(
this,
1894 contentChildren_append,
1895 contentChildren_count,
1897 contentChildren_clear);
1902 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1907 view->m_contentData.
append(
object);
1910 if (item && item->
inherits(
"QQuickRepeater")) {
1913 connect(item, SIGNAL(modelChanged()), view->m_contentItem, SLOT(updateRepeaterModel()));
1916 view->m_contentItem->m_items.append(item);
1918 view->removeItem(item);
1922 attached->setOriginalParent(item->
parentItem());
1928 object->setParent(view);
1934 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1939 return view->m_contentData.
count();
1944 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1949 if (index < 0 || index >= view->m_contentData.
count()) {
1952 return view->m_contentData.
value(index);
1957 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1962 return view->m_contentData.
clear();
1967 return QQmlListProperty<QObject>(
this,
1975#include "moc_columnview.cpp"
1976#include "moc_columnview_p.cpp"
This is an attached property to every item that is inserted in the ColumnView, used to access the vie...
qreal reservedSpace
When a column is fillWidth, it will keep reservedSpace amount of pixels from going to fill the full v...
bool preventStealing
Like the same property of MouseArea, when this is true, the column view won't try to manage events by...
bool inViewport
True if this column is at least partly visible in the ColumnView's viewport.
bool pinned
If true the page will never go out of view, but will stay either at the right or left side of the Col...
ColumnView * view
The view this column belongs to.
bool fillWidth
If true, the column will expand to take the whole viewport space minus reservedSpace.
int index
The index position of the column in the view, starting from 0.
ColumnView is a container that lays out items horizontally in a row, when not all items fit in the Co...
QQuickItem * removeItem(QQuickItem *item)
This method removes the specified item from the view.
QQmlListProperty< QQuickItem > contentChildren
Every column item the view contains.
bool moving
True both when the user is dragging around with touch gestures the view contents or the view is anima...
QQmlListProperty< QObject > contentData
every item declared inside the view, both visual and non-visual items
void itemRemoved(QQuickItem *item)
An item has just been removed from the view.
QQuickItem * contentItem
The main content item of this view: it's the parent of the column items.
int currentIndex
The position of the currently active column.
void replaceItem(int pos, QQuickItem *item)
Replaces an item in the view at a given position with a new item.
QQuickItem * trailingVisibleItem
The last of visibleItems provided from convenience.
qreal topPadding
The padding this will have at the top.
int count
How many columns this view containsItem.
void addItem(QQuickItem *item)
Pushes a new item at the end of the view.
QQuickItem * itemAt(qreal x, qreal y)
Returns the visible item containing the point x, y in content coordinates.
bool containsItem(QQuickItem *item)
void clear()
Removes every item in the view.
qreal columnWidth
The width of all columns when columnResizeMode is FixedColumns.
QList< QQuickItem * > visibleItems
The list of all visible column items that are at least partially in the viewport at any given moment.
Q_INVOKABLE QQuickItem * pop()
This method removes the last item from the view and returns it.
QQuickItem * currentItem
The currently active column.
qreal contentX
The value of the horizontal scroll of the view, in pixels.
qreal bottomPadding
The padding this will have at the bottom.
QML_ELEMENTColumnResizeMode columnResizeMode
The strategy to follow while automatically resizing the columns, the enum can have the following valu...
int scrollDuration
The duration for scrolling animations.
bool dragging
True when the user is dragging around with touch gestures the view contents.
qreal contentWidth
The compound width of all columns in the view.
void moveItem(int from, int to)
Move an item inside the view.
bool acceptsMouse
True if the contents can be dragged also with mouse besides touch.
QQuickItem * leadingVisibleItem
The first of visibleItems provided from convenience.
void insertItem(int pos, QQuickItem *item)
Inserts a new item in the view at a given position.
bool interactive
True if it supports moving the contents by dragging.
bool separatorVisible
True if columns should be visually separated by a separator line.
void itemInserted(int position, QQuickItem *item)
A new item has been inserted.
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
void rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
ObjectOwnership objectOwnership(QObject *object)
void append(QList< T > &&value)
qsizetype count() const const
T value(qsizetype i) const const
Qt::MouseEventSource source() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
bool inherits(const char *className) const const
QObject * parent() const const
QVariant property(const char *name) const const
T qobject_cast(QObject *object)
void setParent(QObject *parent)
bool setProperty(const char *name, QVariant &&value)
virtual void setAccepted(bool accepted) override
QQmlContext * contextForObject(const QObject *object)
T singletonInstance(QAnyStringView uri, QAnyStringView typeName)
QQuickItem(QQuickItem *parent)
void activeFocusChanged(bool)
QQuickItem * childAt(qreal x, qreal y) const const
virtual bool childMouseEventFilter(QQuickItem *item, QEvent *event)
virtual void classBegin() override
virtual void componentComplete() override
virtual bool contains(const QPointF &point) const const
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
virtual void itemChange(ItemChange change, const ItemChangeData &value)
bool keepMouseGrab() const const
bool keepTouchGrab() const const
QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const const
void setParentItem(QQuickItem *parent)
void setKeepMouseGrab(bool keep)
void setSize(const QSizeF &size)
bool isVisible() const const
qreal height() const const
bool intersects(const QRectF &rectangle) const const
QPointF position() const const
QFuture< QtPrivate::MapResultType< Iterator, MapFunctor > > mapped(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool canConvert() const const
QVariant fromValue(T &&value)
bool isNull() const const
bool toBool() const const
int toInt(bool *ok) const const