##// END OF EJS Templates
Merge branch 'feature/DragAndDropVisualization' into develop
Merge branch 'feature/DragAndDropVisualization' into develop

File last commit:

r838:7a8534096878
r843:56046db82688 merge
Show More
VisualizationDragDropContainer.cpp
312 lines | 10.0 KiB | text/x-c | CppLexer
/ gui / src / Visualization / VisualizationDragDropContainer.cpp
#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);
}