ui: redesigned graph overview and use new list layouting within overview bundles
* overview bundles imitate style of bundled node type * removed bundle for built-in types * show contents of overview bundle in vertical list layouted with new ListLayouter * nodes starting with the same letter are grouped with the letter displayed above * pressing the letter key will scroll to the group * also use this list layouting for namespace & package contents * renamed BucketGrid to BucketLayouter * moved used functions of GraphPostProcessor to GraphViewStyle and removed GraphPostProcessor fortune cookie message = Don't be afraid to take that big step
This commit is contained in:
@@ -378,6 +378,14 @@
|
||||
<normal>#FFFFFF</normal>
|
||||
</text>
|
||||
</qualifier>
|
||||
<text>
|
||||
<fill>
|
||||
<normal>transparent</normal>
|
||||
</fill>
|
||||
<text>
|
||||
<normal>white</normal>
|
||||
</text>
|
||||
</text>
|
||||
</node>
|
||||
<edge>
|
||||
<default>
|
||||
|
||||
@@ -349,6 +349,14 @@
|
||||
<normal>black</normal>
|
||||
</text>
|
||||
</qualifier>
|
||||
<text>
|
||||
<fill>
|
||||
<normal>transparent</normal>
|
||||
</fill>
|
||||
<text>
|
||||
<normal>black</normal>
|
||||
</text>
|
||||
</text>
|
||||
</node>
|
||||
<edge>
|
||||
<default>
|
||||
|
||||
@@ -352,6 +352,14 @@
|
||||
<normal>#F7F7F7</normal>
|
||||
</text>
|
||||
</qualifier>
|
||||
<text>
|
||||
<fill>
|
||||
<normal>transparent</normal>
|
||||
</fill>
|
||||
<text>
|
||||
<normal>white</normal>
|
||||
</text>
|
||||
</text>
|
||||
</node>
|
||||
<edge>
|
||||
<default>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
add_files(
|
||||
LIB_FILES
|
||||
|
||||
component/controller/helper/BucketGrid.cpp
|
||||
component/controller/helper/BucketGrid.h
|
||||
component/controller/helper/BucketLayouter.cpp
|
||||
component/controller/helper/BucketLayouter.h
|
||||
component/controller/helper/DummyEdge.h
|
||||
component/controller/helper/DummyNode.h
|
||||
component/controller/helper/GraphPostprocessor.cpp
|
||||
component/controller/helper/GraphPostprocessor.h
|
||||
component/controller/helper/ListLayouter.cpp
|
||||
component/controller/helper/ListLayouter.h
|
||||
component/controller/helper/NetworkProtocolHelper.cpp
|
||||
component/controller/helper/NetworkProtocolHelper.h
|
||||
component/controller/helper/SnippetMerger.cpp
|
||||
@@ -362,7 +362,7 @@ add_files(
|
||||
|
||||
utility/text/TextAccess.cpp
|
||||
utility/text/TextAccess.h
|
||||
|
||||
|
||||
utility/ApplicationArchitectureType.h
|
||||
utility/AppPath.cpp
|
||||
utility/AppPath.h
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
#include "component/controller/helper/BucketGrid.h"
|
||||
#include "component/controller/helper/BucketLayouter.h"
|
||||
#include "component/controller/helper/ListLayouter.h"
|
||||
#include "component/view/GraphView.h"
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "data/access/StorageAccess.h"
|
||||
@@ -72,20 +73,30 @@ void GraphController::handleMessage(MessageActivateTokens* message)
|
||||
|
||||
std::vector<Id> tokenIds = utility::concat(m_activeNodeIds, m_activeEdgeIds);
|
||||
|
||||
std::shared_ptr<Graph> graph = m_storageAccess->getGraphForActiveTokenIds(tokenIds);
|
||||
bool isNamespace = false;
|
||||
std::shared_ptr<Graph> graph = m_storageAccess->getGraphForActiveTokenIds(tokenIds, &isNamespace);
|
||||
|
||||
createDummyGraphForTokenIdsAndSetActiveAndVisibility(tokenIds, graph);
|
||||
|
||||
if (m_activeNodeIds.size() == 1)
|
||||
if (isNamespace)
|
||||
{
|
||||
bundleNodes();
|
||||
addCharacterIndex();
|
||||
layoutNesting();
|
||||
layoutList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_activeNodeIds.size() == 1)
|
||||
{
|
||||
bundleNodes();
|
||||
}
|
||||
|
||||
layoutNesting();
|
||||
layoutGraph(true);
|
||||
assignBundleIds();
|
||||
}
|
||||
|
||||
layoutNesting();
|
||||
layoutGraph(true);
|
||||
assignBundleIds();
|
||||
|
||||
buildGraph(message, true);
|
||||
buildGraph(message, !isNamespace, true, isNamespace);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageFlushUpdates* message)
|
||||
@@ -115,8 +126,16 @@ void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
|
||||
DummyNode* node = m_dummyNodes[i].get();
|
||||
if (node->isBundleNode() && node->tokenId == message->bundleId)
|
||||
{
|
||||
m_dummyNodes.insert(m_dummyNodes.begin() + i + 1, node->bundledNodes.begin(), node->bundledNodes.end());
|
||||
m_dummyNodes.erase(m_dummyNodes.begin() + i);
|
||||
if (message->removeOtherNodes)
|
||||
{
|
||||
std::vector<std::shared_ptr<DummyNode>> nodes(node->bundledNodes.begin(), node->bundledNodes.end());
|
||||
m_dummyNodes = nodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dummyNodes.insert(m_dummyNodes.begin() + i + 1, node->bundledNodes.begin(), node->bundledNodes.end());
|
||||
m_dummyNodes.erase(m_dummyNodes.begin() + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -134,10 +153,19 @@ void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
|
||||
std::vector<Id> tokenIds = utility::concat(m_activeNodeIds, m_activeEdgeIds);
|
||||
setActiveAndVisibility(tokenIds);
|
||||
|
||||
layoutNesting();
|
||||
layoutGraph();
|
||||
if (message->layoutToList)
|
||||
{
|
||||
addCharacterIndex();
|
||||
layoutNesting();
|
||||
layoutList();
|
||||
}
|
||||
else
|
||||
{
|
||||
layoutNesting();
|
||||
layoutGraph();
|
||||
}
|
||||
|
||||
buildGraph(message, false);
|
||||
buildGraph(message, false, true, message->layoutToList);
|
||||
}
|
||||
|
||||
void GraphController::handleMessage(MessageGraphNodeExpand* message)
|
||||
@@ -942,6 +970,7 @@ void GraphController::bundleByType(
|
||||
|
||||
if (bundleNode)
|
||||
{
|
||||
bundleNode->bundledNodeType = type;
|
||||
m_dummyNodes.push_back(bundleNode);
|
||||
}
|
||||
}
|
||||
@@ -959,9 +988,13 @@ void GraphController::bundleNodesByType()
|
||||
nodes.push_back(oldNodes[i]);
|
||||
}
|
||||
|
||||
bundleByType(nodes, Node::NODE_FILE, "Files");
|
||||
bundleByType(nodes, Node::NODE_MACRO, "Macros");
|
||||
|
||||
bundleByType(nodes, Node::NODE_NAMESPACE, "Namespaces");
|
||||
bundleByType(nodes, Node::NODE_PACKAGE, "Packages");
|
||||
bundleByType(nodes, Node::NODE_BUILTIN_TYPE, "Built-in Types");
|
||||
|
||||
// bundleByType(nodes, Node::NODE_BUILTIN_TYPE, "Built-in Types");
|
||||
bundleByType(nodes, Node::NODE_CLASS, "Classes");
|
||||
bundleByType(nodes, Node::NODE_INTERFACE, "Interfaces");
|
||||
bundleByType(nodes, Node::NODE_STRUCT, "Structs");
|
||||
@@ -973,9 +1006,6 @@ void GraphController::bundleNodesByType()
|
||||
bundleByType(nodes, Node::NODE_TYPEDEF, "Typedefs");
|
||||
bundleByType(nodes, Node::NODE_ENUM, "Enums");
|
||||
|
||||
bundleByType(nodes, Node::NODE_FILE, "Files");
|
||||
bundleByType(nodes, Node::NODE_MACRO, "Macros");
|
||||
|
||||
// // should never be visible
|
||||
|
||||
bundleByType(nodes, Node::NODE_METHOD, "Methods");
|
||||
@@ -1022,6 +1052,7 @@ void GraphController::bundleNodesByType()
|
||||
|
||||
if (anonymousBundle)
|
||||
{
|
||||
anonymousBundle->bundledNodeType = Node::NODE_NAMESPACE;
|
||||
bundleNode->bundledNodeCount = bundleNode->getBundledNodeCount() + anonymousBundle->getBundledNodeCount();
|
||||
bundleNode->bundledNodes.insert(anonymousBundle);
|
||||
}
|
||||
@@ -1029,6 +1060,43 @@ void GraphController::bundleNodesByType()
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::addCharacterIndex()
|
||||
{
|
||||
// Remove index characters from last time
|
||||
DummyNode::BundledNodesSet newNodes;
|
||||
for (const std::shared_ptr<DummyNode> node : m_dummyNodes)
|
||||
{
|
||||
if (!node->isTextNode())
|
||||
{
|
||||
newNodes.insert(node);
|
||||
}
|
||||
}
|
||||
m_dummyNodes.clear();
|
||||
m_dummyNodes.insert(m_dummyNodes.end(), newNodes.begin(), newNodes.end());
|
||||
|
||||
// Add index characters
|
||||
char character = 0;
|
||||
for (size_t i = 0; i < m_dummyNodes.size(); i++)
|
||||
{
|
||||
if (!m_dummyNodes[i]->name.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (toupper(m_dummyNodes[i]->name[0]) != character)
|
||||
{
|
||||
character = toupper(m_dummyNodes[i]->name[0]);
|
||||
|
||||
std::shared_ptr<DummyNode> textNode = std::make_shared<DummyNode>();
|
||||
textNode->textNode = true;
|
||||
textNode->name = character;
|
||||
textNode->visible = true;
|
||||
|
||||
m_dummyNodes.insert(m_dummyNodes.begin() + i, textNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::layoutNesting()
|
||||
{
|
||||
TRACE();
|
||||
@@ -1067,12 +1135,23 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
|
||||
}
|
||||
else if (node->isBundleNode())
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsOfBundleNode();
|
||||
if (node->bundledNodeType != Node::NODE_UNDEFINED)
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsForNodeType(node->bundledNodeType, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsOfBundleNode();
|
||||
}
|
||||
}
|
||||
else if (node->isQualifierNode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (node->isTextNode())
|
||||
{
|
||||
margins = GraphViewStyle::getMarginsOfTextNode();
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
int x = 0;
|
||||
@@ -1277,7 +1356,7 @@ void GraphController::layoutGraph(bool getSortedNodes)
|
||||
{
|
||||
TRACE();
|
||||
|
||||
BucketGrid grid(getView()->getViewSize());
|
||||
BucketLayouter grid(getView()->getViewSize());
|
||||
grid.createBuckets(m_dummyNodes, m_dummyEdges);
|
||||
grid.layoutBuckets();
|
||||
|
||||
@@ -1287,6 +1366,14 @@ void GraphController::layoutGraph(bool getSortedNodes)
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::layoutList()
|
||||
{
|
||||
TRACE();
|
||||
|
||||
ListLayouter layouter(getView()->getViewSize());
|
||||
layouter.layoutList(m_dummyNodes);
|
||||
}
|
||||
|
||||
void GraphController::assignBundleIds()
|
||||
{
|
||||
Id bundleId = 0;
|
||||
@@ -1307,13 +1394,15 @@ DummyNode* GraphController::getDummyGraphNodeById(Id tokenId) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GraphController::buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition)
|
||||
void GraphController::buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop)
|
||||
{
|
||||
if (!message->isReplayed())
|
||||
{
|
||||
GraphView::GraphParams params;
|
||||
params.centerActiveNode = centerActiveNode;
|
||||
params.animatedTransition = animatedTransition;
|
||||
params.scrollToTop = scrollToTop;
|
||||
params.isIndexedList = scrollToTop;
|
||||
|
||||
getView()->rebuildGraph(m_graph, m_dummyNodes, m_dummyEdges, params);
|
||||
}
|
||||
@@ -1591,7 +1680,7 @@ void GraphController::handleMessage(MessageColorSchemeTest* message)
|
||||
node->layoutBucket.y = (i / 6) + 1;
|
||||
}
|
||||
|
||||
BucketGrid grid(Vec2i(0, 0));
|
||||
BucketLayouter grid(Vec2i(0, 0));
|
||||
grid.createBuckets(m_dummyNodes, std::vector<std::shared_ptr<DummyEdge>>());
|
||||
grid.layoutBuckets();
|
||||
|
||||
|
||||
@@ -83,22 +83,28 @@ private:
|
||||
void setNodeVisibilityRecursiveTopDown(DummyNode* node, bool parentExpanded) const;
|
||||
|
||||
void bundleNodes();
|
||||
void bundleNodesAndEdgesMatching(std::function<bool(const DummyNode::BundleInfo&, const Node*)> matcher, size_t count, const std::string& name);
|
||||
std::shared_ptr<DummyNode> bundleNodesMatching(std::list<std::shared_ptr<DummyNode>>& nodes, std::function<bool(const DummyNode*)> matcher, const std::string& name);
|
||||
void bundleNodesAndEdgesMatching(
|
||||
std::function<bool(const DummyNode::BundleInfo&, const Node*)> matcher, size_t count, const std::string& name);
|
||||
std::shared_ptr<DummyNode> bundleNodesMatching(
|
||||
std::list<std::shared_ptr<DummyNode>>& nodes, std::function<bool(const DummyNode*)> matcher, const std::string& name);
|
||||
void bundleByType(std::list<std::shared_ptr<DummyNode>>& nodes, Node::NodeType type, const std::string& name);
|
||||
void bundleNodesByType();
|
||||
|
||||
void addCharacterIndex();
|
||||
|
||||
void layoutNesting();
|
||||
void layoutNestingRecursive(DummyNode* node) const;
|
||||
void addExpandToggleNode(DummyNode* node) const;
|
||||
void layoutToGrid(DummyNode* node) const;
|
||||
|
||||
void layoutGraph(bool getSortedNodes = false);
|
||||
void layoutList();
|
||||
|
||||
void assignBundleIds();
|
||||
|
||||
DummyNode* getDummyGraphNodeById(Id tokenId) const;
|
||||
|
||||
void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition = true);
|
||||
void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition = true, bool scrollToTop = false);
|
||||
|
||||
void forEachDummyNodeRecursive(std::function<void(DummyNode*)> func);
|
||||
void forEachDummyEdge(std::function<void(DummyEdge*)> func);
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
#include "component/controller/helper/BucketGrid.h"
|
||||
#include "component/controller/helper/BucketLayouter.h"
|
||||
|
||||
#include "component/controller/helper/DummyEdge.h"
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
@@ -101,7 +101,7 @@ void Bucket::layout(int x, int y, int width, int height)
|
||||
}
|
||||
|
||||
|
||||
BucketGrid::BucketGrid(Vec2i viewSize)
|
||||
BucketLayouter::BucketLayouter(Vec2i viewSize)
|
||||
: m_viewSize(viewSize)
|
||||
, m_i1(0)
|
||||
, m_j1(0)
|
||||
@@ -111,7 +111,7 @@ BucketGrid::BucketGrid(Vec2i viewSize)
|
||||
m_buckets[0][0] = Bucket(0, 0);
|
||||
}
|
||||
|
||||
void BucketGrid::createBuckets(
|
||||
void BucketLayouter::createBuckets(
|
||||
std::vector<std::shared_ptr<DummyNode>>& nodes, const std::vector<std::shared_ptr<DummyEdge>>& edges
|
||||
){
|
||||
if (!nodes.size())
|
||||
@@ -194,7 +194,7 @@ void BucketGrid::createBuckets(
|
||||
}
|
||||
}
|
||||
|
||||
void BucketGrid::layoutBuckets()
|
||||
void BucketLayouter::layoutBuckets()
|
||||
{
|
||||
std::map<int, int> widths;
|
||||
std::map<int, int> heights;
|
||||
@@ -245,7 +245,7 @@ void BucketGrid::layoutBuckets()
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<DummyNode>> BucketGrid::getSortedNodes()
|
||||
std::vector<std::shared_ptr<DummyNode>> BucketLayouter::getSortedNodes()
|
||||
{
|
||||
std::vector<std::shared_ptr<DummyNode>> sortedNodes;
|
||||
|
||||
@@ -261,7 +261,7 @@ std::vector<std::shared_ptr<DummyNode>> BucketGrid::getSortedNodes()
|
||||
return sortedNodes;
|
||||
}
|
||||
|
||||
std::shared_ptr<DummyNode> BucketGrid::findTopMostDummyNodeRecursive(
|
||||
std::shared_ptr<DummyNode> BucketLayouter::findTopMostDummyNodeRecursive(
|
||||
std::vector<std::shared_ptr<DummyNode>>& nodes, Id tokenId, std::shared_ptr<DummyNode> top
|
||||
){
|
||||
for (std::shared_ptr<DummyNode> node : nodes)
|
||||
@@ -283,13 +283,13 @@ std::shared_ptr<DummyNode> BucketGrid::findTopMostDummyNodeRecursive(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BucketGrid::addNode(std::shared_ptr<DummyNode> node)
|
||||
void BucketLayouter::addNode(std::shared_ptr<DummyNode> node)
|
||||
{
|
||||
Bucket* bucket = getBucket(node->layoutBucket.x, node->layoutBucket.y);
|
||||
bucket->addNode(node);
|
||||
}
|
||||
|
||||
bool BucketGrid::addNode(std::shared_ptr<DummyNode> owner, std::shared_ptr<DummyNode> target, bool horizontal)
|
||||
bool BucketLayouter::addNode(std::shared_ptr<DummyNode> owner, std::shared_ptr<DummyNode> target, bool horizontal)
|
||||
{
|
||||
Bucket* ownerBucket = getBucket(owner);
|
||||
Bucket* targetBucket = getBucket(target);
|
||||
@@ -324,7 +324,7 @@ bool BucketGrid::addNode(std::shared_ptr<DummyNode> owner, std::shared_ptr<Dummy
|
||||
return true;
|
||||
}
|
||||
|
||||
Bucket* BucketGrid::getBucket(int i, int j)
|
||||
Bucket* BucketLayouter::getBucket(int i, int j)
|
||||
{
|
||||
bool newColumn = false;
|
||||
bool newRow = false;
|
||||
@@ -375,7 +375,7 @@ Bucket* BucketGrid::getBucket(int i, int j)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Bucket* BucketGrid::getBucket(std::shared_ptr<DummyNode> node)
|
||||
Bucket* BucketLayouter::getBucket(std::shared_ptr<DummyNode> node)
|
||||
{
|
||||
for (int j = m_j1; j <= m_j2; j++)
|
||||
{
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
#ifndef BUCKET_GRID_H
|
||||
#define BUCKET_GRID_H
|
||||
#ifndef BUCKET_LAYOUTER_H
|
||||
#define BUCKET_LAYOUTER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -38,10 +38,10 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class BucketGrid
|
||||
class BucketLayouter
|
||||
{
|
||||
public:
|
||||
BucketGrid(Vec2i viewSize);
|
||||
BucketLayouter(Vec2i viewSize);
|
||||
void createBuckets(
|
||||
std::vector<std::shared_ptr<DummyNode>>& nodes,
|
||||
const std::vector<std::shared_ptr<DummyEdge>>& edges);
|
||||
@@ -68,4 +68,4 @@ private:
|
||||
int m_j2;
|
||||
};
|
||||
|
||||
#endif // BUCKET_GRID_H
|
||||
#endif // BUCKET_LAYOUTER_H
|
||||
@@ -6,11 +6,10 @@
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
#include "data/graph/Node.h"
|
||||
#include "data/graph/token_component/TokenComponentAccess.h"
|
||||
#include "data/name/NameHierarchy.h"
|
||||
|
||||
class Node;
|
||||
|
||||
// temporary data structure for (visual) graph creation process
|
||||
struct DummyNode
|
||||
{
|
||||
@@ -68,6 +67,8 @@ public:
|
||||
, bundleId(0)
|
||||
, layoutBucket(0, 0)
|
||||
, bundledNodeCount(0)
|
||||
, bundledNodeType(Node::NODE_UNDEFINED)
|
||||
, textNode(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ public:
|
||||
|
||||
bool isExpandToggleNode() const
|
||||
{
|
||||
return !isGraphNode() && !isAccessNode() && !isBundleNode() && !isQualifierNode();
|
||||
return !isGraphNode() && !isAccessNode() && !isBundleNode() && !isQualifierNode() && !isTextNode();
|
||||
}
|
||||
|
||||
bool isBundleNode() const
|
||||
@@ -96,6 +97,11 @@ public:
|
||||
return qualifierName.size();
|
||||
}
|
||||
|
||||
bool isTextNode() const
|
||||
{
|
||||
return textNode;
|
||||
}
|
||||
|
||||
bool isExpanded() const
|
||||
{
|
||||
return expanded;
|
||||
@@ -269,9 +275,13 @@ public:
|
||||
// BundleNode
|
||||
BundledNodesSet bundledNodes;
|
||||
size_t bundledNodeCount;
|
||||
Node::NodeType bundledNodeType;
|
||||
|
||||
// QualifierNode
|
||||
NameHierarchy qualifierName;
|
||||
|
||||
// TextNode
|
||||
bool textNode;
|
||||
};
|
||||
|
||||
#endif // DUMMY_NODE_H
|
||||
|
||||
@@ -1,467 +0,0 @@
|
||||
#include "component/controller/helper/GraphPostprocessor.h"
|
||||
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
|
||||
unsigned int GraphPostprocessor::s_cellWidth = GraphViewStyle::s_gridCellSize;
|
||||
unsigned int GraphPostprocessor::s_cellHeight = GraphViewStyle::s_gridCellSize;
|
||||
unsigned int GraphPostprocessor::s_cellPadding = GraphViewStyle::s_gridCellPadding;
|
||||
|
||||
void GraphPostprocessor::doPostprocessing(std::vector<DummyNode>& nodes)
|
||||
{
|
||||
unsigned int atomarGridWidth = s_cellWidth;
|
||||
unsigned int atomarGridHeight = s_cellHeight;
|
||||
|
||||
if (nodes.size() < 2)
|
||||
{
|
||||
LOG_INFO_STREAM(<< "Skipping postprocessing, need at least 2 nodes but got " << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// determine center of mass (CoD) which is used to get outliers closer to the rest of the graph
|
||||
int divisorWidth = 999999;
|
||||
int divisorHeight = 999999;
|
||||
int maxNodeWidth = 0;
|
||||
int maxNodeHeight = 0;
|
||||
Vec2i centerOfMass(0, 0);
|
||||
float totalMass = 0.0f;
|
||||
|
||||
for (const DummyNode& node : nodes)
|
||||
{
|
||||
if (node.size.x < divisorWidth)
|
||||
{
|
||||
divisorWidth = node.size.x;
|
||||
}
|
||||
|
||||
if (node.size.y < divisorHeight)
|
||||
{
|
||||
divisorHeight = node.size.y;
|
||||
}
|
||||
|
||||
if (node.size.x > maxNodeWidth)
|
||||
{
|
||||
maxNodeWidth = node.size.x;
|
||||
}
|
||||
else if (node.size.y > maxNodeHeight)
|
||||
{
|
||||
maxNodeHeight = node.size.y;
|
||||
}
|
||||
|
||||
float nodeMass = node.size.x * node.size.y;
|
||||
centerOfMass += node.position * nodeMass;
|
||||
totalMass += nodeMass;
|
||||
}
|
||||
|
||||
centerOfMass /= totalMass;
|
||||
|
||||
divisorWidth = (int)atomarGridWidth + s_cellPadding; //std::min(divisor, (int)atomarGridSize);
|
||||
divisorHeight = (int)atomarGridHeight + s_cellPadding;
|
||||
|
||||
resolveOutliers(nodes, centerOfMass);
|
||||
|
||||
// the nodes will be aligned everytime they move during post-processing
|
||||
// align all nodes once here so that nodes that won't be moved again are aligned
|
||||
for (DummyNode& node : nodes)
|
||||
{
|
||||
alignNodeOnRaster(node);
|
||||
}
|
||||
|
||||
MatrixDynamicBase<unsigned int> heatMap = buildHeatMap(nodes, divisorWidth, divisorHeight, maxNodeWidth, maxNodeHeight);
|
||||
|
||||
resolveOverlap(nodes, heatMap, divisorWidth, divisorHeight);
|
||||
}
|
||||
|
||||
void GraphPostprocessor::alignNodeOnRaster(DummyNode& node)
|
||||
{
|
||||
node.position = alignOnRaster(node.position);
|
||||
}
|
||||
|
||||
Vec2i GraphPostprocessor::alignOnRaster(Vec2i position)
|
||||
{
|
||||
int rasterPosDivisor = s_cellWidth + s_cellPadding;
|
||||
|
||||
if (position.x % rasterPosDivisor != 0)
|
||||
{
|
||||
int t = position.x / rasterPosDivisor;
|
||||
int r = position.x % rasterPosDivisor;
|
||||
|
||||
if (std::abs(r) > rasterPosDivisor/2)
|
||||
{
|
||||
if (t != 0)
|
||||
{
|
||||
t += (t / std::abs(t));
|
||||
}
|
||||
else if (r != 0)
|
||||
{
|
||||
t += (r / std::abs(r));
|
||||
}
|
||||
}
|
||||
|
||||
position.x = t * rasterPosDivisor;
|
||||
}
|
||||
|
||||
if (position.y % rasterPosDivisor != 0)
|
||||
{
|
||||
int t = position.y / rasterPosDivisor;
|
||||
int r = position.y % rasterPosDivisor;
|
||||
|
||||
if (std::abs(r) > rasterPosDivisor/2)
|
||||
{
|
||||
if(t != 0)
|
||||
{
|
||||
t += (t / std::abs(t));
|
||||
}
|
||||
else if(r != 0)
|
||||
{
|
||||
t += (r / std::abs(r));
|
||||
}
|
||||
}
|
||||
|
||||
position.y = t * rasterPosDivisor;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
void GraphPostprocessor::resolveOutliers(std::vector<DummyNode>& nodes, const Vec2i& centerPoint)
|
||||
{
|
||||
float maxDist = 0.0f;
|
||||
for (const DummyNode& node : nodes)
|
||||
{
|
||||
Vec2i toCenterOfMass = centerPoint - node.position;
|
||||
if (toCenterOfMass.getLength() > maxDist)
|
||||
{
|
||||
maxDist = toCenterOfMass.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDist == 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (DummyNode& node : nodes)
|
||||
{
|
||||
Vec2i toCenterOfMass = centerPoint - node.position;
|
||||
float dist = toCenterOfMass.getLength();
|
||||
|
||||
// causes far away nodes to be effected stronger than nodes that are already close to the center
|
||||
float distFactor = std::sqrt(dist / maxDist);
|
||||
node.position += toCenterOfMass * distFactor;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixDynamicBase<unsigned int> GraphPostprocessor::buildHeatMap(
|
||||
const std::vector<DummyNode>& nodes, const int atomarNodeWidth, const int atomarNodeHeight, const int maxNodeWidth, const int maxNodeHeight)
|
||||
{
|
||||
int heatMapWidth = (maxNodeWidth * nodes.size() / atomarNodeWidth) * 5; // theoretically the nodes could horizontally or vertically far from the center, therefore '*5' (it's kinda arbitrary, generally *2 should suffice, I use *5 to prevent problems in extrem cases)
|
||||
int heatMapHeight = (maxNodeHeight * nodes.size() / atomarNodeHeight) * 5;
|
||||
|
||||
MatrixDynamicBase<unsigned int> heatMap(heatMapWidth, heatMapHeight);
|
||||
|
||||
for (const DummyNode& node : nodes)
|
||||
{
|
||||
int left = node.position.x / atomarNodeWidth + heatMapWidth/2;
|
||||
int up = node.position.y / atomarNodeHeight + heatMapHeight/2;
|
||||
Vec2i size = calculateRasterNodeSize(node);
|
||||
int width = size.x;
|
||||
int height = size.y;
|
||||
|
||||
if (left + width > heatMapWidth || left < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (up + height > heatMapHeight || up < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
unsigned int x = left + i;
|
||||
unsigned int y = up + j;
|
||||
unsigned int value = heatMap.getValue(x, y);
|
||||
|
||||
heatMap.setValue(x, y, value+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return heatMap;
|
||||
}
|
||||
|
||||
void GraphPostprocessor::resolveOverlap(
|
||||
std::vector<DummyNode>& nodes, MatrixDynamicBase<unsigned int>& heatMap, const int divisorWidth, const int divisorHeight)
|
||||
{
|
||||
int heatMapWidth = heatMap.getColumnsCount();
|
||||
int heatMapHeight = heatMap.getRowsCount();
|
||||
|
||||
bool overlap = true;
|
||||
int iterationCount = 0;
|
||||
int maxIterations = 15;
|
||||
|
||||
while (overlap && iterationCount < maxIterations)
|
||||
{
|
||||
LOG_INFO_STREAM(<< iterationCount);
|
||||
|
||||
overlap = false;
|
||||
iterationCount++;
|
||||
|
||||
for (DummyNode& node : nodes)
|
||||
{
|
||||
Vec2i nodePos(0, 0);
|
||||
nodePos.x = node.position.x / divisorWidth + heatMapWidth/2;
|
||||
nodePos.y = node.position.y / divisorHeight + heatMapHeight/2;
|
||||
Vec2i nodeSize = calculateRasterNodeSize(node);
|
||||
|
||||
if (nodePos.x + nodeSize.x > heatMapWidth || nodePos.x < 0)
|
||||
{
|
||||
LOG_WARNING("Leaving heatmap area in x");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodePos.y + nodeSize.y > heatMapHeight || nodePos.y < 0)
|
||||
{
|
||||
LOG_WARNING("Leaving heatmap area in y");
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec2f grad(0.0f, 0.0f);
|
||||
if (getHeatmapGradient(grad, heatMap, nodePos, nodeSize))
|
||||
{
|
||||
overlap = true;
|
||||
}
|
||||
|
||||
// handle overlap with no gradient
|
||||
// e.g. when a node lies completely on top of another
|
||||
if (grad.getLengthSquared() <= 0.000001f && overlap)
|
||||
{
|
||||
grad = node.position;
|
||||
grad.normalize();
|
||||
|
||||
// catch special case of node being at position 0/0
|
||||
if (grad.getLengthSquared() <= 0.000001f)
|
||||
{
|
||||
grad.y = 1.0f;
|
||||
}
|
||||
|
||||
grad *= -1.0f;
|
||||
}
|
||||
|
||||
// remove node temporarily from heat map, it will be re-added at the new position later on
|
||||
modifyHeatmapArea(heatMap, nodePos, nodeSize, -1);
|
||||
|
||||
// move node to new position
|
||||
int xOffset = grad.x * divisorWidth;
|
||||
int yOffset = grad.y * divisorHeight;
|
||||
|
||||
int maxXOffset = 2*divisorWidth;
|
||||
int maxYOffset = 2*divisorHeight;
|
||||
|
||||
// prevent the graph from "exploding" again...
|
||||
if (xOffset > maxXOffset)
|
||||
{
|
||||
xOffset = maxXOffset;
|
||||
}
|
||||
else if (xOffset < -maxXOffset)
|
||||
{
|
||||
xOffset = -maxXOffset;
|
||||
}
|
||||
|
||||
if (yOffset > maxYOffset)
|
||||
{
|
||||
yOffset = maxYOffset;
|
||||
}
|
||||
else if (yOffset < -maxYOffset)
|
||||
{
|
||||
yOffset = -maxYOffset;
|
||||
}
|
||||
|
||||
node.position += Vec2i(xOffset, yOffset);
|
||||
|
||||
alignNodeOnRaster(node);
|
||||
|
||||
// re-add node to heat map at new position
|
||||
nodePos.x = node.position.x / divisorWidth + heatMapWidth/2;
|
||||
nodePos.y = node.position.y / divisorHeight + heatMapHeight/2;
|
||||
|
||||
modifyHeatmapArea(heatMap, nodePos, nodeSize, 1);
|
||||
|
||||
if (getHeatmapGradient(grad, heatMap, nodePos, nodeSize))
|
||||
{
|
||||
overlap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphPostprocessor::modifyHeatmapArea(
|
||||
MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size, const int modifier
|
||||
){
|
||||
bool wentOutOfRange = false;
|
||||
|
||||
for (int i = 0; i < size.x; i++)
|
||||
{
|
||||
for (int j = 0; j < size.y; j++)
|
||||
{
|
||||
int x = leftUpperCorner.x + i;
|
||||
int y = leftUpperCorner.y + j;
|
||||
|
||||
if (x < 0 || x > static_cast<int>(heatMap.getColumnsCount()-1))
|
||||
{
|
||||
wentOutOfRange = true;
|
||||
continue;
|
||||
}
|
||||
if (y < 0 || y > static_cast<int>(heatMap.getRowsCount()-1))
|
||||
{
|
||||
wentOutOfRange = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int value = heatMap.getValue(x, y);
|
||||
heatMap.setValue(x, y, value+modifier);
|
||||
|
||||
if (wentOutOfRange == true)
|
||||
{
|
||||
LOG_WARNING("Left matrix range while trying to modify values.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphPostprocessor::getHeatmapGradient(
|
||||
Vec2f& outGradient, const MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size
|
||||
){
|
||||
bool overlap = false;
|
||||
|
||||
for (int i = 0; i < size.x; i++)
|
||||
{
|
||||
for (int j = 0; j < size.y; j++)
|
||||
{
|
||||
int x = leftUpperCorner.x + i;
|
||||
int y = leftUpperCorner.y + j;
|
||||
|
||||
// weight factors that emphasize gradients near the nodes center
|
||||
int hMagFactor = std::max(1, (int)(size.x * 0.5 - std::abs(i + 1 - size.x * 0.5)));
|
||||
int vMagFactor = std::max(1, (int)(size.y * 0.5 - std::abs(j + 1 - size.y * 0.5)));
|
||||
|
||||
// if x and y lie directly at the border not all 4 neighbours can be checked
|
||||
if (x < 1 || x > static_cast<int>(heatMap.getColumnsCount() - 2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (y < 1 || y > static_cast<int>(heatMap.getRowsCount() - 2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float val = heatMap.getValue(x, y);
|
||||
|
||||
float xP1 = heatMap.getValue(x + 1, y) * hMagFactor;
|
||||
float xM1 = heatMap.getValue(x - 1, y) * hMagFactor;
|
||||
float yP1 = heatMap.getValue(x, y + 1) * vMagFactor;
|
||||
float yM1 = heatMap.getValue(x, y - 1) * vMagFactor;
|
||||
|
||||
xP1 = std::sqrt(xP1);
|
||||
xM1 = std::sqrt(xM1);
|
||||
yP1 = std::sqrt(yP1);
|
||||
yM1 = std::sqrt(yM1);
|
||||
|
||||
float xOffset = (xM1 - val) + (val - xP1);
|
||||
float yOffset = (yM1 - val) + (val - yP1);
|
||||
|
||||
outGradient += Vec2f(xOffset, yOffset);
|
||||
|
||||
if (val > 1)
|
||||
{
|
||||
overlap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
Vec2f GraphPostprocessor::heatMapRayCast(
|
||||
const MatrixDynamicBase<unsigned int>& heatMap, const Vec2f& startPosition, const Vec2f& direction, unsigned int minValue
|
||||
){
|
||||
float xOffset = 0.0f;
|
||||
float yOffset = 0.0f;
|
||||
|
||||
if (std::abs(direction.x) > 0.0000000001f)
|
||||
{
|
||||
xOffset = direction.x / std::abs(direction.x);
|
||||
}
|
||||
if (std::abs(direction.y) > 0.0000000001f)
|
||||
{
|
||||
yOffset = direction.y / std::abs(direction.y);
|
||||
}
|
||||
|
||||
if (startPosition.x < 1 || startPosition.x > static_cast<int>(heatMap.getColumnsCount() - 2))
|
||||
{
|
||||
return Vec2f(0.0f, 0.0f);
|
||||
}
|
||||
if (startPosition.y < 1 || startPosition.y > static_cast<int>(heatMap.getRowsCount() - 2))
|
||||
{
|
||||
return Vec2f(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
Vec2f length(0.0f, 0.0f);
|
||||
|
||||
bool hit = false;
|
||||
|
||||
float posX = startPosition.x + xOffset;
|
||||
float posY = startPosition.y + yOffset;
|
||||
|
||||
do
|
||||
{
|
||||
if (heatMap.getValue(posX, posY) >= minValue)
|
||||
{
|
||||
hit = true;
|
||||
length.x = length.x + xOffset;
|
||||
length.y = length.y + yOffset;
|
||||
|
||||
posX += xOffset;
|
||||
posY += yOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
hit = false;
|
||||
}
|
||||
}
|
||||
while (hit);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
Vec2i GraphPostprocessor::calculateRasterNodeSize(const DummyNode& node)
|
||||
{
|
||||
Vec2i size = node.size;
|
||||
Vec2i rasterSize(0, 0);
|
||||
|
||||
while (size.x > 0)
|
||||
{
|
||||
size.x = size.x - s_cellWidth;
|
||||
if(size.x > 0)
|
||||
{
|
||||
size.x = size.x - s_cellPadding;
|
||||
}
|
||||
|
||||
rasterSize.x = rasterSize.x + 1;
|
||||
}
|
||||
|
||||
while (size.y > 0)
|
||||
{
|
||||
size.y = size.y - s_cellHeight;
|
||||
if(size.y > 0)
|
||||
{
|
||||
size.y = size.y - s_cellPadding;
|
||||
}
|
||||
|
||||
rasterSize.y = rasterSize.y + 1;
|
||||
}
|
||||
|
||||
return rasterSize;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef GRAPH_POSTPROCESSOR_H
|
||||
#define GRAPH_POSTPROCESSOR_H
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include "utility/math/MatrixDynamicBase.h"
|
||||
|
||||
#include "component/controller/helper/DummyNode.h"
|
||||
|
||||
class GraphPostprocessor
|
||||
{
|
||||
public:
|
||||
static void doPostprocessing(std::vector<DummyNode>& nodes);
|
||||
|
||||
static void alignNodeOnRaster(DummyNode& node);
|
||||
static Vec2i alignOnRaster(Vec2i position);
|
||||
|
||||
private:
|
||||
static unsigned int s_cellWidth;
|
||||
static unsigned int s_cellHeight;
|
||||
static unsigned int s_cellPadding;
|
||||
|
||||
static MatrixDynamicBase<unsigned int> buildHeatMap(const std::vector<DummyNode>& nodes, const int atomarNodeWidth, const int atomarNodeHeight, const int maxNodeWidth, const int maxNodeHeight);
|
||||
static void resolveOutliers(std::vector<DummyNode>& nodes, const Vec2i& centerPoint);
|
||||
static void resolveOverlap(std::vector<DummyNode>& nodes, MatrixDynamicBase<unsigned int>& heatMap, const int divisorWidth, const int divisorHeight);
|
||||
|
||||
static void modifyHeatmapArea(MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size, const int modifier);
|
||||
static bool getHeatmapGradient(Vec2f& outGradient, const MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size);
|
||||
static Vec2f heatMapRayCast(const MatrixDynamicBase<unsigned int>& heatMap, const Vec2f& startPosition, const Vec2f& direction, unsigned int minValue);
|
||||
static Vec2i calculateRasterNodeSize(const DummyNode& node);
|
||||
};
|
||||
|
||||
#endif // GRAPH_POSTPROCESSOR_H
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "component/controller/helper/ListLayouter.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "component/controller/helper/DummyNode.h"
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
|
||||
ListLayouter::ListLayouter(Vec2i viewSize)
|
||||
: m_viewSize(viewSize)
|
||||
{
|
||||
}
|
||||
|
||||
void ListLayouter::layoutList(std::vector<std::shared_ptr<DummyNode>>& nodes)
|
||||
{
|
||||
size_t colsFinal;
|
||||
std::vector<int> maxWidthsFinal;
|
||||
|
||||
int gapX = GraphViewStyle::s_gridCellSize + 2 * GraphViewStyle::s_gridCellPadding;
|
||||
int gapY = GraphViewStyle::s_gridCellPadding;
|
||||
|
||||
for (size_t cols = 1; cols <= 10; cols++)
|
||||
{
|
||||
std::vector<int> maxWidths = std::vector<int>(cols, 0);
|
||||
size_t nodesPerCol = cols == 1 ? nodes.size() : std::ceil((nodes.size() + cols - 1) / double(cols));
|
||||
int maxHeight = 0;
|
||||
int height = -gapY;
|
||||
|
||||
for (size_t i = 0; i < nodes.size(); i++)
|
||||
{
|
||||
size_t j = i / nodesPerCol;
|
||||
|
||||
if (i % nodesPerCol == 0)
|
||||
{
|
||||
height = -gapY;
|
||||
}
|
||||
height += nodes[i]->size.y() + gapY;
|
||||
|
||||
maxWidths[j] = std::max(nodes[i]->size.x(), maxWidths[j]);
|
||||
maxHeight = std::max(height, maxHeight);
|
||||
}
|
||||
|
||||
int width = -gapX;
|
||||
for (size_t j = 0; j < cols; j++)
|
||||
{
|
||||
width += maxWidths[j] + gapX;
|
||||
}
|
||||
|
||||
if (width > m_viewSize.x)
|
||||
{
|
||||
if (!maxWidthsFinal.size())
|
||||
{
|
||||
colsFinal = 1;
|
||||
maxWidthsFinal = maxWidths;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
colsFinal = cols;
|
||||
maxWidthsFinal = maxWidths;
|
||||
|
||||
if (height < m_viewSize.y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
size_t nodesPerCol = colsFinal == 1 ? nodes.size() : std::ceil((nodes.size() + colsFinal - 1) / double(colsFinal));
|
||||
std::shared_ptr<DummyNode> lastTextNode;
|
||||
|
||||
for (size_t i = 0; i < nodes.size(); i++)
|
||||
{
|
||||
size_t j = i / nodesPerCol;
|
||||
if (j != 0 && j != colsFinal && i % nodesPerCol == 0)
|
||||
{
|
||||
if (lastTextNode)
|
||||
{
|
||||
std::shared_ptr<DummyNode> textNode = std::make_shared<DummyNode>(*lastTextNode.get());
|
||||
|
||||
if (nodes[i - 1] == lastTextNode)
|
||||
{
|
||||
lastTextNode->visible = false;
|
||||
}
|
||||
else if (textNode->name.size() == 1)
|
||||
{
|
||||
textNode->name += "..";
|
||||
}
|
||||
|
||||
nodes.insert(nodes.begin() + i, textNode);
|
||||
lastTextNode.reset();
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
y = 0;
|
||||
x += maxWidthsFinal[j - 1] + gapX;
|
||||
}
|
||||
|
||||
nodes[i]->position.x = x;
|
||||
nodes[i]->position.y = y;
|
||||
|
||||
y += nodes[i]->size.y + gapY;
|
||||
|
||||
if (nodes[i]->isTextNode())
|
||||
{
|
||||
lastTextNode = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef LIST_LAYOUTER_H
|
||||
#define LIST_LAYOUTER_H
|
||||
|
||||
#include "utility/math/Vector2.h"
|
||||
|
||||
struct DummyNode;
|
||||
|
||||
class ListLayouter
|
||||
{
|
||||
public:
|
||||
ListLayouter(Vec2i viewSize);
|
||||
|
||||
void layoutList(std::vector<std::shared_ptr<DummyNode>>& nodes);
|
||||
|
||||
private:
|
||||
Vec2i m_viewSize;
|
||||
};
|
||||
|
||||
#endif // LIST_LAYOUTER_H
|
||||
@@ -20,6 +20,8 @@ public:
|
||||
{
|
||||
bool animatedTransition;
|
||||
bool centerActiveNode;
|
||||
bool scrollToTop;
|
||||
bool isIndexedList;
|
||||
};
|
||||
|
||||
GraphView(ViewLayout* viewLayout);
|
||||
|
||||
@@ -23,6 +23,53 @@ float GraphViewStyle::s_zoomFactor;
|
||||
std::map<std::string, GraphViewStyle::NodeColor> GraphViewStyle::s_nodeColors;
|
||||
std::map<std::string, std::string> GraphViewStyle::s_edgeColors;
|
||||
|
||||
Vec2i GraphViewStyle::alignOnRaster(Vec2i position)
|
||||
{
|
||||
int rasterPosDivisor = s_gridCellSize + s_gridCellPadding;
|
||||
|
||||
if (position.x % rasterPosDivisor != 0)
|
||||
{
|
||||
int t = position.x / rasterPosDivisor;
|
||||
int r = position.x % rasterPosDivisor;
|
||||
|
||||
if (std::abs(r) > rasterPosDivisor/2)
|
||||
{
|
||||
if (t != 0)
|
||||
{
|
||||
t += (t / std::abs(t));
|
||||
}
|
||||
else if (r != 0)
|
||||
{
|
||||
t += (r / std::abs(r));
|
||||
}
|
||||
}
|
||||
|
||||
position.x = t * rasterPosDivisor;
|
||||
}
|
||||
|
||||
if (position.y % rasterPosDivisor != 0)
|
||||
{
|
||||
int t = position.y / rasterPosDivisor;
|
||||
int r = position.y % rasterPosDivisor;
|
||||
|
||||
if (std::abs(r) > rasterPosDivisor/2)
|
||||
{
|
||||
if(t != 0)
|
||||
{
|
||||
t += (t / std::abs(t));
|
||||
}
|
||||
else if(r != 0)
|
||||
{
|
||||
t += (r / std::abs(r));
|
||||
}
|
||||
}
|
||||
|
||||
position.y = t * rasterPosDivisor;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeMargins::NodeMargins()
|
||||
: left(0)
|
||||
, right(0)
|
||||
@@ -176,6 +223,11 @@ size_t GraphViewStyle::getFontSizeOfQualifier()
|
||||
return s_fontSize - 3;
|
||||
}
|
||||
|
||||
size_t GraphViewStyle::getFontSizeOfTextNode()
|
||||
{
|
||||
return s_fontSize + 5;
|
||||
}
|
||||
|
||||
std::string GraphViewStyle::getFontNameForNodeType(Node::NodeType type)
|
||||
{
|
||||
return s_fontName;
|
||||
@@ -191,6 +243,11 @@ std::string GraphViewStyle::getFontNameOfExpandToggleNode()
|
||||
return "Fira Sans";
|
||||
}
|
||||
|
||||
std::string GraphViewStyle::getFontNameOfTextNode()
|
||||
{
|
||||
return "Fira Sans";
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeMargins GraphViewStyle::getMarginsForNodeType(Node::NodeType type, bool hasChildren)
|
||||
{
|
||||
NodeMargins margins;
|
||||
@@ -308,6 +365,17 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfBundleNode()
|
||||
return getMarginsForNodeType(Node::NODE_ENUM, false);
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfTextNode()
|
||||
{
|
||||
NodeMargins margins;
|
||||
|
||||
margins.left = margins.right = 0;
|
||||
margins.top = margins.bottom = 6;
|
||||
margins.minWidth = margins.charHeight = getFontSizeOfTextNode();
|
||||
|
||||
return margins;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeStyle GraphViewStyle::getStyleForNodeType(
|
||||
Node::NodeType type, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier
|
||||
){
|
||||
@@ -463,6 +531,21 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfQualifier()
|
||||
return style;
|
||||
}
|
||||
|
||||
GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfTextNode()
|
||||
{
|
||||
NodeStyle style;
|
||||
|
||||
style.color = getNodeColor("text", false);
|
||||
|
||||
style.fontName = getFontNameOfTextNode();
|
||||
style.fontSize = getFontSizeOfTextNode();
|
||||
style.fontBold = true;
|
||||
|
||||
style.textOffset.y = 10;
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused)
|
||||
{
|
||||
EdgeStyle style;
|
||||
|
||||
@@ -14,6 +14,8 @@ class GraphViewStyleImpl;
|
||||
class GraphViewStyle
|
||||
{
|
||||
public:
|
||||
static Vec2i alignOnRaster(Vec2i position);
|
||||
|
||||
struct NodeMargins
|
||||
{
|
||||
NodeMargins();
|
||||
@@ -104,15 +106,18 @@ public:
|
||||
static size_t getFontSizeOfExpandToggleNode();
|
||||
static size_t getFontSizeOfCountCircle();
|
||||
static size_t getFontSizeOfQualifier();
|
||||
static size_t getFontSizeOfTextNode();
|
||||
|
||||
static std::string getFontNameForNodeType(Node::NodeType type);
|
||||
static std::string getFontNameOfAccessNode();
|
||||
static std::string getFontNameOfExpandToggleNode();
|
||||
static std::string getFontNameOfTextNode();
|
||||
|
||||
static NodeMargins getMarginsForNodeType(Node::NodeType type, bool hasChildren);
|
||||
static NodeMargins getMarginsOfAccessNode(AccessKind access);
|
||||
static NodeMargins getMarginsOfExpandToggleNode();
|
||||
static NodeMargins getMarginsOfBundleNode();
|
||||
static NodeMargins getMarginsOfTextNode();
|
||||
|
||||
static NodeStyle getStyleForNodeType(
|
||||
Node::NodeType type, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier);
|
||||
@@ -121,6 +126,7 @@ public:
|
||||
static NodeStyle getStyleOfCountCircle();
|
||||
static NodeStyle getStyleOfBundleNode(bool isFocused);
|
||||
static NodeStyle getStyleOfQualifier();
|
||||
static NodeStyle getStyleOfTextNode();
|
||||
|
||||
static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused);
|
||||
|
||||
|
||||
@@ -756,7 +756,7 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForAll() const
|
||||
return graph;
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(const std::vector<Id>& tokenIds) const
|
||||
std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace) const
|
||||
{
|
||||
TRACE();
|
||||
|
||||
@@ -863,6 +863,11 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(const std::v
|
||||
|
||||
addComponentAccessToGraph(graph);
|
||||
|
||||
if (isActiveNamespace)
|
||||
{
|
||||
*isActiveNamespace = isNamespace;
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
virtual std::vector<SearchMatch> getSearchMatchesForTokenIds(const std::vector<Id>& elementIds) const;
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds) const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
virtual std::vector<SearchMatch> getSearchMatchesForTokenIds(const std::vector<Id>& tokenIds) const = 0;
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const = 0;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds) const = 0;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const = 0;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const = 0;
|
||||
|
||||
|
||||
@@ -117,11 +117,11 @@ std::shared_ptr<Graph> StorageAccessProxy::getGraphForAll() const
|
||||
return std::make_shared<Graph>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Graph> StorageAccessProxy::getGraphForActiveTokenIds(const std::vector<Id>& tokenIds) const
|
||||
std::shared_ptr<Graph> StorageAccessProxy::getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace) const
|
||||
{
|
||||
if (hasSubject())
|
||||
{
|
||||
return m_subject->getGraphForActiveTokenIds(tokenIds);
|
||||
return m_subject->getGraphForActiveTokenIds(tokenIds, isActiveNamespace);
|
||||
}
|
||||
|
||||
return std::make_shared<Graph>();
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
virtual std::vector<SearchMatch> getSearchMatchesForTokenIds(const std::vector<Id>& tokenIds) const;
|
||||
|
||||
virtual std::shared_ptr<Graph> getGraphForAll() const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds) const;
|
||||
virtual std::shared_ptr<Graph> getGraphForActiveTokenIds(const std::vector<Id>& tokenIds, bool* isActiveNamespace = nullptr) const;
|
||||
|
||||
virtual std::vector<Id> getActiveTokenIdsForId(Id tokenId, Id* declarationId) const;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public:
|
||||
Property<T>& operator=(const Property<T>& property);
|
||||
|
||||
operator const T&() const;
|
||||
T& operator()() const;
|
||||
|
||||
private:
|
||||
T* m_valuePointer;
|
||||
@@ -47,4 +48,10 @@ Property<T>::operator const T&() const
|
||||
return *m_valuePointer;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T& Property<T>::operator()() const
|
||||
{
|
||||
return *m_valuePointer;
|
||||
}
|
||||
|
||||
#endif // PROPERTY_H
|
||||
|
||||
@@ -8,8 +8,10 @@ class MessageGraphNodeBundleSplit
|
||||
: public Message<MessageGraphNodeBundleSplit>
|
||||
{
|
||||
public:
|
||||
MessageGraphNodeBundleSplit(Id bundleId)
|
||||
MessageGraphNodeBundleSplit(Id bundleId, bool removeOtherNodes = false, bool layoutToList = false)
|
||||
: bundleId(bundleId)
|
||||
, removeOtherNodes(removeOtherNodes)
|
||||
, layoutToList(layoutToList)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -24,6 +26,8 @@ public:
|
||||
}
|
||||
|
||||
Id bundleId;
|
||||
bool removeOtherNodes;
|
||||
bool layoutToList;
|
||||
};
|
||||
|
||||
#endif // MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H
|
||||
|
||||
@@ -98,6 +98,8 @@ add_files(
|
||||
qt/view/graphElements/QtGraphNodeExpandToggle.h
|
||||
qt/view/graphElements/QtGraphNodeQualifier.cpp
|
||||
qt/view/graphElements/QtGraphNodeQualifier.h
|
||||
qt/view/graphElements/QtGraphNodeText.cpp
|
||||
qt/view/graphElements/QtGraphNodeText.h
|
||||
|
||||
qt/view/QtCodeView.cpp
|
||||
qt/view/QtCodeView.h
|
||||
|
||||
@@ -66,7 +66,7 @@ QtGraphicsView::QtGraphicsView(QWidget* parent)
|
||||
|
||||
float QtGraphicsView::getZoomFactor() const
|
||||
{
|
||||
return m_appZoomFactor;
|
||||
return m_appZoomFactor * m_zoomFactor;
|
||||
}
|
||||
|
||||
void QtGraphicsView::setAppZoomFactor(float appZoomFactor)
|
||||
@@ -181,6 +181,12 @@ void QtGraphicsView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
void QtGraphicsView::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (event->key() >= Qt::Key_A && event->key() <= Qt::Key_Z)
|
||||
{
|
||||
QChar c = event->text().at(0).toUpper();
|
||||
emit characterKeyPressed(c);
|
||||
}
|
||||
|
||||
bool moved = moves();
|
||||
|
||||
switch (event->key())
|
||||
|
||||
@@ -45,6 +45,7 @@ protected:
|
||||
|
||||
signals:
|
||||
void emptySpaceClicked();
|
||||
void characterKeyPressed(QChar c);
|
||||
|
||||
private slots:
|
||||
void updateTimer();
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
#include "component/controller/helper/DummyEdge.h"
|
||||
#include "component/controller/helper/DummyNode.h"
|
||||
#include "component/controller/helper/GraphPostprocessor.h"
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "settings/ColorScheme.h"
|
||||
#include "settings/ApplicationSettings.h"
|
||||
#include "utility/messaging/type/MessageDeactivateEdge.h"
|
||||
#include "utility/ResourcePaths.h"
|
||||
@@ -29,6 +27,7 @@
|
||||
#include "qt/view/graphElements/QtGraphNodeData.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeExpandToggle.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeQualifier.h"
|
||||
#include "qt/view/graphElements/QtGraphNodeText.h"
|
||||
|
||||
QtGraphView::QtGraphView(ViewLayout* viewLayout)
|
||||
: GraphView(viewLayout)
|
||||
@@ -40,6 +39,8 @@ 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_scrollToTop(false)
|
||||
, m_isIndexedList(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,6 +72,7 @@ void QtGraphView::initView()
|
||||
widget->layout()->addWidget(view);
|
||||
|
||||
connect(view, SIGNAL(emptySpaceClicked()), this, SLOT(clickedInEmptySpace()));
|
||||
connect(view, SIGNAL(characterKeyPressed(QChar)), this, SLOT(pressedCharacterKey(QChar)));
|
||||
|
||||
m_scrollSpeedChangeListenerHorizontal.setScrollBar(view->horizontalScrollBar());
|
||||
m_scrollSpeedChangeListenerVertical.setScrollBar(view->verticalScrollBar());
|
||||
@@ -109,18 +111,25 @@ Vec2i QtGraphView::getViewSize() const
|
||||
QtGraphicsView* view = getView();
|
||||
|
||||
float zoomFactor = view->getZoomFactor();
|
||||
return Vec2i(view->width() / zoomFactor - 80, view->height() / zoomFactor - 80);
|
||||
return Vec2i(view->width() / zoomFactor - 60, view->height() / zoomFactor - 60);
|
||||
}
|
||||
|
||||
void QtGraphView::centerScrollBars()
|
||||
void QtGraphView::updateScrollBars()
|
||||
{
|
||||
QGraphicsView* view = getView();
|
||||
|
||||
QScrollBar* hb = view->horizontalScrollBar();
|
||||
QScrollBar* vb = view->verticalScrollBar();
|
||||
|
||||
hb->setValue((hb->minimum() + hb->maximum()) / 2);
|
||||
vb->setValue((vb->minimum() + vb->maximum()) / 2);
|
||||
if (m_scrollToTop)
|
||||
{
|
||||
vb->setValue(vb->minimum());
|
||||
}
|
||||
else
|
||||
{
|
||||
hb->setValue((hb->minimum() + hb->maximum()) / 2);
|
||||
vb->setValue((vb->minimum() + vb->maximum()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void QtGraphView::finishedTransition()
|
||||
@@ -148,6 +157,50 @@ void QtGraphView::clickedInEmptySpace()
|
||||
}
|
||||
}
|
||||
|
||||
void QtGraphView::pressedCharacterKey(QChar c)
|
||||
{
|
||||
if (!m_isIndexedList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QtGraphNode* node = nullptr;
|
||||
bool hasTextNodes = false;
|
||||
|
||||
for (const std::shared_ptr<QtGraphNode>& n : m_oldNodes)
|
||||
{
|
||||
if (n->isTextNode() && n->getName().size())
|
||||
{
|
||||
hasTextNodes = true;
|
||||
QChar start(n->getName()[0]);
|
||||
if (start >= c)
|
||||
{
|
||||
node = n.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTextNodes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QtGraphicsView* view = getView();
|
||||
|
||||
if (!node)
|
||||
{
|
||||
view->ensureVisibleAnimated(QRectF(0, view->scene()->height() - 5, view->scene()->width(), 5), 100, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec2i pos = node->getPosition();
|
||||
Vec2i size = node->getSize();
|
||||
|
||||
view->ensureVisibleAnimated(QRectF(pos.x, pos.y, size.x, size.y + view->height() / 3 * 2), 100, 100);
|
||||
}
|
||||
}
|
||||
|
||||
void QtGraphView::switchToNewGraphData()
|
||||
{
|
||||
m_oldGraph = m_graph;
|
||||
@@ -170,6 +223,11 @@ void QtGraphView::switchToNewGraphData()
|
||||
|
||||
doResize();
|
||||
|
||||
if (m_scrollToTop)
|
||||
{
|
||||
updateScrollBars();
|
||||
}
|
||||
|
||||
// Manually hover the item below the mouse cursor.
|
||||
QtGraphicsView* view = getView();
|
||||
QtGraphNode* node = view->getNodeAtCursorPosition();
|
||||
@@ -244,7 +302,7 @@ void QtGraphView::doRebuildGraph(
|
||||
}
|
||||
|
||||
QPointF center = itemsBoundingRect(m_nodes).center();
|
||||
Vec2i o = GraphPostprocessor::alignOnRaster(Vec2i(center.x(), center.y()));
|
||||
Vec2i o = GraphViewStyle::alignOnRaster(Vec2i(center.x(), center.y()));
|
||||
QPointF offset = QPointF(o.x, o.y);
|
||||
m_sceneRectOffset = offset - center;
|
||||
|
||||
@@ -282,6 +340,9 @@ void QtGraphView::doRebuildGraph(
|
||||
m_activeNode.reset();
|
||||
}
|
||||
|
||||
m_scrollToTop = params.scrollToTop;
|
||||
m_isIndexedList = params.isIndexedList;
|
||||
|
||||
if (params.animatedTransition && ApplicationSettings::getInstance()->getUseAnimations())
|
||||
{
|
||||
createTransition();
|
||||
@@ -364,12 +425,17 @@ std::shared_ptr<QtGraphNode> QtGraphView::createNodeRecursive(
|
||||
}
|
||||
else if (node->isBundleNode())
|
||||
{
|
||||
newNode = std::make_shared<QtGraphNodeBundle>(node->tokenId, node->getBundledNodeCount(), node->name);
|
||||
newNode = std::make_shared<QtGraphNodeBundle>(
|
||||
node->tokenId, node->getBundledNodeCount(), node->bundledNodeType, node->name);
|
||||
}
|
||||
else if (node->isQualifierNode())
|
||||
{
|
||||
newNode = std::make_shared<QtGraphNodeQualifier>(node->qualifierName);
|
||||
}
|
||||
else if (node->isTextNode())
|
||||
{
|
||||
newNode = std::make_shared<QtGraphNodeText>(node->name);
|
||||
}
|
||||
|
||||
newNode->setPosition(node->position);
|
||||
newNode->setSize(node->size);
|
||||
@@ -384,7 +450,7 @@ std::shared_ptr<QtGraphNode> QtGraphView::createNodeRecursive(
|
||||
{
|
||||
newNode->setParent(parentNode);
|
||||
}
|
||||
else
|
||||
else if (!node->isTextNode())
|
||||
{
|
||||
newNode->addComponent(std::make_shared<QtGraphNodeComponentMoveable>(newNode));
|
||||
}
|
||||
@@ -624,9 +690,9 @@ void QtGraphView::createTransition()
|
||||
|
||||
anim->setDuration(300);
|
||||
|
||||
if (!remainingNodes.size())
|
||||
if (!remainingNodes.size() || m_scrollToTop)
|
||||
{
|
||||
connect(anim, SIGNAL(finished()), this, SLOT(centerScrollBars()));
|
||||
connect(anim, SIGNAL(finished()), this, SLOT(updateScrollBars()));
|
||||
}
|
||||
|
||||
remain->addAnimation(anim);
|
||||
|
||||
@@ -49,9 +49,10 @@ public:
|
||||
virtual Vec2i getViewSize() const;
|
||||
|
||||
private slots:
|
||||
void centerScrollBars();
|
||||
void updateScrollBars();
|
||||
void finishedTransition();
|
||||
void clickedInEmptySpace();
|
||||
void pressedCharacterKey(QChar c);
|
||||
|
||||
private:
|
||||
void switchToNewGraphData();
|
||||
@@ -112,6 +113,8 @@ private:
|
||||
std::list<std::shared_ptr<QtGraphNode>> m_oldNodes;
|
||||
|
||||
std::shared_ptr<QtGraphNode> m_activeNode;
|
||||
bool m_scrollToTop;
|
||||
bool m_isIndexedList;
|
||||
|
||||
std::shared_ptr<QSequentialAnimationGroup> m_transition;
|
||||
QPointF m_sceneRectOffset;
|
||||
|
||||
@@ -5,15 +5,12 @@
|
||||
#include <QGraphicsSceneEvent>
|
||||
#include <QPen>
|
||||
|
||||
#include "utility/ResourcePaths.h"
|
||||
|
||||
#include "component/controller/helper/GraphPostprocessor.h"
|
||||
|
||||
#include "qt/graphics/QtRoundedRectItem.h"
|
||||
#include "qt/utility/QtDeviceScaledPixmap.h"
|
||||
#include "qt/utility/utilityQt.h"
|
||||
#include "qt/view/graphElements/nodeComponents/QtGraphNodeComponent.h"
|
||||
#include "qt/view/graphElements/QtGraphEdge.h"
|
||||
#include "utility/ResourcePaths.h"
|
||||
|
||||
void QtGraphNode::blendIn()
|
||||
{
|
||||
@@ -250,6 +247,11 @@ bool QtGraphNode::isQualifierNode() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QtGraphNode::isTextNode() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Id QtGraphNode::getTokenId() const
|
||||
{
|
||||
return 0;
|
||||
@@ -277,7 +279,7 @@ void QtGraphNode::addSubNode(const std::shared_ptr<QtGraphNode>& node)
|
||||
|
||||
void QtGraphNode::moved(const Vec2i& oldPosition)
|
||||
{
|
||||
setPosition(GraphPostprocessor::alignOnRaster(getPosition()));
|
||||
setPosition(GraphViewStyle::alignOnRaster(getPosition()));
|
||||
}
|
||||
|
||||
void QtGraphNode::onClick()
|
||||
|
||||
@@ -77,6 +77,7 @@ public:
|
||||
virtual bool isExpandToggleNode() const;
|
||||
virtual bool isBundleNode() const;
|
||||
virtual bool isQualifierNode() const;
|
||||
virtual bool isTextNode() const;
|
||||
|
||||
virtual Id getTokenId() const;
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
#include "component/view/GraphViewStyle.h"
|
||||
#include "qt/graphics/QtCountCircleItem.h"
|
||||
|
||||
QtGraphNodeBundle::QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name)
|
||||
QtGraphNodeBundle::QtGraphNodeBundle(Id tokenId, size_t nodeCount, Node::NodeType type, std::string name)
|
||||
: QtGraphNode()
|
||||
, m_tokenId(tokenId)
|
||||
, m_type(type)
|
||||
{
|
||||
this->setName(name);
|
||||
|
||||
@@ -38,12 +39,24 @@ Id QtGraphNodeBundle::getTokenId() const
|
||||
|
||||
void QtGraphNodeBundle::onClick()
|
||||
{
|
||||
MessageGraphNodeBundleSplit(m_tokenId).dispatch();
|
||||
MessageGraphNodeBundleSplit(
|
||||
m_tokenId,
|
||||
m_type != Node::NODE_UNDEFINED && getName() != "Anonymous Namespaces",
|
||||
m_type != Node::NODE_UNDEFINED
|
||||
).dispatch();
|
||||
}
|
||||
|
||||
void QtGraphNodeBundle::updateStyle()
|
||||
{
|
||||
GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfBundleNode(m_isHovering);
|
||||
GraphViewStyle::NodeStyle style;
|
||||
if (m_type != Node::NODE_UNDEFINED)
|
||||
{
|
||||
style = GraphViewStyle::getStyleForNodeType(m_type, true, false, m_isHovering, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
style = GraphViewStyle::getStyleOfBundleNode(m_isHovering);
|
||||
}
|
||||
setStyle(style);
|
||||
|
||||
m_circle->setPosition(Vec2f(m_rect->rect().right() - 3, m_rect->rect().top() + 3));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef QT_GRAPH_NODE_BUNDLE_H
|
||||
#define QT_GRAPH_NODE_BUNDLE_H
|
||||
|
||||
#include "data/graph/Node.h"
|
||||
#include "qt/view/graphElements/QtGraphNode.h"
|
||||
|
||||
class QtCountCircleItem;
|
||||
@@ -9,7 +10,7 @@ class QtGraphNodeBundle
|
||||
: public QtGraphNode
|
||||
{
|
||||
public:
|
||||
QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name);
|
||||
QtGraphNodeBundle(Id tokenId, size_t nodeCount, Node::NodeType type, std::string name);
|
||||
virtual ~QtGraphNodeBundle();
|
||||
|
||||
// QtGraphNode implementation
|
||||
@@ -27,6 +28,7 @@ protected:
|
||||
private:
|
||||
QtCountCircleItem* m_circle;
|
||||
Id m_tokenId;
|
||||
Node::NodeType m_type;
|
||||
};
|
||||
|
||||
#endif // QT_GRAPH_NODE_BUNDLE_H
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "qt/view/graphElements/QtGraphNodeText.h"
|
||||
|
||||
QtGraphNodeText::QtGraphNodeText(const std::string& name)
|
||||
{
|
||||
setName(name);
|
||||
}
|
||||
|
||||
QtGraphNodeText::~QtGraphNodeText()
|
||||
{
|
||||
}
|
||||
|
||||
bool QtGraphNodeText::isTextNode() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtGraphNodeText::updateStyle()
|
||||
{
|
||||
setStyle(GraphViewStyle::getStyleOfTextNode());
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef QT_GRAPH_NODE_TEXT_H
|
||||
#define QT_GRAPH_NODE_TEXT_H
|
||||
|
||||
#include "qt/view/graphElements/QtGraphNode.h"
|
||||
|
||||
class QtGraphNodeText
|
||||
: public QtGraphNode
|
||||
{
|
||||
public:
|
||||
QtGraphNodeText(const std::string& name);
|
||||
virtual ~QtGraphNodeText();
|
||||
|
||||
// QtGraphNode implementation
|
||||
virtual bool isTextNode() const;
|
||||
|
||||
virtual void updateStyle();
|
||||
};
|
||||
|
||||
#endif // QT_GRAPH_NODE_TEXT_H
|
||||
Reference in New Issue
Block a user