summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Dalheimer <jan@dalheimer.de>2013-12-26 21:16:03 +0100
committerJan Dalheimer <jan@dalheimer.de>2013-12-26 21:16:03 +0100
commit525f508d94120feae89ee1d1bd960625ab14ed37 (patch)
treef9e113ac5a3e7fcf86c226ae7c7176d553993a84
parentccbf341dc8d8e515d9cf918bff7ff9435c477847 (diff)
downloadMultiMC-525f508d94120feae89ee1d1bd960625ab14ed37.tar
MultiMC-525f508d94120feae89ee1d1bd960625ab14ed37.tar.gz
MultiMC-525f508d94120feae89ee1d1bd960625ab14ed37.tar.lz
MultiMC-525f508d94120feae89ee1d1bd960625ab14ed37.tar.xz
MultiMC-525f508d94120feae89ee1d1bd960625ab14ed37.zip
Loads of stuff, amongst others d&d and many bug fixes
-rw-r--r--CategorizedProxyModel.cpp11
-rw-r--r--CategorizedView.cpp681
-rw-r--r--CategorizedView.h25
3 files changed, 555 insertions, 162 deletions
diff --git a/CategorizedProxyModel.cpp b/CategorizedProxyModel.cpp
index 2b54ce1b..e4a7563a 100644
--- a/CategorizedProxyModel.cpp
+++ b/CategorizedProxyModel.cpp
@@ -8,5 +8,14 @@ CategorizedProxyModel::CategorizedProxyModel(QObject *parent)
}
bool CategorizedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
- return left.data(CategorizedView::CategoryRole).toString() < right.data(CategorizedView::CategoryRole).toString();
+ const QString leftCategory = left.data(CategorizedView::CategoryRole).toString();
+ const QString rightCategory = right.data(CategorizedView::CategoryRole).toString();
+ if (leftCategory == rightCategory)
+ {
+ return left.row() < right.row();
+ }
+ else
+ {
+ return leftCategory < rightCategory;
+ }
}
diff --git a/CategorizedView.cpp b/CategorizedView.cpp
index 46b1e072..95505fd7 100644
--- a/CategorizedView.cpp
+++ b/CategorizedView.cpp
@@ -5,6 +5,23 @@
#include <QtMath>
#include <QDebug>
#include <QMouseEvent>
+#include <QListView>
+#include <QPersistentModelIndex>
+#include <QDrag>
+#include <QMimeData>
+
+template<typename T>
+bool listsIntersect(const QList<T> &l1, const QList<T> t2)
+{
+ foreach (const T &item, l1)
+ {
+ if (t2.contains(item))
+ {
+ return true;
+ }
+ }
+ return false;
+}
CategorizedView::Category::Category(const QString &text, CategorizedView *view)
: view(view), text(text), collapsed(false)
@@ -39,7 +56,7 @@ void CategorizedView::Category::drawHeader(QPainter *painter, const int y)
// the text
int textWidth = painter->fontMetrics().width(text);
textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight());
- painter->drawText(textRect, text, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter));
+ view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->palette(), true, text);
// the line
painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2);
@@ -77,6 +94,11 @@ CategorizedView::CategorizedView(QWidget *parent)
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setWordWrap(true);
+ setDragDropMode(QListView::InternalMove);
+ setAcceptDrops(true);
+
+ m_cachedCategoryToIndexMapping.setMaxCost(50);
+ m_cachedVisualRects.setMaxCost(50);
}
CategorizedView::~CategorizedView()
@@ -130,6 +152,8 @@ void CategorizedView::updateGeometries()
QListView::updateGeometries();
m_cachedItemSize = QSize();
+ m_cachedCategoryToIndexMapping.clear();
+ m_cachedVisualRects.clear();
QMap<QString, Category *> cats;
@@ -189,6 +213,17 @@ CategorizedView::Category *CategorizedView::category(const QString &cat) const
}
return 0;
}
+CategorizedView::Category *CategorizedView::categoryAt(const QPoint &pos) const
+{
+ for (int i = 0; i < m_categories.size(); ++i)
+ {
+ if (m_categories.at(i)->iconRect.contains(pos))
+ {
+ return m_categories.at(i);
+ }
+ }
+ return 0;
+}
int CategorizedView::numItemsForCategory(const CategorizedView::Category *category) const
{
@@ -196,15 +231,47 @@ int CategorizedView::numItemsForCategory(const CategorizedView::Category *catego
}
QList<QModelIndex> CategorizedView::itemsForCategory(const CategorizedView::Category *category) const
{
- QList<QModelIndex> indices;
- for (int i = 0; i < model()->rowCount(); ++i)
+ if (!m_cachedCategoryToIndexMapping.contains(category))
+ {
+ QList<QModelIndex> *indices = new QList<QModelIndex>();
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ if (model()->index(i, 0).data(CategoryRole).toString() == category->text)
+ {
+ indices->append(model()->index(i, 0));
+ }
+ }
+ m_cachedCategoryToIndexMapping.insert(category, indices, indices->size());
+ }
+ return *m_cachedCategoryToIndexMapping.object(category);
+}
+QModelIndex CategorizedView::firstItemForCategory(const CategorizedView::Category *category) const
+{
+ QList<QModelIndex> indices = itemsForCategory(category);
+ QModelIndex first;
+ foreach (const QModelIndex &index, indices)
{
- if (model()->index(i, 0).data(CategoryRole).toString() == category->text)
+ if (index.row() < first.row() || !first.isValid())
{
- indices += model()->index(i, 0);
+ first = index;
}
}
- return indices;
+
+ return first;
+}
+QModelIndex CategorizedView::lastItemForCategory(const CategorizedView::Category *category) const
+{
+ QList<QModelIndex> indices = itemsForCategory(category);
+ QModelIndex last;
+ foreach (const QModelIndex &index, indices)
+ {
+ if (index.row() > last.row() || !last.isValid())
+ {
+ last = index;
+ }
+ }
+
+ return last;
}
int CategorizedView::categoryTop(const CategorizedView::Category *category) const
@@ -231,14 +298,10 @@ int CategorizedView::contentWidth() const
return width() - m_leftMargin - m_rightMargin;
}
-bool CategorizedView::lessThanCategoryPointer(const CategorizedView::Category *c1, const CategorizedView::Category *c2)
-{
- return c1->text < c2->text;
-}
QList<CategorizedView::Category *> CategorizedView::sortedCategories() const
{
QList<Category *> out = m_categories;
- qSort(out.begin(), out.end(), &CategorizedView::lessThanCategoryPointer);
+ qSort(out.begin(), out.end(), [](const Category *c1, const Category *c2) { return c1->text < c2->text; });
return out;
}
@@ -266,85 +329,186 @@ void CategorizedView::mousePressEvent(QMouseEvent *event)
{
//endCategoryEditor();
- if (event->buttons() & Qt::LeftButton)
+ QPoint pos = event->pos();
+ QPersistentModelIndex index = indexAt(pos);
+
+ m_pressedIndex = index;
+ m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
+ QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
+ QPoint offset = QPoint(horizontalOffset(), verticalOffset());
+ if (!(command & QItemSelectionModel::Current))
{
- foreach (Category *category, m_categories)
- {
- if (category->iconRect.contains(event->pos()))
- {
- category->collapsed = !category->collapsed;
- updateGeometries();
- viewport()->update();
- event->accept();
- return;
- }
- }
+ m_pressedPosition = pos + offset;
+ }
+ else if (!indexAt(m_pressedPosition - offset).isValid())
+ {
+ m_pressedPosition = visualRect(currentIndex()).center() + offset;
+ }
- for (int i = 0; i < model()->rowCount(); ++i)
+ m_pressedCategory = categoryAt(m_pressedPosition);
+ if (m_pressedCategory)
+ {
+ setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
+ event->accept();
+ return;
+ }
+
+ if (index.isValid() && (index.flags() & Qt::ItemIsEnabled))
+ {
+ // we disable scrollTo for mouse press so the item doesn't change position
+ // when the user is interacting with it (ie. clicking on it)
+ bool autoScroll = hasAutoScroll();
+ setAutoScroll(false);
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ setAutoScroll(autoScroll);
+ QRect rect(m_pressedPosition - offset, pos);
+ if (command.testFlag(QItemSelectionModel::Toggle))
{
- QModelIndex index = model()->index(i, 0);
- if (visualRect(index).contains(event->pos()))
- {
- selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
- event->accept();
- return;
- }
+ command &= ~QItemSelectionModel::Toggle;
+ m_ctrlDragSelectionFlag = selectionModel()->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
+ command |= m_ctrlDragSelectionFlag;
}
- }
+ setSelection(rect, command);
+
+ // signal handlers may change the model
+ emit pressed(index);
- QListView::mousePressEvent(event);
+ } else {
+ // Forces a finalize() even if mouse is pressed, but not on a item
+ selectionModel()->select(QModelIndex(), QItemSelectionModel::Select);
+ }
}
void CategorizedView::mouseMoveEvent(QMouseEvent *event)
{
- if (event->buttons() & Qt::LeftButton)
+ QPoint topLeft;
+ QPoint bottomRight = event->pos();
+
+ if (state() == ExpandingState || state() == CollapsingState)
{
- for (int i = 0; i < model()->rowCount(); ++i)
+ return;
+ }
+
+ if (state() == DraggingState)
+ {
+ topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset());
+ if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance())
{
- QModelIndex index = model()->index(i, 0);
- if (visualRect(index).contains(event->pos()))
- {
- selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
- event->accept();
- return;
- }
+ m_pressedIndex = QModelIndex();
+ startDrag(model()->supportedDragActions());
+ setState(NoState);
+ stopAutoScroll();
}
+ return;
}
- QListView::mouseMoveEvent(event);
-}
-void CategorizedView::mouseReleaseEvent(QMouseEvent *event)
-{
- if (event->buttons() & Qt::LeftButton)
+ QPersistentModelIndex index = indexAt(bottomRight);
+
+ if (selectionMode() != SingleSelection)
{
- for (int i = 0; i < model()->rowCount(); ++i)
+ topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset());
+ }
+ else
+ {
+ topLeft = bottomRight;
+ }
+
+ if (m_pressedIndex.isValid()
+ && (state() != DragSelectingState)
+ && (event->buttons() != Qt::NoButton)
+ && !selectedIndexes().isEmpty())
+ {
+ setState(DraggingState);
+ return;
+ }
+
+ if ((event->buttons() & Qt::LeftButton) && selectionModel())
+ {
+ setState(DragSelectingState);
+ QItemSelectionModel::SelectionFlags command = selectionCommand(index, event);
+ if (m_ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle))
{
- QModelIndex index = model()->index(i, 0);
- if (visualRect(index).contains(event->pos()))
- {
- selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
- event->accept();
- return;
- }
+ command &= ~QItemSelectionModel::Toggle;
+ command |= m_ctrlDragSelectionFlag;
}
- }
- QListView::mouseReleaseEvent(event);
+ // Do the normalize ourselves, since QRect::normalized() is flawed
+ QRect selectionRect = QRect(topLeft, bottomRight);
+ setSelection(selectionRect, command);
+
+ // set at the end because it might scroll the view
+ if (index.isValid()
+ && (index != selectionModel()->currentIndex())
+ && (index.flags() & Qt::ItemIsEnabled))
+ {
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
+ }
+ }
}
-void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event)
+void CategorizedView::mouseReleaseEvent(QMouseEvent *event)
{
- /*endCategoryEditor();
+ QPoint pos = event->pos();
+ QPersistentModelIndex index = indexAt(pos);
- foreach (Category *category, m_categories)
+ bool click = (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(pos));
+
+ if (click && m_pressedCategory)
{
- if (category->textRect.contains(event->pos()) && m_categoryEditor == 0)
+ if (state() == ExpandingState)
{
- startCategoryEditor(category);
+ m_pressedCategory->collapsed = false;
+ updateGeometries();
+ viewport()->update();
event->accept();
return;
}
- }*/
+ else if (state() == CollapsingState)
+ {
+ m_pressedCategory->collapsed = true;
+ updateGeometries();
+ viewport()->update();
+ event->accept();
+ return;
+ }
+ }
+
+ m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;
+
+ setState(NoState);
- QListView::mouseDoubleClickEvent(event);
+ if (click)
+ {
+ if (event->button() == Qt::LeftButton)
+ {
+ emit clicked(index);
+ }
+ QStyleOptionViewItem option = viewOptions();
+ if (m_pressedAlreadySelected)
+ {
+ option.state |= QStyle::State_Selected;
+ }
+ if ((model()->flags(index) & Qt::ItemIsEnabled)
+ && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
+ {
+ emit activated(index);
+ }
+ }
+}
+void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QModelIndex index = indexAt(event->pos());
+ if (!index.isValid()
+ || !(index.flags() & Qt::ItemIsEnabled)
+ || (m_pressedIndex != index))
+ {
+ QMouseEvent me(QEvent::MouseButtonPress,
+ event->localPos(), event->windowPos(), event->screenPos(),
+ event->button(), event->buttons(), event->modifiers());
+ mousePressEvent(&me);
+ return;
+ }
+ // signal handlers may change the model
+ QPersistentModelIndex persistent = index;
+ emit doubleClicked(persistent);
}
void CategorizedView::paintEvent(QPaintEvent *event)
{
@@ -385,6 +549,33 @@ void CategorizedView::paintEvent(QPaintEvent *event)
}
itemDelegate()->paint(&painter, option, index);
}
+
+ if (!m_lastDragPosition.isNull())
+ {
+ QPair<Category *, int> pair = rowDropPos(m_lastDragPosition);
+ Category *category = pair.first;
+ int row = pair.second;
+ if (category)
+ {
+ int internalRow = row - firstItemForCategory(category).row();
+ qDebug() << internalRow << numItemsForCategory(category) << model()->index(row, 0).data().toString();
+ QLine line;
+ if (internalRow >= numItemsForCategory(category))
+ {
+ QRect toTheRightOfRect = visualRect(lastItemForCategory(category));
+ line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight());
+ }
+ else
+ {
+ QRect toTheLeftOfRect = visualRect(model()->index(row, 0));
+ line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft());
+ }
+ painter.save();
+ painter.setPen(QPen(Qt::black, 3));
+ painter.drawLine(line);
+ painter.restore();
+ }
+ }
}
void CategorizedView::resizeEvent(QResizeEvent *event)
{
@@ -400,18 +591,33 @@ void CategorizedView::resizeEvent(QResizeEvent *event)
void CategorizedView::dragEnterEvent(QDragEnterEvent *event)
{
- // TODO
+ if (!isDragEventAccepted(event))
+ {
+ return;
+ }
+ m_lastDragPosition = event->pos();
+ viewport()->update();
+ event->accept();
}
void CategorizedView::dragMoveEvent(QDragMoveEvent *event)
{
- // TODO
+ if (!isDragEventAccepted(event))
+ {
+ return;
+ }
+ m_lastDragPosition = event->pos();
+ viewport()->update();
+ event->accept();
}
void CategorizedView::dragLeaveEvent(QDragLeaveEvent *event)
{
- // TODO
+ m_lastDragPosition = QPoint();
+ viewport()->update();
}
void CategorizedView::dropEvent(QDropEvent *event)
{
+ m_lastDragPosition = QPoint();
+
stopAutoScroll();
setState(NoState);
@@ -420,93 +626,68 @@ void CategorizedView::dropEvent(QDropEvent *event)
return;
}
- // check that we aren't on a category header and calculate which category we're in
- Category *category = 0;
+ QPair<Category *, int> dropPos = rowDropPos(event->pos());
+ const Category *category = dropPos.first;
+ const int row = dropPos.second;
+
+ if (row == -1)
{
- int y = 0;
- foreach (Category *cat, m_categories)
- {
- if (event->pos().y() > y && event->pos().y() < (y + cat->headerHeight()))
- {
- viewport()->update();
- return;
- }
- y += cat->totalHeight() + m_categoryMargin;
- if (event->pos().y() < y)
- {
- category = cat;
- break;
- }
- }
+ viewport()->update();
+ return;
}
- // calculate the internal column
- int internalColumn = -1;
+ const QString categoryText = category->text;
+ if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex()))
{
- const int itemWidth = itemSize().width();
- for (int i = 0, c = 0;
- i < contentWidth();
- i += itemWidth, ++c)
- {
- if (event->pos().x() > (i - itemWidth / 2) &&
- event->pos().x() < (i + itemWidth / 2))
- {
- internalColumn = c;
- break;
- }
- }
- if (internalColumn == -1)
- {
- viewport()->update();
- return;
- }
+ model()->setData(model()->index(row, 0), categoryText, CategoryRole);
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
}
+ updateGeometries();
+ viewport()->update();
+}
- // calculate the internal row
- int internalRow = -1;
+void CategorizedView::startDrag(Qt::DropActions supportedActions)
+{
+ QModelIndexList indexes = selectionModel()->selectedIndexes();
+ if (indexes.count() > 0)
{
- const int itemHeight = itemSize().height();
- const int top = categoryTop(category);
- for (int i = top + category->headerHeight(), r = 0;
- i < top + category->totalHeight();
- i += itemHeight, ++r)
+ QMimeData *data = model()->mimeData(indexes);
+ if (!data)
{
- if (event->pos().y() > i && event->pos().y() < (i + itemHeight))
- {
- internalRow = r;
- break;
- }
+ return;
}
- if (internalRow == -1)
+ QRect rect;
+ QPixmap pixmap = renderToPixmap(indexes, &rect);
+ rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
+ QDrag *drag = new QDrag(this);
+ drag->setPixmap(pixmap);
+ drag->setMimeData(data);
+ drag->setHotSpot(m_pressedPosition - rect.topLeft());
+ Qt::DropAction defaultDropAction = Qt::IgnoreAction;
+ if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction()))
{
- viewport()->update();
- return;
+ defaultDropAction = this->defaultDropAction();
}
- }
-
- QList<QModelIndex> indices = itemsForCategory(category);
-
- // flaten the internalColumn/internalRow to one row
- int categoryRow;
- {
- for (int i = 0; i < internalRow; ++i)
+ if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
{
- if (i == internalRow)
- {
- break;
+ const QItemSelection selection = selectionModel()->selection();
+
+ for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) {
+ QModelIndex parent = (*it).parent();
+ if ((*it).left() != 0)
+ {
+ continue;
+ }
+ if ((*it).right() != (model()->columnCount(parent) - 1))
+ {
+ continue;
+ }
+ int count = (*it).bottom() - (*it).top() + 1;
+ model()->removeRows((*it).top(), count, parent);
}
- categoryRow += itemsPerRow();
}
- categoryRow += internalColumn;
- }
-
- int row = indices.at(categoryRow).row();
- if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex()))
- {
- event->setDropAction(Qt::MoveAction);
- event->accept();
}
- updateGeometries();
}
bool lessThanQModelIndex(const QModelIndex &i1, const QModelIndex &i2)
@@ -520,34 +701,39 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const
return QRect();
}
- const Category *cat = category(index);
- QList<QModelIndex> indices = itemsForCategory(cat);
- qSort(indices.begin(), indices.end(), &lessThanQModelIndex);
- int x = 0;
- int y = 0;
- const int perRow = itemsPerRow();
- for (int i = 0; i < indices.size(); ++i)
+ if (!m_cachedVisualRects.contains(index))
{
- if (indices.at(i) == index)
- {
- break;
- }
- ++x;
- if (x == perRow)
+ const Category *cat = category(index);
+ QList<QModelIndex> indices = itemsForCategory(cat);
+ qSort(indices.begin(), indices.end(), &lessThanQModelIndex);
+ int x = 0;
+ int y = 0;
+ const int perRow = itemsPerRow();
+ for (int i = 0; i < indices.size(); ++i)
{
- x = 0;
- ++y;
+ if (indices.at(i) == index)
+ {
+ break;
+ }
+ ++x;
+ if (x == perRow)
+ {
+ x = 0;
+ ++y;
+ }
}
- }
- QSize size = itemSize();
+ QSize size = itemSize();
- QRect out;
- out.setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height());
- out.setLeft(x * size.width());
- out.setSize(size);
+ QRect *out = new QRect;
+ out->setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height());
+ out->setLeft(x * size.width());
+ out->setSize(size);
- return out;
+ m_cachedVisualRects.insert(index, out);
+ }
+
+ return *m_cachedVisualRects.object(index);
}
/*
void CategorizedView::startCategoryEditor(Category *category)
@@ -585,3 +771,176 @@ void CategorizedView::endCategoryEditor()
updateGeometries();
}
*/
+
+QModelIndex CategorizedView::indexAt(const QPoint &point) const
+{
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ QModelIndex index = model()->index(i, 0);
+ if (visualRect(index).contains(point))
+ {
+ return index;
+ }
+ }
+ return QModelIndex();
+}
+void CategorizedView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands)
+{
+ QItemSelection selection;
+ for (int i = 0; i < model()->rowCount(); ++i)
+ {
+ QModelIndex index = model()->index(i, 0);
+ if (visualRect(index).intersects(rect))
+ {
+ selection.merge(QItemSelection(index, index), QItemSelectionModel::Select);
+ }
+ }
+ selectionModel()->select(selection, commands);
+}
+
+QPixmap CategorizedView::renderToPixmap(const QModelIndexList &indices, QRect *r) const
+{
+ Q_ASSERT(r);
+ QList<QPair<QRect, QModelIndex> > paintPairs = draggablePaintPairs(indices, r);
+ if (paintPairs.isEmpty())
+ {
+ return QPixmap();
+ }
+ QPixmap pixmap(r->size());
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ QStyleOptionViewItem option = viewOptions();
+ option.state |= QStyle::State_Selected;
+ for (int j = 0; j < paintPairs.count(); ++j)
+ {
+ option.rect = paintPairs.at(j).first.translated(-r->topLeft());
+ const QModelIndex &current = paintPairs.at(j).second;
+ itemDelegate()->paint(&painter, option, current);
+ }
+ return pixmap;
+}
+QList<QPair<QRect, QModelIndex> > CategorizedView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const
+{
+ Q_ASSERT(r);
+ QRect &rect = *r;
+ const QRect viewportRect = viewport()->rect();
+ QList<QPair<QRect, QModelIndex> > ret;
+ for (int i = 0; i < indices.count(); ++i) {
+ const QModelIndex &index = indices.at(i);
+ const QRect current = visualRect(index);
+ if (current.intersects(viewportRect)) {
+ ret += qMakePair(current, index);
+ rect |= current;
+ }
+ }
+ rect &= viewportRect;
+ return ret;
+}
+
+bool CategorizedView::isDragEventAccepted(QDropEvent *event)
+{
+ if (event->source() != this)
+ {
+ return false;
+ }
+ if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes()))
+ {
+ return false;
+ }
+ if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), rowDropPos(event->pos()).second, 0, QModelIndex()))
+ {
+ return false;
+ }
+ return true;
+}
+QPair<CategorizedView::Category *, int> CategorizedView::rowDropPos(const QPoint &pos)
+{
+ // check that we aren't on a category header and calculate which category we're in
+ Category *category = 0;
+ {
+ int y = 0;
+ foreach (Category *cat, m_categories)
+ {
+ if (pos.y() > y && pos.y() < (y + cat->headerHeight()))
+ {
+ return qMakePair(nullptr, -1);
+ }
+ y += cat->totalHeight() + m_categoryMargin;
+ if (pos.y() < y)
+ {
+ category = cat;
+ break;
+ }
+ }
+ if (category == 0)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ // calculate the internal column
+ int internalColumn = -1;
+ {
+ const int itemWidth = itemSize().width();
+ for (int i = 0, c = 0;
+ i < contentWidth();
+ i += itemWidth, ++c)
+ {
+ if (pos.x() > (i - itemWidth / 2) &&
+ pos.x() < (i + itemWidth / 2))
+ {
+ internalColumn = c;
+ break;
+ }
+ }
+ if (internalColumn == -1)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ // calculate the internal row
+ int internalRow = -1;
+ {
+ const int itemHeight = itemSize().height();
+ const int top = categoryTop(category);
+ for (int i = top + category->headerHeight(), r = 0;
+ i < top + category->totalHeight();
+ i += itemHeight, ++r)
+ {
+ if (pos.y() > i && pos.y() < (i + itemHeight))
+ {
+ internalRow = r;
+ break;
+ }
+ }
+ if (internalRow == -1)
+ {
+ return qMakePair(nullptr, -1);
+ }
+ }
+
+ QList<QModelIndex> indices = itemsForCategory(category);
+
+ // flaten the internalColumn/internalRow to one row
+ int categoryRow = 0;
+ {
+ for (int i = 0; i < internalRow; ++i)
+ {
+ if ((i + 1) >= internalRow)
+ {
+ break;
+ }
+ categoryRow += itemsPerRow();
+ }
+ categoryRow += internalColumn;
+ }
+
+ // this is used if we're past the last item
+ if (internalColumn >= qMin(itemsPerRow(), indices.size()))
+ {
+ return qMakePair(category, indices.last().row() + 1);
+ }
+
+ return qMakePair(category, indices.at(categoryRow).row());
+}
diff --git a/CategorizedView.h b/CategorizedView.h
index 1e918496..0756629a 100644
--- a/CategorizedView.h
+++ b/CategorizedView.h
@@ -3,6 +3,7 @@
#include <QListView>
#include <QLineEdit>
+#include <QCache>
class CategorizedView : public QListView
{
@@ -18,6 +19,8 @@ public:
};
virtual QRect visualRect(const QModelIndex &index) const;
+ QModelIndex indexAt(const QPoint &point) const;
+ void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override;
protected slots:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
@@ -39,6 +42,8 @@ protected:
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) override;
+ void startDrag(Qt::DropActions supportedActions) override;
+
private:
struct Category
{
@@ -59,6 +64,8 @@ private:
friend struct Category;
QList<Category *> m_categories;
+ mutable QCache<const Category *, QList<QModelIndex> > m_cachedCategoryToIndexMapping;
+ mutable QCache<const QModelIndex, QRect> m_cachedVisualRects;
int m_leftMargin;
int m_rightMargin;
@@ -69,8 +76,11 @@ private:
Category *category(const QModelIndex &index) const;
Category *category(const QString &cat) const;
+ Category *categoryAt(const QPoint &pos) const;
int numItemsForCategory(const Category *category) const;
QList<QModelIndex> itemsForCategory(const Category *category) const;
+ QModelIndex firstItemForCategory(const Category *category) const;
+ QModelIndex lastItemForCategory(const Category *category) const;
int categoryTop(const Category *category) const;
@@ -91,6 +101,21 @@ private:
private slots:
void endCategoryEditor();*/
+
+private:
+ QPoint m_pressedPosition;
+ QPersistentModelIndex m_pressedIndex;
+ bool m_pressedAlreadySelected;
+ Category *m_pressedCategory;
+ QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag;
+ QPoint m_lastDragPosition;
+
+ QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const;
+ QList<QPair<QRect, QModelIndex> > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const;
+
+ bool isDragEventAccepted(QDropEvent *event);
+
+ QPair<Category *, int> rowDropPos(const QPoint &pos);
};
#endif // WIDGET_H