|
@@ -0,0 +1,228 @@
|
|
|
+#include "dragabletable.h"
|
|
|
+#include <QDrag>
|
|
|
+#include <QMimeData>
|
|
|
+#include <QUuid>
|
|
|
+#include <QDebug>
|
|
|
+#include <QtEvents>
|
|
|
+#include <QPainter>
|
|
|
+#include <QHeaderView>
|
|
|
+#include "../PaintHelper/painthelper.h"
|
|
|
+
|
|
|
+DragableTable::DragableTable(QWidget *parent)
|
|
|
+ : QTableView(parent)
|
|
|
+ , m_UUID(QUuid::createUuid())
|
|
|
+ , m_bSignalOnly(false)
|
|
|
+ , m_bDragEnable(true)
|
|
|
+ , m_nTargetRow(-1)
|
|
|
+ , m_bIsUpon(false)
|
|
|
+{
|
|
|
+ SetDragTargetRow(-1);
|
|
|
+ setAcceptDrops(true);
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTable::SetDragEnable(bool value)
|
|
|
+{
|
|
|
+ setAcceptDrops(value);
|
|
|
+ m_bDragEnable = value;
|
|
|
+}
|
|
|
+//某行之下或之上
|
|
|
+void DragableTable::SetDragTargetRow(int row, bool isUpon)
|
|
|
+{
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ DragableTableDelegate *pDelegate = dynamic_cast<DragableTableDelegate*>(itemDelegate());
|
|
|
+ if(pDelegate == nullptr) return;
|
|
|
+ pDelegate->SetDragTarget(row, isUpon);
|
|
|
+
|
|
|
+// setProperty("dragTargetRow", row);
|
|
|
+// setProperty("isUpon", isUpon);
|
|
|
+ m_nTargetRow = row;
|
|
|
+ m_bIsUpon = isUpon;
|
|
|
+ update();
|
|
|
+}
|
|
|
+void DragableTable::mouseMoveEvent(QMouseEvent *event)
|
|
|
+{
|
|
|
+ QTableView::mouseMoveEvent(event);
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ if(!m_indexPressed.isValid()) return;
|
|
|
+ //启动Drag事件
|
|
|
+ QDrag *drag = new QDrag(this);
|
|
|
+ QMimeData *mimeData = new QMimeData();
|
|
|
+ mimeData->setText(m_UUID.toString());
|
|
|
+ drag->setMimeData(mimeData);
|
|
|
+ drag->exec(Qt::MoveAction, Qt::CopyAction);
|
|
|
+ m_indexPressed = QModelIndex();
|
|
|
+ SetDragTargetRow(-1);
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTable::mousePressEvent(QMouseEvent *event)
|
|
|
+{
|
|
|
+ QTableView::mousePressEvent(event);
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ //只允许左键按住
|
|
|
+ if(event->button() != Qt::LeftButton) return;
|
|
|
+
|
|
|
+ //根据触发鼠标位置获得QModelIndex
|
|
|
+ m_indexPressed = indexAt(event->pos());
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTable::mouseReleaseEvent(QMouseEvent *event)
|
|
|
+{
|
|
|
+ QTableView::mouseReleaseEvent(event);
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ m_indexPressed = QModelIndex();
|
|
|
+ SetDragTargetRow(-1);
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTable::dragEnterEvent(QDragEnterEvent *event)
|
|
|
+{
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ const QMimeData *mimeData = event->mimeData();
|
|
|
+ if(mimeData == nullptr || !mimeData->hasText()) return;
|
|
|
+ if(event->mimeData()->text() != m_UUID.toString()) return;
|
|
|
+
|
|
|
+ {//下面内容和dragMoveEvent里的一样, 避免dragMoveEvent未被调用而直接进入dropEvent
|
|
|
+ QModelIndex index = indexAt(event->pos());
|
|
|
+ int row = -1;
|
|
|
+ //如拖拽到表格的空白位置, 视为目标是最后一行之下
|
|
|
+ if(!index.isValid())
|
|
|
+ {
|
|
|
+ row = model()->rowCount() - 1;
|
|
|
+ SetDragTargetRow(row, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ QRect indexRect = visualRect(index);
|
|
|
+ int rowHeight = verticalHeader()->sectionSize(index.row());
|
|
|
+ int center = indexRect.top() + rowHeight/2;
|
|
|
+ SetDragTargetRow(index.row(), event->pos().y()<=center);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ event->accept();
|
|
|
+}
|
|
|
+void DragableTable::dragMoveEvent(QDragMoveEvent *event)
|
|
|
+{
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ QModelIndex index = indexAt(event->pos());
|
|
|
+ int row = -1;
|
|
|
+ //如拖拽到表格的空白位置, 视为目标是最后一行之下
|
|
|
+ if(!index.isValid())
|
|
|
+ {
|
|
|
+ row = model()->rowCount() - 1;
|
|
|
+ SetDragTargetRow(row, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ QRect indexRect = visualRect(index);
|
|
|
+ int rowHeight = verticalHeader()->sectionSize(index.row());
|
|
|
+ int center = indexRect.top() + rowHeight/2;
|
|
|
+ SetDragTargetRow(index.row(), event->pos().y()<=center);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTable::dropEvent(QDropEvent *event)
|
|
|
+{
|
|
|
+ if(!m_bDragEnable) return;
|
|
|
+ /*----------------------------------------------------------------
|
|
|
+ * 设置QDrag::exec()的返回值, 必须配合accept使用
|
|
|
+ * 也可以直接调用acceptProposedAction方法来将返回值设为调用QDrag::exec时传入的defaultDropAction
|
|
|
+ * event->setDropAction(Qt::MoveAction);
|
|
|
+ * event->accept();
|
|
|
+ * 不管之前执行了accept还是acceptProposedAction, 只要执行了event->ignore, QDrag::exec()的返回值都会是Qt::IgnoreAction
|
|
|
+ * event->ignore();
|
|
|
+ ----------------------------------------------------------------*/
|
|
|
+ do
|
|
|
+ {
|
|
|
+ int sourceRow = m_indexPressed.row();
|
|
|
+ if(m_nTargetRow == -1) break;
|
|
|
+ if(sourceRow == m_nTargetRow) break;
|
|
|
+
|
|
|
+ //计算实际要插入到的行: 如果是插入到"之下", 则要加1; 如果把上方的行往下移, 则要减1
|
|
|
+ int insertedRow = m_nTargetRow + (m_bIsUpon?0:1) - ((sourceRow>m_nTargetRow)?0:1);
|
|
|
+ if(sourceRow == insertedRow) break;
|
|
|
+
|
|
|
+ //发送信号
|
|
|
+ emit sig_Drop(sourceRow, insertedRow);
|
|
|
+ if(m_bSignalOnly) break;
|
|
|
+
|
|
|
+ QStandardItemModel *pModel = dynamic_cast<QStandardItemModel*>(model());
|
|
|
+ if(pModel == nullptr) break;
|
|
|
+
|
|
|
+ QList<QStandardItem*> targetRowItems = pModel->takeRow(sourceRow);
|
|
|
+ if(!targetRowItems.isEmpty()) pModel->insertRow(insertedRow, targetRowItems);
|
|
|
+
|
|
|
+ /*----------------------------------------------------------------
|
|
|
+ * 另一种思路:先交换源行和目标行的数据, 交换后目标行去到了源行, 其他行不受影响; 再将目标行移到当前源行位置之后
|
|
|
+ ----------------------------------------------------------------*/
|
|
|
+
|
|
|
+ }while (false);
|
|
|
+
|
|
|
+ m_indexPressed = QModelIndex();
|
|
|
+ SetDragTargetRow(-1);
|
|
|
+ event->acceptProposedAction();
|
|
|
+}
|
|
|
+
|
|
|
+DragableTableDelegate::DragableTableDelegate(QObject *parent)
|
|
|
+ : BaseItemDelegate(parent)
|
|
|
+// , m_pTableView(parentTable)
|
|
|
+ , m_nTargetRow(-1)
|
|
|
+ , m_bIsUpon(false)
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTableDelegate::SetDragTarget(int targetRow, bool isUpon)
|
|
|
+{
|
|
|
+ m_nTargetRow = targetRow;
|
|
|
+ m_bIsUpon = isUpon;
|
|
|
+}
|
|
|
+
|
|
|
+void DragableTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
|
|
+{
|
|
|
+ BaseItemDelegate::paint(painter,option,index);
|
|
|
+
|
|
|
+ if(m_nTargetRow == -1) return;
|
|
|
+ PainterEx *painterEx = static_cast<PainterEx*>(painter);
|
|
|
+
|
|
|
+ //绘制第一列单元格圆圈和线
|
|
|
+ if(index.row() == m_nTargetRow && index.column() == 0)
|
|
|
+ {
|
|
|
+ int y = m_bIsUpon?option.rect.top():option.rect.bottom();
|
|
|
+ painterEx->DrawCircle(QPoint(km_nRadius, y), km_nRadius, Qt::transparent, QPen(QColor(86,135,228), 2));
|
|
|
+ painterEx->setPen(QPen(QColor(86,135,228), 2));
|
|
|
+ painterEx->drawLine(option.rect.left()+km_nRadius*2, y, option.rect.right(), y);
|
|
|
+ }
|
|
|
+ //如果指示器绘制在当前行的下方, 因为头部圆圈会跨到下一行, 所以在绘制下一行的时候, 又绘制一次
|
|
|
+ if(!m_bIsUpon && (index.row() == m_nTargetRow+1) && index.column() == 0)
|
|
|
+ {
|
|
|
+ int y = option.rect.top() - 1;
|
|
|
+ painterEx->DrawCircle(QPoint(km_nRadius, y), km_nRadius, Qt::transparent, QPen(QColor(86,135,228), 2));
|
|
|
+ painterEx->setPen(QPen(QColor(86,135,228), 2));
|
|
|
+ painterEx->drawLine(option.rect.left()+km_nRadius*2, y, option.rect.right(), y);
|
|
|
+ }
|
|
|
+ //绘制后面单元格的线
|
|
|
+ if(index.row() == m_nTargetRow && index.column() != 0)
|
|
|
+ {
|
|
|
+ painterEx->DrawBorder(option.rect, QPen(QColor(86,135,228), 2), m_bIsUpon?PainterEx::RectBorderTop:PainterEx::RectBorderBottom);
|
|
|
+ }
|
|
|
+
|
|
|
+// //绘制拖拽指示器(绘制2*列数次, 防止被覆盖)
|
|
|
+// if(index.row() == m_nTargetRow||index.row() == m_nTargetRow+1)
|
|
|
+// {
|
|
|
+// painter->setRenderHint(QPainter::Antialiasing);
|
|
|
+// painter->setPen(QPen(QColor(86,135,228), 2));
|
|
|
+// painter->setBrush(Qt::transparent);
|
|
|
+
|
|
|
+// //int rowCount = m_pTableView->model()->rowCount();
|
|
|
+// int colCount = m_pTableView->model()->columnCount();
|
|
|
+// QPoint targetRowRect_topLeft = m_pTableView->visualRect(m_pTableView->model()->index(row,0)).topLeft();
|
|
|
+// QPoint targetRowRect_bottomRight = m_pTableView->visualRect(m_pTableView->model()->index(row,colCount-1)).bottomRight();
|
|
|
+// QRect targetRowRect(targetRowRect_topLeft, targetRowRect_bottomRight);
|
|
|
+
|
|
|
+// //头部圆圈
|
|
|
+// int radius = 8;
|
|
|
+// int y = m_bIsUpon?targetRowRect.top():targetRowRect.bottom();
|
|
|
+// QPoint topleft = QPoint(targetRowRect.left(), y - radius);
|
|
|
+// painter->drawEllipse(QRect(topleft, QSize(radius*2, radius*2)));
|
|
|
+// painter->drawLine(targetRowRect.left()+radius*2, y, targetRowRect.right(), y);
|
|
|
+ // }
|
|
|
+}
|