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)
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) {
224 disconnect(m_view.data(),
nullptr,
this,
nullptr);
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) {
328 QQuickItem *oldHeader = 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) {
353 QQuickItem *oldFooter = 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 if (m_view->count() == 1) {
479 return qRound(parentItem()->width());
482 return qRound(qBound(m_columnWidth, (parentItem()->width() - attached->
reservedSpace()), std::max(m_columnWidth, parentItem()->width())));
484 }
else if (m_columnResizeMode == ColumnView::FixedColumns) {
485 return qRound(qMin(parentItem()->width(), m_columnWidth));
493 width = m_columnWidth;
496 return qRound(qMin(m_view->width(), width));
500void ContentItem::layoutItems()
502 setY(m_view->topPadding());
503 setHeight(m_view->height() - m_view->topPadding() - m_view->bottomPadding());
505 qreal implicitWidth = 0;
506 qreal implicitHeight = 0;
507 qreal partialWidth = 0;
509 m_leftPinnedSpace = 0;
510 m_rightPinnedSpace = 0;
513 auto it = !reverse ? m_items.begin() : m_items.end();
514 int increment = reverse ? -1 : +1;
515 auto lastPos = reverse ? m_items.begin() : m_items.end();
517 for (; it != lastPos; it += increment) {
519 QQuickItem *child = reverse ? *(it - 1) : *it;
520 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
521 if (child == m_globalHeaderParent || child == m_globalFooterParent) {
526 if (attached->isPinned() && m_view->columnResizeMode() != ColumnView::SingleColumn) {
529 if (m_view->separatorVisible()) {
530 sep = ensureTrailingSeparator(child);
531 sepWidth = (sep ? sep->
width() : 0);
533 const qreal width = childWidth(child);
534 const qreal widthDiff = std::max(0.0, m_view->width() - child->
width());
535 const qreal pageX = std::clamp(partialWidth, -x(), -x() + widthDiff);
536 qreal headerHeight = .0;
537 qreal footerHeight = .0;
538 if (
QQuickItem *header = attached->globalHeader()) {
541 header->setPosition(
QPointF(pageX, .0));
543 if (m_view->separatorVisible()) {
544 QQuickItem *sep = ensureTrailingSeparator(header);
548 if (
QQuickItem *footer = attached->globalFooter()) {
551 footer->setPosition(
QPointF(pageX, height() - footerHeight));
553 if (m_view->separatorVisible()) {
554 QQuickItem *sep = ensureTrailingSeparator(footer);
559 child->
setSize(
QSizeF(width + sepWidth, height() - headerHeight - footerHeight));
560 child->setPosition(
QPointF(pageX, headerHeight));
563 if (partialWidth <= -x()) {
564 m_leftPinnedSpace = qMax(m_leftPinnedSpace, width);
565 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
566 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
569 partialWidth += width;
572 const qreal width = childWidth(child);
573 qreal headerHeight = .0;
574 qreal footerHeight = .0;
575 if (
QQuickItem *header = attached->globalHeader(); header && qmlEngine(header)) {
576 if (m_view->separatorVisible()) {
577 QQuickItem *sep = ensureLeadingSeparator(header);
582 header->setPosition(
QPointF(partialWidth, .0));
584 auto it = m_trailingSeparators.find(header);
585 if (it != m_trailingSeparators.end()) {
586 it.value()->deleteLater();
587 m_trailingSeparators.erase(it);
590 if (
QQuickItem *footer = attached->globalFooter(); footer && qmlEngine(footer)) {
591 if (m_view->separatorVisible()) {
592 QQuickItem *sep = ensureLeadingSeparator(footer);
597 footer->setPosition(
QPointF(partialWidth, height() - footerHeight));
599 auto it = m_trailingSeparators.find(footer);
600 if (it != m_trailingSeparators.end()) {
601 it.value()->deleteLater();
602 m_trailingSeparators.erase(it);
606 child->
setSize(
QSizeF(width, height() - headerHeight - footerHeight));
608 auto it = m_trailingSeparators.find(child);
609 if (it != m_trailingSeparators.end()) {
610 it.value()->deleteLater();
611 m_trailingSeparators.erase(it);
613 child->setPosition(
QPointF(partialWidth, headerHeight));
616 partialWidth += child->
width();
621 attached->setIndex(m_items.count() - (++i));
623 attached->setIndex(i++);
631 setWidth(partialWidth);
633 setImplicitWidth(implicitWidth);
634 setImplicitHeight(implicitHeight);
636 m_view->setImplicitWidth(implicitWidth);
637 m_view->setImplicitHeight(implicitHeight + m_view->topPadding() + m_view->bottomPadding());
639 const qreal newContentX = m_viewAnchorItem ? -m_viewAnchorItem->x() : 0.0;
640 if (m_shouldAnimate) {
641 animateX(newContentX);
643 setBoundedX(newContentX);
646 updateVisibleItems();
649void ContentItem::layoutPinnedItems()
651 if (m_view->columnResizeMode() == ColumnView::SingleColumn) {
655 qreal partialWidth = 0;
656 m_leftPinnedSpace = 0;
657 m_rightPinnedSpace = 0;
659 for (
QQuickItem *child : std::as_const(m_items)) {
660 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(child,
true));
663 if (attached->isPinned()) {
666 if (m_view->separatorVisible()) {
667 sep = ensureTrailingSeparator(child);
668 sepWidth = (sep ? sep->
width() : 0);
671 const qreal pageX = qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->
width() + sepWidth);
672 qreal headerHeight = .0;
673 qreal footerHeight = .0;
674 if (
QQuickItem *header = attached->globalHeader()) {
676 header->setPosition(
QPointF(pageX, .0));
677 if (m_view->separatorVisible()) {
678 QQuickItem *sep = ensureTrailingSeparator(header);
682 if (
QQuickItem *footer = attached->globalFooter()) {
684 footer->setPosition(
QPointF(pageX, height() - footerHeight));
685 if (m_view->separatorVisible()) {
686 QQuickItem *sep = ensureTrailingSeparator(footer);
690 child->setPosition(
QPointF(pageX, headerHeight));
692 if (partialWidth <= -x()) {
693 m_leftPinnedSpace = qMax(m_leftPinnedSpace, child->
width() - sepWidth);
694 }
else if (partialWidth > -x() + m_view->width() - child->
width() + sepWidth) {
695 m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->
width());
699 partialWidth += child->
width();
704void ContentItem::updateVisibleItems()
708 for (
auto *item : std::as_const(m_items)) {
709 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
711 if (item->isVisible() && item->x() + x() < m_view->width() && item->x() + item->width() + x() > 0) {
714 m_visibleItems.removeAll(item);
716 attached->setInViewport(
true);
717 item->setEnabled(
true);
719 attached->setInViewport(
false);
720 item->setEnabled(
false);
724 for (
auto *item : std::as_const(m_visibleItems)) {
728 const QQuickItem *oldLeadingVisibleItem = m_view->leadingVisibleItem();
729 const QQuickItem *oldTrailingVisibleItem = m_view->trailingVisibleItem();
731 if (newItems != m_visibleItems) {
732 m_visibleItems = newItems;
733 Q_EMIT m_view->visibleItemsChanged();
734 if (!m_visibleItems.isEmpty() && m_visibleItems.first() != oldLeadingVisibleItem) {
735 Q_EMIT m_view->leadingVisibleItemChanged();
737 if (!m_visibleItems.isEmpty() && m_visibleItems.last() != oldTrailingVisibleItem) {
738 Q_EMIT m_view->trailingVisibleItemChanged();
745 if (!m_items.contains(item)) {
749 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(item,
true));
750 attached->setView(
nullptr);
751 attached->setIndex(-1);
753 disconnect(attached,
nullptr,
this,
nullptr);
754 disconnect(item,
nullptr,
this,
nullptr);
755 disconnect(item,
nullptr, m_view,
nullptr);
757 QQuickItem *separatorItem = m_leadingSeparators.take(item);
761 separatorItem = m_trailingSeparators.take(item);
766 if (
QQuickItem *header = attached->globalHeader()) {
769 separatorItem = m_leadingSeparators.take(header);
773 separatorItem = m_trailingSeparators.take(header);
778 if (
QQuickItem *footer = attached->globalFooter()) {
781 separatorItem = m_leadingSeparators.take(footer);
785 separatorItem = m_trailingSeparators.take(footer);
791 const int index = m_items.indexOf(item);
792 m_items.removeAll(item);
794 disconnect(item,
nullptr,
this,
nullptr);
795 updateVisibleItems();
796 m_shouldAnimate =
true;
799 if (index <= m_view->currentIndex()) {
800 m_view->setCurrentIndex(m_items.isEmpty() ? 0 : qBound(0, index - 1, m_items.count() - 1));
802 Q_EMIT m_view->countChanged();
807 QQuickItem *separatorItem = m_leadingSeparators.value(item);
809 if (!separatorItem) {
810 separatorItem = qobject_cast<QQuickItem *>(
815 separatorItem->
setZ(9999);
818 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_leadingSeparatorComponent->completeCreate();
819 m_leadingSeparators[item] = separatorItem;
823 return separatorItem;
828 QQuickItem *separatorItem = m_trailingSeparators.value(item);
830 if (!separatorItem) {
831 separatorItem = qobject_cast<QQuickItem *>(
836 separatorItem->
setZ(9999);
838 QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_trailingSeparatorComponent->completeCreate();
839 m_trailingSeparators[item] = separatorItem;
843 return separatorItem;
848 if (m_creationInProgress) {
854 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(value.item,
true));
855 attached->setView(m_view);
858 connect(attached, &ColumnViewAttached::fillWidthChanged,
this, [
this] {
863 value.item->setVisible(
true);
865 if (!m_items.contains(value.item)) {
870 m_view->removeItem(item);
874 if (m_view->separatorVisible()) {
875 ensureLeadingSeparator(value.item);
878 m_shouldAnimate =
true;
880 Q_EMIT m_view->countChanged();
884 forgetItem(value.item);
888 updateVisibleItems();
889 if (value.boolValue) {
899void ContentItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
901 updateVisibleItems();
905void ContentItem::syncItemsOrder()
907 if (m_items == childItems()) {
911 m_items = childItems();
916void ContentItem::updateRepeaterModel()
925 m_models.remove(sender());
929 if (m_models[sender()]) {
930 disconnect(m_models[sender()],
nullptr,
this,
nullptr);
933 m_models[sender()] = modelObj;
941 connect(modelObj, SIGNAL(childrenChanged()),
this, SLOT(syncItemsOrder()));
948 disconnect(oldHeader,
nullptr,
this,
nullptr);
961 disconnect(oldFooter,
nullptr,
this,
nullptr);
973 , m_contentItem(nullptr)
976 m_contentItem =
new ContentItem(
this);
980 setAcceptTouchEvents(
false);
981 setFiltersChildMouseEvents(
true);
985 Q_EMIT movingChanged();
987 connect(m_contentItem, &ContentItem::widthChanged,
this, &ColumnView::contentWidthChanged);
988 connect(m_contentItem, &ContentItem::xChanged,
this, &ColumnView::contentXChanged);
991 if (hasActiveFocus() && m_currentItem) {
992 m_currentItem->forceActiveFocus();
995 ColumnViewAttached *attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(
this,
true));
996 attached->setView(
this);
997 attached = qobject_cast<ColumnViewAttached *>(qmlAttachedPropertiesObject<ColumnView>(m_contentItem,
true));
998 attached->setView(
this);
1001ColumnView::~ColumnView()
1007 return m_contentItem->m_columnResizeMode;
1010void ColumnView::setColumnResizeMode(ColumnResizeMode mode)
1012 if (m_contentItem->m_columnResizeMode == mode) {
1016 m_contentItem->m_columnResizeMode = mode;
1017 if (mode == SingleColumn && m_currentItem) {
1018 m_contentItem->m_viewAnchorItem = m_currentItem;
1020 m_contentItem->m_shouldAnimate =
false;
1022 Q_EMIT columnResizeModeChanged();
1027 return m_contentItem->m_columnWidth;
1030void ColumnView::setColumnWidth(qreal width)
1033 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this,
nullptr);
1035 if (m_contentItem->m_columnWidth ==
width) {
1039 m_contentItem->m_columnWidth =
width;
1040 m_contentItem->m_shouldAnimate =
false;
1042 Q_EMIT columnWidthChanged();
1047 return m_currentIndex;
1050void ColumnView::setCurrentIndex(
int index)
1052 if (m_currentIndex == index || index < -1 || index >= m_contentItem->m_items.count()) {
1056 m_currentIndex = index;
1059 m_currentItem.clear();
1062 m_currentItem = m_contentItem->m_items[index];
1063 Q_ASSERT(m_currentItem);
1064 m_currentItem->forceActiveFocus();
1067 QRectF mappedCurrent = m_currentItem->mapRectToItem(
this, QRectF(QPointF(0, 0), m_currentItem->size()));
1070 mappedCurrent.
moveLeft(mappedCurrent.
left() + m_contentItem->x() + m_contentItem->m_slideAnim->endValue().toInt());
1075 QRectF contentsRect(m_contentItem->m_leftPinnedSpace,
1077 width() - m_contentItem->m_rightPinnedSpace - m_contentItem->m_leftPinnedSpace,
1081 if (!contentsRect.contains(mappedCurrent)) {
1082 m_contentItem->m_viewAnchorItem = m_currentItem;
1084 m_contentItem->animateX(-m_currentItem->x() - m_currentItem->width() +
width());
1086 m_contentItem->animateX(-m_currentItem->x() + m_contentItem->m_leftPinnedSpace);
1089 m_contentItem->snapToItem();
1094 Q_EMIT currentIndexChanged();
1095 Q_EMIT currentItemChanged();
1100 return m_currentItem;
1105 return m_contentItem->m_visibleItems;
1110 if (m_contentItem->m_visibleItems.isEmpty()) {
1114 return m_contentItem->m_visibleItems.first();
1119 if (m_contentItem->m_visibleItems.isEmpty()) {
1128 return m_contentItem->m_items.count();
1133 return m_topPadding;
1136void ColumnView::setTopPadding(qreal padding)
1138 if (padding == m_topPadding) {
1142 m_topPadding = padding;
1144 Q_EMIT topPaddingChanged();
1149 return m_bottomPadding;
1152void ColumnView::setBottomPadding(qreal padding)
1154 if (padding == m_bottomPadding) {
1158 m_bottomPadding = padding;
1160 Q_EMIT bottomPaddingChanged();
1165 return m_contentItem;
1170 return m_contentItem->m_slideAnim->duration();
1173void ColumnView::setScrollDuration(
int duration)
1175 disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this,
nullptr);
1177 if (m_contentItem->m_slideAnim->duration() == duration) {
1181 m_contentItem->m_slideAnim->setDuration(duration);
1182 Q_EMIT scrollDurationChanged();
1187 return m_separatorVisible;
1190void ColumnView::setSeparatorVisible(
bool visible)
1192 if (
visible == m_separatorVisible) {
1198 Q_EMIT separatorVisibleChanged();
1213 return m_contentItem->width();
1218 return -m_contentItem->x();
1221void ColumnView::setContentX(qreal x)
const
1223 m_contentItem->setX(qRound(-
x));
1228 return m_interactive;
1231void ColumnView::setInteractive(
bool interactive)
1239 if (!m_interactive) {
1242 Q_EMIT draggingChanged();
1245 m_contentItem->snapToItem();
1249 Q_EMIT interactiveChanged();
1254 return m_acceptsMouse;
1257void ColumnView::setAcceptsMouse(
bool accepts)
1259 if (m_acceptsMouse == accepts) {
1263 m_acceptsMouse = accepts;
1265 if (!m_acceptsMouse) {
1268 Q_EMIT draggingChanged();
1271 m_contentItem->snapToItem();
1275 Q_EMIT acceptsMouseChanged();
1280 insertItem(m_contentItem->m_items.length(), item);
1285 if (!item || m_contentItem->m_items.
contains(item)) {
1289 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1295 attached->setOriginalParent(item->
parentItem());
1301 if (attached->globalHeader()) {
1302 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1304 if (attached->globalFooter()) {
1305 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1307 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1308 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1311 m_contentItem->m_shouldAnimate =
true;
1312 m_contentItem->layoutItems();
1313 Q_EMIT contentChildrenChanged();
1317 if (m_currentIndex >= pos) {
1319 Q_EMIT currentIndexChanged();
1327 if (pos < 0 || pos >= m_contentItem->m_items.length()) {
1328 qCWarning(KirigamiLayoutsLog) <<
"Position" << pos <<
"passed to ColumnView::replaceItem is out of range.";
1333 qCWarning(KirigamiLayoutsLog) <<
"Null item passed to ColumnView::replaceItem.";
1337 QQuickItem *oldItem = m_contentItem->m_items[pos];
1340 if (m_currentIndex >= pos) {
1341 setCurrentIndex(m_currentIndex - 1);
1344 m_contentItem->forgetItem(oldItem);
1349 if (attached && attached->shouldDeleteOnRemove()) {
1352 oldItem->
setParentItem(attached ? attached->originalParent() :
nullptr);
1357 if (!m_contentItem->m_items.contains(item)) {
1358 m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item);
1364 attached->setOriginalParent(item->
parentItem());
1368 if (attached->globalHeader()) {
1369 m_contentItem->connectHeader(
nullptr, attached->globalHeader());
1371 if (attached->globalFooter()) {
1372 m_contentItem->connectFooter(
nullptr, attached->globalFooter());
1374 connect(attached, &ColumnViewAttached::globalHeaderChanged, m_contentItem, &ContentItem::connectHeader);
1375 connect(attached, &ColumnViewAttached::globalFooterChanged, m_contentItem, &ContentItem::connectFooter);
1377 if (m_currentIndex >= pos) {
1379 Q_EMIT currentIndexChanged();
1386 m_contentItem->m_shouldAnimate =
false;
1387 m_contentItem->layoutItems();
1388 Q_EMIT contentChildrenChanged();
1393 if (m_contentItem->m_items.isEmpty()
1394 || from < 0 || from >= m_contentItem->m_items.length()
1395 || to < 0 || to >= m_contentItem->m_items.length()) {
1399 m_contentItem->m_items.move(from, to);
1400 m_contentItem->m_shouldAnimate =
true;
1402 if (from == m_currentIndex) {
1403 m_currentIndex = to;
1404 Q_EMIT currentIndexChanged();
1405 }
else if (from < m_currentIndex && to > m_currentIndex) {
1407 Q_EMIT currentIndexChanged();
1408 }
else if (from > m_currentIndex && to <= m_currentIndex) {
1410 Q_EMIT currentIndexChanged();
1418 if (m_contentItem->m_items.isEmpty() || !m_contentItem->m_items.contains(item)) {
1422 const int index = m_contentItem->m_items.indexOf(item);
1425 if (m_currentIndex >= index) {
1426 setCurrentIndex(m_currentIndex - 1);
1429 m_contentItem->forgetItem(item);
1434 if (attached && attached->shouldDeleteOnRemove()) {
1437 item->
setParentItem(attached ? attached->originalParent() :
nullptr);
1440 Q_EMIT contentChildrenChanged();
1448 if (m_contentItem->m_items.isEmpty() || index < 0 || index >=
count()) {
1451 return removeItem(m_contentItem->m_items[index]);
1472 }
else if (item.
isNull()) {
1481 while (!m_contentItem->m_items.isEmpty() && m_contentItem->m_items.last() != item) {
1482 removed =
removeItem(m_contentItem->m_items.last());
1489 if (index >= 0 && index <
count() - 1) {
1490 return pop(m_contentItem->m_items.at(index));
1491 }
else if (index == -1) {
1492 return pop(
nullptr);
1508 while (!m_contentItem->m_items.isEmpty()) {
1509 QQuickItem *item = m_contentItem->m_items.first();
1513 m_contentItem->m_items.clear();
1514 Q_EMIT contentChildrenChanged();
1519 return m_contentItem->m_items.contains(item);
1532void ColumnView::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1534 m_contentItem->setY(m_topPadding);
1535 m_contentItem->setHeight(newGeometry.
height() - m_topPadding - m_bottomPadding);
1536 m_contentItem->m_shouldAnimate =
false;
1539 m_contentItem->updateVisibleItems();
1545 if (!m_interactive || item == m_contentItem) {
1549 switch (
event->type()) {
1551 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1562 if (
int idx = m_contentItem->m_items.indexOf(candidateItem); idx >= 0 && candidateItem->
parentItem() == m_contentItem) {
1563 setCurrentIndex(idx);
1568 event->setAccepted(
false);
1572 m_contentItem->m_slideAnim->stop();
1574 m_contentItem->snapToItem();
1587 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1599 bool verticalScrollIntercepted =
false;
1605 if (candidateItem->
parentItem() == m_contentItem) {
1615 ScrollIntentionEvent scrollIntentionEvent;
1616 scrollIntentionEvent.delta = QPointF(pos.
x() - m_oldMouseX, pos.
y() - m_oldMouseY);
1618 Q_EMIT attached->scrollIntention(&scrollIntentionEvent);
1620 if (scrollIntentionEvent.accepted) {
1621 verticalScrollIntercepted =
true;
1622 event->setAccepted(
true);
1627 m_contentItem->snapToItem();
1628 m_oldMouseX = pos.
x();
1629 m_oldMouseY = pos.
y();
1633 const bool wasDragging = m_dragging;
1637 if (m_dragging != wasDragging) {
1640 Q_EMIT draggingChanged();
1644 m_contentItem->setBoundedX(m_contentItem->x() + pos.
x() - m_oldMouseX);
1647 m_contentItem->m_lastDragDelta = pos.
x() - m_oldMouseX;
1648 m_oldMouseX = pos.
x();
1649 m_oldMouseY = pos.
y();
1654 return m_dragging && !verticalScrollIntercepted;
1657 QMouseEvent *me =
static_cast<QMouseEvent *
>(
event);
1663 setCurrentIndex(m_currentIndex - 1);
1667 setCurrentIndex(m_currentIndex + 1);
1680 m_mouseDown =
false;
1683 m_contentItem->snapToItem();
1684 m_contentItem->m_lastDragDelta = 0;
1686 Q_EMIT draggingChanged();
1705void ColumnView::mousePressEvent(
QMouseEvent *event)
1708 event->setAccepted(
false);
1717 if (!m_interactive) {
1721 m_contentItem->snapToItem();
1722 m_oldMouseX =
event->position().x();
1723 m_startMouseX =
event->position().x();
1729void ColumnView::mouseMoveEvent(
QMouseEvent *event)
1736 if (!m_interactive) {
1740 const bool wasDragging = m_dragging;
1742 m_dragging =
keepMouseGrab() || qAbs(
event->position().x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 2;
1743 if (m_dragging != wasDragging) {
1746 Q_EMIT draggingChanged();
1752 m_contentItem->setBoundedX(m_contentItem->x() +
event->pos().x() - m_oldMouseX);
1755 m_contentItem->m_lastDragDelta =
event->pos().x() - m_oldMouseX;
1756 m_oldMouseX =
event->pos().x();
1760void ColumnView::mouseReleaseEvent(
QMouseEvent *event)
1763 setCurrentIndex(m_currentIndex - 1);
1767 setCurrentIndex(m_currentIndex + 1);
1772 m_mouseDown =
false;
1774 if (!m_interactive) {
1778 m_contentItem->snapToItem();
1779 m_contentItem->m_lastDragDelta = 0;
1783 Q_EMIT draggingChanged();
1790void ColumnView::mouseUngrabEvent()
1792 m_mouseDown =
false;
1795 m_contentItem->snapToItem();
1797 m_contentItem->m_lastDragDelta = 0;
1801 Q_EMIT draggingChanged();
1807void ColumnView::classBegin()
1809 auto syncColumnWidth = [
this]() {
1810 m_contentItem->m_columnWidth = privateQmlComponentsPoolSelf->instance(qmlEngine(
this))->m_units->gridUnit() * 20;
1811 Q_EMIT columnWidthChanged();
1814 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::gridUnitChanged,
this, syncColumnWidth);
1817 auto syncDuration = [
this]() {
1818 m_contentItem->m_slideAnim->setDuration(QmlComponentsPoolSingleton::instance(qmlEngine(
this))->m_units->veryLongDuration());
1819 Q_EMIT scrollDurationChanged();
1822 connect(QmlComponentsPoolSingleton::instance(qmlEngine(
this)), &QmlComponentsPool::longDurationChanged,
this, syncDuration);
1828void ColumnView::componentComplete()
1834void ColumnView::updatePolish()
1836 m_contentItem->layoutItems();
1843 if (m_contentItem && value.item != m_contentItem && !value.item->inherits(
"QQuickRepeater")) {
1856 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1861 view->m_contentItem->m_items.append(item);
1863 view->removeItem(item);
1867 attached->setOriginalParent(item->
parentItem());
1875 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1880 return view->m_contentItem->m_items.count();
1885 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1890 if (index < 0 || index >= view->m_contentItem->m_items.count()) {
1893 return view->m_contentItem->m_items.value(index);
1898 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1903 return view->m_contentItem->m_items.clear();
1908 return QQmlListProperty<QQuickItem>(
this,
1910 contentChildren_append,
1911 contentChildren_count,
1913 contentChildren_clear);
1918 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1923 view->m_contentData.
append(
object);
1926 if (item && item->
inherits(
"QQuickRepeater")) {
1929 connect(item, SIGNAL(modelChanged()), view->m_contentItem, SLOT(updateRepeaterModel()));
1932 view->m_contentItem->m_items.append(item);
1934 view->removeItem(item);
1938 attached->setOriginalParent(item->
parentItem());
1944 object->setParent(view);
1950 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1955 return view->m_contentData.
count();
1960 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1965 if (index < 0 || index >= view->m_contentData.
count()) {
1968 return view->m_contentData.
value(index);
1973 ColumnView *view =
static_cast<ColumnView *
>(prop->object);
1978 return view->m_contentData.
clear();
1983 return QQmlListProperty<QObject>(
this,
1991#include "moc_columnview.cpp"
1992#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