diff --git a/bin/app/data/color_schemes/bright.xml b/bin/app/data/color_schemes/bright.xml index 2f3847c6..53660827 100644 --- a/bin/app/data/color_schemes/bright.xml +++ b/bin/app/data/color_schemes/bright.xml @@ -295,8 +295,8 @@ - #EEE - #DDD + #DDD + #CCC diff --git a/bin/app/data/color_schemes/dark.xml b/bin/app/data/color_schemes/dark.xml index 975e5dda..6f49c946 100644 --- a/bin/app/data/color_schemes/dark.xml +++ b/bin/app/data/color_schemes/dark.xml @@ -317,8 +317,8 @@ - #5D5D5D - #6D6D6D + #3D3D3D + #4D4D4D diff --git a/src/lib/component/view/GraphViewStyle.cpp b/src/lib/component/view/GraphViewStyle.cpp index 4c3c640c..c6f291e5 100644 --- a/src/lib/component/view/GraphViewStyle.cpp +++ b/src/lib/component/view/GraphViewStyle.cpp @@ -433,8 +433,14 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ switch (type) { case Edge::EDGE_AGGREGATION: - style.isStraight = true; - style.width = 4; + style.width = 2; + style.arrowLength = 7; + style.arrowWidth = 10; + style.originOffset.x = 22; + style.targetOffset.x = 22; + style.originOffset.y = 0; + style.targetOffset.y = 0; + style.verticalOffset = 0; style.zValue = isActive ? -2 : -5; break; case Edge::EDGE_CALL: @@ -452,6 +458,7 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ style.arrowWidth = 14; style.arrowClosed = true; style.targetOffset.x = 34; + style.zValue = isActive ? -1 : -3; break; default: break; diff --git a/src/lib_gui/qt/graphics/QtAngledLineItem.cpp b/src/lib_gui/qt/graphics/QtAngledLineItem.cpp index caef62f7..8c520ecb 100644 --- a/src/lib_gui/qt/graphics/QtAngledLineItem.cpp +++ b/src/lib_gui/qt/graphics/QtAngledLineItem.cpp @@ -9,8 +9,12 @@ QtAngledLineItem::QtAngledLineItem(QGraphicsItem* parent) : QGraphicsLineItem(parent) + , m_route(ROUTE_ANY) + , m_pivot(PIVOT_THIRD) + , m_onFront(false) , m_onBack(false) , m_horizontalIn(false) + , m_showArrow(true) { this->setAcceptHoverEvents(true); this->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); @@ -23,7 +27,8 @@ QtAngledLineItem::~QtAngledLineItem() void QtAngledLineItem::updateLine( Vec4i ownerRect, Vec4i targetRect, Vec4i ownerParentRect, Vec4i targetParentRect, - GraphViewStyle::EdgeStyle style + GraphViewStyle::EdgeStyle style, + size_t weight, bool showArrow ){ prepareGeometryChange(); m_polygon.clear(); @@ -39,8 +44,24 @@ void QtAngledLineItem::updateLine( m_targetRect.z = m_targetRect.z + 1; m_style = style; + m_showArrow = showArrow; - this->setPen(QPen(QBrush(style.color.c_str()), style.width, Qt::SolidLine, Qt::RoundCap)); + this->setPen(QPen(QBrush(style.color.c_str()), style.width + int(log10(weight)), Qt::SolidLine, Qt::RoundCap)); +} + +void QtAngledLineItem::setRoute(Route route) +{ + m_route = route; +} + +void QtAngledLineItem::setPivot(Pivot pivot) +{ + m_pivot = pivot; +} + +void QtAngledLineItem::setOnFront(bool front) +{ + m_onFront = front; } void QtAngledLineItem::setOnBack(bool back) @@ -68,7 +89,7 @@ QPainterPath QtAngledLineItem::shape() const return path; } -void QtAngledLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem* options, QWidget* widget) +void QtAngledLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget) { painter->setPen(pen()); @@ -79,6 +100,17 @@ void QtAngledLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem* path.moveTo(poly.at(i)); + + // while (i > 0) + // { + // i--; + + // path.lineTo(poly.at(i)); + // } + // painter->drawPath(path); + // return; + + QRectF drawRect = options->exposedRect; QRectF partRect; @@ -194,7 +226,7 @@ void QtAngledLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem* if (drawRect.intersects(partRect)) { partRect = getArrowBoundingRect(poly); - if (drawRect.intersects(partRect)) + if (drawRect.intersects(partRect) && m_showArrow) { drawArrow(poly, &path); } @@ -215,30 +247,61 @@ QPolygon QtAngledLineItem::getPath() const } const Vec4i& oR = m_ownerRect; + const Vec4i& oPR = m_ownerParentRect; const Vec4i& tR = m_targetRect; + const Vec4i& tPR = m_targetParentRect; - Vec2f o[2] = { Vec2f(m_ownerParentRect.x, (2 * oR.y + oR.w) / 3), Vec2f(m_ownerParentRect.z, (2 * oR.y + oR.w) / 3) }; - Vec2f t[2] = { Vec2f(m_targetParentRect.x, (tR.y + 2 * tR.w) / 3), Vec2f(m_targetParentRect.z, (tR.y + 2 * tR.w) / 3) }; + const Vec2i& oOff = m_style.originOffset; + const Vec2i& tOff = m_style.targetOffset; + + Vec2f oP[4]; + getPivotPoints(oP, oR, oPR, oOff.y, false); + + Vec2f tP[4]; + getPivotPoints(tP, tR, tPR, tOff.y, true); int io = -1; int it = -1; - float dist = -1; - if (m_onBack) + float dist = -1; + std::map dists; + + if (m_onFront) + { + io = 3; + it = 3; + } + else if (m_onBack) { io = 1; it = 1; } else { - for (int i = 0; i < 2; i++) + for (int i = 0; i < 4; i++) { - for (int j = 0; j < 2; j++) + for (int j = 0; j < 4; j++) { - Vec2f diff = o[i] - t[j]; - if (dist < 0 || diff.getLength() < dist) + if (m_route == ROUTE_HORIZONTAL && (i % 2 == 0 || j % 2 == 0)) { - dist = diff.getLength(); + continue; + } + else if (m_route == ROUTE_VERTICAL && (i % 2 == 1 || j % 2 == 1)) + { + continue; + } + else if (i % 2 != j % 2) + { + continue; + } + + Vec2f diff = oP[i] - tP[j]; + float d = diff.getLength(); + dists.emplace((i << 2) + j, d); + + if (dist < 0 || d < dist) + { + dist = d; io = i; it = j; } @@ -246,67 +309,139 @@ QPolygon QtAngledLineItem::getPath() const } } - QPoint tp((it ? 1 : -1) * m_style.targetOffset.x + t[it].x, t[it].y + m_style.targetOffset.y); - QPoint op((io ? 1 : -1) * m_style.originOffset.x + o[io].x, o[io].y + m_style.originOffset.y); + Vec2f o[4]; + getPivotPoints(o, oR, oR, oOff.y, false); + + Vec2f t[4]; + getPivotPoints(t, tR, tR, tOff.y, true); + + QPoint a(t[it].x, t[it].y); + QPoint d(o[io].x, o[io].y); + + QPoint b(tP[it].x, tP[it].y); + QPoint c(oP[io].x, oP[io].y); + + switch (it) + { + case 0: b.setY(b.y() - tOff.x); break; + case 1: b.setX(b.x() + tOff.x); break; + case 2: b.setY(b.y() + tOff.x); break; + case 3: b.setX(b.x() - tOff.x); break; + } + + switch (io) + { + case 0: c.setY(c.y() - oOff.x); break; + case 1: c.setX(c.x() + oOff.x); break; + case 2: c.setY(c.y() + oOff.x); break; + case 3: c.setX(c.x() - oOff.x); break; + } if (it != io) { - if (it && tp.x() < op.x()) + if ((it == 1 && b.x() < c.x()) || (io == 1 && b.x() > c.x())) { - io = 0; - op = QPoint(o[io].x - m_style.originOffset.x, o[io].y + m_style.originOffset.y); + if (m_horizontalIn) + { + b.setX(c.x()); + } + else + { + c.setX(b.x()); + } } - else if (!it && tp.x() < op.x()) + else if ((it == 2 && b.y() < c.y()) || (io == 2 && b.y() > c.y())) { - it = 1; - tp = QPoint(t[it].x + m_style.targetOffset.x, t[it].y + m_style.targetOffset.y); + c.setY(b.y()); } - else if (io && tp.x() > op.x()) + else if ( + (it == 3 && b.x() < c.x()) || (io == 3 && b.x() > c.x()) || + (it == 0 && b.y() < c.y()) || (io == 0 && b.y() > c.y())) { - io = 1; - op = QPoint(o[io].x + m_style.originOffset.x, o[io].y + m_style.originOffset.y); - } - else if (!io && tp.x() > op.x()) - { - it = 0; - tp = QPoint(t[it].x - m_style.targetOffset.x, t[it].y + m_style.targetOffset.y); + float dist1 = dists[(io << 2) + ((it + 2) % 4)]; + float dist2 = dists[(((io + 2) % 4) << 2) + it]; + + if (dist1 < dist2) + { + it = (it + 2) % 4; + + a = QPoint(t[it].x, t[it].y); + b = QPoint(tP[it].x, tP[it].y); + + switch (it) + { + case 0: b.setY(b.y() - tOff.x); break; + case 1: b.setX(b.x() + tOff.x); break; + case 2: b.setY(b.y() + tOff.x); break; + case 3: b.setX(b.x() - tOff.x); break; + } + } + else + { + io = (io + 2) % 4; + + c = QPoint(o[io].x, o[io].y); + d = QPoint(oP[io].x, oP[io].y); + + switch (io) + { + case 0: c.setY(c.y() - oOff.x); break; + case 1: c.setX(c.x() + oOff.x); break; + case 2: c.setY(c.y() + oOff.x); break; + case 3: c.setX(c.x() - oOff.x); break; + } + } } } - if (it == io && ((it && tp.x() < op.x()) || (!it && tp.x() > op.x()))) + if (it % 2 == 1) { - tp.setX(op.x()); - } - else if (it != io && m_horizontalIn) - { - tp.setX(op.x()); + if (it == io && ((it == 1 && b.x() < c.x()) || (it == 3 && b.x() > c.x()))) + { + b.setX(c.x()); + } + else + { + c.setX(b.x()); + } } else { - op.setX(tp.x()); + if (it == io && ((it == 0 && b.y() > c.y()) || (it == 2 && b.y() < c.y()))) + { + b.setY(c.y()); + } + else + { + c.setY(b.y()); + } } - o[0] = Vec2f(oR.x, (2 * oR.y + oR.w) / 3); - o[1] = Vec2f(oR.z, (2 * oR.y + oR.w) / 3); - t[0] = Vec2f(tR.x, (tR.y + 2 * tR.w) / 3); - t[1] = Vec2f(tR.z, (tR.y + 2 * tR.w) / 3); - - if (o[io].y < t[it].y) + if (it % 2 == 0) { - op.setX(op.x() + m_style.verticalOffset); - tp.setX(tp.x() + m_style.verticalOffset); + int vo = m_style.verticalOffset; + if (c.x() > b.x()) + { + vo *= -1; + } + + b.setY(b.y() + vo); + c.setY(c.y() + vo); } else { - op.setX(op.x() - m_style.verticalOffset); - tp.setX(tp.x() - m_style.verticalOffset); + int vo = m_style.verticalOffset; + if (c.y() > b.y()) + { + vo *= -1; + } + + b.setX(b.x() + vo); + c.setX(c.x() + vo); } QPolygon poly; - poly << QPoint(t[it].x, t[it].y + m_style.targetOffset.y); - poly << tp; - poly << op; - poly << QPoint(o[io].x, o[io].y + m_style.originOffset.y); + poly << a << b << c << d; m_polygon = poly; @@ -423,3 +558,14 @@ void QtAngledLineItem::drawArrow(const QPolygon& poly, QPainterPath* path) const path->lineTo(tip); } + +void QtAngledLineItem::getPivotPoints(Vec2f* p, const Vec4i& in, const Vec4i& out, int offset, bool target) const +{ + float f = m_pivot == PIVOT_THIRD ? (target ? 2 / 3.f : 1 / 3.f) : 1 / 2.f; + + p[0] = Vec2f(in.x + (in.z - in.x) * f + offset, out.y); + p[2] = Vec2f(in.x + (in.z - in.x) * f + offset, out.w); + + p[1] = Vec2f(out.z, in.y + (in.w - in.y) * f + offset); + p[3] = Vec2f(out.x, in.y + (in.w - in.y) * f + offset); +} diff --git a/src/lib_gui/qt/graphics/QtAngledLineItem.h b/src/lib_gui/qt/graphics/QtAngledLineItem.h index e7b36d97..855e9b8b 100644 --- a/src/lib_gui/qt/graphics/QtAngledLineItem.h +++ b/src/lib_gui/qt/graphics/QtAngledLineItem.h @@ -11,19 +11,37 @@ class QtAngledLineItem : public QGraphicsLineItem { public: + enum Route + { + ROUTE_ANY, + ROUTE_HORIZONTAL, + ROUTE_VERTICAL + }; + + enum Pivot + { + PIVOT_THIRD, + PIVOT_MIDDLE + }; + QtAngledLineItem(QGraphicsItem* parent); virtual ~QtAngledLineItem(); void updateLine( Vec4i ownerRect, Vec4i targetRect, Vec4i ownerParentRect, Vec4i targetParentRect, - GraphViewStyle::EdgeStyle style); + GraphViewStyle::EdgeStyle style, + size_t weight, bool showArrow); + void setRoute(Route route); + void setPivot(Pivot pivot); + + void setOnFront(bool front); void setOnBack(bool back); void setHorizontalIn(bool horizontal); virtual QPainterPath shape() const; - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem* options, QWidget* widget); + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget); private: QPolygon getPath() const; @@ -32,6 +50,8 @@ private: QRectF getArrowBoundingRect(const QPolygon& poly) const; void drawArrow(const QPolygon& poly, QPainterPath* path) const; + void getPivotPoints(Vec2f* p, const Vec4i& in, const Vec4i& out, int offset, bool target) const; + Vec4i m_ownerRect; Vec4i m_targetRect; @@ -40,8 +60,13 @@ private: GraphViewStyle::EdgeStyle m_style; + Route m_route; + Pivot m_pivot; + + bool m_onFront; bool m_onBack; bool m_horizontalIn; + bool m_showArrow; mutable QPolygon m_polygon; }; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp index 4ae034ae..02b326ce 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp @@ -34,6 +34,11 @@ QtGraphEdge::QtGraphEdge( , m_mousePos(0.0f, 0.0f) , m_mouseMoved(false) { + if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD) + { + m_owner.swap(m_target); + } + m_fromActive = m_owner.lock()->getIsActive(); m_toActive = m_target.lock()->getIsActive(); @@ -91,11 +96,6 @@ void QtGraphEdge::updateLine() bool showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE; - if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD) - { - owner.swap(target); - } - GraphViewStyle::NodeStyle countStyle = GraphViewStyle::getStyleOfCountCircle(); dynamic_cast(m_child)->updateLine( @@ -108,30 +108,52 @@ void QtGraphEdge::updateLine() m_child = new QtAngledLineItem(this); } - dynamic_cast(m_child)->updateLine( - owner->getBoundingRect(), target->getBoundingRect(), - owner->getParentBoundingRect(), target->getParentBoundingRect(), - style); + QtAngledLineItem* child = dynamic_cast(m_child); if (m_fromActive && owner->getLastParent() == target->getLastParent()) { - dynamic_cast(m_child)->setOnBack(true); + child->setOnBack(true); } if (m_toActive) { - dynamic_cast(m_child)->setHorizontalIn(true); + child->setHorizontalIn(true); + + if (owner->getLastParent() == target->getLastParent()) + { + child->setOnFront(true); + } } + + if (type != Edge::EDGE_INHERITANCE && type != Edge::EDGE_AGGREGATION) + { + child->setRoute(QtAngledLineItem::ROUTE_HORIZONTAL); + } + + bool showArrow = true; + if (type == Edge::EDGE_AGGREGATION) + { + child->setPivot(QtAngledLineItem::PIVOT_MIDDLE); + + showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE; + } + + child->updateLine( + owner->getBoundingRect(), target->getBoundingRect(), + owner->getParentBoundingRect(), target->getParentBoundingRect(), + style, m_weight, showArrow); } - if (m_data) + QString toolTip = Edge::getTypeString(type).c_str(); + if (type == Edge::EDGE_AGGREGATION) { - m_child->setToolTip(QString::fromStdString(m_data->getTypeString())); - } - else - { - m_child->setToolTip(QString::fromStdString(Edge::getTypeString(Edge::EDGE_AGGREGATION))); + toolTip += ": " + QString::number(m_weight) + " edge"; + if (m_weight != 1) + { + toolTip += "s"; + } } + m_child->setToolTip(toolTip); this->setZValue(style.zValue); // Used to draw edges always on top of nodes. }