From dbc5fd7a491f95ca4084a113d0f902ea975478fd Mon Sep 17 00:00:00 2001 From: Peter Penz Date: Wed, 22 Feb 2012 18:28:11 +0100 Subject: [PATCH] Fix alternate backgrounds when enabling grouping Up to now the alternating backgrounds just have been calculated by checking whether the item index is odd. This does not work well when grouping is enabled: In this case the alternate background color of the first item of a group should stay consistent. --- src/kitemviews/kfileitemlistview.cpp | 18 +-- src/kitemviews/kitemlistview.cpp | 159 ++++++++++++++++------- src/kitemviews/kitemlistview.h | 29 ++++- src/kitemviews/kitemlistviewlayouter.cpp | 8 ++ src/kitemviews/kitemlistviewlayouter_p.h | 9 ++ src/kitemviews/kitemlistwidget.cpp | 18 +-- src/kitemviews/kitemlistwidget.h | 8 +- 7 files changed, 172 insertions(+), 77 deletions(-) diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index 25f7738bf..27ef7fdb5 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -324,9 +324,6 @@ void KFileItemListView::initializeItemListWidget(KItemListWidget* item) case DetailsLayout: fileItemListWidget->setLayout(KFileItemListWidget::DetailsLayout); break; default: Q_ASSERT(false); break; } - - fileItemListWidget->setAlternatingBackgroundColors(m_itemLayout == DetailsLayout && - visibleRoles().count() > 1); } bool KFileItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const @@ -379,20 +376,9 @@ void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous) void KFileItemListView::onVisibleRolesChanged(const QList& current, const QList& previous) { + Q_UNUSED(current); + Q_UNUSED(previous); applyRolesToModel(); - - if (m_itemLayout == DetailsLayout) { - // Only enable the alternating background colors if more than one role - // is visible - const int previousCount = previous.count(); - const int currentCount = current.count(); - if ((previousCount <= 1 && currentCount > 1) || (previousCount > 1 && currentCount <= 1)) { - const bool enabled = (currentCount > 1); - foreach (KItemListWidget* widget, visibleItemListWidgets()) { - widget->setAlternatingBackgroundColors(enabled); - } - } - } } void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 907bf624d..527436725 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -156,9 +156,24 @@ void KItemListView::setItemSize(const QSizeF& itemSize) const bool animate = !changesItemGridLayout(m_layouter->size(), itemSize, m_layouter->itemMargin()); - + + const bool updateAlternateBackgrounds = (m_visibleRoles.count() > 1) && + (( m_itemSize.isEmpty() && !itemSize.isEmpty()) || + (!m_itemSize.isEmpty() && itemSize.isEmpty())); + m_itemSize = itemSize; + if (updateAlternateBackgrounds) { + // For an empty item size alternate backgrounds are drawn if more than + // one role is shown. Assure that the backgrounds for visible items are + // updated when changing the size in this context. + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateAlternateBackgroundForWidget(it.value()); + } + } + if (itemSize.isEmpty()) { updateVisibleRolesSizes(); } else { @@ -238,12 +253,19 @@ void KItemListView::setVisibleRoles(const QList& roles) const QList previousRoles = m_visibleRoles; m_visibleRoles = roles; + const bool updateAlternateBackgrounds = m_itemSize.isEmpty() && + ((roles.count() > 1 && previousRoles.count() <= 1) || + (roles.count() <= 1 && previousRoles.count() > 1)); + QHashIterator it(m_visibleItems); while (it.hasNext()) { it.next(); KItemListWidget* widget = it.value(); widget->setVisibleRoles(roles); widget->setVisibleRolesSizes(m_stretchedVisibleRolesSizes); + if (updateAlternateBackgrounds) { + updateAlternateBackgroundForWidget(widget); + } } m_sizeHintResolver->clearCache(); @@ -785,9 +807,6 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) beginTransaction(); } - // Important: Don't read any m_layouter-property inside the for-loop in case if - // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is - // updated in each loop-cycle and has only a consistent state after the loop. m_layouter->markAsDirty(); int previouslyInsertedCount = 0; @@ -823,11 +842,12 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) for (int i = itemsToMove.count() - 1; i >= 0; --i) { KItemListWidget* widget = m_visibleItems.value(itemsToMove[i]); Q_ASSERT(widget); + const int newIndex = widget->index() + count; if (hasMultipleRanges) { - setWidgetIndex(widget, widget->index() + count); + setWidgetIndex(widget, newIndex); } else { // Try to animate the moving of the item - moveWidgetToIndex(widget, widget->index() + count); + moveWidgetToIndex(widget, newIndex); } } @@ -861,6 +881,12 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) } if (hasMultipleRanges) { +#ifndef QT_NO_DEBUG + // Important: Don't read any m_layouter-property inside the for-loop in case if + // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is + // updated in each loop-cycle and has only a consistent state after the loop. + Q_ASSERT(m_layouter->isDirty()); +#endif m_endTransactionAnimationHint = NoAnimation; endTransaction(); updateSiblingsInformation(); @@ -876,9 +902,6 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) beginTransaction(); } - // Important: Don't read any m_layouter-property inside the for-loop in case if - // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is - // updated in each loop-cycle and has only a consistent state after the loop. m_layouter->markAsDirty(); for (int i = itemRanges.count() - 1; i >= 0; --i) { @@ -959,6 +982,12 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) } if (hasMultipleRanges) { +#ifndef QT_NO_DEBUG + // Important: Don't read any m_layouter-property inside the for-loop in case if + // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is + // updated in each loop-cycle and has only a consistent state after the loop. + Q_ASSERT(m_layouter->isDirty()); +#endif m_endTransactionAnimationHint = NoAnimation; endTransaction(); updateSiblingsInformation(); @@ -981,9 +1010,6 @@ void KItemListView::slotItemsMoved(const KItemRange& itemRange, const QList KItemListWidget* widget = m_visibleItems.value(index); if (widget) { updateWidgetProperties(widget, index); - if (m_grouped) { - updateGroupHeaderForWidget(widget); - } initializeItemListWidget(widget); } } @@ -1048,6 +1074,17 @@ void KItemListView::slotGroupedSortingChanged(bool current) Q_ASSERT(m_visibleGroups.isEmpty()); } + if (useAlternateBackgrounds()) { + // Changing the group mode requires to update the alternate backgrounds + // as with the enabled group mode the altering is done on base of the first + // group item. + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateAlternateBackgroundForWidget(it.value()); + } + } + doLayout(NoAnimation); } @@ -1077,13 +1114,11 @@ void KItemListView::slotCurrentChanged(int current, int previous) KItemListWidget* previousWidget = m_visibleItems.value(previous, 0); if (previousWidget) { - Q_ASSERT(previousWidget->isCurrent()); previousWidget->setCurrent(false); } KItemListWidget* currentWidget = m_visibleItems.value(current, 0); if (currentWidget) { - Q_ASSERT(!currentWidget->isCurrent()); currentWidget->setCurrent(true); } } @@ -1406,10 +1441,8 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha const int oldIndex = reusableItems.takeLast(); widget = m_visibleItems.value(oldIndex); setWidgetIndex(widget, i); - - if (m_grouped) { - updateGroupHeaderForWidget(widget); - } + updateWidgetProperties(widget, i); + initializeItemListWidget(widget); } else { // No reusable KItemListWidget instance is available, create a new one widget = createWidget(i); @@ -1626,14 +1659,9 @@ KItemListWidget* KItemListView::createWidget(int index) KItemListWidget* widget = m_widgetCreator->create(this); widget->setFlag(QGraphicsItem::ItemStacksBehindParent); - updateWidgetProperties(widget, index); m_visibleItems.insert(index, widget); m_visibleCells.insert(index, Cell()); - - if (m_grouped) { - updateGroupHeaderForWidget(widget); - } - + updateWidgetProperties(widget, index); initializeItemListWidget(widget); return widget; } @@ -1654,16 +1682,13 @@ void KItemListView::recycleWidget(KItemListWidget* widget) void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) { const int oldIndex = widget->index(); - m_visibleItems.remove(oldIndex); m_visibleCells.remove(oldIndex); - updateWidgetProperties(widget, index); - m_visibleItems.insert(index, widget); m_visibleCells.insert(index, Cell()); - initializeItemListWidget(widget); + widget->setIndex(index); } void KItemListView::moveWidgetToIndex(KItemListWidget* widget, int index) @@ -1701,11 +1726,15 @@ void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index) widget->setCurrent(index == selectionManager->currentItem()); widget->setSelected(selectionManager->isSelected(index)); widget->setHovered(false); - widget->setAlternatingBackgroundColors(false); widget->setEnabledSelectionToggle(enabledSelectionToggles()); widget->setIndex(index); widget->setData(m_model->data(index)); widget->setSiblingsInformation(QBitArray()); + updateAlternateBackgroundForWidget(widget); + + if (m_grouped) { + updateGroupHeaderForWidget(widget); + } } void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) @@ -1734,21 +1763,9 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) } Q_ASSERT(groupHeader->parentItem() == widget); - // Determine the shown data for the header by doing a binary - // search in the groups-list - int min = 0; - int max = groups.count() - 1; - int mid = 0; - do { - mid = (min + max) / 2; - if (index > groups.at(mid).first) { - min = mid + 1; - } else { - max = mid - 1; - } - } while (groups.at(mid).first != index && min <= max); - - groupHeader->setData(groups.at(mid).second); + const int groupIndex = groupIndexForItem(index); + Q_ASSERT(groupIndex >= 0); + groupHeader->setData(groups.at(groupIndex).second); groupHeader->setRole(model()->sortRole()); groupHeader->setStyleOption(m_styleOption); groupHeader->setScrollOrientation(scrollOrientation()); @@ -1803,6 +1820,60 @@ void KItemListView::updateVisibleGroupHeaders() } } +int KItemListView::groupIndexForItem(int index) const +{ + Q_ASSERT(m_grouped); + + const QList > groups = model()->groups(); + if (groups.isEmpty()) { + return -1; + } + + int min = 0; + int max = groups.count() - 1; + int mid = 0; + do { + mid = (min + max) / 2; + if (index > groups[mid].first) { + min = mid + 1; + } else { + max = mid - 1; + } + } while (groups[mid].first != index && min <= max); + + if (min > max) { + while (groups[mid].first > index && mid > 0) { + --mid; + } + } + + return mid; +} + +void KItemListView::updateAlternateBackgroundForWidget(KItemListWidget* widget) +{ + bool enabled = useAlternateBackgrounds(); + if (enabled) { + const int index = widget->index(); + enabled = (index & 0x1) > 0; + if (m_grouped) { + const int groupIndex = groupIndexForItem(index); + if (groupIndex >= 0) { + const QList > groups = model()->groups(); + const int indexOfFirstGroupItem = groups[groupIndex].first; + const int relativeIndex = index - indexOfFirstGroupItem; + enabled = (relativeIndex & 0x1) == 0; + } + } + } + widget->setAlternateBackground(enabled); +} + +bool KItemListView::useAlternateBackgrounds() const +{ + return m_itemSize.isEmpty() && m_visibleRoles.count() > 1; +} + QHash KItemListView::headerRolesWidths() const { QHash rolesWidths; diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index d524dbdf4..17b7b8880 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -396,14 +396,15 @@ private: void recycleWidget(KItemListWidget* widget); /** - * Changes the index of the widget to \a index. The cell-information - * for the widget gets reset and will be updated in the next doLayout(). + * Changes the index of the widget to \a index and assures a consistent + * update for m_visibleItems and m_visibleCells. The cell-information + * for the new index will not be updated and be initialized as empty cell. */ void setWidgetIndex(KItemListWidget* widget, int index); /** * Changes the index of the widget to \a index. In opposite to - * setWidgetIndex() the cell-information of the widget gets updated. + * setWidgetIndex() the cell-information for the widget gets updated. * This update gives doLayout() the chance to animate the moving * of the item visually (see moveWidget()). */ @@ -421,7 +422,7 @@ private: void updateWidgetProperties(KItemListWidget* widget, int index); /** - * Helper method for createWidget() and setWidgetIndex() to create or update + * Helper method for updateWidgetPropertes() to create or update * the itemlist group-header. */ void updateGroupHeaderForWidget(KItemListWidget* widget); @@ -445,6 +446,26 @@ private: */ void updateVisibleGroupHeaders(); + /** + * @return Index for the item in the list returned by KItemModelBase::groups() + * that represents the group where the item with the index \a index + * belongs to. -1 is returned if no groups are available. + */ + int groupIndexForItem(int index) const; + + /** + * Updates the alternateBackground-property of the widget dependent + * on the state of useAlternateBackgrounds() and the grouping state. + */ + void updateAlternateBackgroundForWidget(KItemListWidget* widget); + + /** + * @return True if alternate backgrounds should be used for the items. + * This is the case if an empty item-size is given and if there + * is more than one visible role. + */ + bool useAlternateBackgrounds() const; + /** * @return The widths of each visible role that is shown in the KItemListHeader. */ diff --git a/src/kitemviews/kitemlistviewlayouter.cpp b/src/kitemviews/kitemlistviewlayouter.cpp index 744914929..d8b5796ec 100644 --- a/src/kitemviews/kitemlistviewlayouter.cpp +++ b/src/kitemviews/kitemlistviewlayouter.cpp @@ -340,6 +340,14 @@ void KItemListViewLayouter::markAsDirty() m_dirty = true; } + +#ifndef QT_NO_DEBUG + bool KItemListViewLayouter::isDirty() + { + return m_dirty; + } +#endif + void KItemListViewLayouter::doLayout() { if (m_dirty) { diff --git a/src/kitemviews/kitemlistviewlayouter_p.h b/src/kitemviews/kitemlistviewlayouter_p.h index 017f520fe..0d7c0d040 100644 --- a/src/kitemviews/kitemlistviewlayouter_p.h +++ b/src/kitemviews/kitemlistviewlayouter_p.h @@ -139,6 +139,15 @@ public: void markAsDirty(); +#ifndef QT_NO_DEBUG + /** + * @return True if the layouter has been marked as dirty and hence has + * not called yet doLayout(). Is enabled only in the debugging + * mode, as it is not useful to check the dirty state otherwise. + */ + bool isDirty(); +#endif + private: void doLayout(); void updateVisibleIndexes(); diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 94eed6840..9d58a283e 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -40,7 +40,7 @@ KItemListWidget::KItemListWidget(QGraphicsItem* parent) : m_selected(false), m_current(false), m_hovered(false), - m_alternatingBackgroundColors(false), + m_alternateBackground(false), m_enabledSelectionToggle(false), m_data(), m_visibleRoles(), @@ -105,7 +105,7 @@ void KItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* o { Q_UNUSED(option); - if (m_alternatingBackgroundColors && (m_index & 0x1)) { + if (m_alternateBackground) { const QColor backgroundColor = m_styleOption.palette.color(QPalette::AlternateBase); const QRectF backgroundRect(0, 0, size().width(), size().height()); painter->fillRect(backgroundRect, backgroundColor); @@ -276,18 +276,18 @@ bool KItemListWidget::isHovered() const return m_hovered; } -void KItemListWidget::setAlternatingBackgroundColors(bool enable) +void KItemListWidget::setAlternateBackground(bool enable) { - if (m_alternatingBackgroundColors != enable) { - m_alternatingBackgroundColors = enable; - alternatingBackgroundColorsChanged(enable); + if (m_alternateBackground != enable) { + m_alternateBackground = enable; + alternateBackgroundChanged(enable); update(); } } -bool KItemListWidget::alternatingBackgroundColors() const +bool KItemListWidget::alternateBackground() const { - return m_alternatingBackgroundColors; + return m_alternateBackground; } void KItemListWidget::setEnabledSelectionToggle(bool enable) @@ -381,7 +381,7 @@ void KItemListWidget::hoveredChanged(bool hovered) Q_UNUSED(hovered); } -void KItemListWidget::alternatingBackgroundColorsChanged(bool enabled) +void KItemListWidget::alternateBackgroundChanged(bool enabled) { Q_UNUSED(enabled); } diff --git a/src/kitemviews/kitemlistwidget.h b/src/kitemviews/kitemlistwidget.h index 20efa7c94..84bd15fa5 100644 --- a/src/kitemviews/kitemlistwidget.h +++ b/src/kitemviews/kitemlistwidget.h @@ -82,8 +82,8 @@ public: void setHovered(bool hovered); bool isHovered() const; - void setAlternatingBackgroundColors(bool enable); - bool alternatingBackgroundColors() const; + void setAlternateBackground(bool enable); + bool alternateBackground() const; void setEnabledSelectionToggle(bool enabled); bool enabledSelectionToggle() const; @@ -138,7 +138,7 @@ protected: virtual void currentChanged(bool current); virtual void selectedChanged(bool selected); virtual void hoveredChanged(bool hovered); - virtual void alternatingBackgroundColorsChanged(bool enabled); + virtual void alternateBackgroundChanged(bool enabled); virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); virtual void resizeEvent(QGraphicsSceneResizeEvent* event); @@ -164,7 +164,7 @@ private: bool m_selected; bool m_current; bool m_hovered; - bool m_alternatingBackgroundColors; + bool m_alternateBackground; bool m_enabledSelectionToggle; QHash m_data; QList m_visibleRoles; -- 2.47.3