Commit 12884560 authored by Matěj Laitl's avatar Matěj Laitl
Browse files

Collection Browser: restore scroll location when the filter is cleared

FEATURES:
 * Collection Browser scrolls back to its original position when the
   filter is cleared.

FEATURE: 188074
FIXED-IN: 2.8.1
DIGEST: Amarok implements popular demand to restore scroll location
        when collection filter is cleared
parent 9de699b9
......@@ -5,6 +5,8 @@ Amarok ChangeLog
VERSION 2.8.1
FEATURES:
* Collection Browser scrolls back to its original position when the filter is cleared.
(BR 188074)
* Statistics synchronization between Amarok collections and
Amarok 1.4, Amarok 2.x, Apple iTunes, Banshee, Clementine, and
Rhythmbox track databases.
......
......@@ -240,7 +240,7 @@ CollectionTreeItemModel::requestCollectionsExpansion()
{
for( int i = 0, count = m_rootItem->childCount(); i < count; i++ )
{
emit expandIndex( createIndex( i, 0, m_rootItem->child( i ) ) );
emit expandIndex( itemIndex( m_rootItem->child( i ) ) );
}
}
......
......@@ -367,10 +367,7 @@ CollectionTreeItemModelBase::parent(const QModelIndex & index) const
CollectionTreeItem *childItem = static_cast<CollectionTreeItem*>(index.internalPointer());
CollectionTreeItem *parentItem = childItem->parent();
if ( (parentItem == m_rootItem) || !parentItem )
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
return itemIndex( parentItem );
}
int
......@@ -490,6 +487,24 @@ CollectionTreeItemModelBase::ensureChildrenLoaded( CollectionTreeItem *item )
}
}
CollectionTreeItem *
CollectionTreeItemModelBase::treeItem( const QModelIndex &index ) const
{
if( !index.isValid() || index.model() != this )
return 0;
return static_cast<CollectionTreeItem *>( index.internalPointer() );
}
QModelIndex
CollectionTreeItemModelBase::itemIndex( CollectionTreeItem *item ) const
{
if( !item || item == m_rootItem )
return QModelIndex();
return createIndex( item->row(), 0, item );
}
void CollectionTreeItemModelBase::listForLevel(int level, Collections::QueryMaker * qm, CollectionTreeItem * parent)
{
if( qm && parent )
......@@ -639,7 +654,7 @@ CollectionTreeItemModelBase::queryDone()
//reset icon for this item
if( item && item != m_rootItem )
{
emit dataChanged( createIndex(item->row(), 0, item), createIndex(item->row(), 0, item) );
emit dataChanged( itemIndex( item ), itemIndex( item ) );
}
//stop timer if there are no more animations active
......@@ -738,13 +753,9 @@ CollectionTreeItemModelBase::handleSpecialQueryResult( CollectionTreeItem::Type
else if( type == CollectionTreeItem::NoLabel )
parent = m_noLabelsQueries.value( qm );
QModelIndex parentIndex;
if( parent )
{
if (parent == m_rootItem ) // will never happen in CollectionTreeItemModel
parentIndex = QModelIndex();
else
parentIndex = createIndex( parent->row(), 0, parent );
QModelIndex parentIndex = itemIndex( parent );
//if the special query did not return a result we have to remove the
//the special node itself
......@@ -803,7 +814,7 @@ CollectionTreeItemModelBase::handleSpecialQueryResult( CollectionTreeItem::Type
//only call populateChildren for the special node if we have not
//created it in this method call. The special node ctor takes care
//of that itself
populateChildren( dataList, specialNode, createIndex( specialNode->row(), 0, specialNode ) );
populateChildren( dataList, specialNode, itemIndex( specialNode ) );
}
//populate children will call setRequiresUpdate on vaNode
//but as the special query is based on specialNode's parent,
......@@ -836,13 +847,8 @@ void
CollectionTreeItemModelBase::handleNormalQueryResult( Collections::QueryMaker *qm, const Meta::DataList &dataList )
{
CollectionTreeItem *parent = m_childQueries.value( qm );
QModelIndex parentIndex;
if( parent ) {
if( parent == m_rootItem ) // will never happen in CollectionTreeItemModel, but will happen in Single!
parentIndex = QModelIndex();
else
parentIndex = createIndex( parent->row(), 0, parent );
QModelIndex parentIndex = itemIndex( parent );
populateChildren( dataList, parent, parentIndex );
if ( parent->isDataItem() )
......@@ -1065,7 +1071,7 @@ void CollectionTreeItemModelBase::loadingAnimationTick()
{
if( item == m_rootItem )
continue;
emit dataChanged ( createIndex(item->row(), 0, item), createIndex(item->row(), 0, item) );
emit dataChanged( itemIndex( item ), itemIndex( item ) );
}
}
......@@ -1093,7 +1099,7 @@ CollectionTreeItemModelBase::slotFilter( bool autoExpand )
{
CollectionTreeItem *expandedItem = m_collections.value( expanded->collectionId() ).second;
if( expandedItem )
emit expandIndex( createIndex( expandedItem->row(), 0, expandedItem ) );
emit expandIndex( itemIndex( expandedItem ) );
}
}
......
......@@ -96,6 +96,18 @@ class AMAROK_EXPORT CollectionTreeItemModelBase : public QAbstractItemModel
void ensureChildrenLoaded( CollectionTreeItem *item );
/**
* Get a pointer to colleciton tree item given its index. It is not safe to
* cache this pointer unless QWeakPointer is used.
*/
CollectionTreeItem *treeItem( const QModelIndex &index ) const;
/**
* Get (create) index for a collection tree item. The caller must ensure this
* item is in this model. Invalid model index is returned on null or root item.
*/
QModelIndex itemIndex( CollectionTreeItem *item ) const;
signals:
void expandIndex( const QModelIndex &index );
void allQueriesFinished( bool autoExpand );
......
......@@ -56,9 +56,60 @@
#include <QHash>
#include <QMouseEvent>
#include <QSortFilterProxyModel>
#include <QScrollBar>
using namespace Collections;
/**
* RAII class to perform restoring of the scroll position once all queries are
* finished. DelayedScroller auto-deletes itself once its job is over (ot if it finds
* it is useless).
*/
class DelayedScroller : public QObject
{
Q_OBJECT
public:
DelayedScroller( CollectionTreeView *treeView,
CollectionTreeItemModelBase *treeModel,
const QModelIndex &treeModelScrollToIndex, int topOffset )
: QObject( treeView )
, m_treeView( treeView )
, m_treeModel( treeModel )
, m_topOffset( topOffset )
{
connect( treeModel, SIGNAL(destroyed(QObject*)), SLOT(deleteLater()) );
connect( treeModel, SIGNAL(allQueriesFinished(bool)), SLOT(slotScroll()) );
m_scrollToItem = m_treeModel->treeItem( treeModelScrollToIndex );
if( m_scrollToItem )
connect( m_scrollToItem, SIGNAL(destroyed(QObject*)), SLOT(deleteLater()) );
else
deleteLater(); // nothing to do
}
private slots:
void slotScroll()
{
deleteLater();
QModelIndex idx = m_treeModel->itemIndex( m_scrollToItem );
QSortFilterProxyModel *filterModel = m_treeView->filterModel();
idx = filterModel ? filterModel->mapFromSource( idx ) : QModelIndex();
QScrollBar *scrollBar = m_treeView->verticalScrollBar();
if( !idx.isValid() || !scrollBar )
return;
int newTopOffset = m_treeView->visualRect( idx ).top();
scrollBar->setValue( scrollBar->value() + (newTopOffset - m_topOffset) );
}
private:
CollectionTreeView *m_treeView;
CollectionTreeItemModelBase *m_treeModel;
CollectionTreeItem *m_scrollToItem;
int m_topOffset;
};
CollectionTreeView::CollectionTreeView( QWidget *parent)
: Amarok::PrettyTreeView( parent )
, m_filterModel( 0 )
......@@ -900,8 +951,28 @@ CollectionTreeView::editTracks( const QSet<CollectionTreeItem *> &items ) const
void
CollectionTreeView::slotSetFilter( const QString &filter )
{
if( m_treeModel && m_treeModel->currentFilter() != filter )
m_treeModel->setCurrentFilter( filter );
QString currentFilter = m_treeModel ? m_treeModel->currentFilter() : QString();
if( !m_filterModel || !m_treeModel || filter == currentFilter )
return;
// special case: transitioning from non-empty to empty buffer
// -> trigger later restoring of the scroll position
if( filter.isEmpty() ) // currentFilter must not be empty then (see earlier check)
{
// take first item, descending to leaf ones if expanded. There may be better
// ways to determine what item should stay "fixed".
QModelIndex scrollToIndex = m_filterModel->index( 0, 0 );
while( isExpanded( scrollToIndex ) && m_filterModel->rowCount( scrollToIndex ) > 0 )
scrollToIndex = scrollToIndex.child( 0, 0 );
int topOffset = visualRect( scrollToIndex ).top();
QModelIndex bottomIndex = m_filterModel->mapToSource( scrollToIndex );
// if we have somewhere to scroll to after filter is cleared...
if( bottomIndex.isValid() )
// auto-destroys itself
new DelayedScroller( this, m_treeModel, bottomIndex, topOffset );
}
m_treeModel->setCurrentFilter( filter );
}
void
......@@ -1329,4 +1400,5 @@ CollectionTreeView::createMetaQueryFromItems( const QSet<CollectionTreeItem *> &
return new Collections::MetaQueryMaker( queryMakers );
}
#include "CollectionTreeView.moc"
#include "CollectionTreeView.moc" // Q_OBJECTs defined in CollectionTreeView.cpp
#include "moc_CollectionTreeView.cpp" // Q_OBJECTs defined in CollectionTreeView.h
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment