diff --git a/bin/app/data/color_schemes/bad_rainbow.xml b/bin/app/data/color_schemes/bad_rainbow.xml
index 688a9d11..31bc0eae 100644
--- a/bin/app/data/color_schemes/bad_rainbow.xml
+++ b/bin/app/data/color_schemes/bad_rainbow.xml
@@ -344,6 +344,9 @@
#FFFFFF
+
+ #B05151
+
namespace
@@ -352,7 +355,7 @@
#4E82F6
- #7FA9F4
+ #6793F6
#20FFFFFF
@@ -365,7 +368,7 @@
#FFFFFF
- #999999
+ #416CCC
@@ -423,6 +426,41 @@
+
+
+ transparent
+
+
+ #FFFFFF
+
+
+ #807F80
+
+
+
+
+ transparent
+
+
+ #D4D4D4
+
+
+ #4E82F6
+ #6793F6
+
+
+
+
+ transparent
+
+
+ #D4D4D4
+
+
+ #873E3E
+ #944444
+
+
transparent
diff --git a/bin/app/data/color_schemes/bright.xml b/bin/app/data/color_schemes/bright.xml
index 1e218a82..65de7496 100644
--- a/bin/app/data/color_schemes/bright.xml
+++ b/bin/app/data/color_schemes/bright.xml
@@ -333,6 +333,9 @@
#3D3D3D
+
+ #FFDEDE
+
namespace
@@ -347,6 +350,9 @@
#203C3C3C
#3C3C3C
+
+ #D7F7C8
+
file
@@ -397,6 +403,38 @@
+
+
+ transparent
+
+
+ #D0D0D0
+
+
+
+
+ transparent
+
+
+ #5C5C5C
+
+
+ #BEE0AF
+ #A6D492
+
+
+
+
+ transparent
+
+
+ #5C5C5C
+
+
+ #EFC5C5
+ #EFB3B3
+
+
transparent
@@ -413,7 +451,7 @@
- #955ECC
+ #A37ACC
diff --git a/bin/app/data/color_schemes/dark.xml b/bin/app/data/color_schemes/dark.xml
index c4ea6641..dbe2b91b 100644
--- a/bin/app/data/color_schemes/dark.xml
+++ b/bin/app/data/color_schemes/dark.xml
@@ -336,6 +336,9 @@
#C3C3C3
+
+ #521B1F
+
namespace
@@ -344,12 +347,15 @@
#1B564E
- #247368
+ #21695F
#20C3C3C3
#C3C3C3
+
+ #123B35
+
file
@@ -400,6 +406,38 @@
+
+
+ transparent
+
+
+ #5A5A5A
+
+
+
+
+ transparent
+
+
+ #C3C3C3
+
+
+ #1B564E
+ #1E6158
+
+
+
+
+ transparent
+
+
+ #C3C3C3
+
+
+ #78282D
+ #872D33
+
+
transparent
@@ -416,7 +454,7 @@
- #B059C2
+ #8A5A94
diff --git a/bin/app/data/gui/graph_view/graph_view.css b/bin/app/data/gui/graph_view/graph_view.css
index ec848d86..7cb5bf64 100644
--- a/bin/app/data/gui/graph_view/graph_view.css
+++ b/bin/app/data/gui/graph_view/graph_view.css
@@ -14,7 +14,7 @@ QPushButton:disabled {
background: ;
}
-QPushButton:hover {
+QPushButton:hover, QPushButton:checked {
background: ;
}
@@ -25,6 +25,7 @@ QPushButton:pressed {
#expand_button {
max-width: 26px;
max-height: 26px;
+ border-radius: 9px;
}
#collapse_button {
@@ -82,3 +83,21 @@ QPushButton:pressed {
#depth_slider::groove:disabled, #depth_slider::handle:disabled {
background: ;
}
+
+#group_left_button {
+ border-radius: 0;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ max-width: 26px;
+ max-height: 26px;
+ padding-left: 2px;
+}
+
+#group_right_button {
+ border-radius: 0;
+ border-top-right-radius: 10px;
+ border-bottom-right-radius: 10px;
+ max-width: 26px;
+ max-height: 26px;
+ padding-right: 2px;
+}
diff --git a/bin/app/data/gui/graph_view/images/group_namespace.png b/bin/app/data/gui/graph_view/images/group_namespace.png
new file mode 100644
index 00000000..27e31992
Binary files /dev/null and b/bin/app/data/gui/graph_view/images/group_namespace.png differ
diff --git a/bin/app/user/ApplicationSettings_template.xml b/bin/app/user/ApplicationSettings_template.xml
index d74522f3..cede33f6 100644
--- a/bin/app/user/ApplicationSettings_template.xml
+++ b/bin/app/user/ApplicationSettings_template.xml
@@ -32,6 +32,7 @@
+
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index a50a17fd..84e7e39e 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -265,6 +265,8 @@ add_files(
data/ErrorCountInfo.h
data/ErrorFilter.h
data/ErrorInfo.h
+ data/GroupType.cpp
+ data/GroupType.h
data/HierarchyCache.cpp
data/HierarchyCache.h
data/NodeType.cpp
diff --git a/src/lib/component/controller/ActivationController.cpp b/src/lib/component/controller/ActivationController.cpp
index 20a95af8..6dd4c31b 100644
--- a/src/lib/component/controller/ActivationController.cpp
+++ b/src/lib/component/controller/ActivationController.cpp
@@ -79,12 +79,22 @@ void ActivationController::handleMessage(MessageActivateNodes* message)
MessageActivateTokens m(message);
for (const MessageActivateNodes::ActiveNode& node : message->nodes)
{
- Id nodeId = node.nodeId ? node.nodeId : m_storageAccess->getNodeIdForNameHierarchy(node.nameHierarchy);
+ Id nodeId = node.nodeId;
+ NameHierarchy name = node.nameHierarchy;
+ if (!nodeId)
+ {
+ nodeId = m_storageAccess->getNodeIdForNameHierarchy(name);
+ }
+ else if (!name.size())
+ {
+ name = m_storageAccess->getNameHierarchyForNodeId(nodeId);
+ }
+
if (nodeId > 0)
{
m.tokenIds.push_back(nodeId);
}
- m.tokenNames.push_back(node.nameHierarchy);
+ m.tokenNames.push_back(name);
}
m.searchMatches = m_storageAccess->getSearchMatchesForTokenIds(m.tokenIds);
m.dispatchImmediately();
@@ -104,7 +114,7 @@ void ActivationController::handleMessage(MessageActivateSourceLocations* message
MessageActivateNodes m;
for (Id nodeId : m_storageAccess->getNodeIdsForLocationIds(message->locationIds))
{
- m.addNode(nodeId, m_storageAccess->getNameHierarchyForNodeId(nodeId));
+ m.addNode(nodeId);
}
m.dispatchImmediately();
}
diff --git a/src/lib/component/controller/BookmarkController.cpp b/src/lib/component/controller/BookmarkController.cpp
index d3be4d23..e5148728 100644
--- a/src/lib/component/controller/BookmarkController.cpp
+++ b/src/lib/component/controller/BookmarkController.cpp
@@ -188,9 +188,7 @@ void BookmarkController::activateBookmark(const std::shared_ptr bookma
Id activeNodeId = edgeBookmark->getActiveNodeId();
if (activeNodeId)
{
- MessageActivateNodes activateNodes;
- activateNodes.addNode(activeNodeId, m_storageAccess->getNameHierarchyForNodeId(activeNodeId));
- activateNodes.dispatch();
+ MessageActivateNodes(activeNodeId).dispatch();
}
MessageActivateEdge(firstEdgeId, Edge::intToType(storageEdge.type), sourceName, targetName).dispatch();
@@ -216,7 +214,7 @@ void BookmarkController::activateBookmark(const std::shared_ptr bookma
for (Id nodeId: nodeBookmark->getNodeIds())
{
- activateNodes.addNode(nodeId, m_storageAccess->getNameHierarchyForNodeId(nodeId));
+ activateNodes.addNode(nodeId);
}
activateNodes.dispatch();
diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp
index 548bab85..d2135dc5 100644
--- a/src/lib/component/controller/GraphController.cpp
+++ b/src/lib/component/controller/GraphController.cpp
@@ -109,6 +109,16 @@ void GraphController::handleMessage(MessageActivateTokens* message)
if (isNamespace)
{
addCharacterIndex();
+
+ DummyNode* group = groupAllNodes(GroupType::NAMESPACE, tokenIds[0]);
+ group->groupLayout = GroupLayout::LIST;
+
+ if (!group->name.size())
+ {
+ group->name = m_storageAccess->getNameHierarchyForNodeId(tokenIds[0]).getQualifiedName();
+ group->tokenId = tokenIds[0];
+ }
+
layoutNesting();
layoutList();
}
@@ -141,6 +151,8 @@ void GraphController::handleMessage(MessageActivateTokens* message)
m_useBezierEdges = !isInheritanceChain;
}
+ groupNodesByParents(getView()->getGrouping());
+
layoutNesting();
layoutGraph(true);
assignBundleIds();
@@ -187,7 +199,7 @@ void GraphController::handleMessage(MessageActivateTrail* message)
if (message->trailType & Edge::EDGE_INHERITANCE)
{
- groupTrailNodes(NodeType::GROUP_INHERITANCE);
+ groupTrailNodes(GroupType::INHERITANCE);
}
layoutNesting();
@@ -241,11 +253,14 @@ void GraphController::handleMessage(MessageFocusOut *message)
void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
{
+ std::wstring name;
for (size_t i = 0; i < m_dummyNodes.size(); i++)
{
DummyNode* node = m_dummyNodes[i].get();
if ((node->isBundleNode() || node->isGroupNode()) && node->tokenId == message->bundleId)
{
+ name = node->name;
+
std::vector> nodes;
if (node->isBundleNode())
{
@@ -296,7 +311,7 @@ void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
}
}
- relayoutGraph(message, false, true, message->layoutToList, message->layoutToList);
+ relayoutGraph(message, false, true, message->layoutToList, message->layoutToList, name);
}
void GraphController::handleMessage(MessageGraphNodeExpand* message)
@@ -310,9 +325,7 @@ void GraphController::handleMessage(MessageGraphNodeExpand* message)
{
if (!message->isReplayed())
{
- MessageActivateNodes msg;
- msg.addNode(message->tokenId, m_storageAccess->getNameHierarchyForNodeId(message->tokenId));
- msg.dispatch();
+ MessageActivateNodes(message->tokenId).dispatch();
}
return;
}
@@ -433,7 +446,7 @@ void GraphController::handleMessage(MessageGraphNodeHide* message)
if (node || edge)
{
- relayoutGraph(message, false, true, false, false);
+ relayoutGraph(message, false, true, false, false, L"");
}
}
@@ -542,8 +555,8 @@ void GraphController::createDummyGraph(const std::shared_ptr graph)
updateDummyNodeNamesAndAddQualifiers(dummyNodes);
m_dummyNodes = dummyNodes;
-
m_graph = graph;
+
m_useBezierEdges = false;
}
@@ -662,7 +675,7 @@ void GraphController::updateDummyNodeNamesAndAddQualifiers(
qualifierNode->visible = true;
node->subNodes.push_back(qualifierNode);
- node->hasQualifier = true;
+ node->qualifierName = qualifier;
}
}
}
@@ -986,7 +999,7 @@ void GraphController::bundleNodes()
// std::cout << node->bundleInfo.layoutVertical << " ";
// std::cout << node->bundleInfo.isReferenced << " ";
// std::cout << node->bundleInfo.isReferencing << " ";
- // std::cout << node->name << std::endl;
+ // std::wcout << node->name << std::endl;
// }
// bundle
@@ -1380,7 +1393,174 @@ bool GraphController::hasCharacterIndex() const
return false;
}
-void GraphController::groupTrailNodes(NodeType::GroupType groupType)
+void GraphController::groupNodesByParents(GroupType groupType)
+{
+ TRACE();
+
+ if (groupType != GroupType::FILE && groupType != GroupType::NAMESPACE)
+ {
+ return;
+ }
+
+ std::map> groupNodes;
+ std::map>> nodesToGroup;
+
+ std::map> nodeIdtoParentMap;
+ if (groupType == GroupType::FILE)
+ {
+ std::vector nodeIds;
+ for (const std::shared_ptr& dummyNode : m_dummyNodes)
+ {
+ if (dummyNode->isGraphNode())
+ {
+ nodeIds.push_back(dummyNode->tokenId);
+ }
+ }
+
+ nodeIdtoParentMap = m_storageAccess->getNodeIdToParentFileMap(nodeIds);
+ }
+
+ std::map qualifierNameToIdMap;
+ for (const std::shared_ptr& dummyNode : m_dummyNodes)
+ {
+ if (dummyNode->isGroupNode())
+ {
+ groupNodes.emplace(dummyNode->name, dummyNode);
+ }
+ else if (dummyNode->visible)
+ {
+ if (groupType == GroupType::FILE)
+ {
+ if (dummyNode->isGraphNode())
+ {
+ auto it = nodeIdtoParentMap.find(dummyNode->tokenId);
+ if (it != nodeIdtoParentMap.end())
+ {
+ nodesToGroup[it->second.second.getQualifiedName()].push_back(dummyNode);
+ }
+ }
+ }
+ else if (groupType == GroupType::NAMESPACE)
+ {
+ const DummyNode* qualifierNode = dummyNode->getQualifierNode();
+ if (qualifierNode)
+ {
+ Id qualifierId = 0;
+ std::wstring qualifierName = qualifierNode->qualifierName.getQualifiedName();
+ auto it = qualifierNameToIdMap.find(qualifierName);
+ if (it != qualifierNameToIdMap.end())
+ {
+ qualifierId = it->second;
+ }
+ else
+ {
+ qualifierId = m_storageAccess->getNodeIdForNameHierarchy(qualifierNode->qualifierName);
+ qualifierNameToIdMap.emplace(qualifierName, qualifierId);
+ }
+
+ nodesToGroup[qualifierName].push_back(dummyNode);
+ nodeIdtoParentMap.emplace(
+ dummyNode->tokenId, std::make_pair(qualifierId, qualifierNode->qualifierName));
+ }
+ }
+ }
+ }
+
+ std::set groupedNodeIds;
+ for (const std::pair>>& p : nodesToGroup)
+ {
+ std::shared_ptr groupNode;
+
+ std::wstring name = p.first;
+ if (groupType == GroupType::FILE)
+ {
+ name = FilePath(p.first).fileName();
+ }
+
+ auto it = groupNodes.find(name);
+ if (it != groupNodes.end())
+ {
+ groupNode = it->second;
+ }
+ else
+ {
+ groupNode = std::make_shared(DummyNode::DUMMY_GROUP);
+ groupNode->visible = true;
+ groupNode->groupType = groupType;
+ groupNode->groupLayout = GroupLayout::BUCKET;
+ groupNode->name = name;
+
+ auto it = nodeIdtoParentMap.find(p.second[0]->tokenId);
+ if (it != nodeIdtoParentMap.end())
+ {
+ groupNode->tokenId = it->second.first;
+ }
+ m_topLevelAncestorIds[groupNode->tokenId] = groupNode->tokenId;
+ m_dummyNodes.push_back(groupNode);
+ }
+
+ std::vector bundleInfos;
+ for (std::shared_ptr dummyNode : p.second)
+ {
+ if (dummyNode->hasActiveSubNode())
+ {
+ groupNode->bundleInfo = dummyNode->bundleInfo;
+ groupNode->bundleId = dummyNode->bundleId;
+ }
+ else
+ {
+ bundleInfos.push_back(dummyNode->bundleInfo);
+ }
+
+ groupNode->subNodes.push_back(dummyNode);
+ m_topLevelAncestorIds[dummyNode->tokenId] = groupNode->tokenId;
+ groupedNodeIds.insert(dummyNode->tokenId);
+ }
+
+ if (!groupNode->bundleId)
+ {
+ groupNode->bundleId = groupNode->subNodes[0]->bundleId;
+ groupNode->bundleInfo = DummyNode::BundleInfo::averageBundleInfo(bundleInfos);
+ }
+
+ groupNode->sortSubNodesByName();
+ }
+
+ for (int i = 0; i < int(m_dummyNodes.size()); i++)
+ {
+ if (groupedNodeIds.find(m_dummyNodes[i]->tokenId) != groupedNodeIds.end())
+ {
+ m_dummyNodes.erase(m_dummyNodes.begin() + i);
+ i--;
+ }
+ }
+}
+
+DummyNode* GraphController::groupAllNodes(GroupType groupType, Id groupNodeId)
+{
+ TRACE();
+
+ std::shared_ptr groupNode = std::make_shared(DummyNode::DUMMY_GROUP);
+ groupNode->visible = true;
+ groupNode->groupType = groupType;
+ groupNode->tokenId = groupNodeId;
+ m_topLevelAncestorIds[groupNode->tokenId] = groupNode->tokenId;
+
+ for (std::shared_ptr dummyNode : m_dummyNodes)
+ {
+ groupNode->subNodes.push_back(dummyNode);
+ m_topLevelAncestorIds[dummyNode->tokenId] = groupNode->tokenId;
+ }
+
+ if (groupNode->subNodes.size())
+ {
+ m_dummyNodes = { groupNode };
+ }
+
+ return groupNode.get();
+}
+
+void GraphController::groupTrailNodes(GroupType groupType)
{
TRACE();
@@ -1455,6 +1635,7 @@ void GraphController::groupTrailNodes(NodeType::GroupType groupType)
std::shared_ptr groupNode = std::make_shared(DummyNode::DUMMY_GROUP);
groupNode->visible = true;
groupNode->groupType = groupType;
+ groupNode->groupLayout = GroupLayout::SKEWED;
// Use token Id of first node and make first 2 bits 1
groupNode->tokenId = ~(~size_t(0) >> 2) + node.nodeId;
@@ -1626,6 +1807,10 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
{
width = margins.charWidth * node->name.size();
}
+ else if (node->isGroupNode())
+ {
+ width = margins.charWidth * node->name.size() + 5;
+ }
width += margins.iconWidth;
width = std::max(width, margins.minWidth);
@@ -1651,19 +1836,44 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
}
}
- int top = margins.top + margins.charHeight + margins.spacingA;
- int left = margins.left;
- Vec2i size;
if (node->isGroupNode())
{
- size = ListLayouter::layoutSkewed(
- &node->subNodes, top, left, margins.spacingX, margins.spacingY, getView()->getViewSize().x() * 1.5);
+ Vec2i viewSize = getView()->getViewSize();
+
+ switch (node->groupLayout)
+ {
+ case GroupLayout::LIST:
+ viewSize.x = viewSize.x - 150; // prevent horizontal scroll
+ ListLayouter::layoutMultiColumn(viewSize, &node->subNodes);
+ break;
+
+ case GroupLayout::SKEWED:
+ ListLayouter::layoutSkewed(&node->subNodes, margins.spacingX, margins.spacingY, viewSize.x() * 1.5);
+ break;
+
+ case GroupLayout::BUCKET:
+ if (node->hasActiveSubNode())
+ {
+ BucketLayouter grid(viewSize);
+ grid.createBuckets(node->subNodes, m_dummyEdges);
+ grid.layoutBuckets(true);
+ node->subNodes = grid.getSortedNodes();
+ }
+ else
+ {
+ ListLayouter::layoutColumn(&node->subNodes, margins.spacingY);
+ }
+ break;
+ }
}
else
{
- size = ListLayouter::layoutColumn(&node->subNodes, top, left, margins.spacingY);
+ ListLayouter::layoutColumn(&node->subNodes, margins.spacingY);
}
+ Vec2i size = ListLayouter::offsetNodes(
+ node->subNodes, margins.top + margins.charHeight + margins.spacingA, margins.left);
+
width = std::max(size.x(), width);
height = size.y();
@@ -1795,7 +2005,7 @@ void GraphController::layoutGraph(bool getSortedNodes)
BucketLayouter grid(getView()->getViewSize());
grid.createBuckets(visibleNodes, m_dummyEdges);
- grid.layoutBuckets();
+ grid.layoutBuckets(false);
if (getSortedNodes)
{
@@ -1891,7 +2101,8 @@ DummyEdge* GraphController::getDummyGraphEdgeById(Id tokenId) const
}
void GraphController::relayoutGraph(
- MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop, bool withCharacterIndex)
+ MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop, bool withCharacterIndex,
+ const std::wstring& groupName)
{
bool showsTrail = m_graph->getTrailMode() != Graph::TRAIL_NONE;
@@ -1900,11 +2111,28 @@ void GraphController::relayoutGraph(
if (hasCharacterIndex() || withCharacterIndex)
{
addCharacterIndex();
+
+ if (withCharacterIndex && m_dummyNodes.size())
+ {
+ // Use token Id of first node and make first 2 bits 1
+ Id groupId = ~(~size_t(0) >> 2) + m_dummyNodes[0]->tokenId;
+
+ DummyNode* group = groupAllNodes(GroupType::DEFAULT, groupId);
+ group->groupLayout = GroupLayout::LIST;
+ group->interactive = false;
+ group->name = groupName;
+ }
+
layoutNesting();
layoutList();
}
else
{
+ if (!showsTrail)
+ {
+ groupNodesByParents(getView()->getGrouping());
+ }
+
layoutNesting();
if (showsTrail)
diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h
index 820b8a7c..d0c0f12a 100644
--- a/src/lib/component/controller/GraphController.h
+++ b/src/lib/component/controller/GraphController.h
@@ -109,7 +109,9 @@ private:
void addCharacterIndex();
bool hasCharacterIndex() const;
- void groupTrailNodes(NodeType::GroupType groupType);
+ void groupNodesByParents(GroupType groupType);
+ DummyNode* groupAllNodes(GroupType groupType, Id groupNodeId);
+ void groupTrailNodes(GroupType groupType);
void layoutNesting();
void layoutNestingRecursive(DummyNode* node) const;
@@ -126,7 +128,8 @@ private:
DummyEdge* getDummyGraphEdgeById(Id tokenId) const;
void relayoutGraph(
- MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop, bool withCharacterIndex);
+ MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop, bool withCharacterIndex,
+ const std::wstring& groupName);
void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop);
void forEachDummyNodeRecursive(std::function func);
diff --git a/src/lib/component/controller/helper/BucketLayouter.cpp b/src/lib/component/controller/helper/BucketLayouter.cpp
index c0056aae..093c8f5a 100644
--- a/src/lib/component/controller/helper/BucketLayouter.cpp
+++ b/src/lib/component/controller/helper/BucketLayouter.cpp
@@ -194,7 +194,7 @@ void BucketLayouter::createBuckets(
}
}
-void BucketLayouter::layoutBuckets()
+void BucketLayouter::layoutBuckets(bool addVerticalOffset)
{
std::map widths;
std::map heights;
@@ -236,6 +236,11 @@ void BucketLayouter::layoutBuckets()
Bucket* midBucket = &m_buckets[0][0];
yOff = (heights[0] - midBucket->getHeight()) / 2 * -j;
}
+ // move every second bucket in a row slighly lower to avoid edges over nodes
+ else if (addVerticalOffset && std::abs(i) % 2 == 1)
+ {
+ yOff += GraphViewStyle::toGridGap(10);
+ }
bucket->layout(x, y + yOff, widths[i], heights[j]);
x += widths[i] + GraphViewStyle::toGridGap(85);
diff --git a/src/lib/component/controller/helper/BucketLayouter.h b/src/lib/component/controller/helper/BucketLayouter.h
index e9d312a6..98cbfc0d 100644
--- a/src/lib/component/controller/helper/BucketLayouter.h
+++ b/src/lib/component/controller/helper/BucketLayouter.h
@@ -44,7 +44,7 @@ public:
void createBuckets(
std::vector>& nodes,
const std::vector>& edges);
- void layoutBuckets();
+ void layoutBuckets(bool addVerticalOffset);
std::vector> getSortedNodes();
diff --git a/src/lib/component/controller/helper/DummyNode.h b/src/lib/component/controller/helper/DummyNode.h
index 8e5cc558..ab444cb7 100644
--- a/src/lib/component/controller/helper/DummyNode.h
+++ b/src/lib/component/controller/helper/DummyNode.h
@@ -7,6 +7,7 @@
#include "utility/utilityString.h"
#include "data/graph/Node.h"
+#include "data/GroupType.h"
#include "data/name/NameHierarchy.h"
#include "data/parser/AccessKind.h"
@@ -54,6 +55,32 @@ public:
, isReferencing(false)
{}
+ static BundleInfo averageBundleInfo(const std::vector& bundleInfos)
+ {
+ size_t activeCount = 0;
+ size_t definedCount = 0;
+ size_t verticalLayoutCount = 0;
+ size_t referencedCount = 0;
+ size_t referencingCount = 0;
+
+ for (const BundleInfo& info : bundleInfos)
+ {
+ if (info.isActive) activeCount++;
+ if (info.isDefined) definedCount++;
+ if (info.layoutVertical) verticalLayoutCount++;
+ if (info.isReferenced) referencedCount++;
+ if (info.isReferencing) referencingCount++;
+ }
+
+ BundleInfo info;
+ if (activeCount > bundleInfos.size() / 2) info.isActive = true;
+ if (definedCount > bundleInfos.size() / 2) info.isDefined = true;
+ if (verticalLayoutCount > bundleInfos.size() / 2) info.layoutVertical = true;
+ if (referencedCount > bundleInfos.size() / 2) info.isReferenced = true;
+ if (referencingCount > bundleInfos.size() / 2) info.isReferencing = true;
+ return info;
+ }
+
bool isActive;
bool isDefined;
bool layoutVertical;
@@ -73,7 +100,6 @@ public:
, expanded(false)
, autoExpanded(false)
, hasParent(true)
- , hasQualifier(false)
, accessKind(ACCESS_NONE)
, invisibleSubNodeCount(0)
, bundleId(0)
@@ -81,7 +107,9 @@ public:
, bundledNodeCount(0)
, bundledNodeType(NodeType::NODE_SYMBOL)
, qualifierName(NAME_DELIMITER_UNKNOWN)
- , groupType(NodeType::GROUP_FRAMELESS)
+ , groupType(GroupType::DEFAULT)
+ , groupLayout(GroupLayout::LIST)
+ , interactive(true)
{
}
@@ -365,6 +393,23 @@ public:
);
}
+ bool getsLayouted() const
+ {
+ return visible && !isExpandToggleNode() && !isQualifierNode();
+ }
+
+ const DummyNode* getQualifierNode() const
+ {
+ for (const std::shared_ptr& subNode : subNodes)
+ {
+ if (subNode->isQualifierNode())
+ {
+ return subNode.get();
+ }
+ }
+ return nullptr;
+ }
+
Type type;
Vec2i position;
@@ -387,7 +432,6 @@ public:
bool expanded;
bool autoExpanded;
bool hasParent;
- bool hasQualifier;
// AccessNode
AccessKind accessKind;
@@ -411,8 +455,10 @@ public:
NameHierarchy qualifierName;
// GroupNode
- NodeType::GroupType groupType;
+ GroupType groupType;
+ GroupLayout groupLayout;
std::vector hiddenEdgeIds;
+ bool interactive;
};
#endif // DUMMY_NODE_H
diff --git a/src/lib/component/controller/helper/ListLayouter.cpp b/src/lib/component/controller/helper/ListLayouter.cpp
index 8f1fdc40..bc310fbd 100644
--- a/src/lib/component/controller/helper/ListLayouter.cpp
+++ b/src/lib/component/controller/helper/ListLayouter.cpp
@@ -5,17 +5,17 @@
#include "component/controller/helper/DummyNode.h"
#include "component/view/GraphViewStyle.h"
-Vec2i ListLayouter::layoutRow(std::vector>* nodes, int top, int left, int gap)
+void ListLayouter::layoutRow(std::vector>* nodes, int gap)
{
- return layoutSimple(nodes, top, left, gap, 0, true);
+ layoutSimple(nodes, gap, 0, true);
}
-Vec2i ListLayouter::layoutColumn(std::vector>* nodes, int top, int left, int gap)
+void ListLayouter::layoutColumn(std::vector>* nodes, int gap)
{
- return layoutSimple(nodes, top, left, 0, gap, false);
+ layoutSimple(nodes, 0, gap, false);
}
-Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector>* nodes)
+void ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector>* nodes)
{
size_t colsFinal;
std::vector maxWidthsFinal;
@@ -26,7 +26,7 @@ Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector> visibleNodes;
for (auto node : *nodes)
{
- if (node->visible)
+ if (node->getsLayouted())
{
visibleNodes.push_back(node);
}
@@ -82,8 +82,6 @@ Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vectorposition.y = y;
y += visibleNodes[i]->size.y + gapY;
- height = std::max(height, y);
if (visibleNodes[i]->isTextNode())
{
lastTextNode = visibleNodes[i];
}
}
-
- for (int w : maxWidthsFinal)
- {
- width += w + gapX;
- }
-
- return Vec2i(width - gapX, height - gapY);
}
-Vec2i ListLayouter::layoutSkewed(
- std::vector>* nodes, int top, int left, int gapX, int gapY, int maxWidth)
+void ListLayouter::layoutSkewed(std::vector>* nodes, int gapX, int gapY, int maxWidth)
{
std::vector> visibleNodes;
std::multiset nodeWidths;
for (auto node : *nodes)
{
- if (node->visible)
+ if (node->getsLayouted())
{
visibleNodes.push_back(node);
nodeWidths.insert(node->size.x());
@@ -185,8 +174,8 @@ Vec2i ListLayouter::layoutSkewed(
}
DummyNode* node = visibleNodes[i].get();
- node->position.x = left + x + (nodeWidth - node->size.x()) / 2;
- node->position.y = top + height;
+ node->position.x = x + (nodeWidth - node->size.x()) / 2;
+ node->position.y = height;
rowHeight = std::max(rowHeight, node->size.y());
x += nodeWidth + gapX;
@@ -201,16 +190,6 @@ Vec2i ListLayouter::layoutSkewed(
break;
}
}
-
- Vec4i rect = boundingRect(visibleNodes);
- Vec2i offset(left - rect.x(), top - rect.y());
-
- for (auto node : visibleNodes)
- {
- node->position += offset;
- }
-
- return Vec2i(rect.z() - rect.x(), rect.w() - rect.y());
}
Vec4i ListLayouter::boundingRect(const std::vector>& nodes)
@@ -219,7 +198,7 @@ Vec4i ListLayouter::boundingRect(const std::vector>&
for (auto node : nodes)
{
- if (!node->visible)
+ if (!node->getsLayouted())
{
continue;
}
@@ -243,46 +222,44 @@ Vec4i ListLayouter::boundingRect(const std::vector>&
return rect;
}
-Vec2i ListLayouter::layoutSimple(
- std::vector>* nodes, int top, int left, int gapX, int gapY, bool horizontal)
+Vec2i ListLayouter::offsetNodes(std::vector> nodes, int top, int left)
+{
+ Vec4i rect = boundingRect(nodes);
+ Vec2i offset(left - rect.x(), top - rect.y());
+
+ for (auto node : nodes)
+ {
+ if (node->getsLayouted())
+ {
+ node->position += offset;
+ }
+ }
+
+ return Vec2i(rect.z() - rect.x(), rect.w() - rect.y());
+}
+
+void ListLayouter::layoutSimple(std::vector>* nodes, int gapX, int gapY, bool horizontal)
{
int y = 0;
int x = 0;
- int width = 0;
- int height = 0;
-
for (const std::shared_ptr& node : *nodes)
{
- if (!node->visible || node->isExpandToggleNode() || node->isQualifierNode())
+ if (!node->getsLayouted())
{
continue;
}
- node->position.x = left + x;
- node->position.y = top + y;
+ node->position.x = x;
+ node->position.y = y;
if (horizontal)
{
x += node->size.x + gapX;
- height = std::max(height, node->size.y());
}
else
{
y += node->size.y + gapY;
- width = std::max(width, node->size.x());
}
}
-
- if (x > 0)
- {
- width = x - gapX;
- }
-
- if (y > 0)
- {
- height = y - gapY;
- }
-
- return Vec2i(width, height);
}
diff --git a/src/lib/component/controller/helper/ListLayouter.h b/src/lib/component/controller/helper/ListLayouter.h
index b72a563b..e91d0ac0 100644
--- a/src/lib/component/controller/helper/ListLayouter.h
+++ b/src/lib/component/controller/helper/ListLayouter.h
@@ -12,17 +12,17 @@ struct DummyNode;
class ListLayouter
{
public:
- static Vec2i layoutRow(std::vector>* nodes, int top, int left, int gap);
- static Vec2i layoutColumn(std::vector>* nodes, int top, int left, int gap);
+ static void layoutRow(std::vector>* nodes, int gap);
+ static void layoutColumn(std::vector>* nodes, int gap);
- static Vec2i layoutMultiColumn(Vec2i viewSize, std::vector>* nodes);
- static Vec2i layoutSkewed(
- std::vector>* nodes, int top, int left, int gapX, int gapY, int maxWidth);
+ static void layoutMultiColumn(Vec2i viewSize, std::vector>* nodes);
+ static void layoutSkewed(std::vector>* nodes, int gapX, int gapY, int maxWidth);
static Vec4i boundingRect(const std::vector>& nodes);
+ static Vec2i offsetNodes(std::vector> nodes, int top, int left);
+
private:
- static Vec2i layoutSimple(
- std::vector>* nodes, int top, int left, int gapX, int gapY, bool horizontal);
+ static void layoutSimple(std::vector>* nodes, int gapX, int gapY, bool horizontal);
};
#endif // LIST_LAYOUTER_H
diff --git a/src/lib/component/view/GraphView.h b/src/lib/component/view/GraphView.h
index 97f3fbef..dae5e791 100644
--- a/src/lib/component/view/GraphView.h
+++ b/src/lib/component/view/GraphView.h
@@ -8,6 +8,7 @@
#include "component/controller/helper/ScreenSearchInterfaces.h"
#include "component/view/View.h"
+#include "data/GroupType.h"
struct DummyEdge;
struct DummyNode;
@@ -47,6 +48,7 @@ public:
virtual void resizeView() = 0;
virtual Vec2i getViewSize() const = 0;
+ virtual GroupType getGrouping() const = 0;
virtual void scrollToValues(int xValue, int yValue) = 0;
diff --git a/src/lib/component/view/GraphViewStyle.cpp b/src/lib/component/view/GraphViewStyle.cpp
index fbb82523..1e94b36f 100644
--- a/src/lib/component/view/GraphViewStyle.cpp
+++ b/src/lib/component/view/GraphViewStyle.cpp
@@ -156,6 +156,9 @@ size_t GraphViewStyle::getFontSizeForStyleType(NodeType::StyleType type)
case NodeType::STYLE_SMALL_NODE:
return s_fontSize - 3;
+ case NodeType::STYLE_GROUP:
+ return s_fontSize - 2;
+
default:
return s_fontSize;
}
@@ -188,7 +191,7 @@ size_t GraphViewStyle::getFontSizeOfTextNode()
size_t GraphViewStyle::getFontSizeOfGroupNode()
{
- return s_fontSize;
+ return getFontSizeForStyleType(NodeType::STYLE_GROUP);
}
std::string GraphViewStyle::getFontNameForDataNode()
@@ -225,6 +228,7 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsForDataNode(NodeType::Styl
switch (type)
{
case NodeType::STYLE_PACKAGE:
+ case NodeType::STYLE_GROUP:
margins.left = margins.right = 5;
margins.top = margins.bottom = 3;
margins.iconWidth = s_fontSize - 3;
@@ -331,20 +335,20 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfTextNode()
return margins;
}
-GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfGroupNode(NodeType::GroupType type, bool hasName)
+GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfGroupNode(GroupType type, bool hasName)
{
NodeMargins margins;
margins.spacingX = margins.spacingY = GraphViewStyle::s_gridCellPadding;
- if (type == NodeType::GROUP_FRAMELESS)
+ if (type == GroupType::FRAMELESS)
{
return margins;
}
- margins.spacingA = (hasName ? 22 : 0);
+ margins.spacingA = (hasName ? 14 : 0);
margins.left = margins.right = 20;
- margins.top = (hasName ? 8 : 20);
+ margins.top = (hasName ? 12 : 20);
margins.bottom = 20;
if (hasName)
@@ -352,7 +356,7 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfGroupNode(NodeType::Grou
margins.minWidth = margins.charHeight = getFontSizeOfGroupNode();
}
- margins.charWidth = getCharWidth(NodeType::STYLE_BIG_NODE);
+ margins.charWidth = getCharWidth(NodeType::STYLE_GROUP);
return margins;
}
@@ -401,6 +405,7 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleForNodeType(
switch (type)
{
case NodeType::STYLE_PACKAGE:
+ case NodeType::STYLE_GROUP:
style.cornerRadius = 0;
style.textOffset.x = 5;
style.textOffset.y = 3;
@@ -555,27 +560,48 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfTextNode()
return style;
}
-GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfGroupNode(NodeType::GroupType type, bool isFocused)
+GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfGroupNode(GroupType type, bool isFocused)
{
NodeStyle style;
+ style.cornerRadius = 15;
+ style.borderWidth = 2;
+
std::string colorType = "group/";
- if (type == NodeType::GROUP_INHERITANCE)
+ if (type == GroupType::DEFAULT)
+ {
+ colorType += "default";
+ }
+ else if (type == GroupType::FILE)
+ {
+ colorType += "file";
+ }
+ else if (type == GroupType::NAMESPACE)
+ {
+ colorType += "namespace";
+ }
+ else if (type == GroupType::INHERITANCE)
{
colorType += "inheritance";
+
+ if (isFocused)
+ {
+ style.borderWidth = 3;
+ }
+ }
+ else
+ {
+ return style;
}
style.color = getNodeColor(colorType, isFocused);
- style.cornerRadius = 15;
- style.borderWidth = 2;
-
style.fontName = getFontNameOfGroupNode();
style.fontSize = getFontSizeOfGroupNode();
style.fontBold = true;
- style.textOffset.x = 15;
- style.textOffset.y = 6;
+ style.textOffset.x = 12;
+ style.textOffset.y = 5;
return style;
}
@@ -638,7 +664,7 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(
style.verticalOffset = 6;
break;
case Edge::EDGE_INHERITANCE:
- style.arrowLength = 20;
+ style.arrowLength = 17;
style.arrowWidth = 14;
style.arrowClosed = true;
style.originOffset.x = 7;
@@ -648,6 +674,7 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(
style.verticalOffset = 0;
style.cornerRadius = 7;
style.zValue = isActive ? 2 : -3;
+ style.width = isActive ? 3 : 2;
if (isTrailEdge)
{
diff --git a/src/lib/component/view/GraphViewStyle.h b/src/lib/component/view/GraphViewStyle.h
index 5c8bd7cf..c160fdcb 100644
--- a/src/lib/component/view/GraphViewStyle.h
+++ b/src/lib/component/view/GraphViewStyle.h
@@ -7,6 +7,7 @@
#include "utility/math/Vector2.h"
#include "data/graph/Node.h"
+#include "data/GroupType.h"
#include "data/parser/AccessKind.h"
class GraphViewStyleImpl;
@@ -117,7 +118,7 @@ public:
static NodeMargins getMarginsOfExpandToggleNode();
static NodeMargins getMarginsOfBundleNode();
static NodeMargins getMarginsOfTextNode();
- static NodeMargins getMarginsOfGroupNode(NodeType::GroupType type, bool hasName);
+ static NodeMargins getMarginsOfGroupNode(GroupType type, bool hasName);
static NodeStyle getStyleForNodeType(
NodeType type, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier);
@@ -127,7 +128,7 @@ public:
static NodeStyle getStyleOfBundleNode(bool isFocused);
static NodeStyle getStyleOfQualifier();
static NodeStyle getStyleOfTextNode();
- static NodeStyle getStyleOfGroupNode(NodeType::GroupType type, bool isFocused);
+ static NodeStyle getStyleOfGroupNode(GroupType type, bool isFocused);
static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused, bool isTrailEdge);
diff --git a/src/lib/data/GroupType.cpp b/src/lib/data/GroupType.cpp
new file mode 100644
index 00000000..1efbe65b
--- /dev/null
+++ b/src/lib/data/GroupType.cpp
@@ -0,0 +1,28 @@
+#include "data/GroupType.h"
+
+std::wstring groupTypeToString(GroupType type)
+{
+ switch (type)
+ {
+ case GroupType::NONE: return L"none";
+ case GroupType::DEFAULT: return L"default";
+ case GroupType::FRAMELESS: return L"frameless";
+ case GroupType::FILE: return L"file";
+ case GroupType::NAMESPACE: return L"namespace";
+ case GroupType::INHERITANCE: return L"inheritance";
+ }
+
+ return L"none";
+}
+
+GroupType stringToGroupType(const std::wstring& value)
+{
+ if (value == groupTypeToString(GroupType::NONE)) return GroupType::NONE;
+ if (value == groupTypeToString(GroupType::DEFAULT)) return GroupType::DEFAULT;
+ if (value == groupTypeToString(GroupType::FRAMELESS)) return GroupType::FRAMELESS;
+ if (value == groupTypeToString(GroupType::FILE)) return GroupType::FILE;
+ if (value == groupTypeToString(GroupType::NAMESPACE)) return GroupType::NAMESPACE;
+ if (value == groupTypeToString(GroupType::INHERITANCE)) return GroupType::INHERITANCE;
+
+ return GroupType::NONE;
+}
diff --git a/src/lib/data/GroupType.h b/src/lib/data/GroupType.h
new file mode 100644
index 00000000..0ad2582c
--- /dev/null
+++ b/src/lib/data/GroupType.h
@@ -0,0 +1,26 @@
+#ifndef GROUP_TYPE_H
+#define GROUP_TYPE_H
+
+#include
+
+enum class GroupType
+{
+ NONE,
+ DEFAULT,
+ FRAMELESS,
+ FILE,
+ NAMESPACE,
+ INHERITANCE
+};
+
+std::wstring groupTypeToString(GroupType type);
+GroupType stringToGroupType(const std::wstring& value);
+
+enum class GroupLayout
+{
+ LIST,
+ SKEWED,
+ BUCKET
+};
+
+#endif // GROUP_TYPE_H
diff --git a/src/lib/data/NodeType.h b/src/lib/data/NodeType.h
index 5e31aa0e..bb5bf1e2 100644
--- a/src/lib/data/NodeType.h
+++ b/src/lib/data/NodeType.h
@@ -48,13 +48,8 @@ public:
{
STYLE_PACKAGE = 0,
STYLE_SMALL_NODE = 1,
- STYLE_BIG_NODE = 2
- };
-
- enum GroupType
- {
- GROUP_FRAMELESS,
- GROUP_INHERITANCE
+ STYLE_BIG_NODE = 2,
+ STYLE_GROUP = 3
};
struct BundleInfo
diff --git a/src/lib/data/access/StorageAccess.h b/src/lib/data/access/StorageAccess.h
index 37e8bbd4..99079280 100644
--- a/src/lib/data/access/StorageAccess.h
+++ b/src/lib/data/access/StorageAccess.h
@@ -41,6 +41,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id id) const = 0;
virtual std::vector getNameHierarchiesForNodeIds(const std::vector& nodeIds) const = 0;
+ virtual std::map> getNodeIdToParentFileMap(const std::vector& nodeIds) const = 0;
virtual NodeType getNodeTypeForNodeWithId(Id id) const = 0;
diff --git a/src/lib/data/access/StorageAccessProxy.cpp b/src/lib/data/access/StorageAccessProxy.cpp
index 46763a57..218ec5bd 100644
--- a/src/lib/data/access/StorageAccessProxy.cpp
+++ b/src/lib/data/access/StorageAccessProxy.cpp
@@ -85,6 +85,16 @@ std::vector StorageAccessProxy::getNameHierarchiesForNodeIds(cons
return std::vector();
}
+std::map> StorageAccessProxy::getNodeIdToParentFileMap(const std::vector& nodeIds) const
+{
+ if (hasSubject())
+ {
+ return m_subject->getNodeIdToParentFileMap(nodeIds);
+ }
+
+ return { };
+}
+
NodeType StorageAccessProxy::getNodeTypeForNodeWithId(Id id) const
{
if (hasSubject())
diff --git a/src/lib/data/access/StorageAccessProxy.h b/src/lib/data/access/StorageAccessProxy.h
index c6a1d06d..bcb1936e 100644
--- a/src/lib/data/access/StorageAccessProxy.h
+++ b/src/lib/data/access/StorageAccessProxy.h
@@ -24,6 +24,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id id) const override;
virtual std::vector getNameHierarchiesForNodeIds(const std::vector& nodeIds) const override;
+ virtual std::map> getNodeIdToParentFileMap(const std::vector& nodeIds) const override;
virtual NodeType getNodeTypeForNodeWithId(Id id) const override;
diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp
index 7eb517dc..78cd6085 100644
--- a/src/lib/data/storage/PersistentStorage.cpp
+++ b/src/lib/data/storage/PersistentStorage.cpp
@@ -445,6 +445,28 @@ std::vector PersistentStorage::getNameHierarchiesForNodeIds(const
return nameHierarchies;
}
+std::map> PersistentStorage::getNodeIdToParentFileMap(const std::vector& nodeIds) const
+{
+ std::map> nodeIdToParentFileMap;
+
+ std::shared_ptr locations = m_sqliteIndexStorage.getSourceLocationsForElementIds(nodeIds);
+ locations->forEachSourceLocation(
+ [this, &nodeIdToParentFileMap](SourceLocation* location)
+ {
+ if (location->isStartLocation() && location->isScopeLocation())
+ {
+ for (Id tokenId : location->getTokenIds())
+ {
+ nodeIdToParentFileMap.emplace(tokenId, std::make_pair(getFileNodeId(location->getFilePath()),
+ NameHierarchy(location->getFilePath().wstr(), NAME_DELIMITER_FILE)));
+ }
+ }
+ }
+ );
+
+ return nodeIdToParentFileMap;
+}
+
NodeType PersistentStorage::getNodeTypeForNodeWithId(Id nodeId) const
{
return utility::intToType(m_sqliteIndexStorage.getFirstById(nodeId).type);
diff --git a/src/lib/data/storage/PersistentStorage.h b/src/lib/data/storage/PersistentStorage.h
index 26154204..29008215 100644
--- a/src/lib/data/storage/PersistentStorage.h
+++ b/src/lib/data/storage/PersistentStorage.h
@@ -77,6 +77,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id nodeId) const override;
virtual std::vector getNameHierarchiesForNodeIds(const std::vector& nodeIds) const override;
+ virtual std::map> getNodeIdToParentFileMap(const std::vector& nodeIds) const override;
virtual NodeType getNodeTypeForNodeWithId(Id nodeId) const override;
diff --git a/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp b/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp
index 23f15ebe..e0b87a9d 100644
--- a/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp
+++ b/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp
@@ -6,6 +6,9 @@
#include "utility/text/TextAccess.h"
#include "utility/utilityString.h"
+#include "data/location/SourceLocationCollection.h"
+#include "data/location/SourceLocationFile.h"
+
const size_t SqliteIndexStorage::s_storageVersion = 15;
SqliteIndexStorage::SqliteIndexStorage(const FilePath& dbFilePath)
@@ -704,9 +707,60 @@ std::shared_ptr SqliteIndexStorage::getSourceLocationsOfType
return getSourceLocationsForFile(filePath, "AND type == " + std::to_string(locationTypeToInt(type)));
}
+std::shared_ptr SqliteIndexStorage::getSourceLocationsForElementIds(
+ const std::vector& elementIds) const
+{
+ std::vector sourceLocationIds;
+ std::map> sourceLocationIdToElementIds;
+ for (const StorageOccurrence& occurrence : getOccurrencesForElementIds(elementIds))
+ {
+ sourceLocationIds.push_back(occurrence.sourceLocationId);
+ sourceLocationIdToElementIds[occurrence.sourceLocationId].push_back(occurrence.elementId);
+ }
+
+ CppSQLite3Query q = executeQuery(
+ "SELECT source_location.id, file.path, source_location.start_line, source_location.start_column, "
+ "source_location.end_line, source_location.end_column, source_location.type "
+ "FROM source_location INNER JOIN file ON (file.id = source_location.file_node_id) "
+ "WHERE source_location.id IN (" + utility::join(utility::toStrings(sourceLocationIds), ',') + ");"
+ );
+
+ std::shared_ptr ret = std::make_shared();
+
+ while (!q.eof())
+ {
+ const Id id = q.getIntField(0, 0);
+ const std::string filePath = q.getStringField(1, "");
+ const int startLineNumber = q.getIntField(2, -1);
+ const int startColNumber = q.getIntField(3, -1);
+ const int endLineNumber = q.getIntField(4, -1);
+ const int endColNumber = q.getIntField(5, -1);
+ const int type = q.getIntField(6, -1);
+
+ if (id != 0 && filePath.size() && startLineNumber != -1 && startColNumber != -1 && endLineNumber != -1 &&
+ endColNumber != -1 && type != -1)
+ {
+ ret->addSourceLocation(
+ intToLocationType(type),
+ id,
+ sourceLocationIdToElementIds[id],
+ FilePath(utility::decodeFromUtf8(filePath)),
+ startLineNumber,
+ startColNumber,
+ endLineNumber,
+ endColNumber
+ );
+ }
+
+ q.nextRow();
+ }
+
+ return ret;
+}
+
std::vector SqliteIndexStorage::getOccurrencesForLocationId(Id locationId) const
{
- std::vector locationIds {locationId};
+ std::vector locationIds { locationId };
return getOccurrencesForLocationIds(locationIds);
}
diff --git a/src/lib/data/storage/sqlite/SqliteIndexStorage.h b/src/lib/data/storage/sqlite/SqliteIndexStorage.h
index 7514d7fd..7ec586b9 100644
--- a/src/lib/data/storage/sqlite/SqliteIndexStorage.h
+++ b/src/lib/data/storage/sqlite/SqliteIndexStorage.h
@@ -5,7 +5,7 @@
#include
#include
-#include "data/location/SourceLocationFile.h"
+#include "data/location/LocationType.h"
#include "data/storage/sqlite/SqliteDatabaseIndex.h"
#include "data/storage/sqlite/SqliteStorage.h"
#include "data/storage/type/StorageCommentLocation.h"
@@ -24,6 +24,8 @@
class TextAccess;
class Version;
+class SourceLocationCollection;
+class SourceLocationFile;
struct ParseLocation;
class SqliteIndexStorage
@@ -95,6 +97,8 @@ public:
std::shared_ptr getSourceLocationsOfTypeInFile(
const FilePath& filePath, LocationType type) const;
+ std::shared_ptr getSourceLocationsForElementIds(const std::vector& elementIds) const;
+
std::vector getOccurrencesForLocationId(Id locationId) const;
std::vector getOccurrencesForLocationIds(const std::vector& locationIds) const;
std::vector getOccurrencesForElementIds(const std::vector& elementIds) const;
diff --git a/src/lib/settings/ApplicationSettings.cpp b/src/lib/settings/ApplicationSettings.cpp
index eb253322..267bc48e 100644
--- a/src/lib/settings/ApplicationSettings.cpp
+++ b/src/lib/settings/ApplicationSettings.cpp
@@ -241,6 +241,16 @@ void ApplicationSettings::setGraphControlsVisible(bool visible)
setValue("application/graph_controls_visible", visible);
}
+GroupType ApplicationSettings::getGraphGrouping() const
+{
+ return stringToGroupType(getValue("application/graph_grouping", groupTypeToString(GroupType::NONE)));
+}
+
+void ApplicationSettings::setGraphGrouping(GroupType type)
+{
+ setValue("application/graph_grouping", groupTypeToString(type));
+}
+
int ApplicationSettings::getScreenAutoScaling() const
{
return getValue("screen/auto_scaling", 1);
diff --git a/src/lib/settings/ApplicationSettings.h b/src/lib/settings/ApplicationSettings.h
index 78fa7a0a..4497496c 100644
--- a/src/lib/settings/ApplicationSettings.h
+++ b/src/lib/settings/ApplicationSettings.h
@@ -3,6 +3,7 @@
#include
+#include "data/GroupType.h"
#include "settings/Settings.h"
class TimeStamp;
@@ -63,6 +64,9 @@ public:
bool getGraphControlsVisible() const;
void setGraphControlsVisible(bool visible);
+ GroupType getGraphGrouping() const;
+ void setGraphGrouping(GroupType type);
+
// screen
int getScreenAutoScaling() const;
void setScreenAutoScaling(int autoScaling);
diff --git a/src/lib/utility/messaging/type/MessageActivateNodes.h b/src/lib/utility/messaging/type/MessageActivateNodes.h
index 828613d1..3db732e3 100644
--- a/src/lib/utility/messaging/type/MessageActivateNodes.h
+++ b/src/lib/utility/messaging/type/MessageActivateNodes.h
@@ -18,8 +18,19 @@ public:
NameHierarchy nameHierarchy;
};
- MessageActivateNodes()
+ MessageActivateNodes(Id tokenId = 0)
{
+ if (tokenId > 0)
+ {
+ addNode(tokenId);
+ }
+ }
+
+ void addNode(Id tokenId)
+ {
+ ActiveNode node;
+ node.nodeId = tokenId;
+ nodes.push_back(node);
}
void addNode(Id tokenId, const NameHierarchy& nameHierarchy)
@@ -27,7 +38,6 @@ public:
ActiveNode node;
node.nodeId = tokenId;
node.nameHierarchy = nameHierarchy;
-
nodes.push_back(node);
}
diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp
index def2027a..74a2d1a0 100644
--- a/src/lib_gui/qt/view/QtGraphView.cpp
+++ b/src/lib_gui/qt/view/QtGraphView.cpp
@@ -15,6 +15,7 @@
#include "utility/messaging/type/MessageActivateTrail.h"
#include "utility/messaging/type/MessageDeactivateEdge.h"
+#include "utility/messaging/type/MessageRefresh.h"
#include "utility/messaging/type/MessageScrollGraph.h"
#include "utility/messaging/type/MessageStatus.h"
#include "utility/ResourcePaths.h"
@@ -158,6 +159,52 @@ void QtGraphView::initView()
updateTrailButtons();
trailDepthChanged(0);
}
+
+ // group controls
+ {
+ m_groupFileButton = new QtSelfRefreshIconButton(
+ "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/file.png"), "search/button");
+ m_groupNamespaceButton = new QtSelfRefreshIconButton(
+ "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/group_namespace.png"), "search/button");
+
+ m_groupFileButton->setObjectName("group_right_button");
+ m_groupNamespaceButton->setObjectName("group_left_button");
+
+ m_groupFileButton->setToolTip("group by file");
+ m_groupNamespaceButton->setToolTip("group by package/namespace");
+
+ m_groupFileButton->setCheckable(true);
+ m_groupNamespaceButton->setCheckable(true);
+
+ m_groupFileButton->setIconSize(QSize(14, 14));
+ m_groupNamespaceButton->setIconSize(QSize(14, 14));
+
+ connect(m_groupFileButton, &QPushButton::clicked, [this](){ groupingUpdated(m_groupFileButton); });
+ connect(m_groupNamespaceButton, &QPushButton::clicked, [this]() { groupingUpdated(m_groupNamespaceButton); });
+
+ GroupType type = ApplicationSettings::getInstance()->getGraphGrouping();
+ if (type == GroupType::FILE)
+ {
+ m_groupFileButton->setChecked(true);
+ }
+ else if (type == GroupType::NAMESPACE)
+ {
+ m_groupNamespaceButton->setChecked(true);
+ }
+
+
+ m_groupWidget = new QWidget(widget);
+ m_groupWidget->setGeometry(38, 8, 54, 26);
+
+ QHBoxLayout* layout = new QHBoxLayout();
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(2);
+
+ layout->addWidget(m_groupNamespaceButton);
+ layout->addWidget(m_groupFileButton);
+
+ m_groupWidget->setLayout(layout);
+ }
}
void QtGraphView::refreshView()
@@ -173,6 +220,8 @@ void QtGraphView::refreshView()
view->setAppZoomFactor(GraphViewStyle::getZoomFactor());
m_trailWidget->setStyleSheet(css.c_str());
+ m_groupWidget->setStyleSheet(css.c_str());
+
updateTrailButtons();
});
}
@@ -407,7 +456,7 @@ void QtGraphView::defocusTokenIds(const std::vector& defocusedTokenIds)
for (const Id& tokenId : defocusedTokenIds)
{
QtGraphNode* node = findNodeRecursive(m_oldNodes, tokenId);
- if (node && node->isDataNode())
+ if (node && (node->isDataNode() || node->isGroupNode()))
{
node->focusOut();
continue;
@@ -441,6 +490,20 @@ Vec2i QtGraphView::getViewSize() const
return Vec2i((view->width() - 50) / zoomFactor, (view->height() - 100) / zoomFactor);
}
+GroupType QtGraphView::getGrouping() const
+{
+ if (m_groupFileButton->isChecked())
+ {
+ return GroupType::FILE;
+ }
+ else if (m_groupNamespaceButton->isChecked())
+ {
+ return GroupType::NAMESPACE;
+ }
+
+ return GroupType::NONE;
+}
+
void QtGraphView::scrollToValues(int xValue, int yValue)
{
m_restoreScroll = true;
@@ -560,9 +623,17 @@ void QtGraphView::pressedCharacterKey(QChar c)
const QtGraphNode* node = nullptr;
bool hasTextNodes = false;
- for (const QtGraphNode* n : m_oldNodes)
+ std::vector nodes(m_oldNodes.begin(), m_oldNodes.end());
+
+ size_t i = 0;
+ while (i < nodes.size())
{
- if (n->isTextNode() && n->getName().size())
+ QtGraphNode* n = nodes[i++];
+ if (n->isGroupNode())
+ {
+ nodes.insert(nodes.end(), n->getSubNodes().begin(), n->getSubNodes().end());
+ }
+ else if (n->isTextNode() && n->getName().size())
{
hasTextNodes = true;
QChar start(n->getName()[0]);
@@ -657,6 +728,20 @@ void QtGraphView::clickedForwardTrail()
activateTrail(true);
}
+void QtGraphView::groupingUpdated(QPushButton* button)
+{
+ (button == m_groupFileButton ? m_groupNamespaceButton : m_groupFileButton)->setChecked(false);
+
+ ApplicationSettings* appSettings = ApplicationSettings::getInstance().get();
+ if (appSettings->getGraphGrouping() != getGrouping())
+ {
+ appSettings->setGraphGrouping(getGrouping());
+ appSettings->save();
+ }
+
+ MessageRefresh().refreshUiOnly().noReloadStyle().dispatch();
+}
+
MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
{
MessageActivateTrail message(0, 0, 0, 0, false);
@@ -868,7 +953,7 @@ QtGraphNode* QtGraphView::createNodeRecursive(
QtGraphNode* newNode = nullptr;
if (node->isGraphNode())
{
- newNode = new QtGraphNodeData(node->data, node->name, node->childVisible, node->hasQualifier);
+ newNode = new QtGraphNodeData(node->data, node->name, node->childVisible, node->getQualifierNode() != nullptr);
}
else if (node->isAccessNode())
{
@@ -892,7 +977,7 @@ QtGraphNode* QtGraphView::createNodeRecursive(
}
else if (node->isGroupNode())
{
- newNode = new QtGraphNodeGroup(node->tokenId, node->name, node->groupType);
+ newNode = new QtGraphNodeGroup(node->tokenId, node->name, node->groupType, node->interactive);
}
else
{
diff --git a/src/lib_gui/qt/view/QtGraphView.h b/src/lib_gui/qt/view/QtGraphView.h
index c77377b8..6cfed72a 100644
--- a/src/lib_gui/qt/view/QtGraphView.h
+++ b/src/lib_gui/qt/view/QtGraphView.h
@@ -61,6 +61,7 @@ public:
virtual void resizeView();
virtual Vec2i getViewSize() const;
+ virtual GroupType getGrouping() const;
virtual void scrollToValues(int xValue, int yValue);
@@ -84,6 +85,8 @@ private slots:
void clickedBackwardTrail();
void clickedForwardTrail();
+ void groupingUpdated(QPushButton* button);
+
private:
MessageActivateTrail getMessageActivateTrail(bool forward);
void activateTrail(bool forward);
@@ -153,6 +156,10 @@ private:
QSlider* m_trailDepthSlider;
QLabel* m_trailDepthLabel;
+ QWidget* m_groupWidget;
+ QtSelfRefreshIconButton* m_groupFileButton;
+ QtSelfRefreshIconButton* m_groupNamespaceButton;
+
std::vector m_virtualNodeRects;
// Name matches
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp
index 8985b501..1821d7b4 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp
+++ b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp
@@ -88,8 +88,8 @@ Id QtGraphEdge::getTokenId() const
void QtGraphEdge::updateLine()
{
- QtGraphNode* owner = m_owner;
- QtGraphNode* target = m_target;
+ const QtGraphNode* owner = m_owner;
+ const QtGraphNode* target = m_target;
Edge::EdgeType type = (getData() ? getData()->getType() : Edge::EDGE_AGGREGATION);
GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive | m_isFocused, false, m_isTrailEdge);
@@ -97,8 +97,22 @@ void QtGraphEdge::updateLine()
Vec4i ownerRect = owner->getBoundingRect();
Vec4i targetRect = target->getBoundingRect();
- Vec4i ownerParentRect = owner->getParentBoundingRect();
- Vec4i targetParentRect = target->getParentBoundingRect();
+ Vec4i ownerParentRect;
+ Vec4i targetParentRect;
+
+ const QtGraphNode* ownerNonGroupParent = owner->getLastNonGroupParent();
+ const QtGraphNode* targetNonGroupParent = target->getLastNonGroupParent();
+
+ if (owner->getLastParent() == target->getLastParent() && owner->getLastParent()->isGroupNode())
+ {
+ ownerParentRect = ownerNonGroupParent->getBoundingRect();
+ targetParentRect = targetNonGroupParent->getBoundingRect();
+ }
+ else
+ {
+ ownerParentRect = owner->getLastParent()->getBoundingRect();
+ targetParentRect = target->getLastParent()->getBoundingRect();
+ }
if (m_useBezier)
{
@@ -142,7 +156,7 @@ void QtGraphEdge::updateLine()
bezier->setRoute(route);
bezier->setPivot(QtLineItemBase::PIVOT_MIDDLE);
- if (owner->getLastParent() == target->getLastParent())
+ if (ownerNonGroupParent == targetNonGroupParent)
{
if (ownerRect.y() < target->getBoundingRect().y())
{
@@ -163,7 +177,7 @@ void QtGraphEdge::updateLine()
QtLineItemAngled* child = dynamic_cast(m_child);
- if (owner->getIsActive() && owner->getLastParent() == target->getLastParent())
+ if (owner->getIsActive() && ownerNonGroupParent == targetNonGroupParent)
{
child->setOnBack(true);
}
@@ -172,7 +186,7 @@ void QtGraphEdge::updateLine()
{
child->setEarlyBend(true);
- if (owner->getLastParent() == target->getLastParent() ||
+ if (ownerNonGroupParent == targetNonGroupParent ||
(type == Edge::EDGE_OVERRIDE &&
targetParentRect.z() + style.targetOffset.x + style.originOffset.x > ownerParentRect.x()))
{
@@ -185,7 +199,7 @@ void QtGraphEdge::updateLine()
}
if (type == Edge::EDGE_INHERITANCE || (type == Edge::EDGE_TEMPLATE_SPECIALIZATION &&
- owner == owner->getLastParent() && target == target->getLastParent()))
+ owner == owner->getLastNonGroupParent() && target == target->getLastNonGroupParent()))
{
child->setRoute(QtLineItemBase::ROUTE_VERTICAL);
@@ -194,7 +208,8 @@ void QtGraphEdge::updateLine()
child->setEarlyBend(true);
}
}
- else if (type != Edge::EDGE_AGGREGATION || owner != owner->getLastParent() || target != target->getLastParent())
+ else if (type != Edge::EDGE_AGGREGATION ||
+ owner != owner->getLastNonGroupParent() || target != target->getLastNonGroupParent())
{
child->setRoute(QtLineItemBase::ROUTE_HORIZONTAL);
}
@@ -250,13 +265,18 @@ void QtGraphEdge::setIsFocused(bool isFocused)
void QtGraphEdge::onClick()
{
- Edge::EdgeType type = (getData() ? getData()->getType() : Edge::EDGE_AGGREGATION);
-
if (!getData() || m_owner->isGroupNode() || m_target->isGroupNode())
{
- QtGraphNode* node =
- ((m_direction == TokenComponentAggregation::DIRECTION_BACKWARD) == (type != Edge::EDGE_INHERITANCE))
- ? m_owner : m_target;
+ QtGraphNode* node = (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD ? m_owner : m_target);
+ if (m_owner->isGroupNode())
+ {
+ node = m_owner;
+ }
+ else if (m_target->isGroupNode())
+ {
+ node = m_target;
+ }
+
MessageGraphNodeBundleSplit(node->getTokenId()).dispatch();
}
else if (isTrailEdge())
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp
index 59bb187f..8c6d5faa 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp
+++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp
@@ -61,13 +61,13 @@ QtGraphNode* QtGraphNode::getParent() const
return m_parentNode;
}
-QtGraphNode* QtGraphNode::getLastParent() const
+QtGraphNode* QtGraphNode::getLastParent(bool noGroups) const
{
QtGraphNode* node = const_cast(this);
while (true)
{
QtGraphNode* parent = dynamic_cast(node->parentItem());
- if (!parent)
+ if (!parent || (noGroups && parent->isGroupNode()))
{
break;
}
@@ -76,6 +76,11 @@ QtGraphNode* QtGraphNode::getLastParent() const
return node;
}
+QtGraphNode* QtGraphNode::getLastNonGroupParent() const
+{
+ return getLastParent(true);
+}
+
void QtGraphNode::setParent(QtGraphNode* parentNode)
{
m_parentNode = parentNode;
@@ -86,7 +91,7 @@ void QtGraphNode::setParent(QtGraphNode* parentNode)
}
}
-std::list QtGraphNode::getSubNodes() const
+const std::list& QtGraphNode::getSubNodes() const
{
return m_subNodes;
}
@@ -142,11 +147,6 @@ Vec4i QtGraphNode::getBoundingRect() const
return Vec4i(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
}
-Vec4i QtGraphNode::getParentBoundingRect() const
-{
- return getLastParent()->getBoundingRect();
-}
-
void QtGraphNode::addOutEdge(QtGraphEdge* edge)
{
m_outEdges.push_back(edge);
@@ -348,7 +348,7 @@ void QtGraphNode::addSubNode(QtGraphNode* node)
if (node->getIsActive())
{
QtGraphNode* parent = this;
- while (parent)
+ while (parent && !parent->isGroupNode())
{
parent->setZValue(-10.0f);
parent->m_text->setZValue(-9.0f);
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.h b/src/lib_gui/qt/view/graphElements/QtGraphNode.h
index e43934ab..161a68cb 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphNode.h
+++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.h
@@ -37,10 +37,11 @@ public:
virtual ~QtGraphNode();
QtGraphNode* getParent() const;
- QtGraphNode* getLastParent() const;
+ QtGraphNode* getLastParent(bool noGroups = false) const;
+ QtGraphNode* getLastNonGroupParent() const;
void setParent(QtGraphNode* parentNode);
- std::list getSubNodes() const;
+ const std::list& getSubNodes() const;
Vec2i getPosition() const;
virtual bool setPosition(const Vec2i& position);
@@ -52,7 +53,6 @@ public:
void setSize(const QSize& size);
Vec4i getBoundingRect() const;
- Vec4i getParentBoundingRect() const;
void addOutEdge(QtGraphEdge* edge);
void addInEdge(QtGraphEdge* edge);
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeData.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNodeData.cpp
index f03f19ae..abac6b99 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphNodeData.cpp
+++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeData.cpp
@@ -55,11 +55,7 @@ void QtGraphNodeData::onClick()
return;
}
- FilePath path = getFilePath();
-
- MessageActivateNodes message;
- message.addNode(m_data->getId(), path.empty() ? m_data->getNameHierarchy() : NameHierarchy(path.wstr(), NAME_DELIMITER_FILE));
- message.dispatch();
+ MessageActivateNodes(m_data->getId()).dispatch();
}
void QtGraphNodeData::moved(const Vec2i& oldPosition)
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp
index 442dc00b..8bac3e10 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp
+++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp
@@ -1,24 +1,33 @@
#include "qt/view/graphElements/QtGraphNodeGroup.h"
#include
-#include
+#include
+#include
+#include
#include
+#include "utility/messaging/type/MessageActivateNodes.h"
+#include "utility/messaging/type/MessageFocusIn.h"
+#include "utility/messaging/type/MessageFocusOut.h"
#include "utility/messaging/type/MessageGraphNodeBundleSplit.h"
#include "qt/graphics/QtRoundedRectItem.h"
-QtGraphNodeGroup::QtGraphNodeGroup(Id tokenId, const std::wstring& name, NodeType::GroupType type)
+QtGraphNodeGroup::QtGraphNodeGroup(
+ Id tokenId, const std::wstring& name, GroupType type, bool interactive
+)
: m_tokenId(tokenId)
, m_type(type)
+ , m_interactive(interactive)
{
- setAcceptHoverEvents(true);
+ if (interactive)
+ {
+ setAcceptHoverEvents(true);
+ }
setName(name);
- setZValue(-10.0f);
- m_rect->setZValue(-10.0f);
- if (type == NodeType::GROUP_FRAMELESS)
+ if (type == GroupType::FRAMELESS)
{
m_rect->hide();
return;
@@ -29,25 +38,26 @@ QtGraphNodeGroup::QtGraphNodeGroup(Id tokenId, const std::wstring& name, NodeTyp
return;
}
- m_background = new QtRoundedRectItem(this);
- m_backgroundTopRight = new QGraphicsRectItem(this);
- m_backgroundBottomLeft = new QGraphicsRectItem(this);
-
- m_background->setZValue(-10.0f);
- m_backgroundTopRight->setZValue(-10.0f);
- m_backgroundBottomLeft->setZValue(-10.0f);
+ m_background = new QGraphicsPolygonItem(this);
+ m_background->setZValue(-3.f);
GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfGroupNode(type, false);
GraphViewStyle::NodeMargins margins = GraphViewStyle::getMarginsOfGroupNode(type, true);
int width = style.textOffset.x * 2 + style.borderWidth + margins.charWidth * name.size();
- int height = margins.top * 2 + margins.charHeight;
+ int height = margins.spacingA + margins.charHeight;
+ int radius = style.cornerRadius;
- m_background->setRadius(style.cornerRadius);
- m_background->setRect(0, 0, width, height);
+ QPainterPath path;
+ path.moveTo(width, 0);
+ path.lineTo(radius, 0);
+ path.arcTo(0, 0, 2 * radius, 2 * radius, 90, 90);
+ path.lineTo(0, height);
+ path.lineTo(width - radius, height);
+ path.arcTo(width - 2 * radius, height - 2 * radius, 2 * radius, 2 * radius, 270, 90);
+ path.closeSubpath();
- m_backgroundTopRight->setRect(width - style.cornerRadius, 0, style.cornerRadius, style.cornerRadius);
- m_backgroundBottomLeft->setRect(0, height - style.cornerRadius, style.cornerRadius, style.cornerRadius);
+ m_background->setPolygon(path.toFillPolygon());
}
QtGraphNodeGroup::~QtGraphNodeGroup()
@@ -66,24 +76,64 @@ Id QtGraphNodeGroup::getTokenId() const
void QtGraphNodeGroup::onClick()
{
- MessageGraphNodeBundleSplit(m_tokenId).dispatch();
+ if (!m_interactive || !m_isHovering)
+ {
+ return;
+ }
+
+ if (m_type == GroupType::FILE || m_type == GroupType::NAMESPACE)
+ {
+ MessageActivateNodes(m_tokenId).dispatch();
+ }
+ else
+ {
+ MessageGraphNodeBundleSplit(m_tokenId).dispatch();
+ }
}
void QtGraphNodeGroup::updateStyle()
{
- GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfGroupNode(m_type, false);
+ GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfGroupNode(m_type, m_isHovering);
if (m_background)
{
m_background->setBrush(QColor(style.color.border.c_str()));
m_background->setPen(QPen(Qt::transparent));
-
- m_backgroundTopRight->setBrush(QColor(style.color.border.c_str()));
- m_backgroundTopRight->setPen(QPen(Qt::transparent));
-
- m_backgroundBottomLeft->setBrush(QColor(style.color.border.c_str()));
- m_backgroundBottomLeft->setPen(QPen(Qt::transparent));
}
setStyle(style);
}
+
+void QtGraphNodeGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
+{
+ if (m_type == GroupType::FILE || m_type == GroupType::NAMESPACE)
+ {
+ MessageFocusOut({ m_tokenId }).dispatch();
+ }
+ else
+ {
+ focusOut();
+ }
+}
+
+void QtGraphNodeGroup::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+ if (!m_background || m_background->contains(event->pos()))
+ {
+ if (!m_isHovering)
+ {
+ if (m_type == GroupType::FILE || m_type == GroupType::NAMESPACE)
+ {
+ MessageFocusIn({ m_tokenId }, TOOLTIP_ORIGIN_GRAPH).dispatch();
+ }
+ else
+ {
+ focusIn();
+ }
+ }
+ }
+ else if (m_isHovering)
+ {
+ hoverLeaveEvent(nullptr);
+ }
+}
diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h
index 477a1af1..356fe03e 100644
--- a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h
+++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h
@@ -1,18 +1,17 @@
#ifndef QT_GRAPH_NODE_GROUP_H
#define QT_GRAPH_NODE_GROUP_H
-#include "data/NodeType.h"
+#include "data/GroupType.h"
#include "qt/view/graphElements/QtGraphNode.h"
-class QGraphicsRectItem;
-class QtRoundedRectItem;
+class QGraphicsPolygonItem;
class QtGraphNodeGroup
: public QtGraphNode
{
Q_OBJECT
public:
- QtGraphNodeGroup(Id tokenId, const std::wstring& name, NodeType::GroupType type);
+ QtGraphNodeGroup(Id tokenId, const std::wstring& name, GroupType type, bool interactive);
virtual ~QtGraphNodeGroup();
// QtGraphNode implementation
@@ -22,13 +21,16 @@ public:
virtual void onClick();
virtual void updateStyle();
+protected:
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event);
+
private:
Id m_tokenId;
- NodeType::GroupType m_type;
+ GroupType m_type;
+ const bool m_interactive;
- QtRoundedRectItem* m_background = nullptr;
- QGraphicsRectItem* m_backgroundTopRight = nullptr;
- QGraphicsRectItem* m_backgroundBottomLeft = nullptr;
+ QGraphicsPolygonItem* m_background = nullptr;
};
#endif // QT_GRAPH_NODE_GROUP_H