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));
387 QRectF mapped = m_view->currentItem()->mapRectToItem(m_view, QRectF(QPointF(0, 0), m_view->currentItem()->size()));
388 if (!QRectF(QPointF(0, 0), m_view->size()).intersects(mapped)) {
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 pageX = std::clamp(partialWidth, -x(), -x() + m_view->width() - child->
width());
530 qreal headerHeight = .0;
531 qreal footerHeight = .0;
532 if (
QQuickItem *header = attached->globalHeader()) {
535 header->setPosition(
QPointF(pageX, .0));
537 if (m_view->separatorVisible()) {
538 QQuickItem *sep = ensureTrailingSeparator(header);
542 if (
QQuickItem *footer = attached->globalFooter()) {
545 footer->setPosition(
QPointF(pageX, height() - footerHeight));
547 if (m_view->separatorVisible()) {
548 QQuickItem *sep = ensureTrailingSeparator(footer);
553 child->
setSize(
QSizeF(width + sepWidth, height() - headerHeight - footerHeight));
554 child->setPosition(
QPointF(pageX, headerHeight));
557 if (partialWidth <= -x()) {
558 m_leftPinnedSpace = qMax(m_leftPinnedSpace, width);
559 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
560 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
563 partialWidth += width;
566 const qreal width = childWidth(child);
567 qreal headerHeight = .0;
568 qreal footerHeight = .0;
569 if (
QQuickItem *header = attached->globalHeader(); header && qmlEngine(header)) {
570 if (m_view->separatorVisible()) {
571 QQuickItem *sep = ensureLeadingSeparator(header);
576 header->setPosition(
QPointF(partialWidth, .0));
578 auto it = m_trailingSeparators.find(header);
579 if (it != m_trailingSeparators.end()) {
580 it.value()->deleteLater();
581 m_trailingSeparators.erase(it);
584 if (
QQuickItem *footer = attached->globalFooter(); footer && qmlEngine(footer)) {
585 if (m_view->separatorVisible()) {
586 QQuickItem *sep = ensureLeadingSeparator(footer);
591 footer->setPosition(
QPointF(partialWidth, height() - footerHeight));
593 auto it = m_trailingSeparators.find(footer);
594 if (it != m_trailingSeparators.end()) {
595 it.value()->deleteLater();
596 m_trailingSeparators.erase(it);
600 child->
setSize(
QSizeF(width, height() - headerHeight - footerHeight));
602 auto it = m_trailingSeparators.find(child);
603 if (it != m_trailingSeparators.end()) {
604 it.value()->deleteLater();
605 m_trailingSeparators.erase(it);
607 child->setPosition(
QPointF(partialWidth, headerHeight));
610 partialWidth += child->
width();
615 attached->setIndex(m_items.count() - (++i));
617 attached->setIndex(i++);
625 setWidth(partialWidth);
627 setImplicitWidth(implicitWidth);
628 setImplicitHeight(implicitHeight);
630 m_view->setImplicitWidth(implicitWidth);
631 m_view->setImplicitHeight(implicitHeight + m_view->topPadding() + m_view->bottomPadding());
633 const qreal newContentX = m_viewAnchorItem ? -m_viewAnchorItem->x() : 0.0;
634 if (m_shouldAnimate) {
635 animateX(newContentX);
637 setBoundedX(newContentX);
640 updateVisibleItems();
643void ContentItem::layoutPinnedItems()
645 if (m_view->columnResizeMode() == ColumnView::SingleColumn) {
649 qreal partialWidth = 0;
650 m_leftPinnedSpace = 0;
651 m_rightPinnedSpace = 0;
654 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
657 if (attached->isPinned()) {
660 if (m_view->separatorVisible()) {
661 sep = ensureTrailingSeparator(child);
662 sepWidth = (sep ? sep->
width() : 0);
665 const qreal pageX = qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->
width() + sepWidth);
666 qreal headerHeight = .0;
667 qreal footerHeight = .0;
668 if (
QQuickItem *header = attached->globalHeader()) {
670 header->setPosition(
QPointF(pageX, .0));
671 if (m_view->separatorVisible()) {
672 QQuickItem *sep = ensureTrailingSeparator(header);
676 if (
QQuickItem *footer = attached->globalFooter()) {
678 footer->setPosition(
QPointF(pageX, height() - footerHeight));
679 if (m_view->separatorVisible()) {
680 QQuickItem *sep = ensureTrailingSeparator(footer);
684 child->setPosition(
QPointF(pageX, headerHeight));
686 if (partialWidth <= -x()) {
687 m_leftPinnedSpace = qMax(m_leftPinnedSpace, child->
width() - sepWidth);
688 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
689 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
693 partialWidth += child->
width();
698void ContentItem::updateVisibleItems()
702 for (
auto *item :
std::as_const(m_items)) {
703 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
705 if (item->isVisible() && item->x() + x() < m_view->width() && item->x() + item->width() + x() > 0) {
708 m_visibleItems.removeAll(item);
710 attached->setInViewport(
true);
712 attached->setInViewport(
false);
716 for (
auto *item :
std::as_const(m_visibleItems)) {
720 const QQuickItem *oldLeadingVisibleItem = m_view->leadingVisibleItem();
721 const QQuickItem *oldTrailingVisibleItem = m_view->trailingVisibleItem();
723 if (newItems != m_visibleItems) {
724 m_visibleItems = newItems;
725 Q_EMIT m_view->visibleItemsChanged();
726 if (!m_visibleItems.isEmpty() && m_visibleItems.first() != oldLeadingVisibleItem) {
727 Q_EMIT m_view->leadingVisibleItemChanged();
729 if (!m_visibleItems.isEmpty() && m_visibleItems.last() != oldTrailingVisibleItem) {
730 Q_EMIT m_view->trailingVisibleItemChanged();
737 if (!m_items.contains(item)) {
741 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
742 attached->setView(
nullptr);
743 attached->setIndex(-1);
745 disconnect(attached,
nullptr,
this,
nullptr);
746 disconnect(item,
nullptr,
this,
nullptr);
747 disconnect(item,
nullptr, m_view,
nullptr);
749 QQuickItem *separatorItem = m_leadingSeparators.take(item);
753 separatorItem = m_trailingSeparators.take(item);
758 if (
QQuickItem *header = attached->globalHeader()) {
761 separatorItem = m_leadingSeparators.take(header);
765 separatorItem = m_trailingSeparators.take(header);
770 if (
QQuickItem *footer = attached->globalFooter()) {
773 separatorItem = m_leadingSeparators.take(footer);
777 separatorItem = m_trailingSeparators.take(footer);
783 const int index = m_items.indexOf(item);
784 m_items.removeAll(item);
786 disconnect(item,
nullptr,
this,
nullptr);
787 updateVisibleItems();
788 m_shouldAnimate =
true;
791 if (index <= m_view->currentIndex()) {
792 m_view->setCurrentIndex(m_items.isEmpty() ? 0 : qBound(0, index - 1, m_items.count() - 1));
794 Q_EMIT m_view->countChanged();
799 QQuickItem *separatorItem = m_leadingSeparators.value(item);
801 if (!separatorItem) {
802 separatorItem = qobject_cast<QQuickItem *>(
807 separatorItem->
setZ(9999);
810 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_leadingSeparatorComponent->completeCreate();
811 m_leadingSeparators[item] = separatorItem;
815 return separatorItem;
820 QQuickItem *separatorItem = m_trailingSeparators.value(item);
822 if (!separatorItem) {
823 separatorItem = qobject_cast<QQuickItem *>(
828 separatorItem->
setZ(9999);
830 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_trailingSeparatorComponent->completeCreate();
831 m_trailingSeparators[item] = separatorItem;
835 return separatorItem;
840 if (m_creationInProgress) {
846 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(value.item,
true));
847 attached->setView(m_view);
850 connect(attached, &ColumnViewAttached::fillWidthChanged,
this, [
this] {
853 connect(attached, &ColumnViewAttached::reservedSpaceChanged, m_view, &
ColumnView::polish);
855 value.item->setVisible(
true);
857 if (!m_items.contains(value.item)) {
862 m_view->removeItem(item);
866 if (m_view->separatorVisible()) {
867 ensureLeadingSeparator(value.item);
870 m_shouldAnimate =
true;
872 Q_EMIT m_view->countChanged();
876 forgetItem(value.item);
880 updateVisibleItems();
881 if (value.boolValue) {
891void ContentItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
893 updateVisibleItems();
894 QQuickItem::geometryChange(newGeometry, oldGeometry);
897void ContentItem::syncItemsOrder()
899 if (m_items == childItems()) {
903 m_items = childItems();
908void ContentItem::updateRepeaterModel()
917 m_models.remove(sender());
921 if (m_models[sender()]) {
922 disconnect(m_models[sender()],
nullptr,
this,
nullptr);
925 m_models[sender()] = modelObj;
933 connect(modelObj, SIGNAL(childrenChanged()),
this, SLOT(syncItemsOrder()));
940 disconnect(oldHeader,
nullptr,
this,
nullptr);
953 disconnect(oldFooter,
nullptr,
this,
nullptr);
965 , m_contentItem(nullptr)
968 m_contentItem =
new ContentItem(
this);
972 setAcceptTouchEvents(
false);
973 setFiltersChildMouseEvents(
true);
977 Q_EMIT movingChanged();
979 connect(m_contentItem, &ContentItem::widthChanged,
this, &ColumnView::contentWidthChanged);
980 connect(m_contentItem, &ContentItem::xChanged,
this, &ColumnView::contentXChanged);
983 if (hasActiveFocus() && m_currentItem) {
984 m_currentItem->forceActiveFocus();
987 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(
this,
true));
988 attached->setView(
this);
989 attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(m_contentItem,
true));
990 attached->setView(
this);
993ColumnView::~ColumnView()
999 return m_contentItem->m_columnResizeMode;
1002void ColumnView::setColumnResizeMode(ColumnResizeMode mode)
1004 if (m_contentItem->m_columnResizeMode == mode) {
1008 m_contentItem->m_columnResizeMode = mode;
1009 if (mode == SingleColumn && m_currentItem) {
1010 m_contentItem->m_viewAnchorItem = m_currentItem;
1012 m_contentItem->m_shouldAnimate =
false;
1014 Q_EMIT columnResizeModeChanged();
1019 return m_contentItem->m_columnWidth;
1022void ColumnView::setColumnWidth(qreal width)
1025 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this,
nullptr);
1027 if (m_contentItem->m_columnWidth ==
width) {
1031 m_contentItem->m_columnWidth =
width;
1032 m_contentItem->m_shouldAnimate =
false;
1034 Q_EMIT columnWidthChanged();
1039 return m_currentIndex;
1042void ColumnView::setCurrentIndex(
int index)
1044 if (m_currentIndex == index || index < -1 || index >= m_contentItem->m_items.count()) {
1048 m_currentIndex = index;
1051 m_currentItem.
clear();
1054 m_currentItem = m_contentItem->m_items[index];
1055 Q_ASSERT(m_currentItem);
1056 m_currentItem->forceActiveFocus();
1059 QRectF mappedCurrent = m_currentItem->mapRectToItem(
this,
QRectF(
QPointF(0, 0), m_currentItem->size()));
1062 mappedCurrent.
moveLeft(mappedCurrent.
left() + m_contentItem->x() + m_contentItem->m_slideAnim->endValue().toInt());
1067 QRectF contentsRect(m_contentItem->m_leftPinnedSpace,
1069 width() - m_contentItem->m_rightPinnedSpace - m_contentItem->m_leftPinnedSpace,
1073 if (!contentsRect.contains(mappedCurrent)) {
1074 m_contentItem->m_viewAnchorItem = m_currentItem;
1076 m_contentItem->animateX(-m_currentItem->x() - m_currentItem->width() +
width());
1078 m_contentItem->animateX(-m_currentItem->x() + m_contentItem->m_leftPinnedSpace);
1081 m_contentItem->snapToItem();
1086 Q_EMIT currentIndexChanged();
1087 Q_EMIT currentItemChanged();
1092 return m_currentItem;
1097 return m_contentItem->m_visibleItems;
1102 if (m_contentItem->m_visibleItems.isEmpty()) {
1106 return qobject_cast<QQuickItem *>(m_contentItem->m_visibleItems.first());
1111 if (m_contentItem->m_visibleItems.isEmpty()) {
1115 return qobject_cast<QQuickItem *>(m_contentItem->m_visibleItems.last());
1120 return m_contentItem->m_items.count();
1125 return m_topPadding;
1128void ColumnView::setTopPadding(qreal padding)
1130 if (padding == m_topPadding) {
1134 m_topPadding = padding;
1136 Q_EMIT topPaddingChanged();
1141 return m_bottomPadding;
1144void ColumnView::setBottomPadding(qreal padding)
1146 if (padding == m_bottomPadding) {
1150 m_bottomPadding = padding;
1152 Q_EMIT bottomPaddingChanged();
1157 return m_contentItem;
1162 return m_contentItem->m_slideAnim->duration();
1165void ColumnView::setScrollDuration(
int duration)
1167 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this,
nullptr);
1169 if (m_contentItem->m_slideAnim->duration() == duration) {
1173 m_contentItem->m_slideAnim->setDuration(duration);
1174 Q_EMIT scrollDurationChanged();
1179 return m_separatorVisible;
1182void ColumnView::setSeparatorVisible(
bool visible)
1184 if (
visible == m_separatorVisible) {
1190 Q_EMIT separatorVisibleChanged();
1205 return m_contentItem->width();
1210 return -m_contentItem->x();
1213void ColumnView::setContentX(qreal x)
const
1215 m_contentItem->setX(qRound(-
x));
1220 return m_interactive;
1223void ColumnView::setInteractive(
bool interactive)
1231 if (!m_interactive) {
1234 Q_EMIT draggingChanged();
1237 m_contentItem->snapToItem();
1241 Q_EMIT interactiveChanged();
1246 return m_acceptsMouse;
1249void ColumnView::setAcceptsMouse(
bool accepts)
1251 if (m_acceptsMouse == accepts) {
1255 m_acceptsMouse = accepts;
1257 if (!m_acceptsMouse) {
1260 Q_EMIT draggingChanged();
1263 m_contentItem->snapToItem();
1267 Q_EMIT acceptsMouseChanged();
1272 insertItem(m_contentItem->m_items.length(), item);
1277 if (!item || m_contentItem->m_items.contains(item)) {
1281 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1286 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
1287 attached->setOriginalParent(item->
parentItem());
1293 if (attached->globalHeader()) {
1294 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1296 if (attached->globalFooter()) {
1297 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1299 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1300 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1303 m_contentItem->m_shouldAnimate =
true;
1304 m_contentItem->layoutItems();
1305 Q_EMIT contentChildrenChanged();
1309 if (m_currentIndex >= pos) {
1311 Q_EMIT currentIndexChanged();
1319 if (pos < 0 || pos >= m_contentItem->m_items.length()) {
1320 qCWarning(KirigamiLog) <<
"Position" << pos <<
"passed to ColumnView::replaceItem is out of range.";
1325 qCWarning(KirigamiLog) <<
"Null item passed to ColumnView::replaceItem.";
1329 QQuickItem *oldItem = m_contentItem->m_items[pos];
1332 if (m_currentIndex >= pos) {
1333 setCurrentIndex(m_currentIndex - 1);
1336 m_contentItem->forgetItem(oldItem);
1339 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(oldItem,
false));
1341 if (attached && attached->shouldDeleteOnRemove()) {
1344 oldItem->
setParentItem(attached ? attached->originalParent() :
nullptr);
1349 if (!m_contentItem->m_items.contains(item)) {
1350 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1355 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
1356 attached->setOriginalParent(item->
parentItem());
1360 if (attached->globalHeader()) {
1361 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1363 if (attached->globalFooter()) {
1364 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1366 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1367 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1369 if (m_currentIndex >= pos) {
1371 Q_EMIT currentIndexChanged();
1378 m_contentItem->m_shouldAnimate =
false;
1379 m_contentItem->layoutItems();
1380 Q_EMIT contentChildrenChanged();
1385 if (m_contentItem->m_items.isEmpty()
1386 || from < 0 || from >= m_contentItem->m_items.length()
1387 || to < 0 || to >= m_contentItem->m_items.length()) {
1391 m_contentItem->m_items.move(from, to);
1392 m_contentItem->m_shouldAnimate =
true;
1394 if (from == m_currentIndex) {
1395 m_currentIndex = to;
1396 Q_EMIT currentIndexChanged();
1397 }
else if (from < m_currentIndex && to > m_currentIndex) {
1399 Q_EMIT currentIndexChanged();
1400 }
else if (from > m_currentIndex && to <= m_currentIndex) {
1402 Q_EMIT currentIndexChanged();
1410 if (m_contentItem->m_items.isEmpty() || !m_contentItem->m_items.contains(item)) {
1414 const int index = m_contentItem->m_items.indexOf(item);
1417 if (m_currentIndex >= index) {
1418 setCurrentIndex(m_currentIndex - 1);
1421 m_contentItem->forgetItem(item);
1424 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
false));
1426 if (attached && attached->shouldDeleteOnRemove()) {
1429 item->
setParentItem(attached ? attached->originalParent() :
nullptr);
1432 Q_EMIT contentChildrenChanged();
1440 if (m_contentItem->m_items.isEmpty() || index < 0 || index >=
count()) {
1443 return removeItem(m_contentItem->m_items[index]);
1464 }
else if (item.
isNull()) {
1473 while (!m_contentItem->m_items.isEmpty() && m_contentItem->m_items.last() != item) {
1474 removed =
removeItem(m_contentItem->m_items.last());
1481 if (index >= 0 && index <
count() - 1) {
1482 return pop(m_contentItem->m_items.at(index));
1483 }
else if (index == -1) {
1484 return pop(
nullptr);
1500 while (!m_contentItem->m_items.isEmpty()) {
1501 QQuickItem *item = m_contentItem->m_items.first();
1505 m_contentItem->m_items.clear();
1506 Q_EMIT contentChildrenChanged();
1511 return m_contentItem->m_items.contains(item);
1516 return m_contentItem->childAt(
x,
y);
1524void ColumnView::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1526 m_contentItem->setY(m_topPadding);
1527 m_contentItem->setHeight(newGeometry.
height() - m_topPadding - m_bottomPadding);
1528 m_contentItem->m_shouldAnimate =
false;
1531 m_contentItem->updateVisibleItems();
1532 QQuickItem::geometryChange(newGeometry, oldGeometry);
1537 if (!m_interactive || item == m_contentItem) {
1541 switch (
event->type()) {
1554 if (
int idx = m_contentItem->m_items.indexOf(candidateItem); idx >= 0 && candidateItem->
parentItem() == m_contentItem) {
1555 setCurrentIndex(idx);
1560 event->setAccepted(
false);
1564 m_contentItem->m_slideAnim->stop();
1566 m_contentItem->snapToItem();
1569 m_oldMouseX = m_startMouseX =
mapFromItem(item, me->position()).
x();
1570 m_oldMouseY = m_startMouseY =
mapFromItem(item, me->position()).
y();
1591 bool verticalScrollIntercepted =
false;
1597 if (candidateItem->
parentItem() == m_contentItem) {
1598 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(candidateItem,
true));
1605 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(candidateItem,
true));
1607 ScrollIntentionEvent scrollIntentionEvent;
1608 scrollIntentionEvent.delta =
QPointF(pos.
x() - m_oldMouseX, pos.
y() - m_oldMouseY);
1610 Q_EMIT attached->scrollIntention(&scrollIntentionEvent);
1612 if (scrollIntentionEvent.accepted) {
1613 verticalScrollIntercepted =
true;
1614 event->setAccepted(
true);
1619 m_contentItem->snapToItem();
1620 m_oldMouseX = pos.
x();
1621 m_oldMouseY = pos.
y();
1625 const bool wasDragging = m_dragging;
1627 m_dragging =
keepMouseGrab() || qAbs(
mapFromItem(item, me->position()).
x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 3;
1629 if (m_dragging != wasDragging) {
1632 Q_EMIT draggingChanged();
1636 m_contentItem->setBoundedX(m_contentItem->x() + pos.
x() - m_oldMouseX);
1639 m_contentItem->m_lastDragDelta = pos.
x() - m_oldMouseX;
1640 m_oldMouseX = pos.
x();
1641 m_oldMouseY = pos.
y();
1646 return m_dragging && !verticalScrollIntercepted;
1655 setCurrentIndex(m_currentIndex - 1);
1659 setCurrentIndex(m_currentIndex + 1);
1672 m_mouseDown =
false;
1675 m_contentItem->snapToItem();
1676 m_contentItem->m_lastDragDelta = 0;
1678 Q_EMIT draggingChanged();
1697void ColumnView::mousePressEvent(
QMouseEvent *event)
1700 event->setAccepted(
false);
1709 if (!m_interactive) {
1713 m_contentItem->snapToItem();
1714 m_oldMouseX =
event->position().x();
1715 m_startMouseX =
event->position().x();
1721void ColumnView::mouseMoveEvent(
QMouseEvent *event)
1728 if (!m_interactive) {
1732 const bool wasDragging = m_dragging;
1734 m_dragging =
keepMouseGrab() || qAbs(
event->position().x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 2;
1735 if (m_dragging != wasDragging) {
1738 Q_EMIT draggingChanged();
1744 m_contentItem->setBoundedX(m_contentItem->x() +
event->pos().x() - m_oldMouseX);
1747 m_contentItem->m_lastDragDelta =
event->pos().x() - m_oldMouseX;
1748 m_oldMouseX =
event->pos().x();
1752void ColumnView::mouseReleaseEvent(
QMouseEvent *event)
1755 setCurrentIndex(m_currentIndex - 1);
1759 setCurrentIndex(m_currentIndex + 1);
1764 m_mouseDown =
false;
1766 if (!m_interactive) {
1770 m_contentItem->snapToItem();
1771 m_contentItem->m_lastDragDelta = 0;
1775 Q_EMIT draggingChanged();
1782void ColumnView::mouseUngrabEvent()
1784 m_mouseDown =
false;
1787 m_contentItem->snapToItem();
1789 m_contentItem->m_lastDragDelta = 0;
1793 Q_EMIT draggingChanged();
1799void ColumnView::classBegin()
1801 auto syncColumnWidth = [
this]() {
1802 m_contentItem->m_columnWidth = privateQmlComponentsPoolSelf->instance(qmlEngine(
this))->m_units->gridUnit() * 20;
1803 Q_EMIT columnWidthChanged();
1806 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this, syncColumnWidth);
1809 auto syncDuration = [
this]() {
1810 m_contentItem->m_slideAnim->setDuration(QmlComponentsPoolSingleton::instance(qmlEngine(
this))->m_units->longDuration());
1811 Q_EMIT scrollDurationChanged();
1814 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this, syncDuration);
1820void ColumnView::componentComplete()
1826void ColumnView::updatePolish()
1828 m_contentItem->layoutItems();
1835 if (m_contentItem && value.item != m_contentItem && !value.item->inherits(
"QQuickRepeater")) {
1853 view->m_contentItem->m_items.append(item);
1855 view->removeItem(item);
1858 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
1859 attached->setOriginalParent(item->
parentItem());
1872 return view->m_contentItem->m_items.count();
1882 if (index < 0 || index >= view->m_contentItem->m_items.count()) {
1885 return view->m_contentItem->m_items.value(index);
1895 return view->m_contentItem->m_items.clear();
1902 contentChildren_append,
1903 contentChildren_count,
1905 contentChildren_clear);
1915 view->m_contentData.
append(
object);
1916 QQuickItem *item = qobject_cast<QQuickItem *>(
object);
1918 if (item && item->
inherits(
"QQuickRepeater")) {
1921 connect(item, SIGNAL(modelChanged()), view->m_contentItem, SLOT(updateRepeaterModel()));
1924 view->m_contentItem->m_items.append(item);
1926 view->removeItem(item);
1929 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
1930 attached->setOriginalParent(item->
parentItem());
1936 object->setParent(view);
1947 return view->m_contentData.
count();
1957 if (index < 0 || index >= view->m_contentData.
count()) {
1960 return view->m_contentData.
value(index);
1970 return view->m_contentData.
clear();
1983#include "moc_columnview.cpp"
1984#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...
QList< QObject * > visibleItems
The list of all visible column items that are at least partially in the viewport at any given moment.
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.
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)
KIOCORE_EXPORT DeleteJob * del(const QList< QUrl > &src, JobFlags flags=DefaultFlags)
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
void setAccepted(bool accepted)
void append(const T &value)
int count(const T &value) const const
T value(int i) const const
Qt::MouseEventSource source() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void destroyed(QObject *obj)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
bool inherits(const char *className) const const
QObject * parent() const const
QVariant property(const char *name) const const
void setParent(QObject *parent)
bool setProperty(const char *name, const QVariant &value)
QQmlContext * contextForObject(const QObject *object)
QQmlEngine::ObjectOwnership objectOwnership(QObject *object)
T singletonInstance(int qmlTypeId)
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 itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
bool keepMouseGrab() 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 canConvert(int targetTypeId) const const
QVariant fromValue(const T &value)
bool isNull() const const
bool toBool() const const
int toInt(bool *ok) const const