diff --git a/core/include/Common/MimeTypesDef.h b/core/include/Common/MimeTypesDef.h index 3766fac..27535fa 100644 --- a/core/include/Common/MimeTypesDef.h +++ b/core/include/Common/MimeTypesDef.h @@ -14,6 +14,7 @@ extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_ZONE; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_VARIABLE_LIST; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_PRODUCT_LIST; extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_TIME_RANGE; +extern SCIQLOP_CORE_EXPORT const QString MIME_TYPE_SELECTION_ZONE; #endif // SCIQLOP_MIMETYPESDEF_H diff --git a/core/src/Common/MimeTypesDef.cpp b/core/src/Common/MimeTypesDef.cpp index 0393e1f..c057895 100644 --- a/core/src/Common/MimeTypesDef.cpp +++ b/core/src/Common/MimeTypesDef.cpp @@ -5,3 +5,4 @@ const QString MIME_TYPE_ZONE = QStringLiteral("sciqlop/zone"); const QString MIME_TYPE_VARIABLE_LIST = QStringLiteral("sciqlop/var-list"); const QString MIME_TYPE_PRODUCT_LIST = QStringLiteral("sciqlop/product-list"); const QString MIME_TYPE_TIME_RANGE = QStringLiteral("sciqlop/time-range"); +const QString MIME_TYPE_SELECTION_ZONE = QStringLiteral("sciqlop/selection-zone"); diff --git a/gui/include/Visualization/VisualizationDragWidget.h b/gui/include/Visualization/VisualizationDragWidget.h index 15924d3..bdc71ab 100644 --- a/gui/include/Visualization/VisualizationDragWidget.h +++ b/gui/include/Visualization/VisualizationDragWidget.h @@ -11,9 +11,13 @@ class VisualizationDragWidget : public QWidget { public: VisualizationDragWidget(QWidget *parent = nullptr); - virtual QMimeData *mimeData() const = 0; + virtual QMimeData *mimeData(const QPoint &position) const = 0; virtual bool isDragAllowed() const = 0; - virtual void highlightForMerge(bool highlighted) { Q_UNUSED(highlighted); }; + virtual void highlightForMerge(bool highlighted) { Q_UNUSED(highlighted); } + + /// Custom pixmap to display during a drag operation. + /// If the provided pixmap is null, a pixmap of the entire widget is used. + virtual QPixmap customDragPixmap(const QPoint &dragPosition); protected: virtual void mousePressEvent(QMouseEvent *event) override; diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index 5857782..d085059 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -61,7 +61,8 @@ public: QString name() const override; // VisualisationDragWidget - QMimeData *mimeData() const override; + QMimeData *mimeData(const QPoint &position) const override; + QPixmap customDragPixmap(const QPoint &dragPosition) override; bool isDragAllowed() const override; void highlightForMerge(bool highlighted) override; @@ -92,7 +93,7 @@ protected: void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; - QCustomPlot &plot() noexcept; + QCustomPlot &plot() const noexcept; private: Ui::VisualizationGraphWidget *ui; diff --git a/gui/include/Visualization/VisualizationZoneWidget.h b/gui/include/Visualization/VisualizationZoneWidget.h index 211c0e5..a345b4b 100644 --- a/gui/include/Visualization/VisualizationZoneWidget.h +++ b/gui/include/Visualization/VisualizationZoneWidget.h @@ -67,7 +67,7 @@ public: QString name() const override; // VisualisationDragWidget - QMimeData *mimeData() const override; + QMimeData *mimeData(const QPoint &position) const override; bool isDragAllowed() const override; void notifyMouseMoveInGraph(const QPointF &graphPosition, const QPointF &plotPosition, diff --git a/gui/src/Visualization/VisualizationDragDropContainer.cpp b/gui/src/Visualization/VisualizationDragDropContainer.cpp index 7fc18f1..83da700 100644 --- a/gui/src/Visualization/VisualizationDragDropContainer.cpp +++ b/gui/src/Visualization/VisualizationDragDropContainer.cpp @@ -212,11 +212,15 @@ void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidg // Note: The management of the drag object is done by Qt auto drag = new QDrag{dragWidget}; - auto mimeData = dragWidget->mimeData(); + auto mimeData = dragWidget->mimeData(dragPosition); drag->setMimeData(mimeData); - auto pixmap = QPixmap(dragWidget->size()); - dragWidget->render(&pixmap); + auto pixmap = dragWidget->customDragPixmap(dragPosition); + if (pixmap.isNull()) { + pixmap = QPixmap{dragWidget->size()}; + dragWidget->render(&pixmap); + } + drag->setPixmap(pixmap.scaled(DRAGGED_MINIATURE_WIDTH, DRAGGED_MINIATURE_WIDTH, Qt::KeepAspectRatio, Qt::SmoothTransformation)); @@ -225,17 +229,20 @@ void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidg 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, impl->m_PlaceHolderType, - impl->m_PlaceHolderText); - dragWidget->setVisible(false); - } - else { - // The drag starts directly outside the drop zone - // do not add the placeHolder + if (impl->acceptMimeData(mimeData) && impl->allowInsertForMimeData(mimeData)) { + helper.setCurrentDragWidget(dragWidget); + + if (impl->cursorIsInContainer(this)) { + auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget); + helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex, impl->m_PlaceHolderType, + impl->m_PlaceHolderText); + dragWidget->setVisible(false); + } + else { + // The drag starts directly outside the drop zone + // do not add the placeHolder + } } drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction); diff --git a/gui/src/Visualization/VisualizationDragWidget.cpp b/gui/src/Visualization/VisualizationDragWidget.cpp index 0214e5e..af01ae4 100644 --- a/gui/src/Visualization/VisualizationDragWidget.cpp +++ b/gui/src/Visualization/VisualizationDragWidget.cpp @@ -19,6 +19,12 @@ VisualizationDragWidget::VisualizationDragWidget(QWidget *parent) { } +virtual QPixmap VisualizationDragWidget::customDragPixmap(const QPoint &dragPosition) +{ + Q_UNUSED(dragPosition); + return QPixmap(); +} + void VisualizationDragWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index d28306d..aa6fdb3 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -25,6 +25,9 @@ Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget") namespace { +/// Key pressed to enable drag&drop in all modes +const auto DRAG_DROP_MODIFIER = Qt::AltModifier; + /// Key pressed to enable zoom on horizontal axis const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier; @@ -141,6 +144,22 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { } } + VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos, + const QCustomPlot &plot) const + { + VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr; + auto minDistanceToZone = -1; + for (auto zone : m_SelectionZones) { + auto distanceToZone = zone->selectTest(pos, false); + if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) + && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) { + selectionZoneItemUnderCursor = zone; + } + } + + return selectionZoneItemUnderCursor; + } + QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const { auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); @@ -371,17 +390,50 @@ QString VisualizationGraphWidget::name() const return impl->m_Name; } -QMimeData *VisualizationGraphWidget::mimeData() const +QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const { auto mimeData = new QMimeData; - mimeData->setData(MIME_TYPE_GRAPH, QByteArray{}); - auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); - mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); + auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot()); + if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones + && selectionZoneItemUnderCursor) { + mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange( + selectionZoneItemUnderCursor->range())); + mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange( + selectionZoneItemUnderCursor->range())); + } + else { + mimeData->setData(MIME_TYPE_GRAPH, QByteArray{}); + + auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); + mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); + } return mimeData; } +QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition) +{ + auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot()); + if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones + && selectionZoneItemUnderCursor) { + + auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition(); + auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition(); + + auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()), + qAbs(zoneBottomRight.y() - zoneTopLeft.y())} + .toSize(); + + auto pixmap = QPixmap(zoneSize); + render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}}); + + return pixmap; + } + + return QPixmap(); +} + bool VisualizationGraphWidget::isDragAllowed() const { return true; @@ -482,7 +534,7 @@ void VisualizationGraphWidget::leaveEvent(QEvent *event) } } -QCustomPlot &VisualizationGraphWidget::plot() noexcept +QCustomPlot &VisualizationGraphWidget::plot() const noexcept { return *ui->widget; } @@ -590,16 +642,7 @@ void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept } // Search for the selection zone under the mouse - VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr; - auto minDistanceToZone = -1; - for (auto zone : impl->m_SelectionZones) { - auto distanceToZone = zone->selectTest(event->pos(), true); - if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) && distanceToZone >= 0 - && distanceToZone < plot().selectionTolerance()) { - selectionZoneItemUnderCursor = zone; - } - } - + auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); if (selectionZoneItemUnderCursor && !impl->m_DrawingZone && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) { @@ -663,16 +706,21 @@ void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept { - if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) { - // Starts a zoom box - impl->startDrawingRect(event->pos(), plot()); - } - else if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones - && impl->m_DrawingZone == nullptr) { - // Starts a new selection zone - auto itemAtPos = plot().itemAt(event->pos(), true); - if (!itemAtPos) { - impl->startDrawingZone(event->pos(), plot()); + bool isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER); + + if (!isDragDropClick) { + if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) { + // Starts a zoom box + impl->startDrawingRect(event->pos(), plot()); + } + else if (sqpApp->plotsInteractionMode() + == SqpApplication::PlotsInteractionMode::SelectionZones + && impl->m_DrawingZone == nullptr) { + // Starts a new selection zone + auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot()); + if (!zoneAtPos) { + impl->startDrawingZone(event->pos(), plot()); + } } } else if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) { @@ -681,11 +729,13 @@ void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept // Allows mouse panning only in default mode plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode() - == SqpApplication::PlotsInteractionMode::None); + == SqpApplication::PlotsInteractionMode::None + && !isDragDropClick); - // Allows zone edition only in selection zone mode - impl->setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() - == SqpApplication::PlotsInteractionMode::SelectionZones); + // Allows zone edition only in selection zone mode without ALT pressed (ALT is for drag&drop) + impl->setSelectionZonesEditionEnabled( + sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones + && !isDragDropClick); VisualizationDragWidget::mousePressEvent(event); } diff --git a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp index a5665aa..096dde1 100644 --- a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp +++ b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp @@ -33,7 +33,7 @@ struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate { { auto distanceLeft = m_LeftLine->selectTest(pos, false); auto distanceRight = m_RightLine->selectTest(pos, false); - auto distance = zoneItem->selectTest(pos, true); + auto distance = zoneItem->selectTest(pos, false); if (distanceRight <= distance) { return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight; @@ -173,6 +173,7 @@ void VisualizationSelectionZoneItem::setEditionEnabled(bool value) setSelectable(value); if (!value) { setSelected(false); + impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition; } } diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index e5cfd59..05ba174 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -110,6 +110,8 @@ VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *p VisualizationDragDropContainer::DropBehavior::Merged); ui->dragDropContainer->setMimeType(MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Forbidden); + ui->dragDropContainer->setMimeType(MIME_TYPE_SELECTION_ZONE, + VisualizationDragDropContainer::DropBehavior::Forbidden); ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData, ui->dragDropContainer); @@ -352,8 +354,10 @@ QString VisualizationZoneWidget::name() const return ui->zoneNameLabel->text(); } -QMimeData *VisualizationZoneWidget::mimeData() const +QMimeData *VisualizationZoneWidget::mimeData(const QPoint &position) const { + Q_UNUSED(position); + auto mimeData = new QMimeData; mimeData->setData(MIME_TYPE_ZONE, QByteArray{});