QListView scrolls up on list item drop.
-
Hi guys.
I read a couple of notes about enabling items reordering in the QListView. I reimplemented flags() and supportedDropActions() methods and it seems to be working. I can take an item from the list and drop it in another position and it moves their correctly except the listview itself scrolls to the very top as soon as I drop the item and the wrong item get's selected. Why doesn't it remember where it was before the drop? What I can do to solve this? And also where I should specify which item must be selected?
Thanks for any help!
-
Hi and welcome to devnet,
As for your problem, can you provide a minimal sample that reproduce the behavior ?
Also, what version of Qt are you using ? On which OS ?
-
It's Qt 5.2.0 on Windows 7. I'm using Ct Creator.
What i did is:
1) Added a List View using the disigner
2) Then I set it properties:
dragEnabled - true
dragDropOverWriteMode - false
dragDropOverMode - InternalMove
defaultDropAction - MoveAction
Scroll Modes - perItem
movement - free or snap;*3) *I created a new class named PlayListModel inherited from QStingListModel
@class PlayListModel : public QStringListModel
{
Q_OBJECTpublic:
PlayListModel(QObject *parent) : QStringListModel(parent)
{} Qt::ItemFlags flags(const QModelIndex &index) const; Qt::DropActions supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } int rowCount(const QModelIndex &parent) const;
};
Qt::ItemFlags PlayListModel::flags(const QModelIndex &index) const
{
if (index.isValid())
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
else
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
}int PlayListModel::rowCount(const QModelIndex &parent) const
{
if(parent.isValid())
{
return 0;
}
return stringList().length();
}@*4) *In my mainwindow function I populate my model class with items
@for(int i = 0; i < 25; i++)
{
QModelIndex index = playListsModel->index(i);
playListsModel->setData(index, "Item"+QString::number(i+1));
}@and then assign it to List View's model.
@ui->listsView->setModel(playListsModel); @
Run the program and get such a behaviour.
-
Interesting. It happens only when the list widget or list view are created in the designer. If I create it in code and just
@setDragDropMode(QAbstractItemView::InternalMove);@
then it works as I want it to... hmm.Where's the bug?
Is it the compiler or ide problem?? Or just some parameter I missed somewhere in the designer or maybe the defaults in the designer differ from those in the framework itself?I still can't make it work via the designer.
-
Sounds strange indeed
Can you compare the values of these properties for both widgets ?
-
The same.... Actually I found out that it has nothing to do with the designer and only happens to QListView. I added both QListWidget and QListView to the layout. Added several items to QListWidget and then set the QListView's model to QListWidget::model();
Then I set both's
@plistWidget->setDragDropMode(QAbstractItemView::InternalMove);
plistView->setDragDropMode(QAbstractItemView::InternalMove);@
and what I get is that QListWidget works properly (it does'n scroll anywhere, and the moved item stays selected) but QListView doesn't behave the same way (it scrolls to top and selects eithe the index item used to be at or the one right next to where it was depending on moving the item up or down the list).
Seem like I just don't know something about it but I can't find the answer to this. -
QListWidget reimplements the dropEvent method so it might be that.
Did you have a look at the "bug report system":http://bugreports.qt-project.org ? Somebody might already have posted something about it, if not you could consider open a new bug report providing a minimal compilable example that show the behavior
-
Bugreport "here":https://bugreports.qt-project.org/browse/QTBUG-36610
-
actual in qt5.11
it seem what glitch happens within
QAbstractItemView::rowsAboutToBeRemoved
or so...QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); { Q_D(QAbstractItemView); setState(CollapsingState); ....
CollapsingState... for qlistview ???
workaround for some cases
class listviewscrollfix: public QListView{...}
void listviewscrollfix::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { // Q_D(QListView); // // if the parent is above d->root in the tree, nothing will happen ///QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); { //Q_D(QAbstractItemView); setState(NoState);/// !!! CollapsingState ///main glitch reason // Ensure one selected item in single selection mode. QModelIndex current = currentIndex(); if (selectionMode() == SingleSelection && current.isValid() && current.row() >= start && current.row() <= end && current.parent() == parent) { int totalToRemove = end - start + 1; if (model()->rowCount(parent) <= totalToRemove) { // no more children QModelIndex index = parent; ///we dont have access to some metods so skipped // while (index != d->root && !d->isIndexEnabled(index)) // index = index.parent(); // if (index != d->root) setCurrentIndex(index); } else { ///we dont have access to some metods so skipped // int row = end + 1; // QModelIndex next; // do { // find the next visible and enabled item // next = d->model->index(row++, current.column(), current.parent()); // } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); // if (row > d->model->rowCount(parent)) { // row = start - 1; // do { // find the previous visible and enabled item // next = d->model->index(row--, current.column(), current.parent()); // } while (next.isValid() && (isIndexHidden(next) || !d->isIndexEnabled(next))); // } // setCurrentIndex(next); } } ///we dont have access to some metods & and our list not editable so skipped // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes // QEditorIndexHash::iterator i = d->editorIndexHash.begin(); // while (i != d->editorIndexHash.end()) { // const QModelIndex index = i.value(); // if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { // QWidget *editor = i.key(); // QEditorInfo info = d->indexEditorHash.take(index); // i = d->editorIndexHash.erase(i); // if (info.widget) // d->releaseEditor(editor, index); // } else { // ++i; // } // } } ///we dont have access to some metods & we dont have hidden rows so skipped // if (parent == d->root) { // QSet<QPersistentModelIndex>::iterator it = d->hiddenRows.begin(); // while (it != d->hiddenRows.end()) { // int hiddenRow = it->row(); // if (hiddenRow >= start && hiddenRow <= end) { // it = d->hiddenRows.erase(it); // } else { // ++it; // } // } // } // d->clear();//??? // d->doDelayedItemsLayout(); }
or just
void listviewscrollfix::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end){}
and
bool SomeListModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid()) return false; ... for (int i = 0; i != count; ++i){ ... beginRemoveRows(parent, row, row); someitemcontainer.removeAt(row);//for small listst it will be ok 1 by 1 endRemoveRows(); dataChanged(index(row),index(row));//scroll glitch workaround...dont ask why so...//for update scrollbar max value ... } return true; }