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();
// 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.
- if (m_model->count() > 0) {
- prepareLayoutForIncreasedItemCount(newSize, LayouterSize);
- } else {
- m_layouter->setSize(newSize);
- }
-
+ m_layouter->setSize(newSize);
if (!m_layoutTimer->isActive()) {
m_layoutTimer->start();
}
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();
}
}
return;
}
- const int firstVisibleIndex = m_layouter->firstVisibleIndex();
- const int lastVisibleIndex = m_layouter->lastVisibleIndex();
+ int firstVisibleIndex = m_layouter->firstVisibleIndex();
if (firstVisibleIndex < 0) {
emitOffsetChanges();
return;
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<int> reusableItems;
- QHashIterator<int, KItemListWidget*> 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<int> 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
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, itemBounds);
} else if (itemsInserted && i >= changedIndex) {
// The item is located after the first inserted item
if (i <= changedIndex + changedCount - 1) {
// 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, itemBounds);
}
} 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, itemBounds);
}
}
emitOffsetChanges();
}
+QList<int> 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<int> items;
+
+ QHashIterator<int, KItemListWidget*> 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 QRectF& itemBounds)
+{
+ const QPointF oldPos = widget->pos();
+ const QPointF newPos = itemBounds.topLeft();
+ if (oldPos == newPos) {
+ return false;
+ }
+
+ bool startMovingAnim = m_itemSize.isEmpty();
+ if (!startMovingAnim) {
+ // When having a grid the moving-animation should only be started, if it is done within
+ // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation.
+ // Otherwise instead of a moving-animation a create-animation on the new position will be used
+ // instead. This is done to prevent overlapping (and confusing) moving-animations.
+
+ int zoomDiff = 0;
+ if (widget->size() != itemBounds.size()) {
+ // The item-size has been increased or decreased
+ const bool zoomOut = (widget->size().width() >= itemBounds.size().width()) &&
+ (widget->size().height() >= itemBounds.size().height());
+ zoomDiff = zoomOut ? -1 : +1;
+ }
+
+ const qreal xMax = m_itemSize.width();
+ const qreal yMax = m_itemSize.height();
+ qreal xDiff = oldPos.x() - newPos.x();
+ qreal yDiff = oldPos.y() - newPos.y();
+ if (scrollOrientation() == Qt::Vertical) {
+ if (zoomDiff != 0) {
+ // Skip moving animations that changed the row
+ startMovingAnim = (zoomDiff > 0 && xDiff < xMax) ||
+ (zoomDiff < 0 && -xDiff < xMax);
+ } else {
+ xDiff = qAbs(xDiff);
+ yDiff = qAbs(yDiff);
+ startMovingAnim = (xDiff > yDiff && yDiff < yMax);
+ }
+ } else {
+ if (zoomDiff != 0) {
+ // Skip moving animations that changed the column
+ startMovingAnim = (zoomDiff > 0 && yDiff < yMax) ||
+ (zoomDiff < 0 && -yDiff < yMax);
+ } else {
+ xDiff = qAbs(xDiff);
+ yDiff = qAbs(yDiff);
+ startMovingAnim = (yDiff > xDiff && xDiff < xMax);
+ }
+ }
+ }
+
+ if (startMovingAnim) {
+ 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();
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) {