ui: Show call graphs, inheritance trees and include trees for active symbol (issue 249 337)

* Added TrailLayouter with Sugiyama dependency layouting
* Layouter supports horizontal and vertical layouts
* Added basic ui to graph view for showing incoming and outgoing paths
* Added depth slider, with infinity at maximum
* Disable trail ui when not available
* Added trail messages to undo stack and only use last one when undoing
* Added QtGraphLineBezier for trail graphs and refactored other line types
* Disabled edge activation for trail layouts, shown only visually
* Highlight all connected edges when hovering a node in trail layout
* Use different color when highlighting call edges for better contrast
* Fixed BucketLayouter issue when edge does not leave node
* Increased click area for angled edges

bug id = 249 337

fortune cookie message = Answer just what the heart prompts to you.
This commit is contained in:
Eberhard Graether
2017-04-24 01:14:53 +02:00
parent 01bd0efefb
commit f0b7dcdcdd
40 changed files with 1892 additions and 487 deletions
+8 -4
View File
@@ -57,16 +57,20 @@ add_files(
qt/element/QtUndoRedo.cpp
qt/element/QtUndoRedo.h
qt/graphics/QtAngledLineItem.cpp
qt/graphics/QtAngledLineItem.h
qt/graphics/QtCountCircleItem.cpp
qt/graphics/QtCountCircleItem.h
qt/graphics/QtGraphicsView.cpp
qt/graphics/QtGraphicsView.h
qt/graphics/QtLineItemAngled.cpp
qt/graphics/QtLineItemAngled.h
qt/graphics/QtLineItemBase.cpp
qt/graphics/QtLineItemBase.h
qt/graphics/QtLineItemBezier.cpp
qt/graphics/QtLineItemBezier.h
qt/graphics/QtLineItemStraight.cpp
qt/graphics/QtLineItemStraight.h
qt/graphics/QtRoundedRectItem.cpp
qt/graphics/QtRoundedRectItem.h
qt/graphics/QtStraightLineItem.cpp
qt/graphics/QtStraightLineItem.h
qt/network/QtIDECommunicationController.cpp
qt/network/QtIDECommunicationController.h
@@ -0,0 +1,181 @@
#include "qt/graphics/QtLineItemAngled.h"
#include <cmath>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
QtLineItemAngled::QtLineItemAngled(QGraphicsItem* parent)
: QtLineItemBase(parent)
{
this->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
}
QtLineItemAngled::~QtLineItemAngled()
{
}
QPainterPath QtLineItemAngled::shape() const
{
QPainterPath path;
QPolygon poly = getPath();
for (int i = 0; i < poly.size() - 1; i++)
{
path.addRect(QRectF(poly.at(i), poly.at(i + 1)).normalized().adjusted(-5, -5, 5, 5));
}
path.addRect(getArrowBoundingRect(poly).adjusted(-3, -3, 3, 3));
return path;
}
void QtLineItemAngled::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
{
painter->setPen(pen());
QPainterPath path;
QPolygon poly = getPath();
int i = poly.length() - 1;
path.moveTo(poly.at(i));
// while (i > 0)
// {
// i--;
// path.lineTo(poly.at(i));
// }
// painter->drawPath(path);
// return;
QRectF drawRect = options->exposedRect;
QRectF partRect;
int radius = m_style.cornerRadius;
int dir = getDirection(poly.at(i), poly.at(i - 1));
while (i > 1)
{
i--;
QPointF a = poly.at(i);
QPointF b = poly.at(i - 1);
int newDir = getDirection(a, b);
int ar = radius;
int br = m_style.cornerRadius;
if (i != 1)
{
if (dir % 2 == 1 && std::abs(a.y() - b.y()) < 2 * br)
{
br = std::abs(a.y() - b.y()) / 2;
}
else if (dir % 2 == 0 && std::abs(a.x() - b.x()) < 2 * br)
{
br = std::abs(a.x() - b.x()) / 2;
}
}
switch (dir)
{
case 0: a.setY(a.y() + ar); break;
case 1: a.setX(a.x() - ar); break;
case 2: a.setY(a.y() - ar); break;
case 3: a.setX(a.x() + ar); break;
}
b = poly.at(i);
switch (newDir)
{
case 0: b.setY(b.y() - br); break;
case 1: b.setX(b.x() + br); break;
case 2: b.setY(b.y() + br); break;
case 3: b.setX(b.x() - br); break;
}
partRect = QRectF(poly.at(i + 1), poly.at(i)).adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
path.lineTo(a);
}
else
{
path.moveTo(a);
}
partRect = QRectF(a, b).normalized().adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
switch (dir)
{
case 0:
if (newDir == 1)
{
path.arcTo(a.x(), b.y(), 2 * br, 2 * ar, 180, -90);
}
else if (newDir == 3)
{
path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, 90);
}
break;
case 1:
if (newDir == 0)
{
path.arcTo(a.x() - ar, b.y() - br, 2 * ar, 2 * br, -90, 90);
}
else if (newDir == 2)
{
path.arcTo(a.x() - ar, a.y(), 2 * ar, 2 * br, 90, -90);
}
break;
case 2:
if (newDir == 1)
{
path.arcTo(a.x(), a.y() - ar, 2 * br, 2 * ar, 180, 90);
}
else if (newDir == 3)
{
path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, -90);
}
break;
case 3:
if (newDir == 0)
{
path.arcTo(b.x(), b.y() - br, 2 * ar, 2 * br, -90, -90);
}
else if (newDir == 2)
{
path.arcTo(b.x(), a.y(), 2 * ar, 2 * br, 90, 90);
}
break;
}
}
else
{
path.moveTo(b);
}
dir = newDir;
radius = br;
}
partRect = QRectF(poly.at(0), poly.at(1)).adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
partRect = getArrowBoundingRect(poly);
if (drawRect.intersects(partRect) && m_showArrow)
{
drawArrow(poly, &path);
}
else
{
path.lineTo(poly.at(0));
}
}
painter->drawPath(path);
}
@@ -0,0 +1,17 @@
#ifndef QT_LINE_ITEM_ANGLED_H
#define QT_LINE_ITEM_ANGLED_H
#include "qt/graphics/QtLineItemBase.h"
class QtLineItemAngled
: public QtLineItemBase
{
public:
QtLineItemAngled(QGraphicsItem* parent);
virtual ~QtLineItemAngled();
virtual QPainterPath shape() const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget);
};
#endif // QT_LINE_ITEM_ANGLED_H
@@ -1,30 +1,25 @@
#include "qt/graphics/QtAngledLineItem.h"
#include "qt/graphics/QtLineItemBase.h"
#include <cmath>
#include <QBrush>
#include <QPen>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include "utility/math/Vector2.h"
QtAngledLineItem::QtAngledLineItem(QGraphicsItem* parent)
QtLineItemBase::QtLineItemBase(QGraphicsItem* parent)
: QGraphicsLineItem(parent)
, m_route(ROUTE_ANY)
, m_pivot(PIVOT_THIRD)
, m_showArrow(true)
, m_onFront(false)
, m_onBack(false)
, m_horizontalIn(false)
, m_showArrow(true)
, m_route(ROUTE_ANY)
, m_pivot(PIVOT_THIRD)
{
this->setAcceptHoverEvents(true);
this->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
}
QtAngledLineItem::~QtAngledLineItem()
QtLineItemBase::~QtLineItemBase()
{
}
void QtAngledLineItem::updateLine(
void QtLineItemBase::updateLine(
Vec4i ownerRect, Vec4i targetRect,
Vec4i ownerParentRect, Vec4i targetParentRect,
GraphViewStyle::EdgeStyle style,
@@ -49,197 +44,32 @@ void QtAngledLineItem::updateLine(
this->setPen(QPen(QBrush(style.color.c_str()), style.width + int(log10(weight)), Qt::SolidLine, Qt::RoundCap));
}
void QtAngledLineItem::setRoute(Route route)
void QtLineItemBase::setRoute(Route route)
{
m_route = route;
}
void QtAngledLineItem::setPivot(Pivot pivot)
void QtLineItemBase::setPivot(Pivot pivot)
{
m_pivot = pivot;
}
void QtAngledLineItem::setOnFront(bool front)
void QtLineItemBase::setOnFront(bool front)
{
m_onFront = front;
}
void QtAngledLineItem::setOnBack(bool back)
void QtLineItemBase::setOnBack(bool back)
{
m_onBack = back;
}
void QtAngledLineItem::setHorizontalIn(bool horizontal)
void QtLineItemBase::setHorizontalIn(bool horizontal)
{
m_horizontalIn = horizontal;
}
QPainterPath QtAngledLineItem::shape() const
{
QPainterPath path;
QPolygon poly = getPath();
for (int i = 0; i < poly.size() - 1; i++)
{
path.addRect(QRectF(poly.at(i), poly.at(i + 1)).normalized().adjusted(-3, -3, 3, 3));
}
path.addRect(getArrowBoundingRect(poly).adjusted(-3, -3, 3, 3));
return path;
}
void QtAngledLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
{
painter->setPen(pen());
QPainterPath path;
QPolygon poly = getPath();
int i = poly.length() - 1;
path.moveTo(poly.at(i));
// while (i > 0)
// {
// i--;
// path.lineTo(poly.at(i));
// }
// painter->drawPath(path);
// return;
QRectF drawRect = options->exposedRect;
QRectF partRect;
int radius = m_style.cornerRadius;
int dir = getDirection(poly.at(i), poly.at(i - 1));
while (i > 1)
{
i--;
QPointF a = poly.at(i);
QPointF b = poly.at(i - 1);
int newDir = getDirection(a, b);
int ar = radius;
int br = m_style.cornerRadius;
if (i != 1)
{
if (dir % 2 == 1 && std::abs(a.y() - b.y()) < 2 * br)
{
br = std::abs(a.y() - b.y()) / 2;
}
else if (dir % 2 == 0 && std::abs(a.x() - b.x()) < 2 * br)
{
br = std::abs(a.x() - b.x()) / 2;
}
}
switch (dir)
{
case 0: a.setY(a.y() + ar); break;
case 1: a.setX(a.x() - ar); break;
case 2: a.setY(a.y() - ar); break;
case 3: a.setX(a.x() + ar); break;
}
b = poly.at(i);
switch (newDir)
{
case 0: b.setY(b.y() - br); break;
case 1: b.setX(b.x() + br); break;
case 2: b.setY(b.y() + br); break;
case 3: b.setX(b.x() - br); break;
}
partRect = QRectF(poly.at(i + 1), poly.at(i)).adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
path.lineTo(a);
}
else
{
path.moveTo(a);
}
partRect = QRectF(a, b).normalized().adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
switch (dir)
{
case 0:
if (newDir == 1)
{
path.arcTo(a.x(), b.y(), 2 * br, 2 * ar, 180, -90);
}
else if (newDir == 3)
{
path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, 90);
}
break;
case 1:
if (newDir == 0)
{
path.arcTo(a.x() - ar, b.y() - br, 2 * ar, 2 * br, -90, 90);
}
else if (newDir == 2)
{
path.arcTo(a.x() - ar, a.y(), 2 * ar, 2 * br, 90, -90);
}
break;
case 2:
if (newDir == 1)
{
path.arcTo(a.x(), a.y() - ar, 2 * br, 2 * ar, 180, 90);
}
else if (newDir == 3)
{
path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, -90);
}
break;
case 3:
if (newDir == 0)
{
path.arcTo(b.x(), b.y() - br, 2 * ar, 2 * br, -90, -90);
}
else if (newDir == 2)
{
path.arcTo(b.x(), a.y(), 2 * ar, 2 * br, 90, 90);
}
break;
}
}
else
{
path.moveTo(b);
}
dir = newDir;
radius = br;
}
partRect = QRectF(poly.at(0), poly.at(1)).adjusted(-1, -1, 1, 1);
if (drawRect.intersects(partRect))
{
partRect = getArrowBoundingRect(poly);
if (drawRect.intersects(partRect) && m_showArrow)
{
drawArrow(poly, &path);
}
else
{
path.lineTo(poly.at(0));
}
}
painter->drawPath(path);
}
QPolygon QtAngledLineItem::getPath() const
QPolygon QtLineItemBase::getPath() const
{
if (m_polygon.size() > 0)
{
@@ -448,7 +278,7 @@ QPolygon QtAngledLineItem::getPath() const
return poly;
}
int QtAngledLineItem::getDirection(const QPointF& a, const QPointF& b) const
int QtLineItemBase::getDirection(const QPointF& a, const QPointF& b) const
{
if (a.x() != b.x())
{
@@ -474,7 +304,7 @@ int QtAngledLineItem::getDirection(const QPointF& a, const QPointF& b) const
}
}
QRectF QtAngledLineItem::getArrowBoundingRect(const QPolygon& poly) const
QRectF QtLineItemBase::getArrowBoundingRect(const QPolygon& poly) const
{
int dir = getDirection(poly.at(1), poly.at(0));
@@ -506,7 +336,7 @@ QRectF QtAngledLineItem::getArrowBoundingRect(const QPolygon& poly) const
return rect;
}
void QtAngledLineItem::drawArrow(const QPolygon& poly, QPainterPath* path) const
void QtLineItemBase::drawArrow(const QPolygon& poly, QPainterPath* path) const
{
int dir = getDirection(poly.at(1), poly.at(0));
@@ -559,7 +389,7 @@ 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
void QtLineItemBase::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;
@@ -1,5 +1,5 @@
#ifndef QT_ANGLED_LINE_ITEM_H
#define QT_ANGLED_LINE_ITEM_H
#ifndef QT_LINE_ITEM_BASE_H
#define QT_LINE_ITEM_BASE_H
#include <QGraphicsItem>
@@ -7,7 +7,7 @@
#include "component/view/GraphViewStyle.h"
class QtAngledLineItem
class QtLineItemBase
: public QGraphicsLineItem
{
public:
@@ -24,8 +24,8 @@ public:
PIVOT_MIDDLE
};
QtAngledLineItem(QGraphicsItem* parent);
virtual ~QtAngledLineItem();
QtLineItemBase(QGraphicsItem* parent);
virtual ~QtLineItemBase();
void updateLine(
Vec4i ownerRect, Vec4i targetRect,
@@ -40,10 +40,7 @@ public:
void setOnBack(bool back);
void setHorizontalIn(bool horizontal);
virtual QPainterPath shape() const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget);
private:
protected:
QPolygon getPath() const;
int getDirection(const QPointF& a, const QPointF& b) const;
@@ -52,23 +49,24 @@ private:
void getPivotPoints(Vec2f* p, const Vec4i& in, const Vec4i& out, int offset, bool target) const;
GraphViewStyle::EdgeStyle m_style;
bool m_showArrow;
bool m_onFront;
bool m_onBack;
bool m_horizontalIn;
Route m_route;
Pivot m_pivot;
private:
Vec4i m_ownerRect;
Vec4i m_targetRect;
Vec4i m_ownerParentRect;
Vec4i m_targetParentRect;
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;
};
#endif // QT_ANGLED_LINE_ITEM_H
#endif // QT_LINE_ITEM_BASE_H
@@ -0,0 +1,143 @@
#include "qt/graphics/QtLineItemBezier.h"
#include <QPainter>
QtLineItemBezier::QtLineItemBezier(QGraphicsItem* parent)
: QtLineItemBase(parent)
{
}
QtLineItemBezier::~QtLineItemBezier()
{
}
QPainterPath QtLineItemBezier::shape() const
{
QPainterPathStroker stroker;
stroker.setWidth(10);
stroker.setJoinStyle(Qt::MiterJoin);
QPainterPath path = stroker.createStroke(getCurve());
if (m_showArrow)
{
path.addRect(getArrowBoundingRect(QtLineItemBase::getPath()).adjusted(-3, -3, 3, 3));
}
return path;
}
void QtLineItemBezier::paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget)
{
QPainterPath path = getCurve();
if (m_showArrow)
{
drawArrow(QtLineItemBase::getPath(), &path);
}
painter->setPen(pen());
painter->drawPath(path);
}
QPolygon QtLineItemBezier::getPath() const
{
QPolygon poly = QtLineItemBase::getPath();
QPolygon newPoly;
QPoint tip = poly.at(0);
if (m_showArrow && m_style.arrowClosed)
{
int dir = getDirection(poly.at(1), poly.at(0));
switch (dir)
{
case 0:
tip.ry() += m_style.arrowLength;
break;
case 1:
tip.rx() -= m_style.arrowLength;
break;
case 2:
tip.ry() -= m_style.arrowLength;
break;
case 3:
tip.rx() += m_style.arrowLength;
break;
}
}
newPoly << tip;
QRect rect(poly.at(0), poly.at(3));
if (!rect.contains(poly.at(1)))
{
if (m_route == ROUTE_HORIZONTAL)
{
int diff;
if (std::abs(poly.at(1).x() - poly.at(0).x()) < std::abs(poly.at(2).x() - poly.at(3).x()))
{
diff = poly.at(1).x() - poly.at(0).x();
}
else
{
diff = poly.at(2).x() - poly.at(3).x();
}
newPoly << QPoint(poly.at(1).x() + diff, poly.at(1).y());
newPoly << QPoint(poly.at(2).x() + diff, poly.at(2).y());
}
else
{
int diff;
if (std::abs(poly.at(1).y() - poly.at(0).y()) < std::abs(poly.at(2).y() - poly.at(3).y()))
{
diff = poly.at(1).y() - poly.at(0).y();
}
else
{
diff = poly.at(2).y() - poly.at(3).y();
}
newPoly << QPoint(poly.at(1).x(), poly.at(1).y() + diff);
newPoly << QPoint(poly.at(2).x(), poly.at(2).y() + diff);
}
}
else
{
if (m_route == ROUTE_HORIZONTAL)
{
newPoly << QPoint((poly.at(3).x() + poly.at(0).x()) / 2, poly.at(0).y());
newPoly << QPoint((poly.at(3).x() + poly.at(0).x()) / 2, poly.at(3).y());
}
else
{
newPoly << QPoint(poly.at(0).x(), (poly.at(3).y() + poly.at(0).y()) / 2);
newPoly << QPoint(poly.at(3).x(), (poly.at(3).y() + poly.at(0).y()) / 2);
}
}
newPoly << poly.at(3);
return newPoly;
}
QPainterPath QtLineItemBezier::getCurve() const
{
QPolygon poly = getPath();
QPainterPath path;
path.moveTo(poly.at(3));
path.cubicTo(
poly.at(2),
poly.at(1),
poly.at(0)
);
// QPainterPath path;
// path.moveTo(poly.at(3));
// path.lineTo(poly.at(2));
// path.lineTo(poly.at(1));
// path.lineTo(poly.at(0));
return path;
}
@@ -0,0 +1,23 @@
#ifndef QT_LINE_ITEM_BEZIER_H
#define QT_LINE_ITEM_BEZIER_H
#include "qt/graphics/QtLineItemBase.h"
class QtLineItemBezier
: public QtLineItemBase
{
public:
QtLineItemBezier(QGraphicsItem* parent);
virtual ~QtLineItemBezier();
virtual QPainterPath shape() const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget* widget);
protected:
QPolygon getPath() const;
private:
QPainterPath getCurve() const;
};
#endif // QT_LINE_ITEM_BEZIER_H
@@ -0,0 +1,38 @@
#include "qt/graphics/QtLineItemStraight.h"
#include <QBrush>
#include <QPen>
QtLineItemStraight::QtLineItemStraight(QGraphicsItem* parent)
: QGraphicsLineItem(parent)
{
this->setAcceptHoverEvents(true);
}
QtLineItemStraight::~QtLineItemStraight()
{
}
void QtLineItemStraight::updateLine(Vec2i origin, Vec2i target, GraphViewStyle::EdgeStyle style)
{
prepareGeometryChange();
setLine(origin.x(), origin.y(), target.x(), target.y());
setPen(QPen(QBrush(style.color.c_str()), style.width, Qt::SolidLine));
}
QPainterPath QtLineItemStraight::shape() const
{
QPainterPath path;
QLineF l = line();
path.moveTo(l.p1());
path.lineTo(l.p2());
QPainterPathStroker stroker;
stroker.setWidth(10);
stroker.setJoinStyle(Qt::MiterJoin);
return stroker.createStroke(path);
}
@@ -0,0 +1,20 @@
#ifndef QT_LINE_ITEM_STRAIGHT_H
#define QT_LINE_ITEM_STRAIGHT_H
#include <QGraphicsItem>
#include "component/view/GraphViewStyle.h"
class QtLineItemStraight
: public QGraphicsLineItem
{
public:
QtLineItemStraight(QGraphicsItem* parent);
virtual ~QtLineItemStraight();
void updateLine(Vec2i origin, Vec2i target, GraphViewStyle::EdgeStyle style);
virtual QPainterPath shape() const;
};
#endif // QT_LINE_ITEM_STRAIGHT_H
@@ -1,110 +0,0 @@
#include "qt/graphics/QtStraightLineItem.h"
#include <QBrush>
#include <QPen>
#include "utility/math/Vector2.h"
#include "utility/utility.h"
#include "qt/graphics/QtCountCircleItem.h"
QtStraightLineItem::QtStraightLineItem(QGraphicsItem* parent)
: QGraphicsLineItem(parent)
{
this->setAcceptHoverEvents(true);
m_circle = new QtCountCircleItem(this);
m_arrowLeft = new QGraphicsLineItem(this);
m_arrowRight = new QGraphicsLineItem(this);
}
QtStraightLineItem::~QtStraightLineItem()
{
}
void QtStraightLineItem::updateLine(
Vec4i ownerRect, Vec4i targetRect, int number,
GraphViewStyle::EdgeStyle style, GraphViewStyle::NodeStyle countStyle, bool showArrow
){
prepareGeometryChange();
const Vec4i& o = ownerRect;
const Vec4i& t = targetRect;
Vec2f oc((o.x + o.z) / 2, (o.y + o.w) / 2);
Vec2f tc((t.x + t.z) / 2, (t.y + t.w) / 2);
Vec2f op;
Vec2f tp;
bool intersects = false;
for (int i = 0; !intersects && i < 4; i++)
{
switch (i)
{
case 0: intersects = utility::intersectionPoint(oc, tc, Vec2f(o.x, o.y), Vec2f(o.x, o.w), &op); break;
case 1: intersects = utility::intersectionPoint(oc, tc, Vec2f(o.x, o.y), Vec2f(o.z, o.y), &op); break;
case 2: intersects = utility::intersectionPoint(oc, tc, Vec2f(o.z, o.y), Vec2f(o.z, o.w), &op); break;
case 3: intersects = utility::intersectionPoint(oc, tc, Vec2f(o.x, o.w), Vec2f(o.z, o.w), &op); break;
}
}
if (!intersects)
{
op = oc;
}
intersects = false;
for (int i = 0; !intersects && i < 4; i++)
{
switch (i)
{
case 0: intersects = utility::intersectionPoint(oc, tc, Vec2f(t.x, t.y), Vec2f(t.x, t.w), &tp); break;
case 1: intersects = utility::intersectionPoint(oc, tc, Vec2f(t.x, t.y), Vec2f(t.z, t.y), &tp); break;
case 2: intersects = utility::intersectionPoint(oc, tc, Vec2f(t.z, t.y), Vec2f(t.z, t.w), &tp); break;
case 3: intersects = utility::intersectionPoint(oc, tc, Vec2f(t.x, t.w), Vec2f(t.z, t.w), &tp); break;
}
}
if (!intersects)
{
tp = tc;
}
this->setLine(oc.x, oc.y, tc.x, tc.y);
Vec2f mid = (op + tp) / 2;
m_circle->setPosition(mid);
m_circle->setNumber(number);
size_t radius = m_circle->getRadius();
if (showArrow)
{
Vec2f unit = (tp - op).normalize();
Vec2f nUnit(-unit.y, unit.x);
Vec2f arrow = mid + unit * (radius + 13);
Vec2f arrowSide = mid + unit * (radius + 6) + nUnit * 7;
m_arrowRight->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y);
arrowSide -= nUnit * 14;
m_arrowLeft->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y);
m_arrowLeft->show();
m_arrowRight->show();
}
else
{
m_arrowLeft->hide();
m_arrowRight->hide();
}
this->setPen(QPen(QBrush(style.color.c_str()), style.width, Qt::SolidLine, Qt::RoundCap));
m_circle->setStyle(countStyle.color.fill.c_str(), countStyle.color.text.c_str(), countStyle.color.border.c_str(), 1);
m_arrowLeft->setPen(QPen(QBrush(countStyle.color.border.c_str()), 1, Qt::SolidLine, Qt::RoundCap));
m_arrowRight->setPen(QPen(QBrush(countStyle.color.border.c_str()), 1, Qt::SolidLine, Qt::RoundCap));
}
@@ -1,30 +0,0 @@
#ifndef QT_STRAIGHT_LINE_ITEM_H
#define QT_STRAIGHT_LINE_ITEM_H
#include <QGraphicsItem>
#include "utility/math/Vector4.h"
#include "component/view/GraphViewStyle.h"
class QtCountCircleItem;
class QtStraightLineItem
: public QGraphicsLineItem
{
public:
QtStraightLineItem(QGraphicsItem* parent);
virtual ~QtStraightLineItem();
void updateLine(
Vec4i ownerRect, Vec4i targetRect, int number,
GraphViewStyle::EdgeStyle style, GraphViewStyle::NodeStyle countStyle, bool showArrow);
private:
QtCountCircleItem* m_circle;
QGraphicsLineItem* m_arrowLeft;
QGraphicsLineItem* m_arrowRight;
};
#endif // QT_STRAIGHT_LINE_ITEM_H
+235 -41
View File
@@ -3,16 +3,20 @@
#include <QBoxLayout>
#include <QFrame>
#include <QGraphicsScene>
#include <QLabel>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QScrollBar>
#include <QSequentialAnimationGroup>
#include <QSlider>
#include "component/controller/helper/DummyEdge.h"
#include "component/controller/helper/DummyNode.h"
#include "component/view/GraphViewStyle.h"
#include "settings/ApplicationSettings.h"
#include "utility/messaging/type/MessageActivateTrail.h"
#include "utility/messaging/type/MessageDeactivateEdge.h"
#include "utility/messaging/type/MessageScrollGraph.h"
#include "utility/ResourcePaths.h"
@@ -40,6 +44,7 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout)
, m_refreshFunctor(std::bind(&QtGraphView::doRefreshView, this))
, m_focusInFunctor(std::bind(&QtGraphView::doFocusIn, this, std::placeholders::_1))
, m_focusOutFunctor(std::bind(&QtGraphView::doFocusOut, this, std::placeholders::_1))
, m_centerActiveNode(false)
, m_scrollToTop(false)
, m_restoreScroll(false)
, m_isIndexedList(false)
@@ -82,6 +87,40 @@ void QtGraphView::initView()
connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled(int)));
connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled(int)));
// trail controls
{
m_forwardTrailButton = new QPushButton("<");
m_backwardTrailButton = new QPushButton(">");
connect(m_forwardTrailButton, SIGNAL(clicked()), this, SLOT(clickedForwardTrail()));
connect(m_backwardTrailButton, SIGNAL(clicked()), this, SLOT(clickedBackwardTrail()));
m_trailDepthSlider = new QSlider(Qt::Horizontal);
m_trailDepthSlider->setMinimum(1);
m_trailDepthSlider->setMaximum(51);
m_trailDepthSlider->setValue(10);
m_trailDepthSlider->setFixedWidth(150);
connect(m_trailDepthSlider, SIGNAL(valueChanged(int)), this, SLOT(trailDepthChanged(int)));
m_trailDepthLabel = new QLabel("10");
m_trailDepthLabel->setFixedWidth(30);
QHBoxLayout* trailLayout = new QHBoxLayout();
trailLayout->addWidget(m_backwardTrailButton);
trailLayout->addWidget(m_forwardTrailButton);
trailLayout->addStretch();
trailLayout->addWidget(new QLabel("depth:"));
trailLayout->addWidget(m_trailDepthSlider);
trailLayout->addWidget(m_trailDepthLabel);
QWidget* trailWidget = new QWidget(widget);
trailWidget->setMinimumWidth(380);
trailWidget->setMinimumHeight(50);
trailWidget->setLayout(trailLayout);
updateTrailButtons();
}
doRefreshView();
}
@@ -116,7 +155,7 @@ Vec2i QtGraphView::getViewSize() const
QtGraphicsView* view = getView();
float zoomFactor = view->getZoomFactor();
return Vec2i(view->width() / zoomFactor - 60, view->height() / zoomFactor - 60);
return Vec2i((view->width() - 50) / zoomFactor, (view->height() - 100) / zoomFactor);
}
void QtGraphView::scrollToValues(int xValue, int yValue)
@@ -224,6 +263,129 @@ void QtGraphView::scrolled(int)
MessageScrollGraph(view->horizontalScrollBar()->value(), view->verticalScrollBar()->value()).dispatch();
}
void QtGraphView::trailDepthChanged(int)
{
if (m_trailDepthSlider->value() == m_trailDepthSlider->maximum())
{
m_trailDepthLabel->setText("inf");
}
else
{
m_trailDepthLabel->setText(QString::number(m_trailDepthSlider->value()));
}
}
void QtGraphView::clickedBackwardTrail()
{
activateTrail(false);
}
void QtGraphView::clickedForwardTrail()
{
activateTrail(true);
}
MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
{
MessageActivateTrail message(0, 0, 0, 0, false);
QtGraphNodeData* node = nullptr;
if (m_oldActiveNode && (node = dynamic_cast<QtGraphNodeData*>(m_oldActiveNode.get())))
{
Edge::EdgeTypeMask trailType;
bool horizontalLayout = true;
switch (node->getData()->getType())
{
case Node::NODE_CLASS:
case Node::NODE_STRUCT:
case Node::NODE_INTERFACE:
trailType = Edge::EDGE_INHERITANCE;
horizontalLayout = false;
break;
case Node::NODE_FUNCTION:
case Node::NODE_METHOD:
trailType = Edge::EDGE_CALL | Edge::EDGE_OVERRIDE;
horizontalLayout = true;
break;
case Node::NODE_FILE:
trailType = Edge::EDGE_INCLUDE;
horizontalLayout = true;
break;
default:
return message;
}
Id tokenId = node->getData()->getId();
Id originId = forward ? tokenId : 0;
Id targetId = forward ? 0 : tokenId;
int depth = m_trailDepthSlider->value();
if (depth == m_trailDepthSlider->maximum())
{
depth = 0;
}
return MessageActivateTrail(originId, targetId, trailType, depth, horizontalLayout);
}
return message;
}
void QtGraphView::activateTrail(bool forward)
{
MessageActivateTrail message = getMessageActivateTrail(forward);
if (message.trailType)
{
message.dispatch();
}
}
void QtGraphView::updateTrailButtons()
{
MessageActivateTrail message = getMessageActivateTrail(false);
m_backwardTrailButton->setEnabled(message.trailType);
m_forwardTrailButton->setEnabled(message.trailType);
m_trailDepthSlider->setEnabled(message.trailType);
if (message.trailType & Edge::EDGE_CALL)
{
m_backwardTrailButton->setToolTip("caller graph");
m_forwardTrailButton->setToolTip("callee graph");
m_backwardTrailButton->setText(">");
m_forwardTrailButton->setText("<");
}
else if (message.trailType & Edge::EDGE_INHERITANCE)
{
m_backwardTrailButton->setToolTip("base hierarchy");
m_forwardTrailButton->setToolTip("derived hierarchy");
m_backwardTrailButton->setText("b");
m_forwardTrailButton->setText("d");
}
else if (message.trailType & Edge::EDGE_INCLUDE)
{
m_backwardTrailButton->setToolTip("including files hierarchy");
m_forwardTrailButton->setToolTip("included files hierarchy");
m_backwardTrailButton->setText(">");
m_forwardTrailButton->setText("<");
}
else
{
m_backwardTrailButton->setToolTip("no hierarchy available for active symbol");
m_forwardTrailButton->setToolTip("no hierarchy available for active symbol");
m_backwardTrailButton->setText(">");
m_forwardTrailButton->setText("<");
}
}
void QtGraphView::switchToNewGraphData()
{
m_oldGraph = m_graph;
@@ -231,11 +393,13 @@ void QtGraphView::switchToNewGraphData()
for (const std::shared_ptr<QtGraphNode>& node : m_oldNodes)
{
node->hide();
node->setParentItem(nullptr);
}
for (const std::shared_ptr<QtGraphEdge>& edge : m_oldEdges)
{
edge->hide();
edge->setParentItem(nullptr);
}
m_oldNodes = m_nodes;
@@ -262,24 +426,34 @@ void QtGraphView::switchToNewGraphData()
node->hoverEnter();
}
if (m_activeNode)
if (m_activeNodes.size())
{
Vec2i pos = m_activeNode->getPosition();
Vec2i size = m_activeNode->getSize();
QRectF rect(pos.x, pos.y, size.x, size.y);
if (rect.height() > view->height() - 200)
if (m_centerActiveNode)
{
rect.setHeight(view->height() - 200);
Vec2i pos = m_activeNodes.front()->getPosition();
Vec2i size = m_activeNodes.front()->getSize();
QRectF rect(pos.x, pos.y, size.x, size.y);
if (rect.height() > view->height() - 200)
{
rect.setHeight(view->height() - 200);
}
view->ensureVisibleAnimated(rect, 100, 100);
}
view->ensureVisibleAnimated(rect, 100, 100);
m_activeNode.reset();
if (m_activeNodes.size() == 1)
{
m_oldActiveNode = m_activeNodes.front();
}
m_activeNodes.clear();
}
// Repaint to make sure all artifacts are removed
view->update();
updateTrailButtons();
}
QtGraphicsView* QtGraphView::getView() const
@@ -317,7 +491,10 @@ void QtGraphView::doRebuildGraph(
}
m_nodes.clear();
m_activeNode.reset();
m_activeNodes.clear();
m_oldActiveNode.reset();
m_virtualNodeRects.clear();
for (unsigned int i = 0; i < nodes.size(); i++)
{
std::shared_ptr<QtGraphNode> node = createNodeRecursive(view, NULL, nodes[i].get(), activeNodeCount > 1);
@@ -327,14 +504,17 @@ void QtGraphView::doRebuildGraph(
}
}
QPointF center = itemsBoundingRect(m_nodes).center();
Vec2i o = GraphViewStyle::alignOnRaster(Vec2i(center.x(), center.y()));
QPointF offset = QPointF(o.x, o.y);
m_sceneRectOffset = offset - center;
for (const std::shared_ptr<QtGraphNode>& node : m_nodes)
if (graph->getTrailMode() == Graph::TRAIL_NONE)
{
node->setPos(node->pos() - offset);
QPointF center = itemsBoundingRect(m_nodes).center();
Vec2i o = GraphViewStyle::alignOnRaster(Vec2i(center.x(), center.y()));
QPointF offset = QPointF(o.x, o.y);
m_sceneRectOffset = offset - center;
for (const std::shared_ptr<QtGraphNode>& node : m_nodes)
{
node->setPos(node->pos() - offset);
}
}
m_edges.clear();
@@ -344,7 +524,7 @@ void QtGraphView::doRebuildGraph(
{
if (!edge->data || !edge->data->isType(Edge::EDGE_AGGREGATION))
{
createEdge(view, edge.get(), &visibleEdgeIds);
createEdge(view, edge.get(), &visibleEdgeIds, graph->getTrailMode());
}
}
for (std::shared_ptr<DummyEdge> edge : edges)
@@ -361,11 +541,7 @@ void QtGraphView::doRebuildGraph(
m_graph = graph;
}
if (!params.centerActiveNode)
{
m_activeNode.reset();
}
m_centerActiveNode = params.centerActiveNode;
m_scrollToTop = params.scrollToTop;
m_isIndexedList = params.isIndexedList;
@@ -381,6 +557,9 @@ void QtGraphView::doRebuildGraph(
void QtGraphView::doClear()
{
m_oldActiveNode.reset();
m_activeNodes.clear();
m_nodes.clear();
m_edges.clear();
@@ -481,9 +660,9 @@ std::shared_ptr<QtGraphNode> QtGraphView::createNodeRecursive(
newNode->addComponent(std::make_shared<QtGraphNodeComponentMoveable>(newNode));
}
if (node->active && !m_activeNode)
if (node->active)
{
m_activeNode = newNode;
m_activeNodes.push_back(newNode);
}
for (unsigned int i = 0; i < node->subNodes.size(); i++)
@@ -501,7 +680,7 @@ std::shared_ptr<QtGraphNode> QtGraphView::createNodeRecursive(
}
std::shared_ptr<QtGraphEdge> QtGraphView::createEdge(
QGraphicsView* view, const DummyEdge* edge, std::set<Id>* visibleEdgeIds)
QGraphicsView* view, const DummyEdge* edge, std::set<Id>* visibleEdgeIds, Graph::TrailMode trailMode)
{
if (!edge->visible)
{
@@ -516,6 +695,19 @@ std::shared_ptr<QtGraphEdge> QtGraphView::createEdge(
std::shared_ptr<QtGraphEdge> qtEdge =
std::make_shared<QtGraphEdge>(owner, target, edge->data, edge->getWeight(), edge->active, edge->getDirection());
if (trailMode != Graph::TRAIL_NONE)
{
for (const Vec4i& rect : edge->path)
{
m_virtualNodeRects.push_back(QRectF(QPointF(rect.x(), rect.y()), QPointF(rect.z(), rect.w())));
}
qtEdge->setIsTrailEdge(edge->path, trailMode == Graph::TRAIL_HORIZONTAL);
}
qtEdge->updateLine();
owner->addOutEdge(qtEdge);
target->addInEdge(qtEdge);
@@ -558,7 +750,7 @@ std::shared_ptr<QtGraphEdge> QtGraphView::createAggregationEdge(
return NULL;
}
return createEdge(view, edge, visibleEdgeIds);
return createEdge(view, edge, visibleEdgeIds, Graph::TRAIL_NONE);
}
QRectF QtGraphView::itemsBoundingRect(const std::list<std::shared_ptr<QtGraphNode>>& items) const
@@ -573,7 +765,14 @@ QRectF QtGraphView::itemsBoundingRect(const std::list<std::shared_ptr<QtGraphNod
QRectF QtGraphView::getSceneRect(const std::list<std::shared_ptr<QtGraphNode>>& items) const
{
return itemsBoundingRect(items).adjusted(-25, -25, 25, 25).translated(m_sceneRectOffset);
QRectF sceneRect = itemsBoundingRect(items);
for (const QRectF& rect : m_virtualNodeRects)
{
sceneRect |= rect;
}
return sceneRect.adjusted(-25, -75, 25, 25).translated(m_sceneRectOffset);
}
void QtGraphView::compareNodesRecursive(
@@ -804,16 +1003,11 @@ void QtGraphView::doFocusOut(const std::vector<Id>& tokenIds)
if (node && node->isDataNode())
{
node->focusOut();
continue;
}
for (std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
{
if (edge->getData() && edge->getData()->getId() == tokenId)
{
edge->focusOut();
break;
}
}
}
for (std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
{
edge->focusOut();
}
}
+26 -2
View File
@@ -7,14 +7,19 @@
#include <QPointF>
#include "component/view/GraphView.h"
#include "data/graph/Graph.h"
#include "qt/utility/QtScrollSpeedChangeListener.h"
#include "qt/utility/QtThreadedFunctor.h"
#include "utility/types.h"
struct DummyEdge;
struct DummyNode;
class MessageActivateTrail;
class QLabel;
class QMouseEvent;
class QPushButton;
class QSequentialAnimationGroup;
class QSlider;
class QtGraphEdge;
class QtGraphicsView;
class QtGraphNode;
@@ -57,7 +62,15 @@ private slots:
void pressedCharacterKey(QChar c);
void scrolled(int);
void trailDepthChanged(int);
void clickedBackwardTrail();
void clickedForwardTrail();
private:
MessageActivateTrail getMessageActivateTrail(bool forward);
void activateTrail(bool forward);
void updateTrailButtons();
void switchToNewGraphData();
QtGraphicsView* getView() const;
@@ -78,7 +91,8 @@ private:
std::shared_ptr<QtGraphNode> createNodeRecursive(
QGraphicsView* view, std::shared_ptr<QtGraphNode> parentNode, const DummyNode* node, bool multipleActive);
std::shared_ptr<QtGraphEdge> createEdge(QGraphicsView* view, const DummyEdge* edge, std::set<Id>* visibleEdgeIds);
std::shared_ptr<QtGraphEdge> createEdge(
QGraphicsView* view, const DummyEdge* edge, std::set<Id>* visibleEdgeIds, Graph::TrailMode trailMode);
std::shared_ptr<QtGraphEdge> createAggregationEdge(
QGraphicsView* view, const DummyEdge* edge, std::set<Id>* visibleEdgeIds);
@@ -115,7 +129,10 @@ private:
std::list<std::shared_ptr<QtGraphNode>> m_nodes;
std::list<std::shared_ptr<QtGraphNode>> m_oldNodes;
std::shared_ptr<QtGraphNode> m_activeNode;
std::vector<std::shared_ptr<QtGraphNode>> m_activeNodes;
std::shared_ptr<QtGraphNode> m_oldActiveNode;
bool m_centerActiveNode;
bool m_scrollToTop;
bool m_restoreScroll;
Vec2i m_scrollValues;
@@ -126,6 +143,13 @@ private:
QtScrollSpeedChangeListener m_scrollSpeedChangeListenerHorizontal;
QtScrollSpeedChangeListener m_scrollSpeedChangeListenerVertical;
QPushButton* m_forwardTrailButton;
QPushButton* m_backwardTrailButton;
QSlider* m_trailDepthSlider;
QLabel* m_trailDepthLabel;
std::vector<QRectF> m_virtualNodeRects;
};
#endif // QT_GRAPH_VIEW_H
+134 -31
View File
@@ -1,12 +1,14 @@
#include "qt/view/graphElements/QtGraphEdge.h"
#include <QGraphicsSceneEvent>
#include <QTimer>
#include "component/view/GraphViewStyle.h"
#include "data/graph/Edge.h"
#include "data/graph/token_component/TokenComponentAggregation.h"
#include "qt/graphics/QtAngledLineItem.h"
#include "qt/graphics/QtStraightLineItem.h"
#include "qt/graphics/QtLineItemAngled.h"
#include "qt/graphics/QtLineItemBezier.h"
#include "qt/graphics/QtLineItemStraight.h"
#include "qt/view/graphElements/QtGraphNode.h"
#include "utility/messaging/type/MessageActivateEdge.h"
#include "utility/messaging/type/MessageFocusIn.h"
@@ -30,10 +32,14 @@ QtGraphEdge::QtGraphEdge(
, m_isActive(isActive)
, m_fromActive(false)
, m_toActive(false)
, m_isFocused(false)
, m_weight(weight)
, m_direction(direction)
, m_isTrailEdge(false)
, m_isHorizontalTrail(false)
, m_mousePos(0.0f, 0.0f)
, m_mouseMoved(false)
, m_willDispatchMessageFocusIn(false)
{
if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD)
{
@@ -42,8 +48,6 @@ QtGraphEdge::QtGraphEdge(
m_fromActive = m_owner.lock()->getIsActive();
m_toActive = m_target.lock()->getIsActive();
this->updateLine();
}
QtGraphEdge::~QtGraphEdge()
@@ -86,30 +90,95 @@ void QtGraphEdge::updateLine()
type = Edge::EDGE_AGGREGATION;
}
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive, false);
QString toolTip = Edge::getReadableTypeString(type).c_str();
if (type == Edge::EDGE_AGGREGATION)
{
toolTip += ": " + QString::number(m_weight) + " edge";
if (m_weight != 1)
{
toolTip += "s";
}
}
if (style.isStraight)
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive | m_isFocused, false, m_isTrailEdge);
if (m_isTrailEdge)
{
if (!m_child)
{
m_child = new QtStraightLineItem(this);
m_child = new QGraphicsLineItem(this);
}
else
{
for (QGraphicsItem* item : m_child->childItems())
{
item->hide();
item->setParentItem(nullptr);
}
}
style.originOffset.y() = 0;
style.targetOffset.y() = 0;
Vec4i ownerRect = owner->getBoundingRect();
Vec4i ownerParentRect = owner->getParentBoundingRect();
QtLineItemBase::Route route =
m_isHorizontalTrail ? QtLineItemBase::ROUTE_HORIZONTAL : QtLineItemBase::ROUTE_VERTICAL;
for (const Vec4i& rect : m_path)
{
QtLineItemBezier* bezier = new QtLineItemBezier(m_child);
bezier->updateLine(ownerRect, rect, ownerParentRect, rect, style, m_weight, false);
bezier->setRoute(route);
bezier->setPivot(QtLineItemBase::PIVOT_MIDDLE);
bezier->setToolTip(toolTip);
QtLineItemStraight* line = new QtLineItemStraight(m_child);
line->setToolTip(toolTip);
if (route == QtLineItemBase::ROUTE_HORIZONTAL)
{
line->updateLine(Vec2i(rect.x(), (rect.y() + rect.w()) / 2), Vec2i(rect.z(), (rect.y() + rect.w()) / 2), style);
}
else
{
line->updateLine(Vec2i((rect.x() + rect.z()) / 2, rect.y()), Vec2i((rect.x() + rect.z()) / 2, rect.w()), style);
}
ownerRect = rect;
ownerParentRect = rect;
}
bool showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE;
GraphViewStyle::NodeStyle countStyle = GraphViewStyle::getStyleOfCountCircle();
QtLineItemBezier* bezier = new QtLineItemBezier(m_child);
bezier->updateLine(
ownerRect, target->getBoundingRect(), ownerParentRect, target->getParentBoundingRect(),
style, m_weight, showArrow);
bezier->setRoute(route);
bezier->setPivot(QtLineItemBase::PIVOT_MIDDLE);
bezier->setToolTip(toolTip);
dynamic_cast<QtStraightLineItem*>(m_child)->updateLine(
owner->getBoundingRect(), target->getBoundingRect(), m_weight, style, countStyle, showArrow);
if (owner->getLastParent() == target->getLastParent())
{
if (ownerRect.y() < target->getBoundingRect().y())
{
bezier->setOnBack(true);
}
else
{
bezier->setOnFront(true);
}
}
}
else
{
if (!m_child)
{
m_child = new QtAngledLineItem(this);
m_child = new QtLineItemAngled(this);
}
QtAngledLineItem* child = dynamic_cast<QtAngledLineItem*>(m_child);
QtLineItemAngled* child = dynamic_cast<QtLineItemAngled*>(m_child);
if (m_fromActive && owner->getLastParent() == target->getLastParent())
{
@@ -130,13 +199,13 @@ void QtGraphEdge::updateLine()
(type != Edge::EDGE_AGGREGATION ||
owner.get() != owner->getLastParent() || target.get() != target->getLastParent()))
{
child->setRoute(QtAngledLineItem::ROUTE_HORIZONTAL);
child->setRoute(QtLineItemBase::ROUTE_HORIZONTAL);
}
bool showArrow = true;
if (type == Edge::EDGE_AGGREGATION)
{
child->setPivot(QtAngledLineItem::PIVOT_MIDDLE);
child->setPivot(QtLineItemBase::PIVOT_MIDDLE);
showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE;
}
@@ -145,20 +214,11 @@ void QtGraphEdge::updateLine()
owner->getBoundingRect(), target->getBoundingRect(),
owner->getParentBoundingRect(), target->getParentBoundingRect(),
style, m_weight, showArrow);
child->setToolTip(toolTip);
}
QString toolTip = Edge::getReadableTypeString(type).c_str();
if (type == 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.
this->setZValue(style.zValue);
}
bool QtGraphEdge::getIsActive() const
@@ -177,6 +237,12 @@ void QtGraphEdge::setIsActive(bool isActive)
void QtGraphEdge::onClick()
{
if (isTrailEdge())
{
setIsActive(!getIsActive());
return;
}
if (!getData())
{
std::weak_ptr<QtGraphNode> node =
@@ -204,14 +270,20 @@ void QtGraphEdge::onClick()
void QtGraphEdge::focusIn()
{
bool isActive = m_isActive;
this->setIsActive(true);
m_isActive = isActive;
if (!m_isFocused)
{
m_isFocused = true;
updateLine();
}
}
void QtGraphEdge::focusOut()
{
updateLine();
if (m_isFocused)
{
m_isFocused = false;
updateLine();
}
}
void QtGraphEdge::mousePressEvent(QGraphicsSceneMouseEvent* event)
@@ -246,7 +318,11 @@ void QtGraphEdge::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
return;
}
MessageFocusIn(std::vector<Id>(1, getData()->getId())).dispatch();
if (!m_willDispatchMessageFocusIn)
{
QTimer::singleShot(50, this, SLOT(dispatchMessageFocusIn()));
m_willDispatchMessageFocusIn = true;
}
}
void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
@@ -257,9 +333,24 @@ void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
return;
}
if (m_willDispatchMessageFocusIn)
{
m_willDispatchMessageFocusIn = false;
return;
}
MessageFocusOut(std::vector<Id>(1, getData()->getId())).dispatch();
}
void QtGraphEdge::dispatchMessageFocusIn()
{
if (m_willDispatchMessageFocusIn)
{
m_willDispatchMessageFocusIn = false;
MessageFocusIn(std::vector<Id>(1, getData()->getId())).dispatch();
}
}
void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction)
{
if (m_direction != direction)
@@ -268,3 +359,15 @@ void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction)
updateLine();
}
}
bool QtGraphEdge::isTrailEdge() const
{
return m_isTrailEdge;
}
void QtGraphEdge::setIsTrailEdge(std::vector<Vec4i> path, bool horizontal)
{
m_path = path;
m_isTrailEdge = true;
m_isHorizontalTrail = horizontal;
}
@@ -6,6 +6,7 @@
#include <QGraphicsItem>
#include "utility/math/Vector2.h"
#include "utility/math/Vector4.h"
#include "data/graph/token_component/TokenComponentAggregation.h"
@@ -47,6 +48,9 @@ public:
void setDirection(TokenComponentAggregation::Direction direction);
bool isTrailEdge() const;
void setIsTrailEdge(std::vector<Vec4i> path, bool horizontal);
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
@@ -55,6 +59,9 @@ protected:
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
private slots:
void dispatchMessageFocusIn();
private:
const Edge* m_data;
@@ -66,12 +73,21 @@ private:
bool m_isActive;
bool m_fromActive;
bool m_toActive;
bool m_isFocused;
size_t m_weight;
TokenComponentAggregation::Direction m_direction;
bool m_isTrailEdge;
bool m_isHorizontalTrail;
std::vector<Vec4i> m_path;
Vec2i m_mousePos;
bool m_mouseMoved;
bool m_willDispatchMessageFocusIn;
};
#endif // QT_GRAPH_EDGE_H
@@ -213,12 +213,34 @@ void QtGraphNode::hoverEnter()
void QtGraphNode::focusIn()
{
m_isHovering = true;
forEachEdge(
[](QtGraphEdge* edge)
{
if (edge->isTrailEdge())
{
edge->focusIn();
}
}
);
updateStyle();
}
void QtGraphNode::focusOut()
{
m_isHovering = false;
forEachEdge(
[](QtGraphEdge* edge)
{
if (edge->isTrailEdge())
{
edge->focusOut();
}
}
);
updateStyle();
}
@@ -353,11 +375,11 @@ void QtGraphNode::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
}
}
void QtGraphNode::notifyEdgesAfterMove()
void QtGraphNode::forEachEdge(std::function<void(QtGraphEdge*)> func)
{
for (const std::shared_ptr<QtGraphEdge>& edge : m_outEdges)
{
edge->updateLine();
func(edge.get());
}
for (const std::weak_ptr<QtGraphEdge>& e : m_inEdges)
@@ -365,9 +387,19 @@ void QtGraphNode::notifyEdgesAfterMove()
std::shared_ptr<QtGraphEdge> edge = e.lock();
if (edge)
{
edge->updateLine();
func(edge.get());
}
}
}
void QtGraphNode::notifyEdgesAfterMove()
{
forEachEdge(
[](QtGraphEdge* edge)
{
edge->updateLine();
}
);
for (const std::shared_ptr<QtGraphNode>& node : m_subNodes)
{
@@ -1,6 +1,8 @@
#ifndef QT_GRAPH_NODE_H
#define QT_GRAPH_NODE_H
#include <functional>
#include <QGraphicsItem>
#include "utility/math/Vector4.h"
@@ -93,6 +95,8 @@ protected:
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);
void forEachEdge(std::function<void(QtGraphEdge*)> func);
void notifyEdgesAfterMove();
void setStyle(const GraphViewStyle::NodeStyle& style);