]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistview.cpp
Compact view: Padding- and margin-improvements for grouped alignments
[dolphin.git] / src / kitemviews / kitemlistview.cpp
index 451e5183369c460cae44352f4358c7263768a781..b7d4c247097577ea8759832e57138b1f26397d43 100644 (file)
@@ -833,6 +833,7 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
 
         if (!hasMultipleRanges) {
             doLayout(animateChangedItemCount(count) ? Animation : NoAnimation, index, count);
+            updateSiblingsInformation();
         }
     }
 
@@ -841,7 +842,9 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
     }
 
     if (hasMultipleRanges) {
+        m_endTransactionAnimationHint = NoAnimation;
         endTransaction();
+        updateSiblingsInformation();
     }
 }
 
@@ -919,6 +922,7 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
             m_activeTransactions = 0;
             doLayout(animateChangedItemCount(count) ? Animation : NoAnimation, index, -count);
             m_activeTransactions = activeTransactions;
+            updateSiblingsInformation();
         }
     }
 
@@ -927,7 +931,9 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
     }
 
     if (hasMultipleRanges) {
+        m_endTransactionAnimationHint = NoAnimation;
         endTransaction();
+        updateSiblingsInformation();
     }
 }
 
@@ -955,6 +961,7 @@ void KItemListView::slotItemsMoved(const KItemRange& itemRange, const QList<int>
     }
 
     doLayout(NoAnimation);
+    updateSiblingsInformation();
 }
 
 void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges,
@@ -1224,6 +1231,15 @@ void KItemListView::triggerAutoScrolling()
    m_autoScrollTimer->start(RepeatingAutoScrollDelay);
 }
 
+void KItemListView::slotGeometryOfGroupHeaderParentChanged()
+{
+    KItemListWidget* widget = qobject_cast<KItemListWidget*>(sender());
+    Q_ASSERT(widget);
+    KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget);
+    Q_ASSERT(groupHeader);
+    updateGroupHeaderLayout(widget);
+}
+
 void KItemListView::setController(KItemListController* controller)
 {
     if (m_controller != controller) {
@@ -1337,6 +1353,10 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha
 
     const int lastVisibleIndex = m_layouter->lastVisibleIndex();
 
+    int firstSibblingIndex = -1;
+    int lastSibblingIndex = -1;
+    const bool supportsExpanding = supportsItemExpanding();
+
     QList<int> reusableItems = recycleInvisibleItems(firstVisibleIndex, lastVisibleIndex, hint);
 
     // Assure that for each visible item a KItemListWidget is available. KItemListWidget
@@ -1381,6 +1401,13 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha
                 }
                 applyNewPos = false;
             }
+
+            if (supportsExpanding && changedCount == 0) {
+                if (firstSibblingIndex < 0) {
+                    firstSibblingIndex = i;
+                }
+                lastSibblingIndex = i;
+            }
         }
 
         if (animate) {
@@ -1443,6 +1470,11 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha
         recycleWidget(m_visibleItems.value(index));
     }
 
+    if (supportsExpanding && firstSibblingIndex >= 0) {
+        Q_ASSERT(lastSibblingIndex >= 0);
+        updateSiblingsInformation(firstSibblingIndex, lastSibblingIndex);
+    }
+
     if (m_grouped) {
         // Update the layout of all visible group headers
         QHashIterator<KItemListWidget*, KItemListGroupHeader*> it(m_visibleGroups);
@@ -1618,6 +1650,7 @@ void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index)
     widget->setEnabledSelectionToggle(enabledSelectionToggles());
     widget->setIndex(index);
     widget->setData(m_model->data(index));
+    widget->setSiblingsInformation(QBitArray());
 }
 
 void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
@@ -1642,6 +1675,7 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
         groupHeader = m_groupHeaderCreator->create(this);
         groupHeader->setParentItem(widget);
         m_visibleGroups.insert(widget, groupHeader);
+        connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged()));
     }
     Q_ASSERT(groupHeader->parentItem() == widget);
 
@@ -1663,6 +1697,7 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
     groupHeader->setRole(model()->sortRole());
     groupHeader->setStyleOption(m_styleOption);
     groupHeader->setScrollOrientation(scrollOrientation());
+    groupHeader->setItemIndex(index);
 
     groupHeader->show();
 }
@@ -1678,10 +1713,16 @@ void KItemListView::updateGroupHeaderLayout(KItemListWidget* widget)
 
     // The group-header is a child of the itemlist widget. Translate the
     // group header position to the relative position.
-    const QPointF groupHeaderPos(groupHeaderRect.x() - itemRect.x(),
-                                 - groupHeaderRect.height());
-    groupHeader->setPos(groupHeaderPos);
-    groupHeader->resize(groupHeaderRect.size());
+    if (scrollOrientation() == Qt::Vertical) {
+        // In the vertical scroll orientation the group header should always span
+        // the whole width no matter which temporary position the parent widget
+        // has. In this case the x-position and width will be adjusted manually.
+        groupHeader->setPos(-widget->x(), -groupHeaderRect.height());
+        groupHeader->resize(size().width(), groupHeaderRect.size().height());
+    } else {
+        groupHeader->setPos(groupHeaderRect.x() - itemRect.x(), -widget->y());
+        groupHeader->resize(groupHeaderRect.size());
+    }
 }
 
 void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget)
@@ -1691,6 +1732,7 @@ void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget)
         header->setParentItem(0);
         m_groupHeaderCreator->recycle(header);
         m_visibleGroups.remove(widget);
+        disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged()));
     }
 }
 
@@ -1935,13 +1977,154 @@ bool KItemListView::scrollBarRequired(const QSizeF& size) const
 void KItemListView::updateGroupHeaderHeight()
 {
     qreal groupHeaderHeight = m_styleOption.fontMetrics.height();
-    groupHeaderHeight += (scrollOrientation() == Qt::Vertical)
-                         ? m_styleOption.padding * 4 : m_styleOption.padding * 2;
+    qreal groupHeaderMargin = 0;
+    
+    if (scrollOrientation() == Qt::Horizontal) {
+        // The vertical margin above and below the header should be
+        // equal to the horizontal margin, not the vertical margin
+        // from m_styleOption.
+        groupHeaderHeight += 2 * m_styleOption.horizontalMargin;
+        groupHeaderMargin = m_styleOption.horizontalMargin;
+    } else if (m_itemSize.isEmpty()){
+        groupHeaderHeight += 2 * m_styleOption.padding;
+        groupHeaderMargin = m_styleOption.iconSize / 2;
+    } else {
+        groupHeaderHeight += 2 * m_styleOption.padding;
+        groupHeaderMargin = m_styleOption.iconSize / 4;
+    }
     m_layouter->setGroupHeaderHeight(groupHeaderHeight);
+    m_layouter->setGroupHeaderMargin(groupHeaderMargin);
 
     updateVisibleGroupHeaders();
 }
 
+void KItemListView::updateSiblingsInformation(int firstIndex, int lastIndex)
+{
+    if (!supportsItemExpanding()) {
+        return;
+    }
+
+    if (firstIndex < 0 || lastIndex < 0) {
+        firstIndex = m_layouter->firstVisibleIndex();
+        lastIndex  = m_layouter->lastVisibleIndex();
+    } else {
+        const bool isRangeVisible = (firstIndex <= m_layouter->lastVisibleIndex() &&
+                                     lastIndex  >= m_layouter->firstVisibleIndex());
+        if (!isRangeVisible) {
+            return;
+        }
+    }
+
+    int previousParents = 0;
+    QBitArray previousSiblings;
+
+    // The rootIndex describes the first index where the siblings get
+    // calculated from. For the calculation the upper most parent item
+    // is required. For performance reasons it is checked first whether
+    // the visible items before or after the current range already
+    // contain a siblings information which can be used as base.
+    int rootIndex = firstIndex;
+
+    KItemListWidget* widget = m_visibleItems.value(firstIndex - 1);
+    if (!widget) {
+        // There is no visible widget before the range, check whether there
+        // is one after the range:
+        widget = m_visibleItems.value(lastIndex + 1);
+        if (widget) {
+            // The sibling information of the widget may only be used if
+            // all items of the range have the same number of parents.
+            const int parents = m_model->expandedParentsCount(lastIndex + 1);
+            for (int i = lastIndex; i >= firstIndex; --i) {
+                if (m_model->expandedParentsCount(i) != parents) {
+                    widget = 0;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (widget) {
+        // Performance optimization: Use the sibling information of the visible
+        // widget beside the given range.
+        previousSiblings = widget->siblingsInformation();
+        if (previousSiblings.isEmpty()) {
+            return;
+        }
+        previousParents = previousSiblings.count() - 1;
+        previousSiblings.truncate(previousParents);
+    } else {
+        // Potentially slow path: Go back to the upper most parent of firstIndex
+        // to be able to calculate the initial value for the siblings.
+        while (rootIndex > 0 && m_model->expandedParentsCount(rootIndex) > 0) {
+            --rootIndex;
+        }
+    }
+
+    Q_ASSERT(previousParents >= 0);
+    for (int i = rootIndex; i <= lastIndex; ++i) {
+        // Update the parent-siblings in case if the current item represents
+        // a child or an upper parent.
+        const int currentParents = m_model->expandedParentsCount(i);
+        Q_ASSERT(currentParents >= 0);
+        if (previousParents < currentParents) {
+            previousParents = currentParents;
+            previousSiblings.resize(currentParents);
+            previousSiblings.setBit(currentParents - 1, hasSiblingSuccessor(i - 1));
+        } else if (previousParents > currentParents) {
+            previousParents = currentParents;
+            previousSiblings.truncate(currentParents);
+        }
+
+        if (i >= firstIndex) {
+            // The index represents a visible item. Apply the parent-siblings
+            // and update the sibling of the current item.
+            KItemListWidget* widget = m_visibleItems.value(i);
+            if (!widget) {
+                continue;
+            }
+
+            QBitArray siblings = previousSiblings;
+            siblings.resize(siblings.count() + 1);
+            siblings.setBit(siblings.count() - 1, hasSiblingSuccessor(i));
+
+            widget->setSiblingsInformation(siblings);
+        }
+    }
+}
+
+bool KItemListView::hasSiblingSuccessor(int index) const
+{
+    bool hasSuccessor = false;
+    const int parentsCount = m_model->expandedParentsCount(index);
+    int successorIndex = index + 1;
+
+    // Search the next sibling
+    const int itemCount = m_model->count();
+    while (successorIndex < itemCount) {
+        const int currentParentsCount = m_model->expandedParentsCount(successorIndex);
+        if (currentParentsCount == parentsCount) {
+            hasSuccessor = true;
+            break;
+        } else if (currentParentsCount < parentsCount) {
+            break;
+        }
+        ++successorIndex;
+    }
+
+    if (m_grouped && hasSuccessor) {
+        // If the sibling is part of another group, don't mark it as
+        // successor as the group header is between the sibling connections.
+        for (int i = index + 1; i <= successorIndex; ++i) {
+            if (m_layouter->isFirstGroupItem(i)) {
+                hasSuccessor = false;
+                break;
+            }
+        }
+    }
+
+    return hasSuccessor;
+}
+
 int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc)
 {
     int inc = 0;