diff --git a/gui/include/Visualization/VisualizationSelectionZoneItem.h b/gui/include/Visualization/VisualizationSelectionZoneItem.h index 0629ddd..1eb81c5 100644 --- a/gui/include/Visualization/VisualizationSelectionZoneItem.h +++ b/gui/include/Visualization/VisualizationSelectionZoneItem.h @@ -31,8 +31,22 @@ public: Qt::CursorShape curshorShapeForPosition(const QPoint &position) const; void setHovered(bool value); + /// Sets the zones which should be moved or reisized together with this zone void setAssociatedEditedZones(const QVector &associatedZones); + /// Align the specified zones with this one, vertically with the left border + bool alignZonesVerticallyOnLeft(const QVector &zonesToAlign, + bool allowResize); + /// Align the specified zones with this one, vertically with the right border + bool alignZonesVerticallyOnRight(const QVector &zonesToAlign, + bool allowResize); + /// Align the specified zones with this one, temporally with the left border + bool alignZonesTemporallyOnLeft(const QVector &zonesToAlign, + bool allowResize); + /// Align the specified zones with this one, temporally with the right border + bool alignZonesTemporallyOnRight(const QVector &zonesToAlign, + bool allowResize); + protected: void mousePressEvent(QMouseEvent *event, const QVariant &details) override; void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override; @@ -42,7 +56,6 @@ protected: void resizeRight(double pixelDiff); void move(double pixelDiff); - private: class VisualizationSelectionZoneItemPrivate; spimpl::unique_impl_ptr impl; diff --git a/gui/src/Visualization/VisualizationActionManager.cpp b/gui/src/Visualization/VisualizationActionManager.cpp index 4ee6694..5639d6d 100644 --- a/gui/src/Visualization/VisualizationActionManager.cpp +++ b/gui/src/Visualization/VisualizationActionManager.cpp @@ -11,7 +11,7 @@ void VisualizationActionManager::installSelectionZoneActions() { auto &actionController = sqpApp->actionsGuiController(); - actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto &zones) { + actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) { for (auto selectionZone : zones) { if (auto graph = selectionZone->parentGraphWidget()) { graph->removeSelectionZone(selectionZone); @@ -19,12 +19,89 @@ void VisualizationActionManager::installSelectionZoneActions() } }); - auto alignEnableFuntion = [](auto &items) { return items.count() > 0; }; + auto alignEnableFuntion = [](auto items) { return items.count() > 1; }; - auto alignLeftAction = actionController.addSectionZoneAction("Align Left Vertically", [](auto &zones) {}); + // Vertical alignment actions + auto alignLeftAction + = actionController.addSectionZoneAction("Align Vertically / Left", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesVerticallyOnLeft(zones, false); + }); alignLeftAction->setEnableFunction(alignEnableFuntion); + auto alignLeftBorderAction + = actionController.addSectionZoneAction("Align Vertically / Left Borders", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesVerticallyOnLeft(zones, true); + }); + alignLeftBorderAction->setEnableFunction(alignEnableFuntion); + auto alignRightAction - = actionController.addSectionZoneAction("Align Right vertically", [](auto &zones) {}); + = actionController.addSectionZoneAction("Align Vertically / Right", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesVerticallyOnRight(zones, false); + }); alignRightAction->setEnableFunction(alignEnableFuntion); + + auto alignRightBorderAction + = actionController.addSectionZoneAction("Align Vertically / Right Borders", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesVerticallyOnRight(zones, true); + }); + alignRightBorderAction->setEnableFunction(alignEnableFuntion); + + auto alignLeftAndRightAction = actionController.addSectionZoneAction( + "Align Vertically / Left and Right", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesVerticallyOnLeft(zones, false); + ref->alignZonesVerticallyOnRight(zones, true); + }); + alignLeftAndRightAction->setEnableFunction(alignEnableFuntion); + + // Temporal alignment actions + auto alignLeftTemporallyAction + = actionController.addSectionZoneAction("Align Temporally / Left", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesTemporallyOnLeft(zones, false); + }); + alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion); + + auto alignLeftBorderTemporallyAction + = actionController.addSectionZoneAction("Align Temporally / Left Borders", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesTemporallyOnLeft(zones, true); + }); + alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion); + + auto alignRightTemporallyAction + = actionController.addSectionZoneAction("Align Temporally / Right", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesTemporallyOnRight(zones, false); + }); + alignRightTemporallyAction->setEnableFunction(alignEnableFuntion); + + auto alignRightBorderTemporallyAction + = actionController.addSectionZoneAction("Align Temporally / Right Borders", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesTemporallyOnRight(zones, true); + }); + alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion); + + auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction( + "Align Temporally / Left and Right", [](auto zones) { + Q_ASSERT(zones.count() > 1); + auto ref = zones.takeFirst(); + ref->alignZonesTemporallyOnLeft(zones, false); + ref->alignZonesTemporallyOnRight(zones, true); + }); + alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion); } diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index b3f8fa1..4f13be3 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -614,6 +614,9 @@ void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept auto selectionZoneItem = impl->selectionZoneAt(pos, plot()); if (selectionZoneItem) { auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems(); + selectedItems.removeAll(selectionZoneItem); + selectedItems.prepend(selectionZoneItem); // Put the current selection zone first + auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions(); if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) { graphMenu.addSeparator(); @@ -622,9 +625,8 @@ void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept for (auto zoneAction : zoneActions) { auto action = graphMenu.addAction(zoneAction->name()); action->setEnabled(zoneAction->isEnabled(selectedItems)); - QObject::connect(action, &QAction::triggered, [zoneAction, &selectedItems]() { - zoneAction->execute(selectedItems); - }); + QObject::connect(action, &QAction::triggered, + [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); } } diff --git a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp index fa67517..591953b 100644 --- a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp +++ b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp @@ -55,6 +55,53 @@ struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate { auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom); return axis->pixelToCoord(pixels) - axis->pixelToCoord(0); } + + bool alignZones(VisualizationSelectionZoneItem *referenceZone, + const QVector &zonesToAlign, bool alignOnLeft, + bool allowResize, bool vertically) + { + auto result = false; + + auto referenceTime + = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd; + + auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom); + auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime); + + for (auto otherZone : zonesToAlign) { + + auto otherZoneRange = otherZone->range(); + auto newZoneStart = otherZoneRange.m_TStart; + auto newZoneEnd = otherZoneRange.m_TEnd; + + auto alignedTime = referenceTime; + if (vertically) { + auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom); + alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition); + } + + if (alignOnLeft) { + newZoneStart = alignedTime; + if (!allowResize) { + newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart); + } + } + else { // align on right + newZoneEnd = alignedTime; + if (!allowResize) { + newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart); + } + } + + if (newZoneStart < newZoneEnd) { + result = true; + otherZone->setRange(newZoneStart, newZoneEnd); + otherZone->parentPlot()->replot(); + } + } + + return result; + } }; VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot) @@ -252,6 +299,30 @@ void VisualizationSelectionZoneItem::setAssociatedEditedZones( impl->m_AssociatedEditedZones.removeAll(this); } +bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft( + const QVector &zonesToAlign, bool allowResize) +{ + return impl->alignZones(this, zonesToAlign, true, allowResize, true); +} + +bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight( + const QVector &zonesToAlign, bool allowResize) +{ + return impl->alignZones(this, zonesToAlign, false, allowResize, true); +} + +bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft( + const QVector &zonesToAlign, bool allowResize) +{ + return impl->alignZones(this, zonesToAlign, true, allowResize, false); +} + +bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight( + const QVector &zonesToAlign, bool allowResize) +{ + return impl->alignZones(this, zonesToAlign, false, allowResize, false); +} + void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details) { if (isEditionEnabled() && event->button() == Qt::LeftButton) {