VisualizationSelectionZoneItem.cpp
438 lines
| 15.2 KiB
| text/x-c
|
CppLexer
r1044 | #include "Visualization/VisualizationSelectionZoneItem.h" | |||
r1079 | #include "Visualization/VisualizationGraphWidget.h" | |||
r1084 | #include "Visualization/VisualizationSelectionZoneManager.h" | |||
#include "Visualization/VisualizationWidget.h" | ||||
r1044 | ||||
r1049 | const QString &DEFAULT_COLOR = QStringLiteral("#E79D41"); | |||
r1044 | struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate { | |||
QCustomPlot *m_Plot; | ||||
double m_T1 = 0; | ||||
double m_T2 = 0; | ||||
QColor m_Color; | ||||
bool m_IsEditionEnabled = true; | ||||
double m_MovedOrinalT1 = 0; | ||||
double m_MovedOrinalT2 = 0; | ||||
QCPItemStraightLine *m_LeftLine; | ||||
QCPItemStraightLine *m_RightLine; | ||||
QCPItemText *m_NameLabelItem = nullptr; | ||||
enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move }; | ||||
EditionMode m_CurrentEditionMode; | ||||
r1050 | QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones; | |||
r1044 | VisualizationSelectionZoneItemPrivate(QCustomPlot *plot) | |||
: m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition) | ||||
{ | ||||
} | ||||
void updatePosition(VisualizationSelectionZoneItem *item) | ||||
{ | ||||
item->topLeft->setCoords(m_T1, 0); | ||||
item->bottomRight->setCoords(m_T2, 1); | ||||
} | ||||
EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem) | ||||
{ | ||||
auto distanceLeft = m_LeftLine->selectTest(pos, false); | ||||
auto distanceRight = m_RightLine->selectTest(pos, false); | ||||
r1047 | auto distance = zoneItem->selectTest(pos, false); | |||
r1044 | ||||
if (distanceRight <= distance) { | ||||
return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight; | ||||
} | ||||
else if (distanceLeft <= distance) { | ||||
return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft; | ||||
} | ||||
return VisualizationSelectionZoneItemPrivate::EditionMode::Move; | ||||
} | ||||
r1050 | ||||
double pixelSizeToAxisXSize(double pixels) | ||||
{ | ||||
auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom); | ||||
return axis->pixelToCoord(pixels) - axis->pixelToCoord(0); | ||||
} | ||||
r1081 | ||||
bool alignZones(VisualizationSelectionZoneItem *referenceZone, | ||||
const QVector<VisualizationSelectionZoneItem *> &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; | ||||
} | ||||
r1044 | }; | |||
VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot) | ||||
: QCPItemRect(plot), | ||||
impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)} | ||||
{ | ||||
topLeft->setTypeX(QCPItemPosition::ptPlotCoords); | ||||
topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio); | ||||
bottomRight->setTypeX(QCPItemPosition::ptPlotCoords); | ||||
bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio); | ||||
r1049 | setSelectable(false); | |||
r1044 | ||||
impl->m_RightLine = new QCPItemStraightLine(plot); | ||||
impl->m_RightLine->point1->setParentAnchor(topRight); | ||||
impl->m_RightLine->point2->setParentAnchor(bottomRight); | ||||
impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute); | ||||
impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute); | ||||
impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute); | ||||
impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute); | ||||
r1049 | impl->m_RightLine->setSelectable(false); | |||
r1044 | ||||
impl->m_LeftLine = new QCPItemStraightLine(plot); | ||||
impl->m_LeftLine->point1->setParentAnchor(topLeft); | ||||
impl->m_LeftLine->point2->setParentAnchor(bottomLeft); | ||||
impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute); | ||||
impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute); | ||||
impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute); | ||||
impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute); | ||||
impl->m_LeftLine->setSelectable(false); | ||||
r1051 | connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine, | |||
&QCPItemStraightLine::setSelected); | ||||
connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine, | ||||
&QCPItemStraightLine::setSelected); | ||||
r1049 | setColor(QColor(DEFAULT_COLOR)); | |||
r1044 | } | |||
r1102 | VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem() {} | |||
r1044 | ||||
r1079 | VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept | |||
{ | ||||
auto parent = impl->m_Plot->parentWidget(); | ||||
while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) { | ||||
parent = parent->parentWidget(); | ||||
} | ||||
return qobject_cast<VisualizationGraphWidget *>(parent); | ||||
} | ||||
r1044 | void VisualizationSelectionZoneItem::setName(const QString &name) | |||
{ | ||||
if (name.isEmpty() && impl->m_NameLabelItem) { | ||||
impl->m_Plot->removeItem(impl->m_NameLabelItem); | ||||
impl->m_NameLabelItem = nullptr; | ||||
} | ||||
else if (!impl->m_NameLabelItem) { | ||||
impl->m_NameLabelItem = new QCPItemText(impl->m_Plot); | ||||
impl->m_NameLabelItem->setText(name); | ||||
impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop); | ||||
impl->m_NameLabelItem->setColor(impl->m_Color); | ||||
impl->m_NameLabelItem->position->setParentAnchor(top); | ||||
} | ||||
} | ||||
QString VisualizationSelectionZoneItem::name() const | ||||
{ | ||||
if (!impl->m_NameLabelItem) { | ||||
return QString(); | ||||
} | ||||
return impl->m_NameLabelItem->text(); | ||||
} | ||||
SqpRange VisualizationSelectionZoneItem::range() const | ||||
{ | ||||
SqpRange range; | ||||
range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2; | ||||
range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2; | ||||
return range; | ||||
} | ||||
void VisualizationSelectionZoneItem::setRange(double tstart, double tend) | ||||
{ | ||||
impl->m_T1 = tstart; | ||||
impl->m_T2 = tend; | ||||
impl->updatePosition(this); | ||||
} | ||||
void VisualizationSelectionZoneItem::setStart(double tstart) | ||||
{ | ||||
impl->m_T1 = tstart; | ||||
impl->updatePosition(this); | ||||
} | ||||
void VisualizationSelectionZoneItem::setEnd(double tend) | ||||
{ | ||||
impl->m_T2 = tend; | ||||
impl->updatePosition(this); | ||||
} | ||||
void VisualizationSelectionZoneItem::setColor(const QColor &color) | ||||
{ | ||||
impl->m_Color = color; | ||||
auto brushColor = color; | ||||
r1051 | brushColor.setAlpha(80); | |||
r1044 | setBrush(QBrush(brushColor)); | |||
setPen(QPen(Qt::NoPen)); | ||||
auto selectedBrushColor = brushColor; | ||||
r1051 | selectedBrushColor.setAlpha(150); | |||
r1044 | setSelectedBrush(QBrush(selectedBrushColor)); | |||
setSelectedPen(QPen(Qt::NoPen)); | ||||
auto linePen = QPen(color); | ||||
linePen.setStyle(Qt::SolidLine); | ||||
r1051 | linePen.setWidth(4); | |||
r1044 | ||||
auto selectedLinePen = linePen; | ||||
r1051 | selectedLinePen.setColor(color.darker(120)); | |||
selectedLinePen.setWidth(4); | ||||
r1044 | ||||
impl->m_LeftLine->setPen(linePen); | ||||
impl->m_RightLine->setPen(linePen); | ||||
impl->m_LeftLine->setSelectedPen(selectedLinePen); | ||||
impl->m_RightLine->setSelectedPen(selectedLinePen); | ||||
} | ||||
void VisualizationSelectionZoneItem::setEditionEnabled(bool value) | ||||
{ | ||||
impl->m_IsEditionEnabled = value; | ||||
setSelectable(value); | ||||
if (!value) { | ||||
setSelected(false); | ||||
r1047 | impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition; | |||
r1044 | } | |||
} | ||||
bool VisualizationSelectionZoneItem::isEditionEnabled() const | ||||
{ | ||||
return impl->m_IsEditionEnabled; | ||||
} | ||||
r1085 | void VisualizationSelectionZoneItem::moveToTop() | |||
{ | ||||
moveToLayer(layer(), false); | ||||
} | ||||
r1044 | Qt::CursorShape | |||
VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const | ||||
{ | ||||
auto mode = impl->m_CurrentEditionMode | ||||
== VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition | ||||
? impl->getEditionMode(position, this) | ||||
: impl->m_CurrentEditionMode; | ||||
switch (mode) { | ||||
case VisualizationSelectionZoneItemPrivate::EditionMode::Move: | ||||
return Qt::SizeAllCursor; | ||||
case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft: | ||||
case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough | ||||
return Qt::SizeHorCursor; | ||||
default: | ||||
return Qt::ArrowCursor; | ||||
} | ||||
} | ||||
void VisualizationSelectionZoneItem::setHovered(bool value) | ||||
{ | ||||
if (value) { | ||||
auto linePen = impl->m_LeftLine->pen(); | ||||
linePen.setStyle(Qt::DotLine); | ||||
linePen.setWidth(3); | ||||
auto selectedLinePen = impl->m_LeftLine->selectedPen(); | ||||
; | ||||
selectedLinePen.setStyle(Qt::DotLine); | ||||
selectedLinePen.setWidth(3); | ||||
impl->m_LeftLine->setPen(linePen); | ||||
impl->m_RightLine->setPen(linePen); | ||||
impl->m_LeftLine->setSelectedPen(selectedLinePen); | ||||
impl->m_RightLine->setSelectedPen(selectedLinePen); | ||||
} | ||||
else { | ||||
setColor(impl->m_Color); | ||||
} | ||||
} | ||||
r1050 | void VisualizationSelectionZoneItem::setAssociatedEditedZones( | |||
const QVector<VisualizationSelectionZoneItem *> &associatedZones) | ||||
{ | ||||
impl->m_AssociatedEditedZones = associatedZones; | ||||
impl->m_AssociatedEditedZones.removeAll(this); | ||||
} | ||||
r1081 | bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft( | |||
const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize) | ||||
{ | ||||
return impl->alignZones(this, zonesToAlign, true, allowResize, true); | ||||
} | ||||
bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight( | ||||
const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize) | ||||
{ | ||||
return impl->alignZones(this, zonesToAlign, false, allowResize, true); | ||||
} | ||||
bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft( | ||||
const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize) | ||||
{ | ||||
return impl->alignZones(this, zonesToAlign, true, allowResize, false); | ||||
} | ||||
bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight( | ||||
const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize) | ||||
{ | ||||
return impl->alignZones(this, zonesToAlign, false, allowResize, false); | ||||
} | ||||
r1044 | void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details) | |||
{ | ||||
r1085 | Q_UNUSED(details); | |||
r1049 | if (isEditionEnabled() && event->button() == Qt::LeftButton) { | |||
r1044 | impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this); | |||
impl->m_MovedOrinalT1 = impl->m_T1; | ||||
impl->m_MovedOrinalT2 = impl->m_T2; | ||||
r1050 | for (auto associatedZone : impl->m_AssociatedEditedZones) { | |||
associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1; | ||||
associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2; | ||||
} | ||||
r1044 | } | |||
else { | ||||
r1049 | impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition; | |||
r1044 | event->ignore(); | |||
} | ||||
} | ||||
void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) | ||||
{ | ||||
if (isEditionEnabled()) { | ||||
r1084 | if (!selected()) { | |||
// Force the item to be selected during the edition | ||||
parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected( | ||||
this, true); | ||||
} | ||||
r1044 | auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom); | |||
r1050 | auto pixelDiff = event->pos().x() - startPos.x(); | |||
auto diff = impl->pixelSizeToAxisXSize(pixelDiff); | ||||
r1044 | ||||
switch (impl->m_CurrentEditionMode) { | ||||
case VisualizationSelectionZoneItemPrivate::EditionMode::Move: | ||||
setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff); | ||||
r1050 | for (auto associatedZone : impl->m_AssociatedEditedZones) { | |||
associatedZone->move(pixelDiff); | ||||
} | ||||
r1044 | break; | |||
case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft: | ||||
setStart(impl->m_MovedOrinalT1 + diff); | ||||
r1050 | for (auto associatedZone : impl->m_AssociatedEditedZones) { | |||
impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2 | ||||
? associatedZone->resizeLeft(pixelDiff) | ||||
: associatedZone->resizeRight(pixelDiff); | ||||
} | ||||
r1044 | break; | |||
case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: | ||||
setEnd(impl->m_MovedOrinalT2 + diff); | ||||
r1050 | for (auto associatedZone : impl->m_AssociatedEditedZones) { | |||
impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2 | ||||
? associatedZone->resizeRight(pixelDiff) | ||||
: associatedZone->resizeLeft(pixelDiff); | ||||
} | ||||
r1044 | break; | |||
r1049 | default: | |||
break; | ||||
r1044 | } | |||
r1050 | ||||
for (auto associatedZone : impl->m_AssociatedEditedZones) { | ||||
associatedZone->parentPlot()->replot(); | ||||
} | ||||
r1044 | } | |||
else { | ||||
event->ignore(); | ||||
} | ||||
} | ||||
void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) | ||||
{ | ||||
r1085 | Q_UNUSED(startPos); | |||
r1044 | if (isEditionEnabled()) { | |||
impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition; | ||||
} | ||||
else { | ||||
event->ignore(); | ||||
} | ||||
r1050 | ||||
impl->m_AssociatedEditedZones.clear(); | ||||
} | ||||
void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff) | ||||
{ | ||||
auto diff = impl->pixelSizeToAxisXSize(pixelDiff); | ||||
if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) { | ||||
setStart(impl->m_MovedOrinalT1 + diff); | ||||
} | ||||
else { | ||||
setEnd(impl->m_MovedOrinalT2 + diff); | ||||
} | ||||
} | ||||
void VisualizationSelectionZoneItem::resizeRight(double pixelDiff) | ||||
{ | ||||
auto diff = impl->pixelSizeToAxisXSize(pixelDiff); | ||||
if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) { | ||||
setStart(impl->m_MovedOrinalT1 + diff); | ||||
} | ||||
else { | ||||
setEnd(impl->m_MovedOrinalT2 + diff); | ||||
} | ||||
} | ||||
void VisualizationSelectionZoneItem::move(double pixelDiff) | ||||
{ | ||||
auto diff = impl->pixelSizeToAxisXSize(pixelDiff); | ||||
setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff); | ||||
r1044 | } | |||