[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [amarok] /: Collection Browser: restore scroll location when the filter is cleared
From:       Matěj_Laitl <matej () laitl ! cz>
Date:       2014-05-18 11:21:27
Message-ID: E1Wlz9j-0004y6-1x () scm ! kde ! org
[Download RAW message or body]

Git commit 12884560c4bec1f94422385fc798a0fd921ba8fc by Matěj Laitl.
Committed on 18/05/2014 at 09:34.
Pushed by laitl into branch 'master'.

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

M  +2    -0    ChangeLog
M  +1    -1    src/browsers/CollectionTreeItemModel.cpp
M  +25   -19   src/browsers/CollectionTreeItemModelBase.cpp
M  +12   -0    src/browsers/CollectionTreeItemModelBase.h
M  +75   -3    src/browsers/CollectionTreeView.cpp

http://commits.kde.org/amarok/12884560c4bec1f94422385fc798a0fd921ba8fc

diff --git a/ChangeLog b/ChangeLog
index a0365ae..387ee7f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/src/browsers/CollectionTreeItemModel.cpp \
b/src/browsers/CollectionTreeItemModel.cpp index 1ac4463..0110c2b 100644
--- a/src/browsers/CollectionTreeItemModel.cpp
+++ b/src/browsers/CollectionTreeItemModel.cpp
@@ -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 ) ) );
     }
 }
 
diff --git a/src/browsers/CollectionTreeItemModelBase.cpp \
b/src/browsers/CollectionTreeItemModelBase.cpp index da35825..c08a197 100644
--- a/src/browsers/CollectionTreeItemModelBase.cpp
+++ b/src/browsers/CollectionTreeItemModelBase.cpp
@@ -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 ) );
     }
 }
 
diff --git a/src/browsers/CollectionTreeItemModelBase.h \
b/src/browsers/CollectionTreeItemModelBase.h index 8517755..804d501 100644
--- a/src/browsers/CollectionTreeItemModelBase.h
+++ b/src/browsers/CollectionTreeItemModelBase.h
@@ -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 );
diff --git a/src/browsers/CollectionTreeView.cpp \
b/src/browsers/CollectionTreeView.cpp index 10b5cbc..816d2e1 100644
--- a/src/browsers/CollectionTreeView.cpp
+++ b/src/browsers/CollectionTreeView.cpp
@@ -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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic