X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/83c5692f5e33c2bb239e1122811ce64ed1f144a3..0222d552811efb7049de2d024247b0b8eaeffe2b:/src/kitemviews/kitemlistview.cpp diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index be03606ce..263c04461 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -147,15 +147,10 @@ void KItemListView::setItemSize(const QSizeF& itemSize) m_itemSize = itemSize; - const bool emptySize = itemSize.isEmpty(); - if (emptySize) { + if (itemSize.isEmpty()) { updateVisibleRolesSizes(); } else { - if (itemSize.width() < previousSize.width() || itemSize.height() < previousSize.height()) { - prepareLayoutForIncreasedItemCount(itemSize, ItemSize); - } else { - m_layouter->setItemSize(itemSize); - } + m_layouter->setItemSize(itemSize); } m_sizeHintResolver->clearCache(); @@ -201,13 +196,19 @@ qreal KItemListView::maximumScrollOffset() const void KItemListView::setItemOffset(qreal offset) { + if (m_layouter->itemOffset() == offset) { + return; + } + m_layouter->setItemOffset(offset); if (m_header) { m_header->setPos(-offset, 0); } - if (!m_layoutTimer->isActive()) { - doLayout(NoAnimation); - } + + // Don't check whether the m_layoutTimer is active: Changing the + // item offset must always trigger a synchronous layout, otherwise + // the smooth-scrolling might get jerky. + doLayout(NoAnimation); } qreal KItemListView::itemOffset() const @@ -349,20 +350,50 @@ void KItemListView::setGeometry(const QRectF& rect) return; } - if (m_model->count() > 0) { - prepareLayoutForIncreasedItemCount(rect.size(), LayouterSize); + const QSizeF newSize = rect.size(); + if (m_itemSize.isEmpty()) { + // The item size is dynamic: + // Changing the geometry does not require to do an expensive + // update of the visible-roles sizes, only the stretched sizes + // need to be adjusted to the new size. + updateStretchedVisibleRolesSizes(); + + if (m_useHeaderWidths) { + QSizeF dynamicItemSize = m_layouter->itemSize(); + + if (m_itemSize.width() < 0) { + const qreal requiredWidth = visibleRolesSizesWidthSum(); + if (newSize.width() > requiredWidth) { + dynamicItemSize.setWidth(newSize.width()); + } + const qreal headerWidth = qMax(newSize.width(), requiredWidth); + m_header->resize(headerWidth, m_header->size().height()); + } + + if (m_itemSize.height() < 0) { + const qreal requiredHeight = visibleRolesSizesHeightSum(); + if (newSize.height() > requiredHeight) { + dynamicItemSize.setHeight(newSize.height()); + } + // TODO: KItemListHeader is not prepared for vertical alignment + } + + m_layouter->setItemSize(dynamicItemSize); + } + + // Triggering a synchronous layout is fine from a performance point of view, + // as with dynamic item sizes no moving animation must be done. + m_layouter->setSize(newSize); + doLayout(Animation); } else { - m_layouter->setSize(rect.size()); - } - - if (!m_layoutTimer->isActive()) { - m_layoutTimer->start(); + // The item size is not dynamic and most probably the geometry change results + // in animated position changes of the items. Trigger an asynchronous relayout + // with m_layoutTimer to prevent performance bottlenecks. + m_layouter->setSize(newSize); + if (!m_layoutTimer->isActive()) { + m_layoutTimer->start(); + } } - - // Changing the geometry does not require to do an expensive - // update of the visible-roles sizes, only the stretched sizes - // need to be adjusted to the new size. - updateStretchedVisibleRolesSizes(); } int KItemListView::itemAt(const QPointF& pos) const @@ -383,6 +414,10 @@ int KItemListView::itemAt(const QPointF& pos) const bool KItemListView::isAboveSelectionToggle(int index, const QPointF& pos) const { + if (!m_enabledSelectionToggles) { + return false; + } + const KItemListWidget* widget = m_visibleItems.value(index); if (widget) { const QRectF selectionToggleRect = widget->selectionToggleRect(); @@ -463,18 +498,16 @@ void KItemListView::scrollToItem(int index) if (!viewGeometry.contains(currentRect)) { qreal newOffset = scrollOffset(); - if (currentRect.top() < viewGeometry.top()) { - Q_ASSERT(scrollOrientation() == Qt::Vertical); - newOffset += currentRect.top() - viewGeometry.top(); - } else if ((currentRect.bottom() > viewGeometry.bottom())) { - Q_ASSERT(scrollOrientation() == Qt::Vertical); - newOffset += currentRect.bottom() - viewGeometry.bottom(); - } else if (currentRect.left() < viewGeometry.left()) { - if (scrollOrientation() == Qt::Horizontal) { - newOffset += currentRect.left() - viewGeometry.left(); + if (scrollOrientation() == Qt::Vertical) { + if (currentRect.top() < viewGeometry.top()) { + newOffset += currentRect.top() - viewGeometry.top(); + } else if (currentRect.bottom() > viewGeometry.bottom()) { + newOffset += currentRect.bottom() - viewGeometry.bottom(); } - } else if ((currentRect.right() > viewGeometry.right())) { - if (scrollOrientation() == Qt::Horizontal) { + } else { + if (currentRect.left() < viewGeometry.left()) { + newOffset += currentRect.left() - viewGeometry.left(); + } else if (currentRect.right() > viewGeometry.right()) { newOffset += currentRect.right() - viewGeometry.right(); } } @@ -697,34 +730,6 @@ QList KItemListView::visibleItemListWidgets() const return m_visibleItems.values(); } -void KItemListView::resizeEvent(QGraphicsSceneResizeEvent* event) -{ - QGraphicsWidget::resizeEvent(event); - if (m_itemSize.isEmpty() && m_useHeaderWidths) { - QSizeF dynamicItemSize = m_layouter->itemSize(); - const QSizeF newSize = event->newSize(); - - if (m_itemSize.width() < 0) { - const qreal requiredWidth = visibleRolesSizesWidthSum(); - if (newSize.width() > requiredWidth) { - dynamicItemSize.setWidth(newSize.width()); - } - const qreal headerWidth = qMax(newSize.width(), requiredWidth); - m_header->resize(headerWidth, m_header->size().height()); - } - - if (m_itemSize.height() < 0) { - const qreal requiredHeight = visibleRolesSizesHeightSum(); - if (newSize.height() > requiredHeight) { - dynamicItemSize.setHeight(newSize.height()); - } - // TODO: KItemListHeader is not prepared for vertical alignment - } - - m_layouter->setItemSize(dynamicItemSize); - } -} - void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) { updateVisibleRolesSizes(itemRanges); @@ -771,15 +776,23 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) } m_layouter->markAsDirty(); - if (m_model->count() == count && maximumScrollOffset() > size().height()) { - const int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); - QSizeF layouterSize = m_layouter->size(); - if (scrollOrientation() == Qt::Vertical) { - layouterSize.rwidth() -= scrollBarExtent; - } else { - layouterSize.rheight() -= scrollBarExtent; + if (m_model->count() == count && m_activeTransactions == 0) { + // Check whether a scrollbar is required to show the inserted items. In this case + // the size of the layouter will be decreased before calling doLayout(): This prevents + // an unnecessary temporary animation due to the geometry change of the inserted scrollbar. + const bool verticalScrollOrientation = (scrollOrientation() == Qt::Vertical); + const bool decreaseLayouterSize = ( verticalScrollOrientation && maximumScrollOffset() > size().height()) || + (!verticalScrollOrientation && maximumScrollOffset() > size().width()); + if (decreaseLayouterSize) { + const int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); + QSizeF layouterSize = m_layouter->size(); + if (verticalScrollOrientation) { + layouterSize.rwidth() -= scrollBarExtent; + } else { + layouterSize.rheight() -= scrollBarExtent; + } + m_layouter->setSize(layouterSize); } - m_layouter->setSize(layouterSize); } if (!hasMultipleRanges) { @@ -863,7 +876,14 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) m_layouter->markAsDirty(); if (!hasMultipleRanges) { + // The decrease-layout-size optimization in KItemListView::slotItemsInserted() + // assumes an updated geometry. If items are removed during an active transaction, + // the transaction will be temporary deactivated so that doLayout() triggers a + // geometry update if necessary. + const int activeTransactions = m_activeTransactions; + m_activeTransactions = 0; doLayout(Animation, index, -count); + m_activeTransactions = activeTransactions; } } @@ -1260,8 +1280,7 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha return; } - const int firstVisibleIndex = m_layouter->firstVisibleIndex(); - const int lastVisibleIndex = m_layouter->lastVisibleIndex(); + int firstVisibleIndex = m_layouter->firstVisibleIndex(); if (firstVisibleIndex < 0) { emitOffsetChanges(); return; @@ -1274,29 +1293,12 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha const qreal maxOffsetToShowFullRange = maximumScrollOffset() - visibleOffsetRange; if (scrollOffset() > maxOffsetToShowFullRange) { m_layouter->setScrollOffset(qMax(qreal(0), maxOffsetToShowFullRange)); + firstVisibleIndex = m_layouter->firstVisibleIndex(); } - // Determine all items that are completely invisible and might be - // reused for items that just got (at least partly) visible. - // Items that do e.g. an animated moving of their position are not - // marked as invisible: This assures that a scrolling inside the view - // can be done without breaking an animation. - QList reusableItems; - QHashIterator it(m_visibleItems); - while (it.hasNext()) { - it.next(); - KItemListWidget* widget = it.value(); - const int index = widget->index(); - const bool invisible = (index < firstVisibleIndex) || (index > lastVisibleIndex); - if (invisible && !m_animation->isStarted(widget)) { - widget->setVisible(false); - reusableItems.append(index); + const int lastVisibleIndex = m_layouter->lastVisibleIndex(); - if (m_grouped) { - recycleGroupHeaderForWidget(widget); - } - } - } + QList reusableItems = recycleInvisibleItems(firstVisibleIndex, lastVisibleIndex); // Assure that for each visible item a KItemListWidget is available. KItemListWidget // instances from invisible items are reused. If no reusable items are @@ -1349,8 +1351,7 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha const bool itemsInserted = (changedCount > 0); if (itemsRemoved && (i >= changedIndex + changedCount + 1)) { // The item is located after the removed items. Animate the moving of the position. - m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); - applyNewPos = false; + applyNewPos = !moveWidget(widget, newPos); } else if (itemsInserted && i >= changedIndex) { // The item is located after the first inserted item if (i <= changedIndex + changedCount - 1) { @@ -1364,13 +1365,11 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha // The item was already there before, so animate the moving of the position. // No moving animation is done if the item is animated by a create animation: This // prevents a "move animation mess" when inserting several ranges in parallel. - m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); - applyNewPos = false; + applyNewPos = !moveWidget(widget, newPos); } } else if (!itemsRemoved && !itemsInserted && !wasHidden) { // The size of the view might have been changed. Animate the moving of the position. - m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); - applyNewPos = false; + applyNewPos = !moveWidget(widget, newPos); } } @@ -1417,6 +1416,60 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha emitOffsetChanges(); } +QList KItemListView::recycleInvisibleItems(int firstVisibleIndex, int lastVisibleIndex) +{ + // Determine all items that are completely invisible and might be + // reused for items that just got (at least partly) visible. + // Items that do e.g. an animated moving of their position are not + // marked as invisible: This assures that a scrolling inside the view + // can be done without breaking an animation. + + QList items; + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + KItemListWidget* widget = it.value(); + const int index = widget->index(); + const bool invisible = (index < firstVisibleIndex) || (index > lastVisibleIndex); + if (invisible && !m_animation->isStarted(widget)) { + widget->setVisible(false); + items.append(index); + + if (m_grouped) { + recycleGroupHeaderForWidget(widget); + } + } + } + + return items; +} + +bool KItemListView::moveWidget(KItemListWidget* widget, const QPointF& newPos) +{ + // The moving-animation should only be started, if it is done within one + // row or one column. Otherwise instead of a moving-animation a + // create-animation on the new position will be used instead. This is done + // to prevent "irritating" moving-animations. + const QPointF oldPos = widget->pos(); + const qreal xMax = m_itemSize.width(); + const qreal yMax = m_itemSize.height(); + + const bool startMovingAnim = xMax <= 0 + || yMax <= 0 + || qAbs(oldPos.x() - newPos.x()) < xMax + || qAbs(oldPos.y() - newPos.y()) < yMax; + if (startMovingAnim) { + // The moving animation is done inside a column or a row. + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + return true; + } + + m_animation->stop(widget); + m_animation->start(widget, KItemListViewAnimation::CreateAnimation); + return false; +} + void KItemListView::emitOffsetChanges() { const qreal newScrollOffset = m_layouter->scrollOffset(); @@ -1480,52 +1533,6 @@ void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) initializeItemListWidget(widget); } -void KItemListView::prepareLayoutForIncreasedItemCount(const QSizeF& size, SizeType sizeType) -{ - // Calculate the first visible index and last visible index for the current size - const int currentFirst = m_layouter->firstVisibleIndex(); - const int currentLast = m_layouter->lastVisibleIndex(); - - const QSizeF currentSize = (sizeType == LayouterSize) ? m_layouter->size() : m_layouter->itemSize(); - - // Calculate the first visible index and last visible index for the new size - setLayouterSize(size, sizeType); - const int newFirst = m_layouter->firstVisibleIndex(); - const int newLast = m_layouter->lastVisibleIndex(); - - if ((currentFirst != newFirst) || (currentLast != newLast)) { - // At least one index has been changed. Assure that widgets for all possible - // visible items get created so that a move-animation can be started later. - const int maxVisibleItems = m_layouter->maximumVisibleItems(); - int minFirst = qMin(newFirst, currentFirst); - const int maxLast = qMax(newLast, currentLast); - - if (maxLast - minFirst + 1 < maxVisibleItems) { - // Increasing the size might result in a smaller KItemListView::offset(). - // Decrease the first visible index in a way that at least the maximum - // visible items are shown. - minFirst = qMax(0, maxLast - maxVisibleItems + 1); - } - - if (maxLast - minFirst > maxVisibleItems + maxVisibleItems / 2) { - // The creating of widgets is quite expensive. Assure that never more - // than 50 % of the maximum visible items get created for the animations. - return; - } - - setLayouterSize(currentSize, sizeType); - for (int i = minFirst; i <= maxLast; ++i) { - if (!m_visibleItems.contains(i)) { - KItemListWidget* widget = createWidget(i); - const QRectF itemRect = m_layouter->itemRect(i); - widget->setPos(itemRect.topLeft()); - widget->resize(itemRect.size()); - } - } - setLayouterSize(size, sizeType); - } -} - void KItemListView::setLayouterSize(const QSizeF& size, SizeType sizeType) { switch (sizeType) {