VisualizationDragDropContainer.cpp
302 lines
| 10.0 KiB
| text/x-c
|
CppLexer
r838 | #include "Visualization/VisualizationDragDropContainer.h" | |||
#include "DragDropHelper.h" | ||||
r844 | #include "SqpApplication.h" | |||
#include "Visualization/VisualizationDragWidget.h" | ||||
r838 | ||||
#include <QDrag> | ||||
#include <QDragEnterEvent> | ||||
r844 | #include <QVBoxLayout> | |||
r838 | ||||
r845 | #include <cmath> | |||
r838 | #include <memory> | |||
struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate { | ||||
r844 | QVBoxLayout *m_Layout; | |||
QStringList m_AcceptedMimeTypes; | ||||
QStringList m_MergeAllowedMimeTypes; | ||||
r838 | ||||
r844 | explicit VisualizationDragDropContainerPrivate(QWidget *widget) | |||
r838 | { | |||
r844 | m_Layout = new QVBoxLayout(widget); | |||
m_Layout->setContentsMargins(0, 0, 0, 0); | ||||
r838 | } | |||
r844 | bool acceptMimeData(const QMimeData *data) const | |||
r838 | { | |||
r844 | for (const auto &type : m_AcceptedMimeTypes) { | |||
if (data->hasFormat(type)) { | ||||
r838 | return true; | |||
r844 | } | |||
r838 | } | |||
return false; | ||||
} | ||||
r844 | bool allowMergeMimeData(const QMimeData *data) const | |||
r838 | { | |||
r844 | for (const auto &type : m_MergeAllowedMimeTypes) { | |||
if (data->hasFormat(type)) { | ||||
r838 | return true; | |||
r844 | } | |||
r838 | } | |||
return false; | ||||
} | ||||
bool hasPlaceHolder() const | ||||
{ | ||||
r844 | return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget(); | |||
r838 | } | |||
r844 | VisualizationDragWidget *getChildDragWidgetAt(QWidget *parent, const QPoint &pos) const | |||
r838 | { | |||
r844 | VisualizationDragWidget *dragWidget = nullptr; | |||
for (auto child : parent->children()) { | ||||
auto widget = qobject_cast<VisualizationDragWidget *>(child); | ||||
if (widget && widget->isVisible()) { | ||||
if (widget->frameGeometry().contains(pos)) { | ||||
r838 | dragWidget = widget; | |||
break; | ||||
} | ||||
} | ||||
} | ||||
return dragWidget; | ||||
} | ||||
r844 | bool cursorIsInContainer(QWidget *container) const | |||
r838 | { | |||
r844 | 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); | ||||
r838 | return containerRect.contains(container->mapFromGlobal(QCursor::pos())); | |||
} | ||||
}; | ||||
VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent) | ||||
r844 | : QWidget{parent}, | |||
impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)} | ||||
r838 | { | |||
setAcceptDrops(true); | ||||
} | ||||
void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget) | ||||
{ | ||||
r844 | impl->m_Layout->addWidget(dragWidget); | |||
r838 | disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | |||
r844 | connect(dragWidget, &VisualizationDragWidget::dragDetected, this, | |||
&VisualizationDragDropContainer::startDrag); | ||||
r838 | } | |||
r844 | void VisualizationDragDropContainer::insertDragWidget(int index, | |||
VisualizationDragWidget *dragWidget) | ||||
r838 | { | |||
r844 | impl->m_Layout->insertWidget(index, dragWidget); | |||
r838 | disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr); | |||
r844 | connect(dragWidget, &VisualizationDragWidget::dragDetected, this, | |||
&VisualizationDragDropContainer::startDrag); | ||||
r838 | } | |||
void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes) | ||||
{ | ||||
r844 | impl->m_AcceptedMimeTypes = mimeTypes; | |||
r838 | } | |||
void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes) | ||||
{ | ||||
r844 | impl->m_MergeAllowedMimeTypes = mimeTypes; | |||
r838 | } | |||
int VisualizationDragDropContainer::countDragWidget() const | ||||
{ | ||||
auto nbGraph = 0; | ||||
r844 | for (auto child : children()) { | |||
r846 | if (qobject_cast<VisualizationDragWidget *>(child)) { | |||
r838 | nbGraph += 1; | |||
} | ||||
} | ||||
return nbGraph; | ||||
} | ||||
r844 | void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget, | |||
const QPoint &dragPosition) | ||||
r838 | { | |||
r844 | auto &helper = sqpApp->dragDropHelper(); | |||
r838 | ||||
r844 | // Note: The management of the drag object is done by Qt | |||
r846 | auto drag = new QDrag{dragWidget}; | |||
r838 | 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)}); | ||||
r844 | if (impl->m_Layout->indexOf(dragWidget) >= 0) { | |||
r838 | helper.setCurrentDragWidget(dragWidget); | |||
r844 | if (impl->cursorIsInContainer(this)) { | |||
auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget); | ||||
helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex); | ||||
r838 | dragWidget->setVisible(false); | |||
} | ||||
} | ||||
r844 | // Note: The exec() is blocking on windows but not on linux and macOS | |||
r838 | drag->exec(Qt::MoveAction | Qt::CopyAction); | |||
} | ||||
void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) | ||||
{ | ||||
r844 | if (impl->acceptMimeData(event->mimeData())) { | |||
r838 | event->acceptProposedAction(); | |||
r844 | auto &helper = sqpApp->dragDropHelper(); | |||
r838 | ||||
r844 | if (!impl->hasPlaceHolder()) { | |||
r838 | auto dragWidget = helper.getCurrentDragWidget(); | |||
r844 | auto parentWidget | |||
= qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget()); | ||||
if (parentWidget) { | ||||
r838 | dragWidget->setVisible(false); | |||
} | ||||
auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | ||||
r844 | 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 | ||||
} | ||||
r838 | ||||
r844 | helper.insertPlaceHolder(impl->m_Layout, hoveredWidgetIndex); | |||
r838 | } | |||
r844 | else { | |||
helper.insertPlaceHolder(impl->m_Layout, 0); | ||||
r838 | } | |||
} | ||||
} | ||||
r846 | else { | |||
r838 | event->ignore(); | |||
r846 | } | |||
r838 | ||||
QWidget::dragEnterEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event) | ||||
{ | ||||
Q_UNUSED(event); | ||||
r844 | auto &helper = sqpApp->dragDropHelper(); | |||
r838 | ||||
r844 | if (!impl->cursorIsInContainer(this)) { | |||
r838 | helper.removePlaceHolder(); | |||
bool isInternal = true; | ||||
r844 | if (isInternal) { | |||
r846 | // Only if the drag is started from the visualization | |||
r844 | // 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) | ||||
r838 | ||||
auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | ||||
r844 | if (dragWidget) { | |||
r838 | dragWidget->setVisible(true); | |||
} | ||||
} | ||||
} | ||||
QWidget::dragLeaveEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event) | ||||
{ | ||||
r844 | if (impl->acceptMimeData(event->mimeData())) { | |||
r838 | auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); | |||
r844 | if (dragWidgetHovered) { | |||
r838 | auto canMerge = impl->allowMergeMimeData(event->mimeData()); | |||
auto nbDragWidget = countDragWidget(); | ||||
r844 | if (nbDragWidget > 0) { | |||
r838 | 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; | ||||
r844 | auto &helper = sqpApp->dragDropHelper(); | |||
auto placeHolderIndex = impl->m_Layout->indexOf(&(helper.placeHolder())); | ||||
r838 | ||||
r844 | if (isOnTop || isOnBottom) { | |||
if (isOnBottom) { | ||||
r838 | dropIndex += 1; | |||
r844 | } | |||
r838 | ||||
r844 | 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 | ||||
} | ||||
r838 | ||||
r844 | if (dropIndex != placeHolderIndex) { | |||
helper.insertPlaceHolder(impl->m_Layout, dropIndex); | ||||
r838 | } | |||
} | ||||
r844 | else if (canMerge) { | |||
// drop on the middle -> merge | ||||
if (impl->hasPlaceHolder()) { | ||||
r838 | helper.removePlaceHolder(); | |||
} | ||||
} | ||||
} | ||||
} | ||||
} | ||||
r846 | else { | |||
r838 | event->ignore(); | |||
r846 | } | |||
r838 | ||||
QWidget::dragMoveEvent(event); | ||||
} | ||||
void VisualizationDragDropContainer::dropEvent(QDropEvent *event) | ||||
{ | ||||
r844 | if (impl->acceptMimeData(event->mimeData())) { | |||
r838 | auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); | |||
r844 | if (impl->hasPlaceHolder() && dragWidget) { | |||
auto &helper = sqpApp->dragDropHelper(); | ||||
r838 | ||||
r844 | auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder()); | |||
r838 | ||||
r844 | 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 | ||||
} | ||||
r838 | ||||
dragWidget->setVisible(true); | ||||
event->acceptProposedAction(); | ||||
helper.removePlaceHolder(); | ||||
emit dropOccured(droppedIndex, event->mimeData()); | ||||
} | ||||
} | ||||
r846 | else { | |||
r838 | event->ignore(); | |||
r846 | } | |||
r838 | ||||
QWidget::dropEvent(event); | ||||
} | ||||