ui: improvements on trail layout
* increased spacing around graph * fixed graph lagging on message focus out * fixed level assignment fails * fixed dead ends in cyclic graph removal * improved node positioning by grouping to same average position * fixed graph image export not centering graph * fixed black dot at graph origin * show status updates while creating trails
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/messaging/type/MessageStatus.h"
|
||||
#include "utility/tracing.h"
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
@@ -106,6 +107,8 @@ void GraphController::handleMessage(MessageActivateTrail* message)
|
||||
{
|
||||
TRACE("trail activate");
|
||||
|
||||
MessageStatus("Retrieving graph data", false, true).dispatch();
|
||||
|
||||
m_activeEdgeIds.clear();
|
||||
|
||||
std::shared_ptr<Graph> graph = m_storageAccess->getGraphForTrail(
|
||||
@@ -142,9 +145,13 @@ void GraphController::handleMessage(MessageActivateTrail* message)
|
||||
}
|
||||
}
|
||||
|
||||
MessageStatus("Layouting graph", false, true).dispatch();
|
||||
|
||||
TrailLayouter layout(direction);
|
||||
layout.layoutGraph(m_dummyNodes, m_dummyEdges, m_topLevelAncestorIds);
|
||||
|
||||
MessageStatus("Displaying graph", false, true).dispatch();
|
||||
|
||||
buildGraph(message, true, true, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
TrailLayouter::TrailLayouter(LayoutDirection dir)
|
||||
: m_direction(dir)
|
||||
, m_rootNode(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -14,7 +15,13 @@ void TrailLayouter::layoutGraph(
|
||||
{
|
||||
buildGraph(dummyNodes, dummyEdges, topLevelAncestorIds);
|
||||
|
||||
makeAcyclic();
|
||||
if (!m_rootNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removeDeadEnds();
|
||||
makeAcyclicRecursive(m_rootNode, std::set<TrailNode*>());
|
||||
|
||||
assignLongestPathLevels();
|
||||
assignRemainingLevels();
|
||||
@@ -44,46 +51,87 @@ void TrailLayouter::buildGraph(
|
||||
{
|
||||
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);
|
||||
}
|
||||
addEdge(dummyEdge, topLevelAncestorIds);
|
||||
}
|
||||
}
|
||||
|
||||
void TrailLayouter::makeAcyclic()
|
||||
void TrailLayouter::removeDeadEnds()
|
||||
{
|
||||
std::set<TrailEdge*> edgesToSwitch;
|
||||
for (std::shared_ptr<TrailNode> node : m_allNodes)
|
||||
{
|
||||
if (!node->incomingEdges.size())
|
||||
{
|
||||
utility::append(edgesToSwitch, node->outgoingEdges);
|
||||
}
|
||||
}
|
||||
std::set<TrailNode*> predecessors;
|
||||
|
||||
for (TrailEdge* edge : edgesToSwitch)
|
||||
{
|
||||
switchEdge(edge);
|
||||
}
|
||||
std::set<TrailNode*> deadEnds;
|
||||
std::set<TrailNode*> loseEnds;
|
||||
|
||||
for (TrailNode* node : m_rootNodes)
|
||||
std::deque<TrailNode*> nodes;
|
||||
nodes.push_back(m_rootNode);
|
||||
|
||||
while (nodes.size())
|
||||
{
|
||||
std::set<TrailEdge*> edgesToSwitch = node->incomingEdges;
|
||||
for (TrailEdge* edge : edgesToSwitch)
|
||||
TrailNode* node = nodes.front();
|
||||
nodes.pop_front();
|
||||
|
||||
if (predecessors.find(node) == predecessors.end())
|
||||
{
|
||||
switchEdge(edge);
|
||||
predecessors.insert(node);
|
||||
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
if (predecessors.find(edge->target) == predecessors.end())
|
||||
{
|
||||
nodes.push_back(edge->target);
|
||||
}
|
||||
}
|
||||
|
||||
for (TrailEdge* edge : node->incomingEdges)
|
||||
{
|
||||
if (predecessors.find(edge->origin) == predecessors.end())
|
||||
{
|
||||
loseEnds.insert(edge->origin);
|
||||
}
|
||||
}
|
||||
|
||||
if (!node->outgoingEdges.size())
|
||||
{
|
||||
deadEnds.insert(node);
|
||||
}
|
||||
}
|
||||
|
||||
makeAcyclicRecursive(node, std::set<TrailNode*>());
|
||||
while (!nodes.size() && (deadEnds.size() || loseEnds.size()) && predecessors.size() < m_allNodes.size())
|
||||
{
|
||||
if (deadEnds.size())
|
||||
{
|
||||
TrailNode* deadEnd = *deadEnds.begin();
|
||||
deadEnds.erase(deadEnds.begin());
|
||||
|
||||
for (TrailEdge* edge : deadEnd->incomingEdges)
|
||||
{
|
||||
if (predecessors.find(edge->origin) == predecessors.end())
|
||||
{
|
||||
nodes.push_back(edge->origin);
|
||||
switchEdge(edge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TrailNode* loseEnd = *loseEnds.begin();
|
||||
loseEnds.erase(loseEnds.begin());
|
||||
|
||||
if (predecessors.find(loseEnd) == predecessors.end())
|
||||
{
|
||||
for (TrailEdge* edge : loseEnd->outgoingEdges)
|
||||
{
|
||||
if (predecessors.find(edge->target) != predecessors.end())
|
||||
{
|
||||
nodes.push_back(loseEnd);
|
||||
switchEdge(edge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +160,9 @@ void TrailLayouter::makeAcyclicRecursive(TrailNode* node, std::set<TrailNode*> p
|
||||
|
||||
void TrailLayouter::assignLongestPathLevels()
|
||||
{
|
||||
std::set<TrailNode*> nodes(m_rootNodes.begin(), m_rootNodes.end());
|
||||
std::set<TrailNode*> nodes;
|
||||
nodes.insert(m_rootNode);
|
||||
|
||||
std::map<TrailNode*, TrailNode*> predecessorNodes;
|
||||
|
||||
int level = 0;
|
||||
@@ -160,16 +210,26 @@ void TrailLayouter::assignLongestPathLevels()
|
||||
|
||||
void TrailLayouter::assignRemainingLevels()
|
||||
{
|
||||
std::set<TrailNode*> nodes(m_rootNodes.begin(), m_rootNodes.end());
|
||||
std::multimap<int, TrailNode*> nodes;
|
||||
nodes.emplace(m_rootNode->level, m_rootNode);
|
||||
|
||||
std::set<TrailNode*> allNodes;
|
||||
allNodes.insert(m_rootNode);
|
||||
|
||||
while (nodes.size())
|
||||
{
|
||||
std::set<TrailNode*> newNodes;
|
||||
std::multimap<int, TrailNode*> newNodes;
|
||||
|
||||
for (TrailNode* node : nodes)
|
||||
for (std::pair<int, TrailNode*> p : nodes)
|
||||
{
|
||||
TrailNode* node = p.second;
|
||||
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
newNodes.insert(edge->target);
|
||||
if (allNodes.insert(edge->target).second)
|
||||
{
|
||||
newNodes.emplace(edge->target->level, edge->target);
|
||||
}
|
||||
}
|
||||
|
||||
if (node->level < 0)
|
||||
@@ -178,7 +238,21 @@ void TrailLayouter::assignRemainingLevels()
|
||||
|
||||
for (TrailEdge* edge : node->incomingEdges)
|
||||
{
|
||||
level = std::max(level, edge->origin->level + 1);
|
||||
if (edge->origin->level == -1)
|
||||
{
|
||||
if (allNodes.insert(edge->origin).second)
|
||||
{
|
||||
newNodes.emplace(edge->origin->level, edge->origin);
|
||||
}
|
||||
|
||||
level = node->level;
|
||||
newNodes.emplace(level, node);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
level = std::max(level, edge->origin->level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
node->level = level;
|
||||
@@ -376,61 +450,134 @@ void TrailLayouter::layout()
|
||||
// process layout before highest column
|
||||
for (size_t i = maxHeightIndex; i > 0; i--)
|
||||
{
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i - 1]);
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i - 1], false);
|
||||
}
|
||||
|
||||
// process layout after highest column
|
||||
for (size_t i = maxHeightIndex + 1; i < m_nodesPerCol.size(); i++)
|
||||
{
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i]);
|
||||
moveNodesToAveragePosition(m_nodesPerCol[i], true);
|
||||
}
|
||||
|
||||
// put into grid
|
||||
}
|
||||
|
||||
void TrailLayouter::moveNodesToAveragePosition(std::vector<TrailNode*> nodes)
|
||||
void TrailLayouter::moveNodesToAveragePosition(std::vector<TrailNode*> nodes, bool forward)
|
||||
{
|
||||
unsigned int yIdx = horizontalLayout() ? 1 : 0;
|
||||
|
||||
for (size_t k = 0; k < 2; k++)
|
||||
std::map<int, std::vector<TrailNode*>> averagePositions;
|
||||
for (TrailNode* node : nodes)
|
||||
{
|
||||
for (size_t j = 0; j < nodes.size(); j++)
|
||||
int sum = 0;
|
||||
int count = 0;
|
||||
|
||||
if ((forward && node->incomingEdges.size()) || (!forward && !node->outgoingEdges.size()))
|
||||
{
|
||||
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)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (TrailEdge* edge : node->outgoingEdges)
|
||||
{
|
||||
node->pos.setValue(yIdx, sum / count - node->size.getValue(yIdx) / 2);
|
||||
sum += edge->target->pos.getValue(yIdx) + edge->target->size.getValue(yIdx) / 2;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (count)
|
||||
{
|
||||
averagePositions[sum / count].push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
if (!averagePositions.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int averagePosition = 0;
|
||||
for (std::pair<int, std::vector<TrailNode*>> p : averagePositions)
|
||||
{
|
||||
averagePosition += p.first;
|
||||
}
|
||||
averagePosition /= averagePositions.size();
|
||||
|
||||
|
||||
std::multimap<int, int> distanceFromAveragePosition;
|
||||
for (std::pair<int, std::vector<TrailNode*>> p : averagePositions)
|
||||
{
|
||||
distanceFromAveragePosition.emplace(std::abs(averagePosition - p.first), p.first);
|
||||
}
|
||||
|
||||
int currentTop = averagePosition;
|
||||
int currentBottom = averagePosition;
|
||||
|
||||
for (std::pair<int, int> p : distanceFromAveragePosition)
|
||||
{
|
||||
int groupAveragePosition = p.second;
|
||||
std::vector<TrailNode*> nodeGroup = averagePositions.find(groupAveragePosition)->second;
|
||||
|
||||
int size = -30;
|
||||
for (TrailNode* node : nodeGroup)
|
||||
{
|
||||
size += node->size.getValue(yIdx) + 30;
|
||||
}
|
||||
|
||||
int top = groupAveragePosition - size / 2;
|
||||
if (currentTop != currentBottom)
|
||||
{
|
||||
if (top < currentTop)
|
||||
{
|
||||
if (top + size + 30 > currentTop)
|
||||
{
|
||||
node->pos.setValue(yIdx, below->pos.getValue(yIdx) - 30 - node->size.getValue(yIdx));
|
||||
top = currentTop - 30 - size;
|
||||
}
|
||||
}
|
||||
else if (top > currentBottom)
|
||||
{
|
||||
if (top - 30 < currentBottom)
|
||||
{
|
||||
top = currentBottom + 30;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((currentTop + currentBottom) / 2 > top + size / 2)
|
||||
{
|
||||
top = currentTop - 30 - size;
|
||||
}
|
||||
else
|
||||
{
|
||||
top = currentBottom + 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int y = top;
|
||||
|
||||
for (TrailNode* node : nodeGroup)
|
||||
{
|
||||
node->pos.setValue(yIdx, y);
|
||||
y += node->size.getValue(yIdx) + 30;
|
||||
}
|
||||
|
||||
if (currentTop == currentBottom)
|
||||
{
|
||||
currentTop = top;
|
||||
currentBottom = top + size;
|
||||
}
|
||||
else if (top < currentTop)
|
||||
{
|
||||
currentTop = top;
|
||||
}
|
||||
else if (top + size > currentBottom)
|
||||
{
|
||||
currentBottom = top + size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,15 +615,21 @@ 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;
|
||||
if (node->id)
|
||||
{
|
||||
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;
|
||||
if (edge->origin->id || edge->target->id)
|
||||
{
|
||||
std::cout << edge->id << "\t" << edge->origin->name << "\t" << edge->target->name << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -498,9 +651,9 @@ void TrailLayouter::addNode(const std::shared_ptr<DummyNode>& dummyNode)
|
||||
m_nodesById.emplace(node->id, node.get());
|
||||
}
|
||||
|
||||
if (dummyNode->hasActiveSubNode())
|
||||
if (!m_rootNode && dummyNode->hasActiveSubNode())
|
||||
{
|
||||
m_rootNodes.push_back(node.get());
|
||||
m_rootNode = node.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& dummyEdges,
|
||||
const std::map<Id, Id>& topLevelAncestorIds);
|
||||
|
||||
void makeAcyclic();
|
||||
void removeDeadEnds();
|
||||
void makeAcyclicRecursive(TrailNode* node, std::set<TrailNode*> predecessors);
|
||||
|
||||
void assignLongestPathLevels();
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
void reduceEdgeCrossings();
|
||||
|
||||
void layout();
|
||||
void moveNodesToAveragePosition(std::vector<TrailNode*> nodes);
|
||||
void moveNodesToAveragePosition(std::vector<TrailNode*> nodes, bool forward);
|
||||
void retrievePositions(const std::map<Id, Id>& topLevelAncestorIds);
|
||||
|
||||
void print();
|
||||
@@ -91,7 +91,7 @@ private:
|
||||
std::vector<std::shared_ptr<TrailEdge>> m_allEdges;
|
||||
|
||||
std::map<Id, TrailNode*> m_nodesById;
|
||||
std::vector<TrailNode*> m_rootNodes;
|
||||
TrailNode* m_rootNode;
|
||||
|
||||
std::vector<std::vector<TrailNode*>> m_nodesPerCol;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "utility/messaging/type/MessageActivateTrail.h"
|
||||
#include "utility/messaging/type/MessageDeactivateEdge.h"
|
||||
#include "utility/messaging/type/MessageScrollGraph.h"
|
||||
#include "utility/messaging/type/MessageStatus.h"
|
||||
#include "utility/ResourcePaths.h"
|
||||
|
||||
#include "qt/graphics/QtGraphicsView.h"
|
||||
@@ -232,16 +233,23 @@ void QtGraphView::finishedTransition()
|
||||
|
||||
void QtGraphView::clickedInEmptySpace()
|
||||
{
|
||||
size_t activeEdgeCount = 0;
|
||||
std::vector<std::shared_ptr<QtGraphEdge>> activeEdges;
|
||||
for (std::shared_ptr<QtGraphEdge> edge : m_oldEdges)
|
||||
{
|
||||
if (edge->getIsActive())
|
||||
{
|
||||
activeEdgeCount++;
|
||||
activeEdges.push_back(edge);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeEdgeCount == 1)
|
||||
if (m_graph && m_graph->getTrailMode() != Graph::TRAIL_NONE)
|
||||
{
|
||||
for (std::shared_ptr<QtGraphEdge> edge : activeEdges)
|
||||
{
|
||||
edge->setIsActive(false);
|
||||
}
|
||||
}
|
||||
else if (activeEdges.size() == 1)
|
||||
{
|
||||
MessageDeactivateEdge(false).dispatch();
|
||||
}
|
||||
@@ -519,6 +527,11 @@ void QtGraphView::switchToNewGraphData()
|
||||
view->update();
|
||||
|
||||
updateTrailButtons();
|
||||
|
||||
if (m_oldGraph && m_oldGraph->getTrailMode() != Graph::TRAIL_NONE)
|
||||
{
|
||||
MessageStatus("Finished graph display").dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
QtGraphicsView* QtGraphView::getView() const
|
||||
@@ -554,6 +567,8 @@ void QtGraphView::doRebuildGraph(
|
||||
|
||||
QGraphicsView* view = getView();
|
||||
|
||||
|
||||
// create nodes
|
||||
size_t activeNodeCount = 0;
|
||||
for (unsigned int i = 0; i < nodes.size(); i++)
|
||||
{
|
||||
@@ -574,31 +589,42 @@ void QtGraphView::doRebuildGraph(
|
||||
}
|
||||
}
|
||||
|
||||
Graph::TrailMode trailMode = m_graph ? m_graph->getTrailMode() : Graph::TRAIL_NONE;
|
||||
if (trailMode == Graph::TRAIL_NONE)
|
||||
{
|
||||
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);
|
||||
}
|
||||
// move graph to center
|
||||
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();
|
||||
|
||||
std::set<Id> visibleEdgeIds;
|
||||
for (std::shared_ptr<DummyEdge> edge : edges)
|
||||
{
|
||||
for (size_t i = 0; i < edge->path.size(); i++)
|
||||
{
|
||||
edge->path[i].x = edge->path[i].x - offset.x();
|
||||
edge->path[i].z = edge->path[i].z - offset.x();
|
||||
edge->path[i].y = edge->path[i].y - offset.y();
|
||||
edge->path[i].w = edge->path[i].w - offset.y();
|
||||
}
|
||||
}
|
||||
|
||||
// create edges
|
||||
Graph::TrailMode trailMode = m_graph ? m_graph->getTrailMode() : Graph::TRAIL_NONE;
|
||||
std::set<Id> visibleEdgeIds;
|
||||
for (const std::shared_ptr<DummyEdge> edge : edges)
|
||||
{
|
||||
if (!edge->data || !edge->data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
createEdge(view, edge.get(), &visibleEdgeIds, trailMode);
|
||||
}
|
||||
}
|
||||
for (std::shared_ptr<DummyEdge> edge : edges)
|
||||
for (const std::shared_ptr<DummyEdge> edge : edges)
|
||||
{
|
||||
if (edge->data && edge->data->isType(Edge::EDGE_AGGREGATION))
|
||||
{
|
||||
@@ -850,7 +876,7 @@ QRectF QtGraphView::getSceneRect(const std::list<std::shared_ptr<QtGraphNode>>&
|
||||
sceneRect |= rect;
|
||||
}
|
||||
|
||||
return sceneRect.adjusted(-25, -25, 25, 25).translated(m_sceneRectOffset);
|
||||
return sceneRect.adjusted(-75, -75, 75, 75).translated(m_sceneRectOffset);
|
||||
}
|
||||
|
||||
void QtGraphView::compareNodesRecursive(
|
||||
@@ -1081,11 +1107,16 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "qt/view/graphElements/QtGraphEdge.h"
|
||||
|
||||
#include <QGraphicsSceneEvent>
|
||||
#include <QGraphicsItemGroup>
|
||||
#include <QTimer>
|
||||
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
@@ -39,7 +40,8 @@ QtGraphEdge::QtGraphEdge(
|
||||
, m_isHorizontalTrail(false)
|
||||
, m_mousePos(0.0f, 0.0f)
|
||||
, m_mouseMoved(false)
|
||||
, m_willDispatchMessageFocusIn(false)
|
||||
, m_willFocusIn(false)
|
||||
, m_ignoreFocusIn(false)
|
||||
{
|
||||
if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD)
|
||||
{
|
||||
@@ -104,17 +106,10 @@ void QtGraphEdge::updateLine()
|
||||
|
||||
if (m_isTrailEdge)
|
||||
{
|
||||
if (!m_child)
|
||||
for (QGraphicsItem* item : childItems())
|
||||
{
|
||||
m_child = new QGraphicsLineItem(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (QGraphicsItem* item : m_child->childItems())
|
||||
{
|
||||
item->hide();
|
||||
item->setParentItem(nullptr);
|
||||
}
|
||||
item->hide();
|
||||
item->setParentItem(nullptr);
|
||||
}
|
||||
|
||||
style.originOffset.y() = 0;
|
||||
@@ -128,13 +123,13 @@ void QtGraphEdge::updateLine()
|
||||
|
||||
for (const Vec4i& rect : m_path)
|
||||
{
|
||||
QtLineItemBezier* bezier = new QtLineItemBezier(m_child);
|
||||
QtLineItemBezier* bezier = new QtLineItemBezier(this);
|
||||
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);
|
||||
QtLineItemStraight* line = new QtLineItemStraight(this);
|
||||
line->setToolTip(toolTip);
|
||||
if (route == QtLineItemBase::ROUTE_HORIZONTAL)
|
||||
{
|
||||
@@ -151,7 +146,7 @@ void QtGraphEdge::updateLine()
|
||||
|
||||
bool showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE;
|
||||
|
||||
QtLineItemBezier* bezier = new QtLineItemBezier(m_child);
|
||||
QtLineItemBezier* bezier = new QtLineItemBezier(this);
|
||||
bezier->updateLine(
|
||||
ownerRect, target->getBoundingRect(), ownerParentRect, target->getParentBoundingRect(),
|
||||
style, m_weight, showArrow);
|
||||
@@ -312,43 +307,47 @@ void QtGraphEdge::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
||||
|
||||
void QtGraphEdge::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
if (!getData())
|
||||
if (!m_ignoreFocusIn)
|
||||
{
|
||||
focusIn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_willDispatchMessageFocusIn)
|
||||
{
|
||||
QTimer::singleShot(50, this, SLOT(dispatchMessageFocusIn()));
|
||||
m_willDispatchMessageFocusIn = true;
|
||||
QTimer::singleShot(50, this, SLOT(doFocusIn()));
|
||||
m_ignoreFocusIn = true;
|
||||
m_willFocusIn = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
|
||||
{
|
||||
m_willFocusIn = false;
|
||||
QTimer::singleShot(100, this, SLOT(doFocusOut()));
|
||||
|
||||
if (!getData())
|
||||
{
|
||||
focusOut();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_willDispatchMessageFocusIn)
|
||||
{
|
||||
m_willDispatchMessageFocusIn = false;
|
||||
return;
|
||||
}
|
||||
|
||||
MessageFocusOut(std::vector<Id>(1, getData()->getId())).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphEdge::dispatchMessageFocusIn()
|
||||
void QtGraphEdge::doFocusIn()
|
||||
{
|
||||
if (m_willDispatchMessageFocusIn)
|
||||
if (!m_willFocusIn)
|
||||
{
|
||||
m_willDispatchMessageFocusIn = false;
|
||||
MessageFocusIn(std::vector<Id>(1, getData()->getId())).dispatch();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getData() || isTrailEdge())
|
||||
{
|
||||
focusIn();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageFocusIn(std::vector<Id>(1, getData()->getId())).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphEdge::doFocusOut()
|
||||
{
|
||||
m_ignoreFocusIn = false;
|
||||
}
|
||||
|
||||
void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction)
|
||||
|
||||
@@ -60,7 +60,8 @@ protected:
|
||||
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
|
||||
|
||||
private slots:
|
||||
void dispatchMessageFocusIn();
|
||||
void doFocusIn();
|
||||
void doFocusOut();
|
||||
|
||||
private:
|
||||
const Edge* m_data;
|
||||
@@ -68,7 +69,7 @@ private:
|
||||
std::weak_ptr<QtGraphNode> m_owner;
|
||||
std::weak_ptr<QtGraphNode> m_target;
|
||||
|
||||
QGraphicsLineItem* m_child;
|
||||
QGraphicsItem* m_child;
|
||||
|
||||
bool m_isActive;
|
||||
bool m_fromActive;
|
||||
@@ -87,7 +88,8 @@ private:
|
||||
Vec2i m_mousePos;
|
||||
bool m_mouseMoved;
|
||||
|
||||
bool m_willDispatchMessageFocusIn;
|
||||
bool m_willFocusIn;
|
||||
bool m_ignoreFocusIn;
|
||||
};
|
||||
|
||||
#endif // QT_GRAPH_EDGE_H
|
||||
|
||||
Reference in New Issue
Block a user