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:
@@ -403,6 +403,7 @@
|
||||
|
||||
<call>
|
||||
<normal>#FBBA00</normal>
|
||||
<trail_focus>#FFFFFF</trail_focus>
|
||||
</call>
|
||||
|
||||
<use>
|
||||
|
||||
@@ -371,6 +371,7 @@
|
||||
|
||||
<call>
|
||||
<normal>#F4BC3D</normal>
|
||||
<trail_focus>#878787</trail_focus>
|
||||
</call>
|
||||
|
||||
<use>
|
||||
|
||||
@@ -374,6 +374,7 @@
|
||||
|
||||
<call>
|
||||
<normal>#9C8528</normal>
|
||||
<trail_focus>#F7F7F7</trail_focus>
|
||||
</call>
|
||||
|
||||
<use>
|
||||
|
||||
@@ -12,6 +12,8 @@ add_files(
|
||||
component/controller/helper/NetworkProtocolHelper.h
|
||||
component/controller/helper/SnippetMerger.cpp
|
||||
component/controller/helper/SnippetMerger.h
|
||||
component/controller/helper/TrailLayouter.cpp
|
||||
component/controller/helper/TrailLayouter.h
|
||||
|
||||
component/controller/ActivationController.cpp
|
||||
component/controller/ActivationController.h
|
||||
@@ -225,7 +227,7 @@ add_files(
|
||||
data/TaskMergeStorages.h
|
||||
data/TaskShowStatusDialog.cpp
|
||||
data/TaskShowStatusDialog.h
|
||||
|
||||
|
||||
project/Project.cpp
|
||||
project/Project.h
|
||||
project/SourceGroupFactory.cpp
|
||||
@@ -234,7 +236,7 @@ add_files(
|
||||
project/SourceGroupFactoryModule.h
|
||||
project/SourceGroup.cpp
|
||||
project/SourceGroup.h
|
||||
|
||||
|
||||
settings/migration/Migration.cpp
|
||||
settings/migration/Migration.h
|
||||
settings/migration/MigrationDeleteKey.cpp
|
||||
@@ -245,7 +247,7 @@ add_files(
|
||||
settings/migration/MigrationMoveKey.h
|
||||
settings/migration/SettingsMigrator.cpp
|
||||
settings/migration/SettingsMigrator.h
|
||||
|
||||
|
||||
settings/ApplicationSettings.cpp
|
||||
settings/ApplicationSettings.h
|
||||
settings/ColorScheme.cpp
|
||||
@@ -327,6 +329,7 @@ add_files(
|
||||
utility/messaging/type/MessageActivateSourceLocations.h
|
||||
utility/messaging/type/MessageActivateTokenIds.h
|
||||
utility/messaging/type/MessageActivateTokens.h
|
||||
utility/messaging/type/MessageActivateTrail.h
|
||||
utility/messaging/type/MessageActivateWindow.h
|
||||
utility/messaging/type/MessageChangeFileView.h
|
||||
utility/messaging/type/MessageClearErrorCount.h
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "component/controller/helper/BucketLayouter.h"
|
||||
#include "component/controller/helper/ListLayouter.h"
|
||||
#include "component/controller/helper/TrailLayouter.h"
|
||||
#include "component/view/GraphView.h"
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "data/access/StorageAccess.h"
|
||||
@@ -42,7 +43,7 @@ void GraphController::handleMessage(MessageActivateAll* message)
|
||||
assignBundleIds();
|
||||
layoutGraph();
|
||||
|
||||
buildGraph(message, false);
|
||||
buildGraph(message, false, true, false);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageActivateTokens* message)
|
||||
@@ -53,7 +54,7 @@ void GraphController::handleMessage(MessageActivateTokens* message)
|
||||
{
|
||||
m_activeEdgeIds = message->tokenIds;
|
||||
setActiveAndVisibility(utility::concat(m_activeNodeIds, m_activeEdgeIds));
|
||||
buildGraph(message, false, false);
|
||||
buildGraph(message, false, false, false);
|
||||
return;
|
||||
}
|
||||
else if (message->isAggregation)
|
||||
@@ -101,9 +102,55 @@ void GraphController::handleMessage(MessageActivateTokens* message)
|
||||
buildGraph(message, !isNamespace, true, isNamespace);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageActivateTrail* message)
|
||||
{
|
||||
TRACE("trail activate");
|
||||
|
||||
m_activeEdgeIds.clear();
|
||||
|
||||
std::shared_ptr<Graph> graph = m_storageAccess->getGraphForTrail(
|
||||
message->originId, message->targetId, message->trailType, message->depth);
|
||||
|
||||
createDummyGraphForTokenIds(m_activeNodeIds, graph);
|
||||
m_graph->setTrailMode(message->horizontalLayout ? Graph::TRAIL_HORIZONTAL : Graph::TRAIL_VERTICAL);
|
||||
|
||||
setVisibility(setActive(m_activeNodeIds, true));
|
||||
|
||||
layoutNesting();
|
||||
|
||||
TrailLayouter::LayoutDirection direction;
|
||||
if (message->horizontalLayout)
|
||||
{
|
||||
if (message->originId)
|
||||
{
|
||||
direction = TrailLayouter::LAYOUT_LEFT_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = TrailLayouter::LAYOUT_RIGHT_LEFT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message->originId)
|
||||
{
|
||||
direction = TrailLayouter::LAYOUT_TOP_BOTTOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = TrailLayouter::LAYOUT_BOTTOM_TOP;
|
||||
}
|
||||
}
|
||||
|
||||
TrailLayouter layout(direction);
|
||||
layout.layoutGraph(m_dummyNodes, m_dummyEdges, m_topLevelAncestorIds);
|
||||
|
||||
buildGraph(message, true, true, false);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageFlushUpdates* message)
|
||||
{
|
||||
buildGraph(message, true, !message->keepContent());
|
||||
buildGraph(message, true, !message->keepContent(), false);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageScrollGraph* message)
|
||||
@@ -228,7 +275,7 @@ void GraphController::handleMessage(MessageGraphNodeExpand* message)
|
||||
layoutNesting();
|
||||
layoutGraph();
|
||||
|
||||
buildGraph(message, false);
|
||||
buildGraph(message, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +288,7 @@ void GraphController::handleMessage(MessageGraphNodeMove* message)
|
||||
|
||||
if (message->isReplayed())
|
||||
{
|
||||
buildGraph(message, false);
|
||||
buildGraph(message, false, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -264,7 +311,7 @@ void GraphController::handleMessage(MessageShowReference* message)
|
||||
|
||||
m_activeEdgeIds = std::vector<Id>(1, message->tokenId);
|
||||
setActiveAndVisibility(utility::concat(m_activeNodeIds, m_activeEdgeIds));
|
||||
buildGraph(message, false, false);
|
||||
buildGraph(message, false, false, false);
|
||||
}
|
||||
|
||||
GraphView* GraphController::getView() const
|
||||
@@ -300,6 +347,7 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
|
||||
|
||||
m_dummyEdges.clear();
|
||||
m_dummyGraphNodes.clear();
|
||||
m_topLevelAncestorIds.clear();
|
||||
|
||||
std::set<Id> addedNodes;
|
||||
std::vector<std::shared_ptr<DummyNode>> dummyNodes;
|
||||
@@ -308,14 +356,14 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
|
||||
[&addedNodes, &dummyNodes, this](Node* node)
|
||||
{
|
||||
Node* parent = node->getLastParentNode();
|
||||
Id id = parent->getId();
|
||||
if (addedNodes.find(id) != addedNodes.end())
|
||||
Id parentId = parent->getId();
|
||||
if (addedNodes.find(parentId) != addedNodes.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
addedNodes.insert(id);
|
||||
addedNodes.insert(parentId);
|
||||
|
||||
utility::append(dummyNodes, createDummyNodeTopDown(parent));
|
||||
utility::append(dummyNodes, createDummyNodeTopDown(parent, parentId));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -370,7 +418,7 @@ void GraphController::createDummyGraphForTokenIdsAndSetActiveAndVisibility(
|
||||
|
||||
createDummyGraphForTokenIds(tokenIds, graph);
|
||||
|
||||
bool noActive = setActive(tokenIds);
|
||||
bool noActive = setActive(tokenIds, false);
|
||||
|
||||
autoExpandActiveNode(tokenIds);
|
||||
setExpandedNodeIds(expandedNodeIds);
|
||||
@@ -378,24 +426,26 @@ void GraphController::createDummyGraphForTokenIdsAndSetActiveAndVisibility(
|
||||
setVisibility(noActive);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<DummyNode>> GraphController::createDummyNodeTopDown(Node* node)
|
||||
std::vector<std::shared_ptr<DummyNode>> GraphController::createDummyNodeTopDown(Node* node, Id ancestorId)
|
||||
{
|
||||
std::vector<std::shared_ptr<DummyNode>> nodes;
|
||||
|
||||
std::shared_ptr<DummyNode> result = std::make_shared<DummyNode>();
|
||||
result->data = node;
|
||||
result->tokenId = node->getId();
|
||||
result->name = node->getName();
|
||||
|
||||
result->tokenId = node->getId();
|
||||
m_topLevelAncestorIds.emplace(node->getId(), ancestorId);
|
||||
|
||||
m_dummyGraphNodes.emplace(result->data->getId(), result);
|
||||
nodes.push_back(result);
|
||||
|
||||
if (node->isType(Node::NODE_NAMESPACE))
|
||||
{
|
||||
node->forEachChildNode(
|
||||
[&nodes, this](Node* child)
|
||||
[&nodes, &ancestorId, this](Node* child)
|
||||
{
|
||||
utility::append(nodes, createDummyNodeTopDown(child));
|
||||
utility::append(nodes, createDummyNodeTopDown(child, ancestorId));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -403,7 +453,7 @@ std::vector<std::shared_ptr<DummyNode>> GraphController::createDummyNodeTopDown(
|
||||
}
|
||||
|
||||
node->forEachChildNode(
|
||||
[node, &result, this](Node* child)
|
||||
[node, &result, &ancestorId, this](Node* child)
|
||||
{
|
||||
DummyNode* parent = nullptr;
|
||||
AccessKind accessKind = ACCESS_NONE;
|
||||
@@ -432,7 +482,7 @@ std::vector<std::shared_ptr<DummyNode>> GraphController::createDummyNodeTopDown(
|
||||
parent = accessNode.get();
|
||||
}
|
||||
|
||||
utility::append(parent->subNodes, createDummyNodeTopDown(child));
|
||||
utility::append(parent->subNodes, createDummyNodeTopDown(child, ancestorId));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -482,7 +532,7 @@ void GraphController::autoExpandActiveNode(const std::vector<Id>& activeTokenIds
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphController::setActive(const std::vector<Id>& activeTokenIds)
|
||||
bool GraphController::setActive(const std::vector<Id>& activeTokenIds, bool showAllEdges)
|
||||
{
|
||||
TRACE();
|
||||
|
||||
@@ -513,7 +563,7 @@ bool GraphController::setActive(const std::vector<Id>& activeTokenIds)
|
||||
DummyNode* from = getDummyGraphNodeById(edge->ownerId);
|
||||
DummyNode* to = getDummyGraphNodeById(edge->targetId);
|
||||
|
||||
if (from && to && (noActive || from->active || to->active || edge->active))
|
||||
if (from && to && (showAllEdges || noActive || from->active || to->active || edge->active))
|
||||
{
|
||||
edge->visible = true;
|
||||
from->connected = true;
|
||||
@@ -540,7 +590,7 @@ void GraphController::setActiveAndVisibility(const std::vector<Id>& activeTokenI
|
||||
{
|
||||
TRACE();
|
||||
|
||||
setVisibility(setActive(activeTokenIds));
|
||||
setVisibility(setActive(activeTokenIds, false));
|
||||
}
|
||||
|
||||
void GraphController::setNodeActiveRecursive(DummyNode* node, const std::vector<Id>& activeTokenIds, bool* noActive) const
|
||||
@@ -1468,7 +1518,8 @@ DummyNode* GraphController::getDummyGraphNodeById(Id tokenId) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GraphController::buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop)
|
||||
void GraphController::buildGraph(
|
||||
MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop)
|
||||
{
|
||||
if (!message->isReplayed())
|
||||
{
|
||||
@@ -1742,7 +1793,7 @@ void GraphController::handleMessage(MessageColorSchemeTest* message)
|
||||
}
|
||||
);
|
||||
|
||||
setActive(activeTokenIds);
|
||||
setActive(activeTokenIds, true);
|
||||
|
||||
layoutNesting();
|
||||
|
||||
@@ -1758,7 +1809,7 @@ void GraphController::handleMessage(MessageColorSchemeTest* message)
|
||||
grid.createBuckets(m_dummyNodes, std::vector<std::shared_ptr<DummyEdge>>());
|
||||
grid.layoutBuckets();
|
||||
|
||||
buildGraph(message, false);
|
||||
buildGraph(message, false, true, false);
|
||||
|
||||
getView()->focusTokenIds(focusedTokenIds);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "utility/messaging/MessageListener.h"
|
||||
#include "utility/messaging/type/MessageActivateAll.h"
|
||||
#include "utility/messaging/type/MessageActivateTokens.h"
|
||||
#include "utility/messaging/type/MessageActivateTrail.h"
|
||||
#include "utility/messaging/type/MessageColorSchemeTest.h"
|
||||
#include "utility/messaging/type/MessageFlushUpdates.h"
|
||||
#include "utility/messaging/type/MessageFocusIn.h"
|
||||
@@ -34,6 +35,7 @@ class GraphController
|
||||
: public Controller
|
||||
, public MessageListener<MessageActivateAll>
|
||||
, public MessageListener<MessageActivateTokens>
|
||||
, public MessageListener<MessageActivateTrail>
|
||||
, public MessageListener<MessageColorSchemeTest>
|
||||
, public MessageListener<MessageFlushUpdates>
|
||||
, public MessageListener<MessageFocusIn>
|
||||
@@ -53,6 +55,7 @@ public:
|
||||
private:
|
||||
virtual void handleMessage(MessageActivateAll* message);
|
||||
virtual void handleMessage(MessageActivateTokens* message);
|
||||
virtual void handleMessage(MessageActivateTrail* message);
|
||||
virtual void handleMessage(MessageFlushUpdates* message);
|
||||
virtual void handleMessage(MessageFocusIn* message);
|
||||
virtual void handleMessage(MessageFocusOut* message);
|
||||
@@ -71,13 +74,13 @@ private:
|
||||
void createDummyGraphForTokenIds(const std::vector<Id>& tokenIds, const std::shared_ptr<Graph> graph);
|
||||
void createDummyGraphForTokenIdsAndSetActiveAndVisibility(
|
||||
const std::vector<Id>& tokenIds, const std::shared_ptr<Graph> graph);
|
||||
std::vector<std::shared_ptr<DummyNode>> createDummyNodeTopDown(Node* node);
|
||||
std::vector<std::shared_ptr<DummyNode>> createDummyNodeTopDown(Node* node, Id ancestorId);
|
||||
|
||||
std::vector<Id> getExpandedNodeIds() const;
|
||||
void setExpandedNodeIds(const std::vector<Id>& nodeIds);
|
||||
void autoExpandActiveNode(const std::vector<Id>& activeTokenIds);
|
||||
|
||||
bool setActive(const std::vector<Id>& activeTokenIds);
|
||||
bool setActive(const std::vector<Id>& activeTokenIds, bool showAllEdges);
|
||||
void setVisibility(bool noActive);
|
||||
void setActiveAndVisibility(const std::vector<Id>& activeTokenIds);
|
||||
void setNodeActiveRecursive(DummyNode* node, const std::vector<Id>& activeTokenIds, bool* noActive) const;
|
||||
@@ -108,7 +111,7 @@ private:
|
||||
|
||||
DummyNode* getDummyGraphNodeById(Id tokenId) const;
|
||||
|
||||
void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition = true, bool scrollToTop = false);
|
||||
void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop);
|
||||
|
||||
void forEachDummyNodeRecursive(std::function<void(DummyNode*)> func);
|
||||
void forEachDummyEdge(std::function<void(DummyEdge*)> func);
|
||||
@@ -126,6 +129,8 @@ private:
|
||||
std::vector<Id> m_activeEdgeIds;
|
||||
|
||||
std::shared_ptr<Graph> m_graph;
|
||||
|
||||
std::map<Id, Id> m_topLevelAncestorIds;
|
||||
};
|
||||
|
||||
#endif // GRAPH_CONTROLLER_H
|
||||
|
||||
@@ -77,6 +77,19 @@ void UndoRedoController::handleMessage(MessageActivateTokens* message)
|
||||
processCommand(command);
|
||||
}
|
||||
|
||||
void UndoRedoController::handleMessage(MessageActivateTrail* message)
|
||||
{
|
||||
if (sameMessageTypeAsLast(message) &&
|
||||
static_cast<MessageActivateTrail*>(lastMessage())->originId == message->originId &&
|
||||
static_cast<MessageActivateTrail*>(lastMessage())->targetId == message->targetId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Command command(std::make_shared<MessageActivateTrail>(*message), Command::ORDER_ADAPT, true);
|
||||
processCommand(command);
|
||||
}
|
||||
|
||||
void UndoRedoController::handleMessage(MessageChangeFileView* message)
|
||||
{
|
||||
Command command(std::make_shared<MessageChangeFileView>(*message), Command::ORDER_VIEW);
|
||||
@@ -320,9 +333,21 @@ void UndoRedoController::replayCommands(std::list<Command>::iterator it)
|
||||
std::vector<std::list<Command>::iterator> viewCommands;
|
||||
bool keepsContent = true;
|
||||
|
||||
std::map<std::string, std::list<Command>::iterator> lastOfType;
|
||||
std::list<Command>::iterator at = it;
|
||||
while (at != m_iterator)
|
||||
{
|
||||
if (at->replayLastOnly)
|
||||
{
|
||||
lastOfType[at->message->getType()] = at;
|
||||
}
|
||||
|
||||
std::advance(at, 1);
|
||||
}
|
||||
|
||||
while (it != m_iterator)
|
||||
{
|
||||
if (it->order != Command::ORDER_VIEW || it->replayLastOnly == false)
|
||||
if (!it->replayLastOnly || lastOfType[it->message->getType()] == it)
|
||||
{
|
||||
replayCommand(it);
|
||||
|
||||
@@ -330,13 +355,8 @@ void UndoRedoController::replayCommands(std::list<Command>::iterator it)
|
||||
{
|
||||
keepsContent = false;
|
||||
}
|
||||
|
||||
if (it->order != Command::ORDER_VIEW)
|
||||
{
|
||||
viewCommands.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (it->order == Command::ORDER_VIEW)
|
||||
{
|
||||
viewCommands.push_back(it);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "utility/messaging/type/MessageActivateAll.h"
|
||||
#include "utility/messaging/type/MessageActivateLocalSymbols.h"
|
||||
#include "utility/messaging/type/MessageActivateTokens.h"
|
||||
#include "utility/messaging/type/MessageActivateTrail.h"
|
||||
#include "utility/messaging/type/MessageChangeFileView.h"
|
||||
#include "utility/messaging/type/MessageDeactivateEdge.h"
|
||||
#include "utility/messaging/type/MessageFinishedParsing.h"
|
||||
@@ -34,6 +35,7 @@ class UndoRedoController
|
||||
, public MessageListener<MessageActivateAll>
|
||||
, public MessageListener<MessageActivateLocalSymbols>
|
||||
, public MessageListener<MessageActivateTokens>
|
||||
, public MessageListener<MessageActivateTrail>
|
||||
, public MessageListener<MessageChangeFileView>
|
||||
, public MessageListener<MessageDeactivateEdge>
|
||||
, public MessageListener<MessageFinishedParsing>
|
||||
@@ -78,6 +80,7 @@ private:
|
||||
virtual void handleMessage(MessageActivateAll* message);
|
||||
virtual void handleMessage(MessageActivateLocalSymbols* message);
|
||||
virtual void handleMessage(MessageActivateTokens* message);
|
||||
virtual void handleMessage(MessageActivateTrail* message);
|
||||
virtual void handleMessage(MessageChangeFileView* message);
|
||||
virtual void handleMessage(MessageDeactivateEdge* message);
|
||||
virtual void handleMessage(MessageFinishedParsing* message);
|
||||
|
||||
@@ -154,7 +154,7 @@ void BucketLayouter::createBuckets(
|
||||
std::shared_ptr<DummyNode> target = findTopMostDummyNodeRecursive(nodes, edge->targetId, nullptr);
|
||||
|
||||
bool removeEdge = false;
|
||||
if (!owner || !target)
|
||||
if (!owner || !target || owner == target)
|
||||
{
|
||||
removeEdge = true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define DUMMY_EDGE_H
|
||||
|
||||
#include "utility/types.h"
|
||||
#include "utility/math/Vector4.h"
|
||||
|
||||
#include "data/graph/Edge.h"
|
||||
#include "data/graph/token_component/TokenComponentAggregation.h"
|
||||
@@ -86,6 +87,8 @@ struct DummyEdge
|
||||
bool visible;
|
||||
bool active;
|
||||
|
||||
std::vector<Vec4i> path;
|
||||
|
||||
// BundleEdge
|
||||
int weight;
|
||||
TokenComponentAggregation::Direction direction;
|
||||
|
||||
@@ -53,7 +53,6 @@ public:
|
||||
DummyNode()
|
||||
: visible(false)
|
||||
, childVisible(false)
|
||||
, topLevelAncestorId(0)
|
||||
, tokenId(0)
|
||||
, data(nullptr)
|
||||
, active(false)
|
||||
@@ -244,7 +243,6 @@ public:
|
||||
bool visible;
|
||||
bool childVisible;
|
||||
|
||||
Id topLevelAncestorId;
|
||||
Id tokenId;
|
||||
|
||||
std::vector<std::shared_ptr<DummyNode>> subNodes;
|
||||
|
||||
@@ -0,0 +1,573 @@
|
||||
#include "component/controller/helper/TrailLayouter.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TrailLayouter::TrailLayouter(LayoutDirection dir)
|
||||
: m_direction(dir)
|
||||
{
|
||||
}
|
||||
|
||||
void TrailLayouter::layoutGraph(
|
||||
std::vector<std::shared_ptr<DummyNode>>& dummyNodes,
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& dummyEdges,
|
||||
const std::map<Id, Id>& topLevelAncestorIds)
|
||||
{
|
||||
buildGraph(dummyNodes, dummyEdges, topLevelAncestorIds);
|
||||
|
||||
makeAcyclic();
|
||||
|
||||
assignLongestPathLevels();
|
||||
assignRemainingLevels();
|
||||
|
||||
addVirtualNodes();
|
||||
|
||||
buildColumns();
|
||||
reduceEdgeCrossings();
|
||||
layout();
|
||||
|
||||
retrievePositions(topLevelAncestorIds);
|
||||
|
||||
// print();
|
||||
}
|
||||
|
||||
void TrailLayouter::buildGraph(
|
||||
std::vector<std::shared_ptr<DummyNode>>& dummyNodes,
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& dummyEdges,
|
||||
const std::map<Id, Id>& topLevelAncestorIds)
|
||||
{
|
||||
for (const std::shared_ptr<DummyNode> dummyNode : dummyNodes)
|
||||
{
|
||||
addNode(dummyNode);
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<DummyEdge> dummyEdge : dummyEdges)
|
||||
{
|
||||
dummyEdge->path.clear();
|
||||
|
||||
if (dummyEdge->data && !dummyEdge->data->isType(Edge::EDGE_OVERRIDE | Edge::EDGE_INHERITANCE))
|
||||
{
|
||||
addEdge(dummyEdge, topLevelAncestorIds);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<DummyEdge> dummyEdge : dummyEdges)
|
||||
{
|
||||
if (dummyEdge->data && dummyEdge->data->isType(Edge::EDGE_OVERRIDE | Edge::EDGE_INHERITANCE))
|
||||
{
|
||||
addEdge(dummyEdge, topLevelAncestorIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::makeAcyclic()
|
||||
{
|
||||
std::set<TrailEdge*> edgesToSwitch;
|
||||
for (std::shared_ptr<TrailNode> node : m_allNodes)
|
||||
{
|
||||
if (!node->incomingEdges.size())
|
||||
{
|
||||
utility::append(edgesToSwitch, node->outgoingEdges);
|
||||
}
|
||||
}
|
||||
|
||||
for (TrailEdge* edge : edgesToSwitch)
|
||||
{
|
||||
switchEdge(edge);
|
||||
}
|
||||
|
||||
for (TrailNode* node : m_rootNodes)
|
||||
{
|
||||
std::set<TrailEdge*> edgesToSwitch = node->incomingEdges;
|
||||
for (TrailEdge* edge : edgesToSwitch)
|
||||
{
|
||||
switchEdge(edge);
|
||||
}
|
||||
|
||||
makeAcyclicRecursive(node, std::set<TrailNode*>());
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::makeAcyclicRecursive(TrailNode* node, std::set<TrailNode*> predecessors)
|
||||
{
|
||||
predecessors.insert(node);
|
||||
|
||||
std::set<TrailEdge*> edgesToSwitch;
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
if (predecessors.find(edge->target) != predecessors.end())
|
||||
{
|
||||
edgesToSwitch.insert(edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAcyclicRecursive(edge->target, predecessors);
|
||||
}
|
||||
}
|
||||
|
||||
for (TrailEdge* edge : edgesToSwitch)
|
||||
{
|
||||
switchEdge(edge);
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::assignLongestPathLevels()
|
||||
{
|
||||
std::set<TrailNode*> nodes(m_rootNodes.begin(), m_rootNodes.end());
|
||||
std::map<TrailNode*, TrailNode*> predecessorNodes;
|
||||
|
||||
int level = 0;
|
||||
|
||||
while (nodes.size())
|
||||
{
|
||||
std::set<TrailNode*> newNodes;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
newNodes.insert(edge->target);
|
||||
predecessorNodes[edge->target] = node;
|
||||
}
|
||||
}
|
||||
|
||||
if (!newNodes.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nodes = newNodes;
|
||||
level++;
|
||||
}
|
||||
|
||||
while (nodes.size())
|
||||
{
|
||||
std::set<TrailNode*> newNodes;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
node->level = level;
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
newNodes.insert(predecessorNodes[node]);
|
||||
}
|
||||
}
|
||||
|
||||
nodes = newNodes;
|
||||
level--;
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::assignRemainingLevels()
|
||||
{
|
||||
std::set<TrailNode*> nodes(m_rootNodes.begin(), m_rootNodes.end());
|
||||
while (nodes.size())
|
||||
{
|
||||
std::set<TrailNode*> newNodes;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
newNodes.insert(edge->target);
|
||||
}
|
||||
|
||||
if (node->level < 0)
|
||||
{
|
||||
int level = -1;
|
||||
|
||||
for (TrailEdge* edge : node->incomingEdges)
|
||||
{
|
||||
level = std::max(level, edge->origin->level + 1);
|
||||
}
|
||||
|
||||
node->level = level;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = newNodes;
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::addVirtualNodes()
|
||||
{
|
||||
std::vector<std::shared_ptr<TrailEdge>> newEdges;
|
||||
|
||||
for (std::shared_ptr<TrailEdge> edge : m_allEdges)
|
||||
{
|
||||
for (int i = edge->origin->level + 1; i < edge->target->level; i++)
|
||||
{
|
||||
std::shared_ptr<TrailNode> virtualNode = std::make_shared<TrailNode>();
|
||||
virtualNode->id = 0;
|
||||
virtualNode->name = "<virtual>";
|
||||
virtualNode->dummyNode = nullptr;
|
||||
virtualNode->level = i;
|
||||
|
||||
virtualNode->size = Vec2i(50, 20);
|
||||
|
||||
m_allNodes.push_back(virtualNode);
|
||||
edge->virtualNodes.push_back(virtualNode.get());
|
||||
|
||||
|
||||
std::shared_ptr<TrailEdge> virtualEdge = std::make_shared<TrailEdge>();
|
||||
virtualEdge->id = 0;
|
||||
|
||||
virtualEdge->origin = edge->origin;
|
||||
virtualEdge->origin->outgoingEdges.erase(edge.get());
|
||||
virtualEdge->origin->outgoingEdges.insert(virtualEdge.get());
|
||||
|
||||
virtualEdge->target = virtualNode.get();
|
||||
virtualEdge->target->incomingEdges.insert(virtualEdge.get());
|
||||
virtualEdge->target->outgoingEdges.insert(edge.get());
|
||||
|
||||
edge->origin = virtualNode.get();
|
||||
newEdges.push_back(virtualEdge);
|
||||
}
|
||||
}
|
||||
|
||||
m_allEdges.insert(m_allEdges.end(), newEdges.begin(), newEdges.end());
|
||||
}
|
||||
|
||||
void TrailLayouter::buildColumns()
|
||||
{
|
||||
for (std::shared_ptr<TrailNode> node : m_allNodes)
|
||||
{
|
||||
int level = node->level + 1;
|
||||
for (int i = m_nodesPerCol.size(); i <= level; i++)
|
||||
{
|
||||
m_nodesPerCol.push_back(std::vector<TrailNode*>());
|
||||
}
|
||||
|
||||
m_nodesPerCol[level].push_back(node.get());
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::reduceEdgeCrossings()
|
||||
{
|
||||
for (size_t i = 1; i < m_nodesPerCol.size(); i++)
|
||||
{
|
||||
std::vector<TrailNode*> nodes = m_nodesPerCol[i];
|
||||
|
||||
bool usePredecessors = true;
|
||||
std::vector<TrailNode*> neighbors = m_nodesPerCol[i - 1];
|
||||
if (neighbors.size() == 1 && i + 1 < m_nodesPerCol.size() && m_nodesPerCol[i + 1].size() > 0)
|
||||
{
|
||||
neighbors = m_nodesPerCol[i + 1];
|
||||
usePredecessors = false;
|
||||
}
|
||||
|
||||
std::map<TrailNode*, size_t> neighborIndices;
|
||||
for (size_t j = 0; j < neighbors.size(); j++)
|
||||
{
|
||||
neighborIndices.emplace(neighbors[j], j);
|
||||
}
|
||||
|
||||
std::multimap<float, TrailNode*> newOrder;
|
||||
|
||||
for (size_t j = 0; j < nodes.size(); j++)
|
||||
{
|
||||
TrailNode* node = nodes[j];
|
||||
|
||||
size_t sum = 0;
|
||||
size_t count = 0;
|
||||
|
||||
if (usePredecessors)
|
||||
{
|
||||
for (TrailEdge* edge : node->incomingEdges)
|
||||
{
|
||||
sum += neighborIndices[edge->origin];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
sum += neighborIndices[edge->target];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
float value = j;
|
||||
if (count)
|
||||
{
|
||||
value = float(sum) / count;
|
||||
}
|
||||
newOrder.emplace(value, node);
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
for (std::pair<float, TrailNode*> p : newOrder)
|
||||
{
|
||||
nodes.push_back(p.second);
|
||||
}
|
||||
m_nodesPerCol[i] = nodes;
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::layout()
|
||||
{
|
||||
// calculate widths and heights of columns, and find largest column
|
||||
std::vector<int> widthsPerCol;
|
||||
std::vector<int> heightsPerCol;
|
||||
|
||||
int maxHeight = 0;
|
||||
size_t maxHeightIndex = 0;
|
||||
|
||||
unsigned int xIdx = horizontalLayout() ? 0 : 1;
|
||||
unsigned int yIdx = horizontalLayout() ? 1 : 0;
|
||||
|
||||
for (size_t i = 0; i < m_nodesPerCol.size(); i++)
|
||||
{
|
||||
std::vector<TrailNode*>& nodes = m_nodesPerCol[i];
|
||||
|
||||
int width = 0;
|
||||
int height = -30;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
height += node->size.getValue(yIdx) + 30;
|
||||
width = std::max(width, node->size.getValue(xIdx));
|
||||
}
|
||||
|
||||
widthsPerCol.push_back(width);
|
||||
heightsPerCol.push_back(height);
|
||||
|
||||
if (height > maxHeight)
|
||||
{
|
||||
maxHeight = height;
|
||||
maxHeightIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// layout nodes within columns
|
||||
int x = 0;
|
||||
for (size_t i = 0; i < m_nodesPerCol.size(); i++)
|
||||
{
|
||||
std::vector<TrailNode*>& nodes = m_nodesPerCol[i];
|
||||
int y = -heightsPerCol[i] / 2;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
node->pos = horizontalLayout() ? Vec2i(x, y) : Vec2i(y, x);
|
||||
y += node->size.getValue(yIdx) + 30;
|
||||
|
||||
if (!node->id)
|
||||
{
|
||||
node->size.setValue(xIdx, widthsPerCol[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 == m_nodesPerCol.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (invertedLayout())
|
||||
{
|
||||
x -= widthsPerCol[i + 1] + 150;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += widthsPerCol[i] + 150;
|
||||
}
|
||||
}
|
||||
|
||||
// process layout before highest column
|
||||
for (size_t i = maxHeightIndex; i > 0; i--)
|
||||
{
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i - 1]);
|
||||
}
|
||||
|
||||
// process layout after highest column
|
||||
for (size_t i = maxHeightIndex + 1; i < m_nodesPerCol.size(); i++)
|
||||
{
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i]);
|
||||
}
|
||||
|
||||
// put into grid
|
||||
}
|
||||
|
||||
void TrailLayouter::moveNodesToAveragePosition(std::vector<TrailNode*> nodes)
|
||||
{
|
||||
unsigned int yIdx = horizontalLayout() ? 1 : 0;
|
||||
|
||||
for (size_t k = 0; k < 2; k++)
|
||||
{
|
||||
for (size_t j = 0; j < nodes.size(); j++)
|
||||
{
|
||||
size_t l = k ? nodes.size() - 1 - j : j;
|
||||
size_t i = l % 2 ? nodes.size() - (l + 1) / 2 : l / 2;
|
||||
TrailNode* node = nodes[i];
|
||||
|
||||
int sum = 0;
|
||||
int count = 0;
|
||||
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
sum += edge->target->pos.getValue(yIdx) + edge->target->size.getValue(yIdx) / 2;
|
||||
count++;
|
||||
}
|
||||
|
||||
for (TrailEdge* edge : node->incomingEdges)
|
||||
{
|
||||
sum += edge->origin->pos.getValue(yIdx) + edge->origin->size.getValue(yIdx) / 2;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
node->pos.setValue(yIdx, sum / count - node->size.getValue(yIdx) / 2);
|
||||
|
||||
TrailNode* above = i > 0 ? nodes[i - 1] : nullptr;
|
||||
if (above && above->pos.getValue(yIdx) + above->size.getValue(yIdx) + 30 > node->pos.getValue(yIdx))
|
||||
{
|
||||
node->pos.setValue(yIdx, above->pos.getValue(yIdx) + above->size.getValue(yIdx) + 30);
|
||||
}
|
||||
|
||||
TrailNode* below = i + 1 < nodes.size() ? nodes[i + 1] : nullptr;
|
||||
if (below && below->pos.getValue(yIdx) - 30 < node->pos.getValue(yIdx) + node->size.getValue(yIdx))
|
||||
{
|
||||
node->pos.setValue(yIdx, below->pos.getValue(yIdx) - 30 - node->size.getValue(yIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::retrievePositions(const std::map<Id, Id>& topLevelAncestorIds)
|
||||
{
|
||||
for (std::shared_ptr<TrailNode> node : m_allNodes)
|
||||
{
|
||||
if (node->dummyNode)
|
||||
{
|
||||
node->dummyNode->position = node->pos;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::shared_ptr<TrailEdge> edge : m_allEdges)
|
||||
{
|
||||
if (edge->virtualNodes.size())
|
||||
{
|
||||
for (DummyEdge* dummyEdge : edge->dummyEdges)
|
||||
{
|
||||
bool forward = edge->target->id == topLevelAncestorIds.find(dummyEdge->targetId)->second;
|
||||
for (size_t i = 0; i < edge->virtualNodes.size(); i++)
|
||||
{
|
||||
TrailNode* node = edge->virtualNodes[forward ? i : edge->virtualNodes.size() - 1 - i];
|
||||
dummyEdge->path.push_back(
|
||||
Vec4i(node->pos.x, node->pos.y, node->pos.x + node->size.x, node->pos.y + node->size.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::print()
|
||||
{
|
||||
std::cout << "graph: " << std::endl;
|
||||
for (std::shared_ptr<TrailNode> node : m_allNodes)
|
||||
{
|
||||
std::cout << node->id << "\t" << node->level << "\t";
|
||||
std::cout << node->incomingEdges.size() << "\t" << node->outgoingEdges.size() << "\t";
|
||||
std::cout << node->name << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
for (std::shared_ptr<TrailEdge> edge : m_allEdges)
|
||||
{
|
||||
std::cout << edge->id << "\t" << edge->origin->name << "\t" << edge->target->name << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void TrailLayouter::addNode(const std::shared_ptr<DummyNode>& dummyNode)
|
||||
{
|
||||
std::shared_ptr<TrailNode> node = std::make_shared<TrailNode>();
|
||||
node->id = dummyNode->tokenId;
|
||||
node->name = dummyNode->name;
|
||||
node->dummyNode = dummyNode.get();
|
||||
node->level = -1;
|
||||
|
||||
node->size = dummyNode->size;
|
||||
|
||||
m_allNodes.push_back(node);
|
||||
|
||||
if (node->id)
|
||||
{
|
||||
m_nodesById.emplace(node->id, node.get());
|
||||
}
|
||||
|
||||
if (dummyNode->hasActiveSubNode())
|
||||
{
|
||||
m_rootNodes.push_back(node.get());
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::addEdge(const std::shared_ptr<DummyEdge> dummyEdge, const std::map<Id, Id>& topLevelAncestorIds)
|
||||
{
|
||||
std::shared_ptr<TrailEdge> edge = std::make_shared<TrailEdge>();
|
||||
if (!dummyEdge->data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
edge->id = dummyEdge->data->getId();
|
||||
|
||||
Id originTopLevelId = topLevelAncestorIds.find(dummyEdge->ownerId)->second;
|
||||
Id targetTopLevelId = topLevelAncestorIds.find(dummyEdge->targetId)->second;
|
||||
|
||||
if (dummyEdge->data->isType(Edge::EDGE_OVERRIDE | Edge::EDGE_INHERITANCE) != invertedLayout())
|
||||
{
|
||||
std::swap(originTopLevelId, targetTopLevelId);
|
||||
}
|
||||
|
||||
auto origin = m_nodesById.find(originTopLevelId);
|
||||
auto target = m_nodesById.find(targetTopLevelId);
|
||||
|
||||
if (origin == m_nodesById.end() || target == m_nodesById.end() || origin == target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
edge->origin = origin->second;
|
||||
edge->target = target->second;
|
||||
|
||||
for (std::shared_ptr<TrailEdge> e : m_allEdges)
|
||||
{
|
||||
if ((e->origin == edge->origin && e->target == edge->target) ||
|
||||
(e->origin == edge->target && e->target == edge->origin))
|
||||
{
|
||||
e->dummyEdges.push_back(dummyEdge.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
edge->dummyEdges.push_back(dummyEdge.get());
|
||||
|
||||
edge->origin->outgoingEdges.insert(edge.get());
|
||||
edge->target->incomingEdges.insert(edge.get());
|
||||
|
||||
m_allEdges.push_back(edge);
|
||||
}
|
||||
|
||||
void TrailLayouter::switchEdge(TrailEdge* edge)
|
||||
{
|
||||
edge->origin->outgoingEdges.erase(edge);
|
||||
edge->origin->incomingEdges.insert(edge);
|
||||
|
||||
edge->target->incomingEdges.erase(edge);
|
||||
edge->target->outgoingEdges.insert(edge);
|
||||
|
||||
std::swap(edge->origin, edge->target);
|
||||
}
|
||||
|
||||
bool TrailLayouter::horizontalLayout() const
|
||||
{
|
||||
return m_direction == LAYOUT_LEFT_RIGHT || m_direction == LAYOUT_RIGHT_LEFT;
|
||||
}
|
||||
|
||||
bool TrailLayouter::invertedLayout() const
|
||||
{
|
||||
return m_direction == LAYOUT_RIGHT_LEFT || m_direction == LAYOUT_BOTTOM_TOP;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef GRAPH_LAYOUTER_H
|
||||
#define GRAPH_LAYOUTER_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "component/controller/helper/DummyEdge.h"
|
||||
#include "component/controller/helper/DummyNode.h"
|
||||
|
||||
// based on Sugiyama dependency graph layouting
|
||||
|
||||
class TrailLayouter
|
||||
{
|
||||
public:
|
||||
enum LayoutDirection
|
||||
{
|
||||
LAYOUT_LEFT_RIGHT,
|
||||
LAYOUT_RIGHT_LEFT,
|
||||
LAYOUT_TOP_BOTTOM,
|
||||
LAYOUT_BOTTOM_TOP
|
||||
};
|
||||
|
||||
TrailLayouter(LayoutDirection dir);
|
||||
|
||||
void layoutGraph(
|
||||
std::vector<std::shared_ptr<DummyNode>>& dummyNodes,
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& dummyEdges,
|
||||
const std::map<Id, Id>& topLevelAncestorIds);
|
||||
|
||||
private:
|
||||
struct TrailEdge;
|
||||
|
||||
struct TrailNode
|
||||
{
|
||||
Id id;
|
||||
int level;
|
||||
std::string name;
|
||||
|
||||
Vec2i pos;
|
||||
Vec2i size;
|
||||
|
||||
std::set<TrailEdge*> incomingEdges;
|
||||
std::set<TrailEdge*> outgoingEdges;
|
||||
|
||||
DummyNode* dummyNode;
|
||||
};
|
||||
|
||||
struct TrailEdge
|
||||
{
|
||||
Id id;
|
||||
TrailNode* origin;
|
||||
TrailNode* target;
|
||||
|
||||
std::vector<TrailNode*> virtualNodes;
|
||||
|
||||
std::vector<DummyEdge*> dummyEdges;
|
||||
};
|
||||
|
||||
void buildGraph(
|
||||
std::vector<std::shared_ptr<DummyNode>>& dummyNodes,
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& dummyEdges,
|
||||
const std::map<Id, Id>& topLevelAncestorIds);
|
||||
|
||||
void makeAcyclic();
|
||||
void makeAcyclicRecursive(TrailNode* node, std::set<TrailNode*> predecessors);
|
||||
|
||||
void assignLongestPathLevels();
|
||||
void assignRemainingLevels();
|
||||
|
||||
void addVirtualNodes();
|
||||
void buildColumns();
|
||||
void reduceEdgeCrossings();
|
||||
|
||||
void layout();
|
||||
void moveNodesToAveragePosition(std::vector<TrailNode*> nodes);
|
||||
void retrievePositions(const std::map<Id, Id>& topLevelAncestorIds);
|
||||
|
||||
void print();
|
||||
|
||||
void addNode(const std::shared_ptr<DummyNode>& dummyNode);
|
||||
void addEdge(const std::shared_ptr<DummyEdge> dummyEdge, const std::map<Id, Id>& topLevelAncestorIds);
|
||||
void switchEdge(TrailEdge* edge);
|
||||
|
||||
bool horizontalLayout() const;
|
||||
bool invertedLayout() const;
|
||||
|
||||
LayoutDirection m_direction;
|
||||
|
||||
std::vector<std::shared_ptr<TrailNode>> m_allNodes;
|
||||
std::vector<std::shared_ptr<TrailEdge>> m_allEdges;
|
||||
|
||||
std::map<Id, TrailNode*> m_nodesById;
|
||||
std::vector<TrailNode*> m_rootNodes;
|
||||
|
||||
std::vector<std::vector<TrailNode*>> m_nodesPerCol;
|
||||
};
|
||||
|
||||
#endif // GRAPH_LAYOUTER_H
|
||||
@@ -99,7 +99,6 @@ GraphViewStyle::NodeStyle::NodeStyle()
|
||||
GraphViewStyle::EdgeStyle::EdgeStyle()
|
||||
: width(0)
|
||||
, zValue(0)
|
||||
, isStraight(false)
|
||||
, arrowLength(0)
|
||||
, arrowWidth(0)
|
||||
, arrowClosed(false)
|
||||
@@ -550,7 +549,8 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfTextNode()
|
||||
return style;
|
||||
}
|
||||
|
||||
GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused)
|
||||
GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(
|
||||
Edge::EdgeType type, bool isActive, bool isFocused, bool isTrailEdge)
|
||||
{
|
||||
EdgeStyle style;
|
||||
|
||||
@@ -588,6 +588,13 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ
|
||||
style.originOffset.y = 1;
|
||||
style.targetOffset.y = -1;
|
||||
style.verticalOffset = 4;
|
||||
|
||||
if (isTrailEdge && isActive)
|
||||
{
|
||||
style.width = 3;
|
||||
style.color = ColorScheme::getInstance()->getColor(
|
||||
"graph/edge/" + Edge::getUnderscoredTypeString(type) + "/trail_focus", style.color);
|
||||
}
|
||||
break;
|
||||
case Edge::EDGE_USAGE:
|
||||
style.originOffset.y = 3;
|
||||
@@ -605,6 +612,11 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ
|
||||
break;
|
||||
}
|
||||
|
||||
if (isTrailEdge)
|
||||
{
|
||||
style.zValue = isActive ? 5 : 2;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,6 @@ public:
|
||||
float width;
|
||||
int zValue;
|
||||
|
||||
bool isStraight;
|
||||
|
||||
int arrowLength;
|
||||
int arrowWidth;
|
||||
bool arrowClosed;
|
||||
@@ -128,7 +126,7 @@ public:
|
||||
static NodeStyle getStyleOfQualifier();
|
||||
static NodeStyle getStyleOfTextNode();
|
||||
|
||||
static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused);
|
||||
static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused, bool isTrailEdge);
|
||||
|
||||
static int toGridOffset(int x);
|
||||
static int toGridSize(int x);
|
||||
|
||||
@@ -157,7 +157,8 @@ void PersistentStorage::addError(
|
||||
Id PersistentStorage::addNodeBookmark(const NodeBookmark& bookmark)
|
||||
{
|
||||
const Id categoryId = addBookmarkCategory(bookmark.getCategory().getName());
|
||||
const Id id = m_sqliteBookmarkStorage.addBookmark(bookmark.getName(), bookmark.getComment(), bookmark.getTimeStamp().toString(), categoryId);
|
||||
const Id id = m_sqliteBookmarkStorage.addBookmark(
|
||||
bookmark.getName(), bookmark.getComment(), bookmark.getTimeStamp().toString(), categoryId);
|
||||
|
||||
for (const Id& nodeId: bookmark.getNodeIds())
|
||||
{
|
||||
@@ -170,7 +171,8 @@ Id PersistentStorage::addNodeBookmark(const NodeBookmark& bookmark)
|
||||
Id PersistentStorage::addEdgeBookmark(const EdgeBookmark& bookmark)
|
||||
{
|
||||
const Id categoryId = addBookmarkCategory(bookmark.getCategory().getName());
|
||||
const Id id = m_sqliteBookmarkStorage.addBookmark(bookmark.getName(), bookmark.getComment(), bookmark.getTimeStamp().toString(), categoryId);
|
||||
const Id id = m_sqliteBookmarkStorage.addBookmark(
|
||||
bookmark.getName(), bookmark.getComment(), bookmark.getTimeStamp().toString(), categoryId);
|
||||
for (const Id& edgeId: bookmark.getEdgeIds())
|
||||
{
|
||||
const StorageEdge storageEdge = m_sqliteIndexStorage.getEdgeById(edgeId);
|
||||
@@ -178,7 +180,8 @@ Id PersistentStorage::addEdgeBookmark(const EdgeBookmark& bookmark)
|
||||
bool sourceNodeActive = storageEdge.sourceNodeId == bookmark.getActiveNodeId();
|
||||
m_sqliteBookmarkStorage.addBookmarkedEdge(
|
||||
id,
|
||||
m_sqliteIndexStorage.getNodeById(storageEdge.sourceNodeId).serializedName, // todo: optimization for multiple edges in same bookmark: use a local cache here
|
||||
// todo: optimization for multiple edges in same bookmark: use a local cache here
|
||||
m_sqliteIndexStorage.getNodeById(storageEdge.sourceNodeId).serializedName,
|
||||
m_sqliteIndexStorage.getNodeById(storageEdge.targetNodeId).serializedName,
|
||||
storageEdge.type,
|
||||
sourceNodeActive
|
||||
@@ -202,7 +205,8 @@ Id PersistentStorage::addBookmarkCategory(const std::string& name)
|
||||
return id;
|
||||
}
|
||||
|
||||
void PersistentStorage::updateBookmark(const Id bookmarkId, const std::string& name, const std::string& comment, const std::string& categoryName)
|
||||
void PersistentStorage::updateBookmark(
|
||||
const Id bookmarkId, const std::string& name, const std::string& comment, const std::string& categoryName)
|
||||
{
|
||||
const Id categoryId = addBookmarkCategory(categoryName); // only creates category if id didn't exist before;
|
||||
m_sqliteBookmarkStorage.updateBookmark(bookmarkId, name, comment, categoryId);
|
||||
@@ -229,7 +233,8 @@ std::vector<NodeBookmark> PersistentStorage::getAllNodeBookmarks() const
|
||||
std::unordered_map<Id, std::vector<Id>> bookmarkIdToBookmarkedNodeIds;
|
||||
for (const StorageBookmarkedNode& bookmarkedNode: m_sqliteBookmarkStorage.getAllBookmarkedNodes())
|
||||
{
|
||||
bookmarkIdToBookmarkedNodeIds[bookmarkedNode.bookmarkId].push_back(m_sqliteIndexStorage.getNodeBySerializedName(bookmarkedNode.serializedNodeName).id);
|
||||
bookmarkIdToBookmarkedNodeIds[bookmarkedNode.bookmarkId].push_back(
|
||||
m_sqliteIndexStorage.getNodeBySerializedName(bookmarkedNode.serializedNodeName).id);
|
||||
}
|
||||
|
||||
std::vector<NodeBookmark> nodeBookmarks;
|
||||
@@ -272,7 +277,12 @@ std::vector<EdgeBookmark> PersistentStorage::getAllEdgeBookmarks() const
|
||||
|
||||
std::vector<EdgeBookmark> edgeBookmarks;
|
||||
|
||||
Cache<std::string, Id> nodeIdCache([&](std::string serializedNodeName){ return m_sqliteIndexStorage.getNodeBySerializedName(serializedNodeName).id; });
|
||||
Cache<std::string, Id> nodeIdCache([&](std::string serializedNodeName)
|
||||
{
|
||||
return m_sqliteIndexStorage.getNodeBySerializedName(serializedNodeName).id;
|
||||
}
|
||||
);
|
||||
|
||||
for (const StorageBookmark& storageBookmark: m_sqliteBookmarkStorage.getAllBookmarks())
|
||||
{
|
||||
auto itCategories = bookmarkCategories.find(storageBookmark.categoryId);
|
||||
@@ -292,7 +302,8 @@ std::vector<EdgeBookmark> PersistentStorage::getAllEdgeBookmarks() const
|
||||
{
|
||||
const Id sourceNodeId = nodeIdCache.getValue(bookmarkedEdge.serializedSourceNodeName);
|
||||
const Id targetNodeId = nodeIdCache.getValue(bookmarkedEdge.serializedTargetNodeName);
|
||||
const Id edgeId = m_sqliteIndexStorage.getEdgeBySourceTargetType(sourceNodeId, targetNodeId, bookmarkedEdge.edgeType).id;
|
||||
const Id edgeId =
|
||||
m_sqliteIndexStorage.getEdgeBySourceTargetType(sourceNodeId, targetNodeId, bookmarkedEdge.edgeType).id;
|
||||
bookmark.addEdgeId(edgeId);
|
||||
|
||||
if (activeNodeId == 0)
|
||||
@@ -360,7 +371,8 @@ void PersistentStorage::forEachLocalSymbol(std::function<void(
|
||||
}
|
||||
}
|
||||
|
||||
void PersistentStorage::forEachSourceLocation(std::function<void(const Id /*id*/, const StorageSourceLocation& /*data*/)> callback) const
|
||||
void PersistentStorage::forEachSourceLocation(
|
||||
std::function<void(const Id /*id*/, const StorageSourceLocation& /*data*/)> callback) const
|
||||
{
|
||||
for (StorageSourceLocation& sourceLocation: m_sqliteIndexStorage.getAll<StorageSourceLocation>())
|
||||
{
|
||||
@@ -767,7 +779,8 @@ std::vector<SearchMatch> PersistentStorage::getAutocompletionMatches(const std::
|
||||
return matches;
|
||||
}
|
||||
|
||||
std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(const std::string& query, size_t maxResultsCount) const
|
||||
std::vector<SearchMatch> PersistentStorage::getAutocompletionSymbolMatches(
|
||||
const std::string& query, size_t maxResultsCount) const
|
||||
{
|
||||
// search in indices
|
||||
std::vector<SearchResult> results = m_symbolIndex.search(query, maxResultsCount, maxResultsCount);
|
||||
@@ -985,7 +998,8 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForAll() const
|
||||
return graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace) const
|
||||
std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(
|
||||
const std::vector<Id>& tokenIds, bool* isActiveNamespace) const
|
||||
{
|
||||
TRACE();
|
||||
|
||||
@@ -1108,6 +1122,71 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(const std::v
|
||||
return g;
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> PersistentStorage::getGraphForTrail(
|
||||
Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth) const
|
||||
{
|
||||
TRACE();
|
||||
|
||||
std::set<Id> nodeIds;
|
||||
std::set<Id> edgeIds;
|
||||
|
||||
std::vector<Id> nodeIdsToProcess;
|
||||
nodeIdsToProcess.push_back(originId ? originId : targetId);
|
||||
bool forward = originId;
|
||||
size_t currentDepth = 0;
|
||||
|
||||
nodeIds.insert(nodeIdsToProcess.back());
|
||||
while (nodeIdsToProcess.size() && (!depth || currentDepth < depth))
|
||||
{
|
||||
std::vector<StorageEdge> edges =
|
||||
forward ?
|
||||
m_sqliteIndexStorage.getEdgesBySourceIds(nodeIdsToProcess) :
|
||||
m_sqliteIndexStorage.getEdgesByTargetIds(nodeIdsToProcess);
|
||||
|
||||
if (trailType & (Edge::EDGE_OVERRIDE | Edge::EDGE_INHERITANCE))
|
||||
{
|
||||
utility::append(edges,
|
||||
forward ?
|
||||
m_sqliteIndexStorage.getEdgesByTargetIds(nodeIdsToProcess) :
|
||||
m_sqliteIndexStorage.getEdgesBySourceIds(nodeIdsToProcess)
|
||||
);
|
||||
}
|
||||
|
||||
nodeIdsToProcess.clear();
|
||||
|
||||
for (const StorageEdge& edge : edges)
|
||||
{
|
||||
if (Edge::intToType(edge.type) & trailType)
|
||||
{
|
||||
bool isForward = forward == !(Edge::intToType(edge.type) & (Edge::EDGE_OVERRIDE | Edge::EDGE_INHERITANCE));
|
||||
|
||||
Id nodeId = isForward ? edge.targetNodeId : edge.sourceNodeId;
|
||||
Id otherNodeId = isForward ? edge.sourceNodeId : edge.targetNodeId;
|
||||
|
||||
if (nodeIds.find(nodeId) == nodeIds.end())
|
||||
{
|
||||
nodeIdsToProcess.push_back(nodeId);
|
||||
nodeIds.insert(nodeId);
|
||||
edgeIds.insert(edge.id);
|
||||
}
|
||||
else if (nodeIds.find(otherNodeId) != nodeIds.end())
|
||||
{
|
||||
edgeIds.insert(edge.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDepth++;
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> graph = std::make_shared<Graph>();
|
||||
|
||||
addNodesWithChildrenAndEdgesToGraph(utility::toVector(nodeIds), utility::toVector(edgeIds), graph.get());
|
||||
addComponentAccessToGraph(graph.get());
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
// TODO: rename: getActiveElementIdsForId; TODO: make separate function for declarationId
|
||||
std::vector<Id> PersistentStorage::getActiveTokenIdsForId(Id tokenId, Id* declarationId) const
|
||||
{
|
||||
@@ -1506,7 +1585,8 @@ std::unordered_map<Id, std::set<Id>> PersistentStorage::getFileIdToImportingFile
|
||||
importedSourceLocationToElementIds[occurrence.sourceLocationId] = occurrence.elementId;
|
||||
}
|
||||
|
||||
for (const StorageSourceLocation& sourceLocation: m_sqliteIndexStorage.getAllByIds<StorageSourceLocation>(importedSourceLocationIds))
|
||||
for (const StorageSourceLocation& sourceLocation:
|
||||
m_sqliteIndexStorage.getAllByIds<StorageSourceLocation>(importedSourceLocationIds))
|
||||
{
|
||||
auto it = importedSourceLocationToElementIds.find(sourceLocation.id);
|
||||
if (it != importedSourceLocationToElementIds.end())
|
||||
@@ -1528,7 +1608,8 @@ std::unordered_map<Id, std::set<Id>> PersistentStorage::getFileIdToImportingFile
|
||||
return fileIdToImportingFileIdMap;
|
||||
}
|
||||
|
||||
std::set<Id> PersistentStorage::getReferenced(const std::set<Id>& ids, std::unordered_map<Id, std::set<Id>> idToReferencingIdMap) const
|
||||
std::set<Id> PersistentStorage::getReferenced(
|
||||
const std::set<Id>& ids, std::unordered_map<Id, std::set<Id>> idToReferencingIdMap) const
|
||||
{
|
||||
std::unordered_map<Id, std::set<Id>> idToReferencedIdMap;
|
||||
for (auto it: idToReferencingIdMap)
|
||||
@@ -1542,7 +1623,8 @@ std::set<Id> PersistentStorage::getReferenced(const std::set<Id>& ids, std::unor
|
||||
return getReferencing(ids, idToReferencedIdMap);
|
||||
}
|
||||
|
||||
std::set<Id> PersistentStorage::getReferencing(const std::set<Id>& ids, std::unordered_map<Id, std::set<Id>> idToReferencingIdMap) const
|
||||
std::set<Id> PersistentStorage::getReferencing(
|
||||
const std::set<Id>& ids, std::unordered_map<Id, std::set<Id>> idToReferencingIdMap) const
|
||||
{
|
||||
std::set<Id> referencingIds;
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ public:
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const;
|
||||
virtual std::shared_ptr<Graph> getGraphForTrail(Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth) const;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const;
|
||||
virtual std::vector<Id> getNodeIdsForLocationIds(const std::vector<Id>& locationIds) const;
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const = 0;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const = 0;
|
||||
virtual std::shared_ptr<Graph> getGraphForTrail(Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth) const = 0;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const = 0;
|
||||
virtual std::vector<Id> getNodeIdsForLocationIds(const std::vector<Id>& locationIds) const = 0;
|
||||
|
||||
@@ -184,6 +184,16 @@ std::shared_ptr<Graph> StorageAccessProxy::getGraphForActiveTokenIds(const std::
|
||||
return std::make_shared<Graph>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> StorageAccessProxy::getGraphForTrail(Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth) const
|
||||
{
|
||||
if (hasSubject())
|
||||
{
|
||||
return m_subject->getGraphForTrail(originId, targetId, trailType, depth);
|
||||
}
|
||||
|
||||
return std::make_shared<Graph>();
|
||||
}
|
||||
|
||||
std::vector<Id> StorageAccessProxy::getActiveTokenIdsForId(Id tokenId, Id* delcarationId) const
|
||||
{
|
||||
if (hasSubject())
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const;
|
||||
virtual std::shared_ptr<Graph> getGraphForTrail(Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth) const;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const;
|
||||
virtual std::vector<Id> getNodeIdsForLocationIds(const std::vector<Id>& locationIds) const;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "utility/logging/logging.h"
|
||||
|
||||
Graph::Graph()
|
||||
: m_trailMode(TRAIL_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -285,6 +286,16 @@ Token* Graph::getTokenById(Id id) const
|
||||
return token;
|
||||
}
|
||||
|
||||
Graph::TrailMode Graph::getTrailMode() const
|
||||
{
|
||||
return m_trailMode;
|
||||
}
|
||||
|
||||
void Graph::setTrailMode(TrailMode trailMode)
|
||||
{
|
||||
m_trailMode = trailMode;
|
||||
}
|
||||
|
||||
void Graph::print(std::ostream& ostream) const
|
||||
{
|
||||
ostream << "Graph:\n";
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
class Graph
|
||||
{
|
||||
public:
|
||||
enum TrailMode
|
||||
{
|
||||
TRAIL_NONE,
|
||||
TRAIL_HORIZONTAL,
|
||||
TRAIL_VERTICAL
|
||||
};
|
||||
|
||||
Graph();
|
||||
virtual ~Graph();
|
||||
|
||||
@@ -49,6 +56,9 @@ public:
|
||||
|
||||
Token* getTokenById(Id id) const;
|
||||
|
||||
TrailMode getTrailMode() const;
|
||||
void setTrailMode(TrailMode trailMode);
|
||||
|
||||
void print(std::ostream& ostream) const;
|
||||
void printBasic(std::ostream& ostream) const;
|
||||
|
||||
@@ -60,6 +70,8 @@ private:
|
||||
|
||||
std::map<Id, std::shared_ptr<Node>> m_nodes;
|
||||
std::map<Id, std::shared_ptr<Edge>> m_edges;
|
||||
|
||||
TrailMode m_trailMode;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& ostream, const Graph& graph);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef MESSAGE_ACTIVATE_TRAIL_H
|
||||
#define MESSAGE_ACTIVATE_TRAIL_H
|
||||
|
||||
#include "utility/messaging/Message.h"
|
||||
#include "utility/types.h"
|
||||
|
||||
class MessageActivateTrail
|
||||
: public Message<MessageActivateTrail>
|
||||
{
|
||||
public:
|
||||
MessageActivateTrail(Id originId, Id targetId, Edge::EdgeTypeMask trailType, size_t depth, bool horizontalLayout)
|
||||
: originId(originId)
|
||||
, targetId(targetId)
|
||||
, trailType(trailType)
|
||||
, depth(depth)
|
||||
, horizontalLayout(horizontalLayout)
|
||||
{
|
||||
}
|
||||
|
||||
static const std::string getStaticType()
|
||||
{
|
||||
return "MessageActivateTrail";
|
||||
}
|
||||
|
||||
const Id originId;
|
||||
const Id targetId;
|
||||
const Edge::EdgeTypeMask trailType;
|
||||
const size_t depth;
|
||||
const bool horizontalLayout;
|
||||
};
|
||||
|
||||
#endif // MESSAGE_ACTIVATE_TRAIL_H
|
||||
@@ -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
|
||||
+19
-189
@@ -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;
|
||||
|
||||
+18
-20
@@ -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
|
||||
@@ -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