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);
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)
65 component.setData(QByteArrayLiteral(R
"(
67import org.kde.kirigami as Kirigami
70 readonly property Component leadingSeparator: Kirigami.Separator {
72 property bool inToolBar
73 property Kirigami.ColumnView view
75 // positioning trick to hide the very first separator
77 if (!view || !view.separatorVisible) {
81 return view && (LayoutMirroring.enabled
82 ? view.contentX + view.width > column.x + column.width
83 : view.contentX < column.x);
86 anchors.top: column.top
87 anchors.left: column.left
88 anchors.bottom: column.bottom
89 anchors.topMargin: inToolBar ? Kirigami.Units.largeSpacing : 0
90 anchors.bottomMargin: inToolBar ? Kirigami.Units.largeSpacing : 0
91 Kirigami.Theme.colorSet: Kirigami.Theme.Header
92 Kirigami.Theme.inherit: false
95 readonly property Component trailingSeparator: Kirigami.Separator {
98 anchors.top: column.top
99 anchors.right: column.right
100 anchors.bottom: column.bottom
101 Kirigami.Theme.colorSet: Kirigami.Theme.Header
102 Kirigami.Theme.inherit: false
105)"), QUrl(QStringLiteral("columnview.cpp")));
108 m_instance = component.create();
110 Q_ASSERT(m_instance);
111 m_instance->setParent(
this);
113 m_leadingSeparatorComponent = m_instance->property(
"leadingSeparator").value<
QQmlComponent *>();
114 Q_ASSERT(m_leadingSeparatorComponent);
116 m_trailingSeparatorComponent = m_instance->property(
"trailingSeparator").value<
QQmlComponent *>();
117 Q_ASSERT(m_trailingSeparatorComponent);
122 connect(m_units, &Kirigami::Platform::Units::gridUnitChanged,
this, &QmlComponentsPool::gridUnitChanged);
123 connect(m_units, &Kirigami::Platform::Units::longDurationChanged,
this, &QmlComponentsPool::longDurationChanged);
126QmlComponentsPool::~QmlComponentsPool()
132ColumnViewAttached::ColumnViewAttached(
QObject *parent)
137ColumnViewAttached::~ColumnViewAttached()
141void ColumnViewAttached::setIndex(
int index)
143 if (!m_customFillWidth && m_view) {
144 const bool oldFillWidth = m_fillWidth;
145 m_fillWidth =
index == m_view->count() - 1;
146 if (oldFillWidth != m_fillWidth) {
147 Q_EMIT fillWidthChanged();
151 if (
index == m_index) {
164void ColumnViewAttached::setFillWidth(
bool fill)
167 disconnect(m_view.
data(), &ColumnView::countChanged,
this,
nullptr);
169 m_customFillWidth =
true;
171 if (fill == m_fillWidth) {
176 Q_EMIT fillWidthChanged();
190 return m_reservedSpace;
193void ColumnViewAttached::setReservedSpace(qreal space)
196 disconnect(m_view.
data(), &ColumnView::columnWidthChanged,
this,
nullptr);
198 m_customReservedSpace =
true;
200 if (qFuzzyCompare(space, m_reservedSpace)) {
204 m_reservedSpace = space;
205 Q_EMIT reservedSpaceChanged();
217void ColumnViewAttached::setView(
ColumnView *view)
219 if (
view == m_view) {
228 if (!m_customFillWidth && m_view) {
229 m_fillWidth = m_index == m_view->count() - 1;
230 connect(m_view.
data(), &ColumnView::countChanged,
this, [
this]() {
231 m_fillWidth = m_index == m_view->count() - 1;
232 Q_EMIT fillWidthChanged();
235 if (!m_customReservedSpace && m_view) {
236 m_reservedSpace = m_view->columnWidth();
237 connect(m_view.
data(), &ColumnView::columnWidthChanged,
this, [
this]() {
238 m_reservedSpace = m_view->columnWidth();
239 Q_EMIT reservedSpaceChanged();
246QQuickItem *ColumnViewAttached::originalParent()
const
248 return m_originalParent;
251void ColumnViewAttached::setOriginalParent(
QQuickItem *parent)
253 m_originalParent =
parent;
256bool ColumnViewAttached::shouldDeleteOnRemove()
const
258 return m_shouldDeleteOnRemove;
261void ColumnViewAttached::setShouldDeleteOnRemove(
bool del)
263 m_shouldDeleteOnRemove =
del;
268 return m_preventStealing;
271void ColumnViewAttached::setPreventStealing(
bool prevent)
273 if (prevent == m_preventStealing) {
277 m_preventStealing = prevent;
278 Q_EMIT preventStealingChanged();
281bool ColumnViewAttached::isPinned()
const
286void ColumnViewAttached::setPinned(
bool pinned)
306void ColumnViewAttached::setInViewport(
bool inViewport)
314 Q_EMIT inViewportChanged();
317QQuickItem *ColumnViewAttached::globalHeader()
const
319 return m_globalHeader;
322void ColumnViewAttached::setGlobalHeader(
QQuickItem *header)
324 if (header == m_globalHeader) {
329 if (m_globalHeader) {
330 disconnect(m_globalHeader,
nullptr,
this,
nullptr);
333 m_globalHeader = header;
336 globalHeaderChanged(header,
nullptr);
339 Q_EMIT globalHeaderChanged(oldHeader, header);
342QQuickItem *ColumnViewAttached::globalFooter()
const
344 return m_globalFooter;
347void ColumnViewAttached::setGlobalFooter(
QQuickItem *footer)
349 if (footer == m_globalFooter) {
354 if (m_globalFooter) {
355 disconnect(m_globalFooter,
nullptr,
this,
nullptr);
358 m_globalFooter = footer;
361 globalFooterChanged(footer,
nullptr);
364 Q_EMIT globalFooterChanged(oldFooter, footer);
376 setFlags(flags() | ItemIsFocusScope);
378 m_slideAnim->setTargetObject(
this);
379 m_slideAnim->setPropertyName(
"x");
381 m_slideAnim->setDuration(0);
384 if (!m_view->currentItem()) {
385 m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem));
389 m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem));
395 m_creationInProgress =
false;
398ContentItem::~ContentItem()
402void ContentItem::setBoundedX(qreal x)
408 setX(qRound(qBound(qMin(0.0, -width() + parentItem()->width()), x, 0.0)));
411void ContentItem::animateX(qreal newX)
417 const qreal to = qRound(qBound(qMin(0.0, -width() + parentItem()->width()), newX, 0.0));
420 m_slideAnim->setStartValue(x());
421 m_slideAnim->setEndValue(to);
422 m_slideAnim->start();
425void ContentItem::snapToItem()
427 QQuickItem *firstItem = childAt(viewportLeft(), height() / 2);
431 QQuickItem *nextItem = childAt(firstItem->
x() + firstItem->
width() + 1, height() / 2);
435 ((m_view->dragging() && m_lastDragDelta < 0)
436 || (!m_view->dragging()
437 && (width() - viewportRight()) < (viewportLeft() - firstItem->
x())))) {
438 m_viewAnchorItem = nextItem;
439 animateX(-nextItem->
x() + m_leftPinnedSpace);
442 }
else if ((m_view->dragging() && m_lastDragDelta >= 0)
443 || (!m_view->dragging() && (viewportLeft() <= (firstItem->
x() + (firstItem->
width() / 2))))
445 m_viewAnchorItem = firstItem;
446 animateX(-firstItem->
x() + m_leftPinnedSpace);
450 m_viewAnchorItem = nextItem;
451 animateX(-nextItem->
x() + m_leftPinnedSpace);
455qreal ContentItem::viewportLeft()
const
457 return -x() + m_leftPinnedSpace;
460qreal ContentItem::viewportRight()
const
462 return -x() + m_view->width() - m_rightPinnedSpace;
465qreal ContentItem::childWidth(
QQuickItem *child)
471 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
473 if (m_columnResizeMode == ColumnView::SingleColumn) {
474 return qRound(parentItem()->width());
477 return qRound(qBound(m_columnWidth, (parentItem()->width() - attached->
reservedSpace()), std::max(m_columnWidth, parentItem()->width())));
479 }
else if (m_columnResizeMode == ColumnView::FixedColumns) {
480 return qRound(qMin(parentItem()->width(), m_columnWidth));
488 width = m_columnWidth;
491 return qRound(qMin(m_view->width(), width));
495void ContentItem::layoutItems()
497 setY(m_view->topPadding());
498 setHeight(m_view->height() - m_view->topPadding() - m_view->bottomPadding());
500 qreal implicitWidth = 0;
501 qreal implicitHeight = 0;
502 qreal partialWidth = 0;
504 m_leftPinnedSpace = 0;
505 m_rightPinnedSpace = 0;
508 auto it = !reverse ? m_items.begin() : m_items.end();
509 int increment = reverse ? -1 : +1;
510 auto lastPos = reverse ? m_items.begin() : m_items.end();
512 for (; it != lastPos; it += increment) {
514 QQuickItem *child = reverse ? *(it - 1) : *it;
515 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
516 if (child == m_globalHeaderParent || child == m_globalFooterParent) {
521 if (attached->isPinned() && m_view->columnResizeMode() != ColumnView::SingleColumn) {
524 if (m_view->separatorVisible()) {
525 sep = ensureTrailingSeparator(child);
526 sepWidth = (sep ? sep->
width() : 0);
528 const qreal width = childWidth(child);
529 const qreal widthDiff = std::max(0.0, m_view->width() - child->
width());
530 const qreal pageX = std::clamp(partialWidth, -x(), -x() + widthDiff);
531 qreal headerHeight = .0;
532 qreal footerHeight = .0;
533 if (
QQuickItem *header = attached->globalHeader()) {
536 header->setPosition(
QPointF(pageX, .0));
538 if (m_view->separatorVisible()) {
539 QQuickItem *sep = ensureTrailingSeparator(header);
543 if (
QQuickItem *footer = attached->globalFooter()) {
546 footer->setPosition(
QPointF(pageX, height() - footerHeight));
548 if (m_view->separatorVisible()) {
549 QQuickItem *sep = ensureTrailingSeparator(footer);
554 child->
setSize(
QSizeF(width + sepWidth, height() - headerHeight - footerHeight));
555 child->setPosition(
QPointF(pageX, headerHeight));
558 if (partialWidth <= -x()) {
559 m_leftPinnedSpace = qMax(m_leftPinnedSpace, width);
560 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
561 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
564 partialWidth += width;
567 const qreal width = childWidth(child);
568 qreal headerHeight = .0;
569 qreal footerHeight = .0;
570 if (
QQuickItem *header = attached->globalHeader(); header && qmlEngine(header)) {
571 if (m_view->separatorVisible()) {
572 QQuickItem *sep = ensureLeadingSeparator(header);
577 header->setPosition(
QPointF(partialWidth, .0));
579 auto it = m_trailingSeparators.find(header);
580 if (it != m_trailingSeparators.end()) {
581 it.value()->deleteLater();
582 m_trailingSeparators.erase(it);
585 if (
QQuickItem *footer = attached->globalFooter(); footer && qmlEngine(footer)) {
586 if (m_view->separatorVisible()) {
587 QQuickItem *sep = ensureLeadingSeparator(footer);
592 footer->setPosition(
QPointF(partialWidth, height() - footerHeight));
594 auto it = m_trailingSeparators.find(footer);
595 if (it != m_trailingSeparators.end()) {
596 it.value()->deleteLater();
597 m_trailingSeparators.erase(it);
601 child->
setSize(
QSizeF(width, height() - headerHeight - footerHeight));
603 auto it = m_trailingSeparators.find(child);
604 if (it != m_trailingSeparators.end()) {
605 it.value()->deleteLater();
606 m_trailingSeparators.erase(it);
608 child->setPosition(
QPointF(partialWidth, headerHeight));
611 partialWidth += child->
width();
616 attached->setIndex(m_items.count() - (++i));
618 attached->setIndex(i++);
626 setWidth(partialWidth);
628 setImplicitWidth(implicitWidth);
629 setImplicitHeight(implicitHeight);
631 m_view->setImplicitWidth(implicitWidth);
632 m_view->setImplicitHeight(implicitHeight + m_view->topPadding() + m_view->bottomPadding());
634 const qreal newContentX = m_viewAnchorItem ? -m_viewAnchorItem->x() : 0.0;
635 if (m_shouldAnimate) {
636 animateX(newContentX);
638 setBoundedX(newContentX);
641 updateVisibleItems();
644void ContentItem::layoutPinnedItems()
646 if (m_view->columnResizeMode() == ColumnView::SingleColumn) {
650 qreal partialWidth = 0;
651 m_leftPinnedSpace = 0;
652 m_rightPinnedSpace = 0;
654 for (
QQuickItem *child : std::as_const(m_items)) {
655 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
658 if (attached->isPinned()) {
661 if (m_view->separatorVisible()) {
662 sep = ensureTrailingSeparator(child);
663 sepWidth = (sep ? sep->
width() : 0);
666 const qreal pageX = qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->
width() + sepWidth);
667 qreal headerHeight = .0;
668 qreal footerHeight = .0;
669 if (
QQuickItem *header = attached->globalHeader()) {
671 header->setPosition(
QPointF(pageX, .0));
672 if (m_view->separatorVisible()) {
673 QQuickItem *sep = ensureTrailingSeparator(header);
677 if (
QQuickItem *footer = attached->globalFooter()) {
679 footer->setPosition(
QPointF(pageX, height() - footerHeight));
680 if (m_view->separatorVisible()) {
681 QQuickItem *sep = ensureTrailingSeparator(footer);
685 child->setPosition(
QPointF(pageX, headerHeight));
687 if (partialWidth <= -x()) {
688 m_leftPinnedSpace = qMax(m_leftPinnedSpace, child->
width() - sepWidth);
689 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
690 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
694 partialWidth += child->
width();
699void ContentItem::updateVisibleItems()
703 for (
auto *item : std::as_const(m_items)) {
704 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
706 if (item->isVisible() && item->x() + x() < m_view->width() && item->x() + item->width() + x() > 0) {
709 m_visibleItems.removeAll(item);
711 attached->setInViewport(
true);
712 item->setEnabled(
true);
714 attached->setInViewport(
false);
715 item->setEnabled(
false);
719 for (
auto *item : std::as_const(m_visibleItems)) {
723 const QQuickItem *oldLeadingVisibleItem = m_view->leadingVisibleItem();
724 const QQuickItem *oldTrailingVisibleItem = m_view->trailingVisibleItem();
726 if (newItems != m_visibleItems) {
727 m_visibleItems = newItems;
728 Q_EMIT m_view->visibleItemsChanged();
729 if (!m_visibleItems.isEmpty() && m_visibleItems.first() != oldLeadingVisibleItem) {
730 Q_EMIT m_view->leadingVisibleItemChanged();
732 if (!m_visibleItems.isEmpty() && m_visibleItems.last() != oldTrailingVisibleItem) {
733 Q_EMIT m_view->trailingVisibleItemChanged();
740 if (!m_items.contains(item)) {
744 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
745 attached->setView(
nullptr);
746 attached->setIndex(-1);
748 disconnect(attached,
nullptr,
this,
nullptr);
749 disconnect(item,
nullptr,
this,
nullptr);
750 disconnect(item,
nullptr, m_view,
nullptr);
752 QQuickItem *separatorItem = m_leadingSeparators.take(item);
756 separatorItem = m_trailingSeparators.take(item);
761 if (
QQuickItem *header = attached->globalHeader()) {
764 separatorItem = m_leadingSeparators.take(header);
768 separatorItem = m_trailingSeparators.take(header);
773 if (
QQuickItem *footer = attached->globalFooter()) {
776 separatorItem = m_leadingSeparators.take(footer);
780 separatorItem = m_trailingSeparators.take(footer);
786 const int index = m_items.indexOf(item);
787 m_items.removeAll(item);
789 disconnect(item,
nullptr,
this,
nullptr);
790 updateVisibleItems();
791 m_shouldAnimate =
true;
794 if (index <= m_view->currentIndex()) {
795 m_view->setCurrentIndex(m_items.isEmpty() ? 0 : qBound(0, index - 1, m_items.count() - 1));
797 Q_EMIT m_view->countChanged();
802 QQuickItem *separatorItem = m_leadingSeparators.value(item);
804 if (!separatorItem) {
805 separatorItem = qobject_cast<QQuickItem *>(
810 separatorItem->
setZ(9999);
813 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_leadingSeparatorComponent->completeCreate();
814 m_leadingSeparators[item] = separatorItem;
818 return separatorItem;
823 QQuickItem *separatorItem = m_trailingSeparators.value(item);
825 if (!separatorItem) {
826 separatorItem = qobject_cast<QQuickItem *>(
831 separatorItem->
setZ(9999);
833 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_trailingSeparatorComponent->completeCreate();
834 m_trailingSeparators[item] = separatorItem;
838 return separatorItem;
843 if (m_creationInProgress) {
849 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(value.item,
true));
850 attached->setView(m_view);
853 connect(attached, &ColumnViewAttached::fillWidthChanged,
this, [
this] {
858 value.item->setVisible(
true);
860 if (!m_items.contains(value.item)) {
865 m_view->removeItem(item);
869 if (m_view->separatorVisible()) {
870 ensureLeadingSeparator(value.item);
873 m_shouldAnimate =
true;
875 Q_EMIT m_view->countChanged();
879 forgetItem(value.item);
883 updateVisibleItems();
884 if (value.boolValue) {
894void ContentItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
896 updateVisibleItems();
900void ContentItem::syncItemsOrder()
902 if (m_items == childItems()) {
906 m_items = childItems();
911void ContentItem::updateRepeaterModel()
920 m_models.remove(sender());
924 if (m_models[sender()]) {
925 disconnect(m_models[sender()],
nullptr,
this,
nullptr);
928 m_models[sender()] = modelObj;
936 connect(modelObj, SIGNAL(childrenChanged()),
this, SLOT(syncItemsOrder()));
943 disconnect(oldHeader,
nullptr,
this,
nullptr);
956 disconnect(oldFooter,
nullptr,
this,
nullptr);
968 , m_contentItem(nullptr)
971 m_contentItem =
new ContentItem(
this);
975 setAcceptTouchEvents(
false);
976 setFiltersChildMouseEvents(
true);
980 Q_EMIT movingChanged();
982 connect(m_contentItem, &ContentItem::widthChanged,
this, &ColumnView::contentWidthChanged);
983 connect(m_contentItem, &ContentItem::xChanged,
this, &ColumnView::contentXChanged);
986 if (hasActiveFocus() && m_currentItem) {
987 m_currentItem->forceActiveFocus();
990 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(
this,
true));
991 attached->setView(
this);
992 attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(m_contentItem,
true));
993 attached->setView(
this);
996ColumnView::~ColumnView()
1002 return m_contentItem->m_columnResizeMode;
1005void ColumnView::setColumnResizeMode(ColumnResizeMode mode)
1007 if (m_contentItem->m_columnResizeMode == mode) {
1011 m_contentItem->m_columnResizeMode = mode;
1012 if (mode == SingleColumn && m_currentItem) {
1013 m_contentItem->m_viewAnchorItem = m_currentItem;
1015 m_contentItem->m_shouldAnimate =
false;
1017 Q_EMIT columnResizeModeChanged();
1022 return m_contentItem->m_columnWidth;
1025void ColumnView::setColumnWidth(qreal width)
1028 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this,
nullptr);
1030 if (m_contentItem->m_columnWidth ==
width) {
1034 m_contentItem->m_columnWidth =
width;
1035 m_contentItem->m_shouldAnimate =
false;
1037 Q_EMIT columnWidthChanged();
1042 return m_currentIndex;
1045void ColumnView::setCurrentIndex(
int index)
1047 if (m_currentIndex == index || index < -1 || index >= m_contentItem->m_items.count()) {
1051 m_currentIndex = index;
1054 m_currentItem.
clear();
1057 m_currentItem = m_contentItem->m_items[index];
1058 Q_ASSERT(m_currentItem);
1059 m_currentItem->forceActiveFocus();
1062 QRectF mappedCurrent = m_currentItem->mapRectToItem(
this,
QRectF(
QPointF(0, 0), m_currentItem->size()));
1065 mappedCurrent.
moveLeft(mappedCurrent.
left() + m_contentItem->x() + m_contentItem->m_slideAnim->endValue().toInt());
1070 QRectF contentsRect(m_contentItem->m_leftPinnedSpace,
1072 width() - m_contentItem->m_rightPinnedSpace - m_contentItem->m_leftPinnedSpace,
1076 if (!contentsRect.contains(mappedCurrent)) {
1077 m_contentItem->m_viewAnchorItem = m_currentItem;
1079 m_contentItem->animateX(-m_currentItem->x() - m_currentItem->width() +
width());
1081 m_contentItem->animateX(-m_currentItem->x() + m_contentItem->m_leftPinnedSpace);
1084 m_contentItem->snapToItem();
1089 Q_EMIT currentIndexChanged();
1090 Q_EMIT currentItemChanged();
1095 return m_currentItem;
1100 return m_contentItem->m_visibleItems;
1105 if (m_contentItem->m_visibleItems.isEmpty()) {
1109 return m_contentItem->m_visibleItems.first();
1114 if (m_contentItem->m_visibleItems.isEmpty()) {
1123 return m_contentItem->m_items.count();
1128 return m_topPadding;
1131void ColumnView::setTopPadding(qreal padding)
1133 if (padding == m_topPadding) {
1137 m_topPadding = padding;
1139 Q_EMIT topPaddingChanged();
1144 return m_bottomPadding;
1147void ColumnView::setBottomPadding(qreal padding)
1149 if (padding == m_bottomPadding) {
1153 m_bottomPadding = padding;
1155 Q_EMIT bottomPaddingChanged();
1160 return m_contentItem;
1165 return m_contentItem->m_slideAnim->duration();
1168void ColumnView::setScrollDuration(
int duration)
1170 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this,
nullptr);
1172 if (m_contentItem->m_slideAnim->duration() == duration) {
1176 m_contentItem->m_slideAnim->setDuration(duration);
1177 Q_EMIT scrollDurationChanged();
1182 return m_separatorVisible;
1185void ColumnView::setSeparatorVisible(
bool visible)
1187 if (
visible == m_separatorVisible) {
1193 Q_EMIT separatorVisibleChanged();
1208 return m_contentItem->width();
1213 return -m_contentItem->x();
1216void ColumnView::setContentX(qreal x)
const
1218 m_contentItem->setX(qRound(-
x));
1223 return m_interactive;
1226void ColumnView::setInteractive(
bool interactive)
1234 if (!m_interactive) {
1237 Q_EMIT draggingChanged();
1240 m_contentItem->snapToItem();
1244 Q_EMIT interactiveChanged();
1249 return m_acceptsMouse;
1252void ColumnView::setAcceptsMouse(
bool accepts)
1254 if (m_acceptsMouse == accepts) {
1258 m_acceptsMouse = accepts;
1260 if (!m_acceptsMouse) {
1263 Q_EMIT draggingChanged();
1266 m_contentItem->snapToItem();
1270 Q_EMIT acceptsMouseChanged();
1275 insertItem(m_contentItem->m_items.length(), item);
1280 if (!item || m_contentItem->m_items.contains(item)) {
1284 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1290 attached->setOriginalParent(item->
parentItem());
1296 if (attached->globalHeader()) {
1297 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1299 if (attached->globalFooter()) {
1300 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1302 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1303 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1306 m_contentItem->m_shouldAnimate =
true;
1307 m_contentItem->layoutItems();
1308 Q_EMIT contentChildrenChanged();
1312 if (m_currentIndex >= pos) {
1314 Q_EMIT currentIndexChanged();
1322 if (pos < 0 || pos >= m_contentItem->m_items.length()) {
1323 qCWarning(KirigamiLayoutsLog) <<
"Position" << pos <<
"passed to ColumnView::replaceItem is out of range.";
1328 qCWarning(KirigamiLayoutsLog) <<
"Null item passed to ColumnView::replaceItem.";
1332 QQuickItem *oldItem = m_contentItem->m_items[pos];
1335 if (m_currentIndex >= pos) {
1336 setCurrentIndex(m_currentIndex - 1);
1339 m_contentItem->forgetItem(oldItem);
1344 if (attached && attached->shouldDeleteOnRemove()) {
1347 oldItem->
setParentItem(attached ? attached->originalParent() :
nullptr);
1352 if (!m_contentItem->m_items.contains(item)) {
1353 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1359 attached->setOriginalParent(item->
parentItem());
1363 if (attached->globalHeader()) {
1364 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1366 if (attached->globalFooter()) {
1367 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1369 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1370 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1372 if (m_currentIndex >= pos) {
1374 Q_EMIT currentIndexChanged();
1381 m_contentItem->m_shouldAnimate =
false;
1382 m_contentItem->layoutItems();
1383 Q_EMIT contentChildrenChanged();
1388 if (m_contentItem->m_items.isEmpty()
1389 || from < 0 || from >= m_contentItem->m_items.length()
1390 || to < 0 || to >= m_contentItem->m_items.length()) {
1394 m_contentItem->m_items.move(from, to);
1395 m_contentItem->m_shouldAnimate =
true;
1397 if (from == m_currentIndex) {
1398 m_currentIndex = to;
1399 Q_EMIT currentIndexChanged();
1400 }
else if (from < m_currentIndex && to > m_currentIndex) {
1402 Q_EMIT currentIndexChanged();
1403 }
else if (from > m_currentIndex && to <= m_currentIndex) {
1405 Q_EMIT currentIndexChanged();
1413 if (m_contentItem->m_items.isEmpty() || !m_contentItem->m_items.contains(item)) {
1417 const int index = m_contentItem->m_items.indexOf(item);
1420 if (m_currentIndex >= index) {
1421 setCurrentIndex(m_currentIndex - 1);
1424 m_contentItem->forgetItem(item);
1429 if (attached && attached->shouldDeleteOnRemove()) {
1432 item->
setParentItem(attached ? attached->originalParent() :
nullptr);
1435 Q_EMIT contentChildrenChanged();
1443 if (m_contentItem->m_items.isEmpty() || index < 0 || index >=
count()) {
1446 return removeItem(m_contentItem->m_items[index]);
1467 }
else if (item.
isNull()) {
1476 while (!m_contentItem->m_items.isEmpty() && m_contentItem->m_items.last() != item) {
1477 removed =
removeItem(m_contentItem->m_items.last());
1484 if (index >= 0 && index <
count() - 1) {
1485 return pop(m_contentItem->m_items.at(index));
1486 }
else if (index == -1) {
1487 return pop(
nullptr);
1503 while (!m_contentItem->m_items.isEmpty()) {
1504 QQuickItem *item = m_contentItem->m_items.first();
1508 m_contentItem->m_items.clear();
1509 Q_EMIT contentChildrenChanged();
1514 return m_contentItem->m_items.contains(item);
1519 return m_contentItem->childAt(
x,
y);
1527void ColumnView::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1529 m_contentItem->setY(m_topPadding);
1530 m_contentItem->setHeight(newGeometry.
height() - m_topPadding - m_bottomPadding);
1531 m_contentItem->m_shouldAnimate =
false;
1534 m_contentItem->updateVisibleItems();
1540 if (!m_interactive || item == m_contentItem) {
1544 switch (
event->type()) {
1557 if (
int idx = m_contentItem->m_items.indexOf(candidateItem); idx >= 0 && candidateItem->
parentItem() == m_contentItem) {
1558 setCurrentIndex(idx);
1563 event->setAccepted(
false);
1567 m_contentItem->m_slideAnim->stop();
1569 m_contentItem->snapToItem();
1594 bool verticalScrollIntercepted =
false;
1600 if (candidateItem->
parentItem() == m_contentItem) {
1610 ScrollIntentionEvent scrollIntentionEvent;
1611 scrollIntentionEvent.delta =
QPointF(pos.
x() - m_oldMouseX, pos.
y() - m_oldMouseY);
1613 Q_EMIT attached->scrollIntention(&scrollIntentionEvent);
1615 if (scrollIntentionEvent.accepted) {
1616 verticalScrollIntercepted =
true;
1617 event->setAccepted(
true);
1622 m_contentItem->snapToItem();
1623 m_oldMouseX = pos.
x();
1624 m_oldMouseY = pos.
y();
1628 const bool wasDragging = m_dragging;
1632 if (m_dragging != wasDragging) {
1635 Q_EMIT draggingChanged();
1639 m_contentItem->setBoundedX(m_contentItem->x() + pos.
x() - m_oldMouseX);
1642 m_contentItem->m_lastDragDelta = pos.
x() - m_oldMouseX;
1643 m_oldMouseX = pos.
x();
1644 m_oldMouseY = pos.
y();
1649 return m_dragging && !verticalScrollIntercepted;
1658 setCurrentIndex(m_currentIndex - 1);
1662 setCurrentIndex(m_currentIndex + 1);
1675 m_mouseDown =
false;
1678 m_contentItem->snapToItem();
1679 m_contentItem->m_lastDragDelta = 0;
1681 Q_EMIT draggingChanged();
1700void ColumnView::mousePressEvent(
QMouseEvent *event)
1703 event->setAccepted(
false);
1712 if (!m_interactive) {
1716 m_contentItem->snapToItem();
1717 m_oldMouseX =
event->position().x();
1718 m_startMouseX =
event->position().x();
1724void ColumnView::mouseMoveEvent(
QMouseEvent *event)
1731 if (!m_interactive) {
1735 const bool wasDragging = m_dragging;
1737 m_dragging =
keepMouseGrab() || qAbs(
event->position().x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 2;
1738 if (m_dragging != wasDragging) {
1741 Q_EMIT draggingChanged();
1747 m_contentItem->setBoundedX(m_contentItem->x() +
event->pos().x() - m_oldMouseX);
1750 m_contentItem->m_lastDragDelta =
event->pos().x() - m_oldMouseX;
1751 m_oldMouseX =
event->pos().x();
1755void ColumnView::mouseReleaseEvent(
QMouseEvent *event)
1758 setCurrentIndex(m_currentIndex - 1);
1762 setCurrentIndex(m_currentIndex + 1);
1767 m_mouseDown =
false;
1769 if (!m_interactive) {
1773 m_contentItem->snapToItem();
1774 m_contentItem->m_lastDragDelta = 0;
1778 Q_EMIT draggingChanged();
1785void ColumnView::mouseUngrabEvent()
1787 m_mouseDown =
false;
1790 m_contentItem->snapToItem();
1792 m_contentItem->m_lastDragDelta = 0;
1796 Q_EMIT draggingChanged();
1802void ColumnView::classBegin()
1804 auto syncColumnWidth = [
this]() {
1805 m_contentItem->m_columnWidth = privateQmlComponentsPoolSelf->instance(qmlEngine(
this))->m_units->gridUnit() * 20;
1806 Q_EMIT columnWidthChanged();
1809 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this, syncColumnWidth);
1812 auto syncDuration = [
this]() {
1813 m_contentItem->m_slideAnim->setDuration(QmlComponentsPoolSingleton::instance(qmlEngine(
this))->m_units->veryLongDuration());
1814 Q_EMIT scrollDurationChanged();
1817 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this, syncDuration);
1823void ColumnView::componentComplete()
1829void ColumnView::updatePolish()
1831 m_contentItem->layoutItems();
1838 if (m_contentItem && value.item != m_contentItem && !value.item->inherits(
"QQuickRepeater")) {
1856 view->m_contentItem->m_items.append(item);
1858 view->removeItem(item);
1862 attached->setOriginalParent(item->
parentItem());
1875 return view->m_contentItem->m_items.count();
1885 if (index < 0 || index >= view->m_contentItem->m_items.count()) {
1888 return view->m_contentItem->m_items.value(index);
1898 return view->m_contentItem->m_items.clear();
1905 contentChildren_append,
1906 contentChildren_count,
1908 contentChildren_clear);
1918 view->m_contentData.
append(
object);
1921 if (item && item->
inherits(
"QQuickRepeater")) {
1924 connect(item, SIGNAL(modelChanged()), view->m_contentItem, SLOT(updateRepeaterModel()));
1927 view->m_contentItem->m_items.append(item);
1929 view->removeItem(item);
1933 attached->setOriginalParent(item->
parentItem());
1939 object->setParent(view);
1950 return view->m_contentData.
count();
1960 if (index < 0 || index >= view->m_contentData.
count()) {
1963 return view->m_contentData.
value(index);
1973 return view->m_contentData.
clear();
1986#include "moc_columnview.cpp"
1987#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)
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)
void activeFocusChanged(bool)
virtual bool childMouseEventFilter(QQuickItem *item, QEvent *event)
virtual void classBegin() override
virtual void componentComplete() override
virtual bool event(QEvent *ev) override
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