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.
This commit is contained in:
Peter Penz 2012-02-22 18:28:11 +01:00
parent bdd0fdf246
commit dbc5fd7a49
7 changed files with 172 additions and 77 deletions

View file

@ -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<QByteArray>& changedRoles) const
@ -379,20 +376,9 @@ void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous)
void KFileItemListView::onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& 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)

View file

@ -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<int, KItemListWidget*> 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<QByteArray>& roles)
const QList<QByteArray> 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<int, KItemListWidget*> 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<int>
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<int, KItemListWidget*> 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<QPair<int, QVariant> > 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<QPair<int, QVariant> > 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<QByteArray, qreal> KItemListView::headerRolesWidths() const
{
QHash<QByteArray, qreal> rolesWidths;

View file

@ -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.
*/

View file

@ -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) {

View file

@ -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();

View file

@ -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);
}

View file

@ -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<QByteArray, QVariant> m_data;
QList<QByteArray> m_visibleRoles;