diff --git a/bin/app/data/color_schemes/bad_rainbow.xml b/bin/app/data/color_schemes/bad_rainbow.xml index 296ce438..f081b76b 100644 --- a/bin/app/data/color_schemes/bad_rainbow.xml +++ b/bin/app/data/color_schemes/bad_rainbow.xml @@ -422,6 +422,16 @@ #B1B1B1 + + + + transparent + + + #797979 + + + diff --git a/bin/app/data/color_schemes/bright.xml b/bin/app/data/color_schemes/bright.xml index 6030bf68..7b2599f6 100644 --- a/bin/app/data/color_schemes/bright.xml +++ b/bin/app/data/color_schemes/bright.xml @@ -390,6 +390,16 @@ #A2A2A2 + + + + transparent + + + #878787 + + + diff --git a/bin/app/data/color_schemes/dark.xml b/bin/app/data/color_schemes/dark.xml index e39464c7..cd85bc89 100644 --- a/bin/app/data/color_schemes/dark.xml +++ b/bin/app/data/color_schemes/dark.xml @@ -393,6 +393,16 @@ #777777 + + + + transparent + + + #797979 + + + diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index a50a17fd..f57347c3 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -127,6 +127,7 @@ add_files( data/graph/token_component/TokenComponentAggregation.h data/graph/token_component/TokenComponentConst.cpp data/graph/token_component/TokenComponentConst.h + data/graph/token_component/TokenComponentEdgeIds.h data/graph/token_component/TokenComponentFilePath.cpp data/graph/token_component/TokenComponentFilePath.h data/graph/token_component/TokenComponentInheritanceChain.h diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index ffcae343..6bcb244f 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -185,7 +185,7 @@ void CodeController::handleMessage(MessageActivateTrailEdge* message) CodeView::CodeParams params; params.clearSnippets = true; params.showContents = !message->isReplayed(); - params.activeTokenIds.push_back(message->tokenId); + params.activeTokenIds = message->edgeIds; m_collection = m_storageAccess->getSourceLocationsForTokenIds(params.activeTokenIds); showCodeSnippets(getSnippetsForActiveSourceLocations(m_collection.get(), 0), params); diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 244648f9..8ae46d62 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -17,6 +17,7 @@ #include "component/view/GraphViewStyle.h" #include "data/access/StorageAccess.h" #include "data/graph/token_component/TokenComponentAccess.h" +#include "data/graph/token_component/TokenComponentEdgeIds.h" #include "data/graph/Graph.h" #include "data/parser/AccessKind.h" #include "settings/ApplicationSettings.h" @@ -185,6 +186,11 @@ void GraphController::handleMessage(MessageActivateTrail* message) MessageStatus(L"Layouting depth graph", false, true).dispatch(); + if (message->trailType == Edge::EDGE_INHERITANCE) + { + groupTrailNodes(NodeType::GROUP_INHERITANCE); + } + layoutNesting(); layoutTrail(message->horizontalLayout, message->originId); @@ -198,7 +204,7 @@ void GraphController::handleMessage(MessageActivateTrailEdge* message) { TRACE("trail edge activate"); - getView()->activateEdge(message->tokenId, message->isReplayed()); + getView()->activateEdge(message->edgeIds.back(), message->isReplayed()); } void GraphController::handleMessage(MessageFlushUpdates* message) @@ -302,7 +308,7 @@ void GraphController::handleMessage(MessageGraphNodeExpand* message) } Id nodeId = message->tokenId; - DummyNode* dummyNode = getDummyGraphNodeById(nodeId); + DummyNode* dummyNode = getDummyGraphNodeById(nodeId).get(); if (dummyNode) { dummyNode->expanded = message->expand; @@ -380,7 +386,7 @@ void GraphController::handleMessage(MessageGraphNodeExpand* message) void GraphController::handleMessage(MessageGraphNodeHide* message) { - DummyNode* node = getDummyGraphNodeById(message->tokenId); + DummyNode* node = getDummyGraphNodeById(message->tokenId).get(); DummyEdge* edge = nullptr; if (node) { @@ -400,8 +406,8 @@ void GraphController::handleMessage(MessageGraphNodeHide* message) edge->hidden = true; edge->visible = false; - DummyNode* from = getDummyGraphNodeById(edge->ownerId); - DummyNode* to = getDummyGraphNodeById(edge->targetId); + DummyNode* from = getDummyGraphNodeById(edge->ownerId).get(); + DummyNode* to = getDummyGraphNodeById(edge->targetId).get(); if (from) { @@ -447,7 +453,7 @@ void GraphController::handleMessage(MessageGraphNodeHide* message) void GraphController::handleMessage(MessageGraphNodeMove* message) { - DummyNode* node = getDummyGraphNodeById(message->tokenId); + DummyNode* node = getDummyGraphNodeById(message->tokenId).get(); if (node) { node->position += message->delta; @@ -564,9 +570,9 @@ void GraphController::createDummyGraph(const std::shared_ptr graph) if (qualifier.size()) { - std::shared_ptr qualifierNode = std::make_shared(); - qualifierNode->visible = true; + std::shared_ptr qualifierNode = std::make_shared(DummyNode::DUMMY_QUALIFIER); qualifierNode->qualifierName = qualifier; + qualifierNode->visible = true; node->subNodes.push_back(qualifierNode); node->hasQualifier = true; @@ -610,7 +616,7 @@ std::vector> GraphController::createDummyNodeTopDown( { std::vector> nodes; - std::shared_ptr result = std::make_shared(); + std::shared_ptr result = std::make_shared(DummyNode::DUMMY_DATA); result->data = node; result->name = node->getName(); @@ -655,9 +661,8 @@ std::vector> GraphController::createDummyNodeTopDown( if (!parent) { - std::shared_ptr accessNode = std::make_shared(); + std::shared_ptr accessNode = std::make_shared(DummyNode::DUMMY_ACCESS); accessNode->accessKind = accessKind; - accessNode->isAccess = true; result->subNodes.push_back(accessNode); parent = accessNode.get(); } @@ -688,7 +693,7 @@ void GraphController::setExpandedNodeIds(const std::vector& nodeIds) { for (Id id : nodeIds) { - DummyNode* node = getDummyGraphNodeById(id); + DummyNode* node = getDummyGraphNodeById(id).get(); if (node) { node->expanded = true; @@ -702,7 +707,7 @@ void GraphController::autoExpandActiveNode(const std::vector& activeTokenIds DummyNode* node = nullptr; if (activeTokenIds.size() == 1) { - node = getDummyGraphNodeById(activeTokenIds[0]); + node = getDummyGraphNodeById(activeTokenIds[0]).get(); } if (node && !node->hasMissingChildNodes()) @@ -740,8 +745,8 @@ bool GraphController::setActive(const std::vector& activeTokenIds, bool show noActive = false; } - DummyNode* from = getDummyGraphNodeById(edge->ownerId); - DummyNode* to = getDummyGraphNodeById(edge->targetId); + DummyNode* from = getDummyGraphNodeById(edge->ownerId).get(); + DummyNode* to = getDummyGraphNodeById(edge->targetId).get(); bool isInheritance = edge->data->isType(Edge::EDGE_INHERITANCE); if (from && to && !edge->hidden && @@ -877,6 +882,7 @@ void GraphController::hideBuiltinTypes() if (node->isGraphNode() && !node->active && node->data->getType().isBuiltin()) { node->visible = false; + node->hidden = true; } } } @@ -1118,7 +1124,7 @@ void GraphController::bundleNodesAndEdgesMatching( return; } - std::shared_ptr bundleNode = std::make_shared(); + std::shared_ptr bundleNode = std::make_shared(DummyNode::DUMMY_BUNDLE); bundleNode->name = name; bundleNode->visible = true; @@ -1213,7 +1219,7 @@ std::shared_ptr GraphController::bundleNodesMatching( return nullptr; } - std::shared_ptr bundleNode = std::make_shared(); + std::shared_ptr bundleNode = std::make_shared(DummyNode::DUMMY_BUNDLE); bundleNode->name = name; bundleNode->visible = true; @@ -1345,8 +1351,7 @@ void GraphController::addCharacterIndex() { character = toupper(m_dummyNodes[i]->name[0]); - std::shared_ptr textNode = std::make_shared(); - textNode->textNode = true; + std::shared_ptr textNode = std::make_shared(DummyNode::DUMMY_TEXT); textNode->name = character; textNode->visible = true; @@ -1367,6 +1372,168 @@ bool GraphController::hasCharacterIndex() const return false; } +void GraphController::groupTrailNodes(NodeType::GroupType groupType) +{ + TRACE(); + + struct TrailNode + { + Id nodeId; + std::set targetNodeIds; + std::set originNodeIds; + + std::vector targetEdges; + std::vector originEdges; + }; + + std::set possibleNodeIds; + for (auto dummyNode : m_dummyNodes) + { + if (dummyNode->visible && dummyNode->tokenId && + (!dummyNode->subNodes.size() || (dummyNode->subNodes.size() == 1 && dummyNode->subNodes[0]->isQualifierNode()))) + { + possibleNodeIds.insert(dummyNode->tokenId); + } + } + + std::map nodes; + + for (const std::shared_ptr& edge : m_dummyEdges) + { + if (edge->visible && + possibleNodeIds.find(edge->ownerId) != possibleNodeIds.end() && + possibleNodeIds.find(edge->targetId) != possibleNodeIds.end()) + { + TrailNode& fromNode = nodes[edge->ownerId]; + fromNode.nodeId = edge->ownerId; + fromNode.targetNodeIds.insert(edge->targetId); + fromNode.targetEdges.push_back(edge.get()); + + TrailNode& toNode = nodes[edge->targetId]; + toNode.nodeId = edge->targetId; + toNode.originNodeIds.insert(edge->ownerId); + toNode.originEdges.push_back(edge.get()); + } + } + + std::set groupedNodeIds; + while (nodes.size()) + { + TrailNode node = nodes.begin()->second; + nodes.erase(nodes.begin()); + + std::vector group; + std::map::iterator it = nodes.begin(); + while (it != nodes.end()) + { + if (node.targetNodeIds.size() <= 1 && it->second.targetNodeIds == node.targetNodeIds && + node.originNodeIds.size() <= 1 && it->second.originNodeIds == node.originNodeIds) + { + group.push_back(it->second); + it = nodes.erase(it); + } + else + { + it++; + } + } + + group.push_back(node); + if (group.size() < 3) + { + continue; + } + + std::shared_ptr groupNode = std::make_shared(DummyNode::DUMMY_GROUP); + groupNode->visible = true; + groupNode->groupType = groupType; + + // Use token Id of first node and make first 2 bits 1 + groupNode->tokenId = ~(~size_t(0) >> 2) + node.nodeId; + + std::shared_ptr targetEdge = std::make_shared(); + targetEdge->ownerId = groupNode->tokenId; + + std::shared_ptr originEdge = std::make_shared(); + originEdge->targetId = groupNode->tokenId; + + std::vector targetEdgeIds; + std::vector originEdgeIds; + + for (TrailNode& node : group) + { + std::shared_ptr dummyNode = getDummyGraphNodeById(node.nodeId); + if (!dummyNode) + { + continue; + } + + groupedNodeIds.insert(node.nodeId); + groupNode->subNodes.push_back(dummyNode); + + m_topLevelAncestorIds[node.nodeId] = groupNode->tokenId; + + for (DummyEdge* edge : node.targetEdges) + { + targetEdge->visible = true; + targetEdge->targetId = edge->targetId; + targetEdge->data = edge->data; + edge->visible = false; + edge->hidden = true; + + if (edge->data) + { + targetEdgeIds.push_back(edge->data->getId()); + } + } + + for (DummyEdge* edge : node.originEdges) + { + originEdge->visible = true; + originEdge->ownerId = edge->ownerId; + originEdge->data = edge->data; + edge->visible = false; + edge->hidden = true; + + if (edge->data) + { + originEdgeIds.push_back(edge->data->getId()); + } + } + } + + if (targetEdge->visible) + { + if (targetEdge->data && targetEdgeIds.size()) + { + const_cast(targetEdge->data)->addComponent(std::make_shared(targetEdgeIds)); + } + m_dummyEdges.push_back(targetEdge); + } + + if (originEdge->visible) + { + if (originEdge->data && originEdgeIds.size()) + { + const_cast(targetEdge->data)->addComponent(std::make_shared(originEdgeIds)); + } + m_dummyEdges.push_back(originEdge); + } + + groupNode->sortSubNodesByName(); + m_dummyNodes.push_back(groupNode); + } + + 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--; + } + } +} + void GraphController::layoutNesting() { TRACE(); @@ -1424,10 +1591,12 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const { margins = GraphViewStyle::getMarginsOfTextNode(); } + else if (node->isGroupNode()) + { + margins = GraphViewStyle::getMarginsOfGroupNode(node->groupType, node->name.size()); + } - int y = 0; - int x = 0; - int width = margins.minWidth; + int width = 0; int height = 0; if (node->isGraphNode()) @@ -1451,9 +1620,7 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const } width += margins.iconWidth; - - // Horizontal layouting is currently not used, but left in place for experimentation. - bool layoutHorizontal = false; + width = std::max(width, margins.minWidth); for (const std::shared_ptr& subNode : node->subNodes) { @@ -1461,6 +1628,12 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const { continue; } + else if (subNode->isQualifierNode()) + { + subNode->position.y = margins.top + margins.charHeight / 2; + width += 5; + continue; + } layoutNestingRecursive(subNode.get()); @@ -1470,56 +1643,24 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const } } - for (const std::shared_ptr& subNode : node->subNodes) + int top = margins.top + margins.charHeight + margins.spacingA; + int left = margins.left; + Vec2i size; + if (node->isGroupNode()) { - if (!subNode->visible || subNode->isExpandToggleNode()) - { - continue; - } - else if (subNode->isQualifierNode()) - { - subNode->position.y = margins.top + margins.charHeight / 2; - width += 5; - continue; - } - - subNode->position.x = margins.left + x; - subNode->position.y = margins.top + margins.charHeight + margins.spacingA + y; - - if (layoutHorizontal) - { - x += subNode->size.x + margins.spacingX; - if (subNode->size.y > height) - { - height = subNode->size.y; - } - } - else - { - y += subNode->size.y + margins.spacingY; - if (subNode->size.x > width) - { - width = subNode->size.x; - } - } + size = ListLayouter::layoutSkewed( + &node->subNodes, top, left, margins.spacingX, margins.spacingY, getView()->getViewSize().x() * 1.5); + } + else + { + size = ListLayouter::layoutColumn(&node->subNodes, top, left, margins.spacingY); } - if (x > 0) - { - x -= margins.spacingX; - } - if (y > 0) - { - y -= margins.spacingY; - } - - if (layoutHorizontal && x > width) - { - width = x; - } + width = std::max(size.x(), width); + height = size.y(); node->size.x = margins.left + width + margins.right; - node->size.y = margins.top + margins.charHeight + margins.spacingA + y + height + margins.bottom; + node->size.y = margins.top + margins.charHeight + margins.spacingA + height + margins.bottom; for (const std::shared_ptr& subNode : node->subNodes) { @@ -1542,9 +1683,9 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const void GraphController::addExpandToggleNode(DummyNode* node) const { - std::shared_ptr expandNode = std::make_shared(); - expandNode->visible = true; + std::shared_ptr expandNode = std::make_shared(DummyNode::DUMMY_EXPAND_TOGGLE); expandNode->expanded = node->expanded; + expandNode->visible = true; size_t visibleSubNodeCount = 0; for (size_t i = 0; i < node->subNodes.size(); i++) @@ -1658,17 +1799,7 @@ void GraphController::layoutList() { TRACE(); - std::vector> visibleNodes; - for (auto node : m_dummyNodes) - { - if (node->visible) - { - visibleNodes.push_back(node); - } - } - - ListLayouter layouter(getView()->getViewSize()); - layouter.layoutList(visibleNodes); + ListLayouter::layoutMultiColumn(getView()->getViewSize(), &m_dummyNodes); } void GraphController::layoutTrail(bool horizontal, bool hasOrigin) @@ -1719,19 +1850,19 @@ void GraphController::assignBundleIds() } } -DummyNode* GraphController::getDummyGraphNodeById(Id tokenId) const +std::shared_ptr GraphController::getDummyGraphNodeById(Id tokenId) const { std::map>::const_iterator it = m_dummyGraphNodes.find(tokenId); if (it != m_dummyGraphNodes.end()) { - return it->second.get(); + return it->second; } for (const std::shared_ptr& node : m_dummyNodes) { if (node->tokenId == tokenId) { - return node.get(); + return node; } } diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h index c11dd9cd..a0891b0f 100644 --- a/src/lib/component/controller/GraphController.h +++ b/src/lib/component/controller/GraphController.h @@ -107,6 +107,8 @@ private: void addCharacterIndex(); bool hasCharacterIndex() const; + void groupTrailNodes(NodeType::GroupType groupType); + void layoutNesting(); void layoutNestingRecursive(DummyNode* node) const; void addExpandToggleNode(DummyNode* node) const; @@ -118,7 +120,7 @@ private: void assignBundleIds(); - DummyNode* getDummyGraphNodeById(Id tokenId) const; + std::shared_ptr getDummyGraphNodeById(Id tokenId) const; DummyEdge* getDummyGraphEdgeById(Id tokenId) const; void buildGraph(MessageBase* message, bool centerActiveNode, bool animatedTransition, bool scrollToTop); diff --git a/src/lib/component/controller/UndoRedoController.cpp b/src/lib/component/controller/UndoRedoController.cpp index 15b8d393..4d793706 100644 --- a/src/lib/component/controller/UndoRedoController.cpp +++ b/src/lib/component/controller/UndoRedoController.cpp @@ -97,7 +97,7 @@ void UndoRedoController::handleMessage(MessageActivateTrail* message) void UndoRedoController::handleMessage(MessageActivateTrailEdge* message) { if (sameMessageTypeAsLast(message) && - static_cast(lastMessage())->tokenId == message->tokenId) + static_cast(lastMessage())->edgeIds == message->edgeIds) { return; } diff --git a/src/lib/component/controller/helper/DummyNode.h b/src/lib/component/controller/helper/DummyNode.h index 5efa8861..660fe902 100644 --- a/src/lib/component/controller/helper/DummyNode.h +++ b/src/lib/component/controller/helper/DummyNode.h @@ -14,6 +14,17 @@ struct DummyNode { public: + enum Type + { + DUMMY_DATA, + DUMMY_ACCESS, + DUMMY_EXPAND_TOGGLE, + DUMMY_BUNDLE, + DUMMY_QUALIFIER, + DUMMY_TEXT, + DUMMY_GROUP + }; + struct DummyNodeComp { bool operator()(const std::shared_ptr a, const std::shared_ptr b) const @@ -50,8 +61,9 @@ public: bool isReferencing; }; - DummyNode() - : visible(false) + DummyNode(Type type) + : type(type) + , visible(false) , hidden(false) , childVisible(false) , tokenId(0) @@ -63,45 +75,49 @@ public: , hasParent(true) , hasQualifier(false) , accessKind(ACCESS_NONE) - , isAccess(false) , invisibleSubNodeCount(0) , bundleId(0) , layoutBucket(0, 0) , bundledNodeCount(0) , bundledNodeType(NodeType::NODE_SYMBOL) , qualifierName(NAME_DELIMITER_UNKNOWN) - , textNode(false) + , groupType(NodeType::GROUP_FRAMELESS) { } bool isGraphNode() const { - return data != nullptr; + return type == DUMMY_DATA; } bool isAccessNode() const { - return isAccess; + return type == DUMMY_ACCESS; } bool isExpandToggleNode() const { - return !isGraphNode() && !isAccessNode() && !isBundleNode() && !isQualifierNode() && !isTextNode(); + return type == DUMMY_EXPAND_TOGGLE; } bool isBundleNode() const { - return bundledNodes.size() > 0; + return type == DUMMY_BUNDLE; } bool isQualifierNode() const { - return qualifierName.size(); + return type == DUMMY_QUALIFIER; } bool isTextNode() const { - return textNode; + return type == DUMMY_TEXT; + } + + bool isGroupNode() const + { + return type == DUMMY_GROUP; } bool isExpanded() const @@ -339,6 +355,18 @@ public: subNodes.insert(subNodes.end(), accessNodes.begin(), accessNodes.end()); } + void sortSubNodesByName() + { + std::sort(subNodes.begin(), subNodes.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool + { + return a->name < b->name; + } + ); + } + + Type type; + Vec2i position; Vec2i size; @@ -363,7 +391,6 @@ public: // AccessNode AccessKind accessKind; - bool isAccess; // ExpandToggleNode size_t invisibleSubNodeCount; @@ -383,8 +410,8 @@ public: // QualifierNode NameHierarchy qualifierName; - // TextNode - bool textNode; + // GroupNode + NodeType::GroupType groupType; }; #endif // DUMMY_NODE_H diff --git a/src/lib/component/controller/helper/ListLayouter.cpp b/src/lib/component/controller/helper/ListLayouter.cpp index 20534250..8f1fdc40 100644 --- a/src/lib/component/controller/helper/ListLayouter.cpp +++ b/src/lib/component/controller/helper/ListLayouter.cpp @@ -5,12 +5,17 @@ #include "component/controller/helper/DummyNode.h" #include "component/view/GraphViewStyle.h" -ListLayouter::ListLayouter(Vec2i viewSize) - : m_viewSize(viewSize) +Vec2i ListLayouter::layoutRow(std::vector>* nodes, int top, int left, int gap) { + return layoutSimple(nodes, top, left, gap, 0, true); } -void ListLayouter::layoutList(std::vector>& nodes) +Vec2i ListLayouter::layoutColumn(std::vector>* nodes, int top, int left, int gap) +{ + return layoutSimple(nodes, top, left, 0, gap, false); +} + +Vec2i ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector>* nodes) { size_t colsFinal; std::vector maxWidthsFinal; @@ -18,14 +23,25 @@ void ListLayouter::layoutList(std::vector>& nodes) int gapX = GraphViewStyle::s_gridCellSize + 2 * GraphViewStyle::s_gridCellPadding; int gapY = GraphViewStyle::s_gridCellPadding; + std::vector> visibleNodes; + for (auto node : *nodes) + { + if (node->visible) + { + visibleNodes.push_back(node); + } + } + for (size_t cols = 1; cols <= 10; cols++) { std::vector maxWidths = std::vector(cols, 0); - size_t nodesPerCol = cols == 1 ? nodes.size() : std::ceil((nodes.size() + cols - 1) / double(cols)); + size_t nodesPerCol = + (cols == 1 ? visibleNodes.size() : std::ceil((visibleNodes.size() + cols - 1) / double(cols))); + int maxHeight = 0; int height = -gapY; - for (size_t i = 0; i < nodes.size(); i++) + for (size_t i = 0; i < visibleNodes.size(); i++) { size_t j = i / nodesPerCol; @@ -33,9 +49,9 @@ void ListLayouter::layoutList(std::vector>& nodes) { height = -gapY; } - height += nodes[i]->size.y() + gapY; + height += visibleNodes[i]->size.y() + gapY; - maxWidths[j] = std::max(nodes[i]->size.x(), maxWidths[j]); + maxWidths[j] = std::max(visibleNodes[i]->size.x(), maxWidths[j]); maxHeight = std::max(height, maxHeight); } @@ -45,7 +61,7 @@ void ListLayouter::layoutList(std::vector>& nodes) width += maxWidths[j] + gapX; } - if (width > m_viewSize.x) + if (width > viewSize.x) { if (!maxWidthsFinal.size()) { @@ -58,7 +74,7 @@ void ListLayouter::layoutList(std::vector>& nodes) colsFinal = cols; maxWidthsFinal = maxWidths; - if (height < m_viewSize.y) + if (height < viewSize.y) { break; } @@ -66,19 +82,23 @@ void ListLayouter::layoutList(std::vector>& nodes) int x = 0; int y = 0; - size_t nodesPerCol = colsFinal == 1 ? nodes.size() : std::ceil((nodes.size() + colsFinal - 1) / double(colsFinal)); + int width = 0; + int height = 0; + + size_t nodesPerCol = + (colsFinal == 1 ? visibleNodes.size() : std::ceil((visibleNodes.size() + colsFinal - 1) / double(colsFinal))); std::shared_ptr lastTextNode; - for (size_t i = 0; i < nodes.size(); i++) + for (size_t i = 0; i < visibleNodes.size(); i++) { size_t j = i / nodesPerCol; if (j != 0 && j != colsFinal && i % nodesPerCol == 0) { - if (lastTextNode) + if (lastTextNode && !visibleNodes[i]->isTextNode()) { std::shared_ptr textNode = std::make_shared(*lastTextNode.get()); - if (nodes[i - 1] == lastTextNode) + if (visibleNodes[i - 1] == lastTextNode) { lastTextNode->visible = false; } @@ -87,7 +107,8 @@ void ListLayouter::layoutList(std::vector>& nodes) textNode->name += L".."; } - nodes.insert(nodes.begin() + i, textNode); + visibleNodes.insert(visibleNodes.begin() + i, textNode); + nodes->push_back(textNode); lastTextNode.reset(); i--; continue; @@ -97,14 +118,171 @@ void ListLayouter::layoutList(std::vector>& nodes) x += maxWidthsFinal[j - 1] + gapX; } - nodes[i]->position.x = x; - nodes[i]->position.y = y; + visibleNodes[i]->position.x = x; + visibleNodes[i]->position.y = y; - y += nodes[i]->size.y + gapY; + y += visibleNodes[i]->size.y + gapY; + height = std::max(height, y); - if (nodes[i]->isTextNode()) + if (visibleNodes[i]->isTextNode()) { - lastTextNode = nodes[i]; + 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) +{ + std::vector> visibleNodes; + std::multiset nodeWidths; + for (auto node : *nodes) + { + if (node->visible) + { + visibleNodes.push_back(node); + nodeWidths.insert(node->size.x()); + } + } + + int nodeWidth = nodeWidths.size() ? *nodeWidths.rbegin() : 0; + if (nodeWidths.size() > 1) + { + nodeWidth = *nodeWidths.rbegin() / 2 + *(++nodeWidths.rbegin()) / 2; + } + + int height = 0; + int width = 0; + + int nodesPerRowStart = std::max(int(std::floor(maxWidth * 2 / (nodeWidth + gapX))), 3); + for (int nodesPerRow = nodesPerRowStart; nodesPerRow >= 3; nodesPerRow--) + { + height = 0; + width = (nodeWidth * nodesPerRow + gapX * (nodesPerRow - 1)) / 2; + + int x = 0; + int rowHeight = 0; + int nodeCount = 0; + bool evenRow = true; + + for (size_t i = 0; i < visibleNodes.size(); i++) + { + if (nodeCount + 2 > nodesPerRow) + { + height += rowHeight + gapY; + rowHeight = 0; + + evenRow = !evenRow; + nodeCount = evenRow ? 0 : 1; + x = evenRow ? 0 : ((nodeWidth + gapX) / 2); + } + + DummyNode* node = visibleNodes[i].get(); + node->position.x = left + x + (nodeWidth - node->size.x()) / 2; + node->position.y = top + height; + + rowHeight = std::max(rowHeight, node->size.y()); + x += nodeWidth + gapX; + + nodeCount += 2; + } + + height += rowHeight; + + if (height * 3 >= width) + { + 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) +{ + Vec4i rect; + + for (auto node : nodes) + { + if (!node->visible) + { + continue; + } + + if (rect.z() - rect.x() == 0) + { + rect.x = node->position.x(); + rect.y = node->position.y(); + rect.z = node->position.x() + node->size.x(); + rect.w = node->position.y() + node->size.y(); + } + else + { + rect.x = std::min(rect.x(), node->position.x()); + rect.y = std::min(rect.y(), node->position.y()); + rect.z = std::max(rect.z(), node->position.x() + node->size.x()); + rect.w = std::max(rect.w(), node->position.y() + node->size.y()); + } + } + + return rect; +} + +Vec2i ListLayouter::layoutSimple( + std::vector>* nodes, int top, int left, 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()) + { + continue; + } + + node->position.x = left + x; + node->position.y = top + 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 b6efc91a..b72a563b 100644 --- a/src/lib/component/controller/helper/ListLayouter.h +++ b/src/lib/component/controller/helper/ListLayouter.h @@ -1,19 +1,28 @@ #ifndef LIST_LAYOUTER_H #define LIST_LAYOUTER_H +#include +#include + #include "utility/math/Vector2.h" +#include "utility/math/Vector4.h" struct DummyNode; class ListLayouter { public: - ListLayouter(Vec2i viewSize); + static Vec2i layoutRow(std::vector>* nodes, int top, int left, int gap); + static Vec2i layoutColumn(std::vector>* nodes, int top, int left, int gap); - void layoutList(std::vector>& nodes); + 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 Vec4i boundingRect(const std::vector>& nodes); private: - Vec2i m_viewSize; + static Vec2i layoutSimple( + std::vector>* nodes, int top, int left, int gapX, int gapY, bool horizontal); }; #endif // LIST_LAYOUTER_H diff --git a/src/lib/component/view/GraphViewStyle.cpp b/src/lib/component/view/GraphViewStyle.cpp index a5a23945..16abacbf 100644 --- a/src/lib/component/view/GraphViewStyle.cpp +++ b/src/lib/component/view/GraphViewStyle.cpp @@ -186,6 +186,11 @@ size_t GraphViewStyle::getFontSizeOfTextNode() return s_fontSize + 5; } +size_t GraphViewStyle::getFontSizeOfGroupNode() +{ + return s_fontSize; +} + std::string GraphViewStyle::getFontNameForDataNode() { return s_fontName; @@ -206,6 +211,11 @@ std::string GraphViewStyle::getFontNameOfTextNode() return "Fira Sans"; } +std::string GraphViewStyle::getFontNameOfGroupNode() +{ + return s_fontName; +} + GraphViewStyle::NodeMargins GraphViewStyle::getMarginsForDataNode(NodeType::StyleType type, bool hasIcon, bool hasChildren) { NodeMargins margins; @@ -275,16 +285,16 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfAccessNode(AccessKind ac margins.minWidth = 30; break; case ACCESS_PUBLIC: - margins.minWidth = 56; + margins.minWidth = 57; break; case ACCESS_PROTECTED: - margins.minWidth = 72; + margins.minWidth = 78; break; case ACCESS_PRIVATE: - margins.minWidth = 58; + margins.minWidth = 62; break; case ACCESS_DEFAULT: - margins.minWidth = 60; + margins.minWidth = 62; break; case ACCESS_TEMPLATE_PARAMETER: case ACCESS_TYPE_PARAMETER: @@ -321,6 +331,32 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfTextNode() return margins; } +GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfGroupNode(NodeType::GroupType type, bool hasName) +{ + NodeMargins margins; + margins.spacingX = margins.spacingY = GraphViewStyle::s_gridCellPadding; + + if (type == NodeType::GROUP_FRAMELESS) + { + return margins; + } + + margins.spacingA = (hasName ? 22 : 0); + + margins.left = margins.right = 20; + margins.top = (hasName ? 8 : 20); + margins.bottom = 20; + + if (hasName) + { + margins.minWidth = margins.charHeight = getFontSizeOfGroupNode(); + } + + margins.charWidth = getCharWidth(NodeType::STYLE_BIG_NODE); + + return margins; +} + GraphViewStyle::NodeStyle GraphViewStyle::getStyleForNodeType( NodeType type, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier ){ @@ -519,6 +555,31 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfTextNode() return style; } +GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfGroupNode(NodeType::GroupType type, bool isFocused) +{ + NodeStyle style; + + std::string colorType = "group/"; + if (type == NodeType::GROUP_INHERITANCE) + { + colorType += "inheritance"; + } + + 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; + + return style; +} + GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType( Edge::EdgeType type, bool isActive, bool isFocused, bool isTrailEdge) { @@ -527,6 +588,11 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType( style.width = isActive ? 4 : 2; style.zValue = isActive ? 5 : 2; + if (isTrailEdge) + { + style.zValue = isActive ? 5 : 2; + } + style.arrowLength = 5; style.arrowWidth = 8; @@ -582,6 +648,11 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType( style.verticalOffset = 0; style.cornerRadius = 7; style.zValue = isActive ? 2 : -3; + + if (isTrailEdge) + { + style.zValue = isActive ? 2 : -20; + } break; case Edge::EDGE_INCLUDE: case Edge::EDGE_MACRO_USAGE: @@ -590,11 +661,6 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType( break; } - if (isTrailEdge) - { - style.zValue = isActive ? 5 : 2; - } - return style; } diff --git a/src/lib/component/view/GraphViewStyle.h b/src/lib/component/view/GraphViewStyle.h index 79057ca7..5c8bd7cf 100644 --- a/src/lib/component/view/GraphViewStyle.h +++ b/src/lib/component/view/GraphViewStyle.h @@ -104,17 +104,20 @@ public: static size_t getFontSizeOfCountCircle(); static size_t getFontSizeOfQualifier(); static size_t getFontSizeOfTextNode(); + static size_t getFontSizeOfGroupNode(); static std::string getFontNameForDataNode(); static std::string getFontNameOfAccessNode(); static std::string getFontNameOfExpandToggleNode(); static std::string getFontNameOfTextNode(); + static std::string getFontNameOfGroupNode(); static NodeMargins getMarginsForDataNode(NodeType::StyleType type, bool hasIcon, bool hasChildren); static NodeMargins getMarginsOfAccessNode(AccessKind access); static NodeMargins getMarginsOfExpandToggleNode(); static NodeMargins getMarginsOfBundleNode(); static NodeMargins getMarginsOfTextNode(); + static NodeMargins getMarginsOfGroupNode(NodeType::GroupType type, bool hasName); static NodeStyle getStyleForNodeType( NodeType type, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier); @@ -124,6 +127,7 @@ public: static NodeStyle getStyleOfBundleNode(bool isFocused); static NodeStyle getStyleOfQualifier(); static NodeStyle getStyleOfTextNode(); + static NodeStyle getStyleOfGroupNode(NodeType::GroupType type, bool isFocused); static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused, bool isTrailEdge); @@ -142,8 +146,8 @@ public: private: static NodeStyle getStyleForNodeType( - NodeType::StyleType type, const std::string& underscoredTypeString, - const FilePath& iconPath, bool defined, bool isActive, bool isFocused, + NodeType::StyleType type, const std::string& underscoredTypeString, + const FilePath& iconPath, bool defined, bool isActive, bool isFocused, bool hasChildren, bool hasQualifier); static float getCharWidth(NodeType::StyleType type); diff --git a/src/lib/data/NodeType.h b/src/lib/data/NodeType.h index eae4505b..5e31aa0e 100644 --- a/src/lib/data/NodeType.h +++ b/src/lib/data/NodeType.h @@ -51,6 +51,12 @@ public: STYLE_BIG_NODE = 2 }; + enum GroupType + { + GROUP_FRAMELESS, + GROUP_INHERITANCE + }; + struct BundleInfo { BundleInfo() diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp index 3f47979d..61ff760c 100644 --- a/src/lib/data/graph/Edge.cpp +++ b/src/lib/data/graph/Edge.cpp @@ -4,7 +4,6 @@ #include "data/graph/Node.h" #include "data/graph/token_component/TokenComponentAggregation.h" -#include "data/graph/token_component/TokenComponentInheritanceChain.h" #include "utility/logging/logging.h" #include "utility/utilityString.h" @@ -119,38 +118,6 @@ bool Edge::isEdge() const return true; } -void Edge::addComponentAggregation(std::shared_ptr component) -{ - if (getComponent()) - { - LOG_ERROR(L"TokenComponentAggregation has been set before!"); - } - else if (m_type != EDGE_AGGREGATION) - { - LOG_ERROR(L"TokenComponentAggregation can't be set on edge of type: " + getReadableTypeString()); - } - else - { - addComponent(component); - } -} - -void Edge::addComponentInheritanceChain(std::shared_ptr component) -{ - if (getComponent()) - { - LOG_ERROR(L"TokenComponentInheritanceChain has been set before!"); - } - else if (m_type != EDGE_INHERITANCE) - { - LOG_ERROR(L"TokenComponentInheritanceChain can't be set on edge of type: " + getReadableTypeString()); - } - else - { - addComponent(component); - } -} - std::wstring Edge::getUnderscoredTypeString(EdgeType type) { return utility::replace(utility::replace(getReadableTypeString(type), L"-", L"_"), L" ", L"_"); @@ -205,7 +172,8 @@ std::wstring Edge::getReadableTypeString() const std::wstring Edge::getAsString() const { std::wstringstream str; - str << L"[" << getId() << L"] " << getReadableTypeString() << L": \"" << m_from->getName() << L"\" -> \"" + m_to->getName() << L"\""; + str << L"[" << getId() << L"] " << getReadableTypeString(); + str << L": \"" << m_from->getName() << L"\" -> \"" + m_to->getName() << L"\""; TokenComponentAggregation* aggregation = getComponent(); if (aggregation) diff --git a/src/lib/data/graph/Edge.h b/src/lib/data/graph/Edge.h index fc4eb3f1..11716969 100644 --- a/src/lib/data/graph/Edge.h +++ b/src/lib/data/graph/Edge.h @@ -7,8 +7,6 @@ #include "data/graph/Token.h" class Node; -class TokenComponentAggregation; -class TokenComponentInheritanceChain; class Edge : public Token @@ -54,10 +52,6 @@ public: virtual bool isNode() const override; virtual bool isEdge() const override; - // Component setters - void addComponentAggregation(std::shared_ptr component); - void addComponentInheritanceChain(std::shared_ptr component); - static std::wstring getUnderscoredTypeString(EdgeType type); static std::wstring getReadableTypeString(EdgeType type); // Logging. diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp index 0e19d3db..60d6bc5d 100644 --- a/src/lib/data/graph/Node.cpp +++ b/src/lib/data/graph/Node.cpp @@ -5,11 +5,9 @@ #include "utility/logging/logging.h" #include "utility/utilityString.h" -#include "data/graph/token_component/TokenComponentAbstraction.h" #include "data/graph/token_component/TokenComponentAccess.h" #include "data/graph/token_component/TokenComponentConst.h" #include "data/graph/token_component/TokenComponentStatic.h" -#include "data/graph/token_component/TokenComponentFilePath.h" Node::Node(Id id, NodeType type, const NameHierarchy& nameHierarchy, bool defined) : Token(id) @@ -47,7 +45,8 @@ void Node::setType(NodeType type) if (!isType(type.getType() | NodeType::NODE_SYMBOL)) { LOG_WARNING( - L"Cannot change NodeType after it was already set from " + getReadableTypeString() + L" to " + type.getReadableTypeWString() + L"Cannot change NodeType after it was already set from " + getReadableTypeString() + + L" to " + type.getReadableTypeWString() ); return; } @@ -288,71 +287,6 @@ bool Node::isEdge() const return false; } -void Node::addComponentAbstraction(std::shared_ptr component) -{ - if (getComponent()) - { - // LOG_ERROR("TokenComponentAbstraction has been set before!"); - return; - } - else - { - addComponent(component); - } -} - -void Node::addComponentConst(std::shared_ptr component) -{ - if (getComponent()) - { - // LOG_ERROR("TokenComponentConst has been set before!"); - return; - } - else - { - addComponent(component); - } -} - -void Node::addComponentStatic(std::shared_ptr component) -{ - if (getComponent()) - { - // LOG_ERROR("TokenComponentStatic has been set before!"); - return; - } - else - { - addComponent(component); - } -} - -void Node::addComponentFilePath(std::shared_ptr component) -{ - if (getComponent()) - { - // LOG_ERROR("TokenComponentFilePath has been set before!"); - return; - } - else - { - addComponent(component); - } -} - -void Node::addComponentAccess(std::shared_ptr component) -{ - if (getComponent()) - { - LOG_WARNING("TokenComponentAccess has been set before!"); - return; - } - else - { - addComponent(component); - } -} - std::wstring Node::getReadableTypeString() const { return m_type.getReadableTypeWString(); diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h index f19ea9c7..792e0c78 100644 --- a/src/lib/data/graph/Node.h +++ b/src/lib/data/graph/Node.h @@ -11,13 +11,6 @@ #include "data/name/NameHierarchy.h" #include "data/NodeType.h" -class TokenComponentAbstraction; -class TokenComponentAccess; -class TokenComponentConst; -class TokenComponentStatic; -class TokenComponentFilePath; -class TokenComponentSignature; - class Node : public Token { @@ -69,13 +62,6 @@ public: virtual bool isNode() const override; virtual bool isEdge() const override; - // Component setters. - void addComponentAbstraction(std::shared_ptr component); - void addComponentConst(std::shared_ptr component); - void addComponentStatic(std::shared_ptr component); - void addComponentFilePath(std::shared_ptr component); - void addComponentAccess(std::shared_ptr component); - // Logging. virtual std::wstring getReadableTypeString() const override; std::wstring getAsString() const; diff --git a/src/lib/data/graph/Token.cpp b/src/lib/data/graph/Token.cpp index c3eda8ba..e4ba7378 100644 --- a/src/lib/data/graph/Token.cpp +++ b/src/lib/data/graph/Token.cpp @@ -40,6 +40,11 @@ void Token::removeLocationId(Id locationId) LOG_ERROR("Location Id was not referenced by this Token."); } +void Token::addComponent(std::shared_ptr component) +{ + m_components.push_back(component); +} + Token::Token(const Token& other) : m_id(other.m_id) { @@ -48,8 +53,3 @@ Token::Token(const Token& other) addComponent(component->copy()); } } - -void Token::addComponent(std::shared_ptr component) -{ - m_components.push_back(component); -} diff --git a/src/lib/data/graph/Token.h b/src/lib/data/graph/Token.h index c9b42d22..f5464a40 100644 --- a/src/lib/data/graph/Token.h +++ b/src/lib/data/graph/Token.h @@ -23,6 +23,8 @@ public: void addLocationId(Id locationId); void removeLocationId(Id locationId); + void addComponent(std::shared_ptr component); + template ComponentType* getComponent() const; @@ -35,7 +37,6 @@ public: protected: Token(const Token& other); - void addComponent(std::shared_ptr component); void copyComponentsFrom(const Token& other); private: diff --git a/src/lib/data/graph/token_component/TokenComponentEdgeIds.h b/src/lib/data/graph/token_component/TokenComponentEdgeIds.h new file mode 100644 index 00000000..79388261 --- /dev/null +++ b/src/lib/data/graph/token_component/TokenComponentEdgeIds.h @@ -0,0 +1,23 @@ +#ifndef TOKEN_COMPONENT_EDGE_IDS_H +#define TOKEN_COMPONENT_EDGE_IDS_H + +#include "data/graph/token_component/TokenComponent.h" + +class TokenComponentEdgeIds + : public TokenComponent +{ +public: + TokenComponentEdgeIds(const std::vector& edgeIds) + : edgeIds(edgeIds) + { + } + + virtual std::shared_ptr copy() const + { + return std::make_shared(*this); + } + + const std::vector edgeIds; +}; + +#endif // TOKEN_COMPONENT_EDGE_IDS_H diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp index ddce502f..b6608519 100644 --- a/src/lib/data/storage/PersistentStorage.cpp +++ b/src/lib/data/storage/PersistentStorage.cpp @@ -2254,7 +2254,7 @@ void PersistentStorage::addNodesToGraph(const std::vector& newNodeIds, Graph NameHierarchy(filePath.fileName(), NAME_DELIMITER_FILE), defined ); - node->addComponentFilePath(std::make_shared(filePath)); + node->addComponent(std::make_shared(filePath)); node->setExplicit(defined); } else @@ -2465,7 +2465,7 @@ void PersistentStorage::addAggregationEdgesToGraph( const Id aggregationId = ~(~Id(0) >> 1) + *componentAggregation->getAggregationIds().begin(); Edge* edge = graph->createEdge(aggregationId, Edge::EDGE_AGGREGATION, sourceNode, targetNode); - edge->addComponentAggregation(componentAggregation); + edge->addComponent(componentAggregation); } } @@ -2486,7 +2486,7 @@ void PersistentStorage::addComponentAccessToGraph(Graph* graph) const { if (access.nodeId != 0) { - graph->getNodeById(access.nodeId)->addComponentAccess( + graph->getNodeById(access.nodeId)->addComponent( std::make_shared(intToAccessKind(access.type))); } } @@ -2564,7 +2564,7 @@ void PersistentStorage::addInheritanceChainsToGraph(const std::vector& activ Edge* inheritanceEdge = graph->createEdge( inheritanceEdgeId, Edge::EDGE_INHERITANCE, graph->getNodeById(sourceId), graph->getNodeById(targetId)); - inheritanceEdge->addComponentInheritanceChain(std::make_shared(edgeIds)); + inheritanceEdge->addComponent(std::make_shared(edgeIds)); } } } diff --git a/src/lib/utility/messaging/type/MessageActivateTrailEdge.h b/src/lib/utility/messaging/type/MessageActivateTrailEdge.h index e9d5097c..e0f53fbb 100644 --- a/src/lib/utility/messaging/type/MessageActivateTrailEdge.h +++ b/src/lib/utility/messaging/type/MessageActivateTrailEdge.h @@ -13,9 +13,10 @@ class MessageActivateTrailEdge { public: MessageActivateTrailEdge( - Id tokenId, Edge::EdgeType type, const NameHierarchy& sourceNameHierarchy, const NameHierarchy& targetNameHierarchy + const std::vector& edgeIds, Edge::EdgeType type, + const NameHierarchy& sourceNameHierarchy, const NameHierarchy& targetNameHierarchy ) - : tokenId(tokenId) + : edgeIds(edgeIds) , type(type) , sourceNameHierarchy(sourceNameHierarchy) , targetNameHierarchy(targetNameHierarchy) @@ -37,10 +38,14 @@ public: virtual void print(std::wostream& os) const { - os << tokenId << L" - " << getFullName(); + for (Id edgeId : edgeIds) + { + os << edgeId << L","; + } + os << L" - " << getFullName(); } - const Id tokenId; + const std::vector edgeIds; const Edge::EdgeType type; const NameHierarchy sourceNameHierarchy; const NameHierarchy targetNameHierarchy; diff --git a/src/lib/utility/messaging/type/MessageTooltipHide.h b/src/lib/utility/messaging/type/MessageTooltipHide.h index 7a16b4d6..ae14002b 100644 --- a/src/lib/utility/messaging/type/MessageTooltipHide.h +++ b/src/lib/utility/messaging/type/MessageTooltipHide.h @@ -10,6 +10,7 @@ public: MessageTooltipHide() { setSendAsTask(false); + setIsLogged(false); } static const std::string getStaticType() diff --git a/src/lib_gui/CMakeLists.txt b/src/lib_gui/CMakeLists.txt index bc6538fe..60f78ffc 100644 --- a/src/lib_gui/CMakeLists.txt +++ b/src/lib_gui/CMakeLists.txt @@ -127,6 +127,8 @@ add_files( qt/view/graphElements/QtGraphNodeData.h qt/view/graphElements/QtGraphNodeExpandToggle.cpp qt/view/graphElements/QtGraphNodeExpandToggle.h + qt/view/graphElements/QtGraphNodeGroup.cpp + qt/view/graphElements/QtGraphNodeGroup.h qt/view/graphElements/QtGraphNodeQualifier.cpp qt/view/graphElements/QtGraphNodeQualifier.h qt/view/graphElements/QtGraphNodeText.cpp diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp index 14ab5cad..50c54e57 100644 --- a/src/lib_gui/qt/view/QtGraphView.cpp +++ b/src/lib_gui/qt/view/QtGraphView.cpp @@ -33,6 +33,7 @@ #include "qt/view/graphElements/QtGraphNodeBundle.h" #include "qt/view/graphElements/QtGraphNodeData.h" #include "qt/view/graphElements/QtGraphNodeExpandToggle.h" +#include "qt/view/graphElements/QtGraphNodeGroup.h" #include "qt/view/graphElements/QtGraphNodeQualifier.h" #include "qt/view/graphElements/QtGraphNodeText.h" #include "settings/ApplicationSettings.h" @@ -890,6 +891,10 @@ QtGraphNode* QtGraphView::createNodeRecursive( { newNode = new QtGraphNodeText(node->name); } + else if (node->isGroupNode()) + { + newNode = new QtGraphNodeGroup(node->tokenId, node->name, node->groupType); + } else { LOG_ERROR("DummyNode is not valid"); @@ -1083,7 +1088,8 @@ void QtGraphView::compareNodesRecursive( dynamic_cast(*it2)->getAccessKind()) || ((*it)->isExpandToggleNode() && (*it2)->isExpandToggleNode()) || ((*it)->isBundleNode() && (*it2)->isBundleNode() && (*it)->getTokenId() == (*it2)->getTokenId()) || - ((*it)->isQualifierNode() && (*it2)->isQualifierNode() && (*it)->getTokenId() == (*it2)->getTokenId())) + ((*it)->isQualifierNode() && (*it2)->isQualifierNode() && (*it)->getTokenId() == (*it2)->getTokenId()) || + ((*it)->isGroupNode() && (*it2)->isGroupNode() && (*it)->getName() == (*it2)->getName())) { remainingNodes->push_back(std::pair(*it, *it2)); compareNodesRecursive( diff --git a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp index fbabbeab..83dbacc0 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp @@ -6,6 +6,7 @@ #include "component/view/GraphViewStyle.h" #include "data/graph/Edge.h" #include "data/graph/token_component/TokenComponentAggregation.h" +#include "data/graph/token_component/TokenComponentEdgeIds.h" #include "data/graph/token_component/TokenComponentInheritanceChain.h" #include "qt/graphics/QtLineItemAngled.h" #include "qt/graphics/QtLineItemBezier.h" @@ -251,8 +252,11 @@ void QtGraphEdge::onClick() { if (isTrailEdge()) { + TokenComponentEdgeIds* componentEdgeIds = getData()->getComponent(); + std::vector ids = { getData()->getId() }; + MessageActivateTrailEdge( - getData()->getId(), + componentEdgeIds ? componentEdgeIds->edgeIds : ids, getData()->getType(), getData()->getFrom()->getNameHierarchy(), getData()->getTo()->getNameHierarchy() diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp index 9e93af6a..59bb187f 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp +++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp @@ -330,6 +330,11 @@ bool QtGraphNode::isTextNode() const return false; } +bool QtGraphNode::isGroupNode() const +{ + return false; +} + Id QtGraphNode::getTokenId() const { return 0; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.h b/src/lib_gui/qt/view/graphElements/QtGraphNode.h index 9807834d..e43934ab 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNode.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.h @@ -87,6 +87,7 @@ public: virtual bool isBundleNode() const; virtual bool isQualifierNode() const; virtual bool isTextNode() const; + virtual bool isGroupNode() const; virtual Id getTokenId() const; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp new file mode 100644 index 00000000..b89e68d8 --- /dev/null +++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.cpp @@ -0,0 +1,80 @@ +#include "qt/view/graphElements/QtGraphNodeGroup.h" + +#include +#include +#include + +#include "qt/graphics/QtRoundedRectItem.h" + +QtGraphNodeGroup::QtGraphNodeGroup(Id tokenId, const std::wstring& name, NodeType::GroupType type) + : m_tokenId(tokenId) + , m_type(type) +{ + setName(name); + setZValue(-10.0f); + m_rect->setZValue(-10.0f); + + if (type == NodeType::GROUP_FRAMELESS) + { + m_rect->hide(); + return; + } + + if (!name.size()) + { + 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); + + 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; + + m_background->setRadius(style.cornerRadius); + m_background->setRect(0, 0, width, height); + + m_backgroundTopRight->setRect(width - style.cornerRadius, 0, style.cornerRadius, style.cornerRadius); + m_backgroundBottomLeft->setRect(0, height - style.cornerRadius, style.cornerRadius, style.cornerRadius); +} + +QtGraphNodeGroup::~QtGraphNodeGroup() +{ +} + +bool QtGraphNodeGroup::isGroupNode() const +{ + return true; +} + +Id QtGraphNodeGroup::getTokenId() const +{ + return m_tokenId; +} + +void QtGraphNodeGroup::updateStyle() +{ + GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfGroupNode(m_type, false); + + 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); +} diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h new file mode 100644 index 00000000..b0765a04 --- /dev/null +++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeGroup.h @@ -0,0 +1,33 @@ +#ifndef QT_GRAPH_NODE_GROUP_H +#define QT_GRAPH_NODE_GROUP_H + +#include "data/NodeType.h" +#include "qt/view/graphElements/QtGraphNode.h" + +class QGraphicsRectItem; +class QtRoundedRectItem; + +class QtGraphNodeGroup + : public QtGraphNode +{ + Q_OBJECT +public: + QtGraphNodeGroup(Id tokenId, const std::wstring& name, NodeType::GroupType type); + virtual ~QtGraphNodeGroup(); + + // QtGraphNode implementation + virtual bool isGroupNode() const; + virtual Id getTokenId() const; + + virtual void updateStyle(); + +private: + Id m_tokenId; + NodeType::GroupType m_type; + + QtRoundedRectItem* m_background = nullptr; + QGraphicsRectItem* m_backgroundTopRight = nullptr; + QGraphicsRectItem* m_backgroundBottomLeft = nullptr; +}; + +#endif // QT_GRAPH_NODE_GROUP_H