#include "dragabletable.h" #include #include #include #include #include #include #include #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(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(model()); if(pModel == nullptr) break; QList 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(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); // } }