ui: Group graph nodes by file or namespace (issues #171, #439, #522)

* added buttons to enable grouping by file or namespace
* last used group settings saved to ApplicationSettings
* made groups hoverable/clickable on label only
* improved node hatching and override edge colors
* group overview lists

fortune cookie message = Seen with the eyes of love, everything gets a new meaning.
This commit is contained in:
Eberhard Graether
2018-04-09 00:09:22 +02:00
parent 20106d5ce1
commit 5303187bea
40 changed files with 956 additions and 197 deletions
+2
View File
@@ -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
@@ -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();
}
@@ -188,9 +188,7 @@ void BookmarkController::activateBookmark(const std::shared_ptr<Bookmark> 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<Bookmark> bookma
for (Id nodeId: nodeBookmark->getNodeIds())
{
activateNodes.addNode(nodeId, m_storageAccess->getNameHierarchyForNodeId(nodeId));
activateNodes.addNode(nodeId);
}
activateNodes.dispatch();
+246 -18
View File
@@ -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<std::shared_ptr<DummyNode>> 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> 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<std::wstring, std::shared_ptr<DummyNode>> groupNodes;
std::map<std::wstring, std::vector<std::shared_ptr<DummyNode>>> nodesToGroup;
std::map<Id, std::pair<Id, NameHierarchy>> nodeIdtoParentMap;
if (groupType == GroupType::FILE)
{
std::vector<Id> nodeIds;
for (const std::shared_ptr<DummyNode>& dummyNode : m_dummyNodes)
{
if (dummyNode->isGraphNode())
{
nodeIds.push_back(dummyNode->tokenId);
}
}
nodeIdtoParentMap = m_storageAccess->getNodeIdToParentFileMap(nodeIds);
}
std::map<std::wstring, Id> qualifierNameToIdMap;
for (const std::shared_ptr<DummyNode>& 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<Id> groupedNodeIds;
for (const std::pair<std::wstring, std::vector<std::shared_ptr<DummyNode>>>& p : nodesToGroup)
{
std::shared_ptr<DummyNode> 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>(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<DummyNode::BundleInfo> bundleInfos;
for (std::shared_ptr<DummyNode> 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<DummyNode> groupNode = std::make_shared<DummyNode>(DummyNode::DUMMY_GROUP);
groupNode->visible = true;
groupNode->groupType = groupType;
groupNode->tokenId = groupNodeId;
m_topLevelAncestorIds[groupNode->tokenId] = groupNode->tokenId;
for (std::shared_ptr<DummyNode> 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<DummyNode> groupNode = std::make_shared<DummyNode>(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)
@@ -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<void(DummyNode*)> func);
@@ -194,7 +194,7 @@ void BucketLayouter::createBuckets(
}
}
void BucketLayouter::layoutBuckets()
void BucketLayouter::layoutBuckets(bool addVerticalOffset)
{
std::map<int, int> widths;
std::map<int, int> 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);
@@ -44,7 +44,7 @@ public:
void createBuckets(
std::vector<std::shared_ptr<DummyNode>>& nodes,
const std::vector<std::shared_ptr<DummyEdge>>& edges);
void layoutBuckets();
void layoutBuckets(bool addVerticalOffset);
std::vector<std::shared_ptr<DummyNode>> getSortedNodes();
@@ -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<DummyNode::BundleInfo>& 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<DummyNode>& 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<Id> hiddenEdgeIds;
bool interactive;
};
#endif // DUMMY_NODE_H
@@ -5,17 +5,17 @@
#include "component/controller/helper/DummyNode.h"
#include "component/view/GraphViewStyle.h"
Vec2i ListLayouter::layoutRow(std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gap)
void ListLayouter::layoutRow(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap)
{
return layoutSimple(nodes, top, left, gap, 0, true);
layoutSimple(nodes, gap, 0, true);
}
Vec2i ListLayouter::layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gap)
void ListLayouter::layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap)
{
return layoutSimple(nodes, top, left, 0, gap, false);
layoutSimple(nodes, 0, gap, false);
}
Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes)
void ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes)
{
size_t colsFinal;
std::vector<int> maxWidthsFinal;
@@ -26,7 +26,7 @@ Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_pt
std::vector<std::shared_ptr<DummyNode>> 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::vector<std::shared_pt
int x = 0;
int y = 0;
int width = 0;
int height = 0;
size_t nodesPerCol =
(colsFinal == 1 ? visibleNodes.size() : std::ceil((visibleNodes.size() + colsFinal - 1) / double(colsFinal)));
@@ -122,30 +120,21 @@ Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_pt
visibleNodes[i]->position.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<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gapX, int gapY, int maxWidth)
void ListLayouter::layoutSkewed(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth)
{
std::vector<std::shared_ptr<DummyNode>> visibleNodes;
std::multiset<int> 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<std::shared_ptr<DummyNode>>& nodes)
@@ -219,7 +198,7 @@ Vec4i ListLayouter::boundingRect(const std::vector<std::shared_ptr<DummyNode>>&
for (auto node : nodes)
{
if (!node->visible)
if (!node->getsLayouted())
{
continue;
}
@@ -243,46 +222,44 @@ Vec4i ListLayouter::boundingRect(const std::vector<std::shared_ptr<DummyNode>>&
return rect;
}
Vec2i ListLayouter::layoutSimple(
std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gapX, int gapY, bool horizontal)
Vec2i ListLayouter::offsetNodes(std::vector<std::shared_ptr<DummyNode>> 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<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, bool horizontal)
{
int y = 0;
int x = 0;
int width = 0;
int height = 0;
for (const std::shared_ptr<DummyNode>& 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);
}
@@ -12,17 +12,17 @@ struct DummyNode;
class ListLayouter
{
public:
static Vec2i layoutRow(std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gap);
static Vec2i layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gap);
static void layoutRow(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap);
static void layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap);
static Vec2i layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes);
static Vec2i layoutSkewed(
std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gapX, int gapY, int maxWidth);
static void layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes);
static void layoutSkewed(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth);
static Vec4i boundingRect(const std::vector<std::shared_ptr<DummyNode>>& nodes);
static Vec2i offsetNodes(std::vector<std::shared_ptr<DummyNode>> nodes, int top, int left);
private:
static Vec2i layoutSimple(
std::vector<std::shared_ptr<DummyNode>>* nodes, int top, int left, int gapX, int gapY, bool horizontal);
static void layoutSimple(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, bool horizontal);
};
#endif // LIST_LAYOUTER_H
+2
View File
@@ -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;
+41 -14
View File
@@ -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)
{
+3 -2
View File
@@ -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);
+28
View File
@@ -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;
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef GROUP_TYPE_H
#define GROUP_TYPE_H
#include <string>
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
+2 -7
View File
@@ -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
+1
View File
@@ -41,6 +41,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id id) const = 0;
virtual std::vector<NameHierarchy> getNameHierarchiesForNodeIds(const std::vector<Id>& nodeIds) const = 0;
virtual std::map<Id, std::pair<Id, NameHierarchy>> getNodeIdToParentFileMap(const std::vector<Id>& nodeIds) const = 0;
virtual NodeType getNodeTypeForNodeWithId(Id id) const = 0;
@@ -85,6 +85,16 @@ std::vector<NameHierarchy> StorageAccessProxy::getNameHierarchiesForNodeIds(cons
return std::vector<NameHierarchy>();
}
std::map<Id, std::pair<Id, NameHierarchy>> StorageAccessProxy::getNodeIdToParentFileMap(const std::vector<Id>& nodeIds) const
{
if (hasSubject())
{
return m_subject->getNodeIdToParentFileMap(nodeIds);
}
return { };
}
NodeType StorageAccessProxy::getNodeTypeForNodeWithId(Id id) const
{
if (hasSubject())
+1
View File
@@ -24,6 +24,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id id) const override;
virtual std::vector<NameHierarchy> getNameHierarchiesForNodeIds(const std::vector<Id>& nodeIds) const override;
virtual std::map<Id, std::pair<Id, NameHierarchy>> getNodeIdToParentFileMap(const std::vector<Id>& nodeIds) const override;
virtual NodeType getNodeTypeForNodeWithId(Id id) const override;
@@ -445,6 +445,28 @@ std::vector<NameHierarchy> PersistentStorage::getNameHierarchiesForNodeIds(const
return nameHierarchies;
}
std::map<Id, std::pair<Id, NameHierarchy>> PersistentStorage::getNodeIdToParentFileMap(const std::vector<Id>& nodeIds) const
{
std::map<Id, std::pair<Id, NameHierarchy>> nodeIdToParentFileMap;
std::shared_ptr<SourceLocationCollection> 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<StorageNode>(nodeId).type);
+1
View File
@@ -77,6 +77,7 @@ public:
virtual NameHierarchy getNameHierarchyForNodeId(Id nodeId) const override;
virtual std::vector<NameHierarchy> getNameHierarchiesForNodeIds(const std::vector<Id>& nodeIds) const override;
virtual std::map<Id, std::pair<Id, NameHierarchy>> getNodeIdToParentFileMap(const std::vector<Id>& nodeIds) const override;
virtual NodeType getNodeTypeForNodeWithId(Id nodeId) const override;
@@ -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<SourceLocationFile> SqliteIndexStorage::getSourceLocationsOfType
return getSourceLocationsForFile(filePath, "AND type == " + std::to_string(locationTypeToInt(type)));
}
std::shared_ptr<SourceLocationCollection> SqliteIndexStorage::getSourceLocationsForElementIds(
const std::vector<Id>& elementIds) const
{
std::vector<Id> sourceLocationIds;
std::map<Id, std::vector<Id>> 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<SourceLocationCollection> ret = std::make_shared<SourceLocationCollection>();
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<StorageOccurrence> SqliteIndexStorage::getOccurrencesForLocationId(Id locationId) const
{
std::vector<Id> locationIds {locationId};
std::vector<Id> locationIds { locationId };
return getOccurrencesForLocationIds(locationIds);
}
@@ -5,7 +5,7 @@
#include <string>
#include <vector>
#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<SourceLocationFile> getSourceLocationsOfTypeInFile(
const FilePath& filePath, LocationType type) const;
std::shared_ptr<SourceLocationCollection> getSourceLocationsForElementIds(const std::vector<Id>& elementIds) const;
std::vector<StorageOccurrence> getOccurrencesForLocationId(Id locationId) const;
std::vector<StorageOccurrence> getOccurrencesForLocationIds(const std::vector<Id>& locationIds) const;
std::vector<StorageOccurrence> getOccurrencesForElementIds(const std::vector<Id>& elementIds) const;
+10
View File
@@ -241,6 +241,16 @@ void ApplicationSettings::setGraphControlsVisible(bool visible)
setValue<bool>("application/graph_controls_visible", visible);
}
GroupType ApplicationSettings::getGraphGrouping() const
{
return stringToGroupType(getValue<std::wstring>("application/graph_grouping", groupTypeToString(GroupType::NONE)));
}
void ApplicationSettings::setGraphGrouping(GroupType type)
{
setValue<std::wstring>("application/graph_grouping", groupTypeToString(type));
}
int ApplicationSettings::getScreenAutoScaling() const
{
return getValue<int>("screen/auto_scaling", 1);
+4
View File
@@ -3,6 +3,7 @@
#include <memory>
#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);
@@ -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);
}