VisualizationDragDropContainer.cpp
312 lines
| 10.0 KiB
| text/x-c
|
CppLexer
r841 | #include "Visualization/VisualizationDragDropContainer.h" | |||
#include "Visualization/VisualizationDragWidget.h" | ||||
#include "SqpApplication.h" | ||||
#include "DragDropHelper.h" | ||||
#include <QDrag> | ||||
#include <QVBoxLayout> | ||||
#include <QDragEnterEvent> | ||||
#include <memory> | ||||
struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate { | ||||
QVBoxLayout* m_layout; | ||||
QStringList m_acceptedMimeTypes; | ||||
QStringList m_mergeAllowedMimeTypes; | ||||
explicit VisualizationDragDropContainerPrivate(QWidget* widget) | ||||
{ | ||||
m_layout = new QVBoxLayout(widget); | ||||
m_layout->setContentsMargins(0,0,0,0); | ||||
} | ||||
bool acceptMimeData(const QMimeData* data) const | ||||
{ | ||||
for (const auto& type : m_acceptedMimeTypes) | ||||
{ | ||||
if (data->hasFormat(type)) | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
bool allowMergeMimeData(const QMimeData* data) const | ||||
{ | ||||
for (const auto& type : m_mergeAllowedMimeTypes) | ||||
{ | ||||
if (data->hasFormat(type)) | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
bool hasPlaceHolder() const | ||||
{ | ||||
return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_layout->parentWidget(); | ||||
} | ||||
VisualizationDragWidget* getChildDragWidgetAt(QWidget* parent, const QPoint &pos) const | ||||
{ | ||||
VisualizationDragWidget* dragWidget = nullptr; | ||||
for (auto child : parent->children()) | ||||
{ | ||||
auto widget = qobject_cast<VisualizationDragWidget*>(child); | ||||
if (widget && widget->isVisible()) | ||||
{ | ||||
if (widget->frameGeometry().contains(pos)) | ||||
{ | ||||
dragWidget = widget; | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
return dragWidget; | ||||
} | ||||
bool cursorIsInContainer(QWidget* container) const | ||||
{ | ||||
auto adustNum = 18; //to be safe, in case of scrollbar on the side | ||||
auto containerRect = QRect(QPoint(), container->contentsRect().size()).adjusted(adustNum, adustNum, -adustNum, -adustNum); | ||||
qDebug() << containerRect << container->mapFromGlobal(QCursor::pos()); | ||||
return containerRect.contains(container->mapFromGlobal(QCursor::pos())); | ||||
} | ||||
}; | ||||
VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent) | ||||
: QWidget{parent}, impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)} | ||||
{ | ||||
setAcceptDrops(true); | ||||
} | ||||
void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget) | ||||
{ | ||||
impl->m_layout->addWidget(dragWidget); | ||||
disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | ||||
connect(dragWidget, &VisualizationDragWidget::dragDetected, this, &VisualizationDragDropContainer::startDrag); | ||||
} | ||||
void VisualizationDragDropContainer::insertDragWidget(int index, VisualizationDragWidget *dragWidget) | ||||
{ | ||||
impl->m_layout->insertWidget(index, dragWidget); | ||||
disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | ||||
connect(dragWidget, &VisualizationDragWidget::dragDetected, this, &VisualizationDragDropContainer::startDrag); | ||||
} | ||||
void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes) | ||||
{ | ||||
impl->m_acceptedMimeTypes = mimeTypes; | ||||
} | ||||
void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes) | ||||
{ | ||||
impl->m_mergeAllowedMimeTypes = mimeTypes; | ||||
} | ||||
int VisualizationDragDropContainer::countDragWidget() const | ||||
{ | ||||
auto nbGraph = 0; | ||||
for (auto child : children()) | ||||
{ | ||||
auto widget = qobject_cast<VisualizationDragWidget*>(child); | ||||
if (widget) | ||||
{ | ||||
nbGraph += 1; | ||||
} | ||||
} | ||||
return nbGraph; | ||||
} | ||||
void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition) | ||||
{ | ||||
auto& helper = sqpApp->dragDropHelper(); | ||||
//Note: The management of the drag object is done by Qt | ||||
auto *drag = new QDrag{dragWidget}; | ||||
drag->setHotSpot(dragPosition); | ||||
auto mimeData = dragWidget->mimeData(); | ||||
drag->setMimeData(mimeData); | ||||
auto pixmap = QPixmap(dragWidget->size()); | ||||
dragWidget->render(&pixmap); | ||||
drag->setPixmap(pixmap); | ||||
auto image = pixmap.toImage(); | ||||
mimeData->setImageData(image); | ||||
mimeData->setUrls({helper.imageTemporaryUrl(image)}); | ||||
if (impl->m_layout->indexOf(dragWidget) >= 0) | ||||
{ | ||||
helper.setCurrentDragWidget(dragWidget); | ||||
if (impl->cursorIsInContainer(this)) | ||||
{ | ||||
auto dragWidgetIndex = impl->m_layout->indexOf(dragWidget); | ||||
helper.insertPlaceHolder(impl->m_layout, dragWidgetIndex); | ||||
dragWidget->setVisible(false); | ||||
} | ||||
} | ||||
//Note: The exec() is blocking on windows but not on linux and macOS | ||||
drag->exec(Qt::MoveAction | Qt::CopyAction); | ||||
} | ||||
void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) | ||||
{ | ||||
if (impl->acceptMimeData(event->mimeData())) | ||||
{ | ||||
event->acceptProposedAction(); | ||||
auto& helper = sqpApp->dragDropHelper(); | ||||
if (!impl->hasPlaceHolder()) | ||||
{ | ||||
auto dragWidget = helper.getCurrentDragWidget(); | ||||
auto parentWidget = qobject_cast<VisualizationDragDropContainer*>(dragWidget->parentWidget()); | ||||
if (parentWidget) | ||||
{ | ||||
dragWidget->setVisible(false); | ||||
} | ||||
auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | ||||
if (dragWidgetHovered) | ||||
{ | ||||
auto hoveredWidgetIndex = impl->m_layout->indexOf(dragWidgetHovered); | ||||
auto dragWidgetIndex = impl->m_layout->indexOf(helper.getCurrentDragWidget()); | ||||
if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) | ||||
hoveredWidgetIndex += 1; //Correction of the index if the drop occurs in the same container | ||||
helper.insertPlaceHolder(impl->m_layout, hoveredWidgetIndex); | ||||
} | ||||
else | ||||
{ | ||||
helper.insertPlaceHolder(impl->m_layout, 0); | ||||
} | ||||
} | ||||
} | ||||
else | ||||
event->ignore(); | ||||
QWidget::dragEnterEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event) | ||||
{ | ||||
Q_UNUSED(event); | ||||
auto& helper = sqpApp->dragDropHelper(); | ||||
if (!impl->cursorIsInContainer(this)) | ||||
{ | ||||
helper.removePlaceHolder(); | ||||
bool isInternal = true; | ||||
if (isInternal) | ||||
{ | ||||
//Only if the drag is strated from the visualization | ||||
//Show the drag widget at its original place | ||||
//So the drag widget doesn't stay hidden if the drop occurs outside the visualization drop zone | ||||
//(It is not possible to catch a drop event outside of the application) | ||||
auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | ||||
if (dragWidget) | ||||
{ | ||||
dragWidget->setVisible(true); | ||||
} | ||||
} | ||||
} | ||||
QWidget::dragLeaveEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event) | ||||
{ | ||||
if (impl->acceptMimeData(event->mimeData())) | ||||
{ | ||||
auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | ||||
if (dragWidgetHovered) | ||||
{ | ||||
auto canMerge = impl->allowMergeMimeData(event->mimeData()); | ||||
auto nbDragWidget = countDragWidget(); | ||||
if (nbDragWidget > 0) | ||||
{ | ||||
auto graphHeight = size().height() / nbDragWidget; | ||||
auto dropIndex = floor(event->pos().y() / graphHeight); | ||||
auto zoneSize = qMin(graphHeight / 3.0, 150.0); | ||||
auto isOnTop = event->pos().y() < dropIndex * graphHeight + zoneSize; | ||||
auto isOnBottom = event->pos().y() > (dropIndex + 1) * graphHeight - zoneSize; | ||||
auto& helper = sqpApp->dragDropHelper(); | ||||
auto placeHolderIndex = impl->m_layout->indexOf(&(helper.placeHolder())); | ||||
if (isOnTop || isOnBottom) | ||||
{ | ||||
if (isOnBottom) | ||||
dropIndex += 1; | ||||
auto dragWidgetIndex = impl->m_layout->indexOf(helper.getCurrentDragWidget()); | ||||
if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) | ||||
dropIndex += 1; //Correction of the index if the drop occurs in the same container | ||||
if (dropIndex != placeHolderIndex) | ||||
{ | ||||
helper.insertPlaceHolder(impl->m_layout, dropIndex); | ||||
} | ||||
} | ||||
else if (canMerge) | ||||
{ | ||||
//drop on the middle -> merge | ||||
if (impl->hasPlaceHolder()) | ||||
{ | ||||
helper.removePlaceHolder(); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
} | ||||
else | ||||
event->ignore(); | ||||
QWidget::dragMoveEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dropEvent(QDropEvent *event) | ||||
{ | ||||
if (impl->acceptMimeData(event->mimeData())) | ||||
{ | ||||
auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | ||||
if (impl->hasPlaceHolder() && dragWidget) | ||||
{ | ||||
auto& helper = sqpApp->dragDropHelper(); | ||||
auto droppedIndex = impl->m_layout->indexOf(&helper.placeHolder()); | ||||
auto dragWidgetIndex = impl->m_layout->indexOf(dragWidget); | ||||
if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) | ||||
droppedIndex -= 1; //Correction of the index if the drop occurs in the same container | ||||
dragWidget->setVisible(true); | ||||
dragWidget->setStyleSheet(""); | ||||
event->acceptProposedAction(); | ||||
helper.removePlaceHolder(); | ||||
emit dropOccured(droppedIndex, event->mimeData()); | ||||
} | ||||
} | ||||
else | ||||
event->ignore(); | ||||
QWidget::dropEvent(event); | ||||
} | ||||