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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user