##// END OF EJS Templates
Fix some glitches which occurred when dragging in the visualization something from the sides.
Fix some glitches which occurred when dragging in the visualization something from the sides.

File last commit:

r876:da9523540c62
r880:4ed691514436
Show More
DragDropHelper.cpp
353 lines | 11.9 KiB | text/x-c | CppLexer
/ gui / src / DragDropHelper.cpp
#include "DragDropHelper.h"
#include "SqpApplication.h"
#include "Visualization/VisualizationDragDropContainer.h"
#include "Visualization/VisualizationDragWidget.h"
#include "Visualization/VisualizationWidget.h"
#include "Visualization/operations/FindVariableOperation.h"
#include "Variable/Variable.h"
#include "Variable/VariableController.h"
#include "Common/MimeTypesDef.h"
#include "Common/VisualizationDef.h"
#include <QDir>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QScrollArea>
#include <QScrollBar>
#include <QTimer>
#include <QVBoxLayout>
const int SCROLL_SPEED = 5;
const int SCROLL_ZONE_SIZE = 50;
Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper")
struct DragDropScroller::DragDropScrollerPrivate {
QList<QScrollArea *> m_ScrollAreas;
QScrollArea *m_CurrentScrollArea = nullptr;
std::unique_ptr<QTimer> m_Timer = nullptr;
enum class ScrollDirection { up, down, unknown };
ScrollDirection m_Direction = ScrollDirection::unknown;
explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
{
m_Timer->setInterval(0);
}
};
DragDropScroller::DragDropScroller(QObject *parent)
: QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
{
connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
}
void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
{
impl->m_ScrollAreas << scrollArea;
scrollArea->viewport()->setAcceptDrops(true);
}
void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
{
impl->m_ScrollAreas.removeAll(scrollArea);
scrollArea->viewport()->setAcceptDrops(false);
}
bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::DragMove) {
auto w = static_cast<QWidget *>(obj);
if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
auto moveEvent = static_cast<QDragMoveEvent *>(event);
auto pos = moveEvent->pos();
if (impl->m_CurrentScrollArea->viewport() != w) {
auto globalPos = w->mapToGlobal(moveEvent->pos());
pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
}
auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
- SCROLL_ZONE_SIZE;
auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
if (!isInTopZone && !isInBottomZone) {
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
impl->m_Timer->stop();
}
else if (!impl->m_Timer->isActive()) {
impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
: DragDropScrollerPrivate::ScrollDirection::down;
impl->m_Timer->start();
}
}
}
else if (event->type() == QEvent::DragEnter) {
auto w = static_cast<QWidget *>(obj);
for (auto scrollArea : impl->m_ScrollAreas) {
if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
auto enterEvent = static_cast<QDragEnterEvent *>(event);
enterEvent->acceptProposedAction();
enterEvent->setDropAction(Qt::IgnoreAction);
impl->m_CurrentScrollArea = scrollArea;
break;
}
}
}
else if (event->type() == QEvent::DragLeave) {
if (impl->m_CurrentScrollArea) {
if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
.contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
impl->m_CurrentScrollArea = nullptr;
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
impl->m_Timer->stop();
}
}
}
else if (event->type() == QEvent::Drop) {
if (impl->m_CurrentScrollArea) {
impl->m_CurrentScrollArea = nullptr;
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
impl->m_Timer->stop();
}
}
return false;
}
void DragDropScroller::onTimer()
{
if (impl->m_CurrentScrollArea) {
auto mvt = 0;
switch (impl->m_Direction) {
case DragDropScrollerPrivate::ScrollDirection::up:
mvt = SCROLL_SPEED;
break;
case DragDropScrollerPrivate::ScrollDirection::down:
mvt = -SCROLL_SPEED;
break;
default:
break;
}
impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
}
}
struct DragDropHelper::DragDropHelperPrivate {
VisualizationDragWidget *m_CurrentDragWidget = nullptr;
std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
// QTemporaryFile to have a name which is not generated.
VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
QMetaObject::Connection m_DragWidgetDestroyedConnection;
QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
explicit DragDropHelperPrivate()
: m_PlaceHolder{std::make_unique<QWidget>()},
m_DragDropScroller{std::make_unique<DragDropScroller>()}
{
m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
sqpApp->installEventFilter(m_DragDropScroller.get());
m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png");
}
void preparePlaceHolder() const
{
if (m_CurrentDragWidget) {
m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
}
else {
// Configuration of the placeHolder when there is no dragWidget
// (for instance with a drag from a variable)
m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
}
};
DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
{
}
DragDropHelper::~DragDropHelper()
{
QFile::remove(impl->m_ImageTempUrl);
}
void DragDropHelper::resetDragAndDrop()
{
setCurrentDragWidget(nullptr);
impl->m_HighlightedDragWidget = nullptr;
}
void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
{
if (impl->m_CurrentDragWidget) {
QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
}
if (dragWidget) {
// ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
impl->m_DragWidgetDestroyedConnection
= QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
[this]() { impl->m_CurrentDragWidget = nullptr; });
}
impl->m_CurrentDragWidget = dragWidget;
}
VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
{
return impl->m_CurrentDragWidget;
}
QWidget &DragDropHelper::placeHolder() const
{
return *impl->m_PlaceHolder;
}
void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
{
removePlaceHolder();
impl->preparePlaceHolder();
layout->insertWidget(index, impl->m_PlaceHolder.get());
impl->m_PlaceHolder->show();
}
void DragDropHelper::removePlaceHolder()
{
auto parentWidget = impl->m_PlaceHolder->parentWidget();
if (parentWidget) {
parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
impl->m_PlaceHolder->setParent(nullptr);
impl->m_PlaceHolder->hide();
}
}
bool DragDropHelper::isPlaceHolderSet() const
{
return impl->m_PlaceHolder->parentWidget();
}
void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
{
impl->m_DragDropScroller->addScrollArea(scrollArea);
}
void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
{
impl->m_DragDropScroller->removeScrollArea(scrollArea);
}
QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
{
image.save(impl->m_ImageTempUrl);
return QUrl::fromLocalFile(impl->m_ImageTempUrl);
}
void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
{
if (impl->m_HighlightedDragWidget) {
impl->m_HighlightedDragWidget->highlightForMerge(false);
QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
}
if (dragWidget) {
dragWidget->highlightForMerge(true);
// ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
impl->m_DragWidgetDestroyedConnection
= QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
[this]() { impl->m_HighlightedDragWidget = nullptr; });
}
impl->m_HighlightedDragWidget = dragWidget;
}
VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const
{
return impl->m_HighlightedDragWidget;
}
bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
VisualizationDragDropContainer *dropContainer)
{
if (!mimeData || !dropContainer) {
qCWarning(LOG_DragDropHelper()) << QObject::tr(
"DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
Q_ASSERT(false);
return false;
}
auto result = false;
if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
auto variables = sqpApp->variableController().variablesForMimeData(
mimeData->data(MIME_TYPE_VARIABLE_LIST));
if (variables.count() == 1) {
auto variable = variables.first();
if (variable->dataSeries() != nullptr) {
// Check that the variable is not already in a graph
auto parent = dropContainer->parentWidget();
while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
parent = parent->parentWidget(); // Search for the top level VisualizationWidget
}
if (parent) {
auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
FindVariableOperation findVariableOperation{variable};
visualizationWidget->accept(&findVariableOperation);
auto variableContainers = findVariableOperation.result();
if (variableContainers.empty()) {
result = true;
}
else {
// result = false: the variable already exist in the visualisation
}
}
else {
qCWarning(LOG_DragDropHelper()) << QObject::tr(
"DragDropHelper::checkMimeDataForVisualization, the parent "
"VisualizationWidget cannot be found. Cannot check if the variable is "
"already used or not.");
}
}
else {
// result = false: the variable is not fully loaded
}
}
else {
// result = false: cannot drop multiple variables in the visualisation
}
}
else {
// Other MIME data
// no special rules, accepted by default
result = true;
}
return result;
}