diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 01d07904..f6a74d34 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -845,18 +845,12 @@ bool GraphController::setActive(const std::vector& activeTokenIds, bool show bool isInheritance = edge->data->isType(Edge::EDGE_INHERITANCE); if (from && to && !edge->hidden && - (showAllEdges || noActive || from->active || to->active || edge->active || isInheritance)) + (showAllEdges || noActive || from->active || to->active || edge->active || isInheritance) && + !(to->active && edge->data->isType(Edge::EDGE_TYPE_USAGE) && to->data->isParentOf(from->data))) // Don't show type use edges to active parent { edge->visible = true; from->connected = true; to->connected = true; - - // Don't show children of active node with a type use edge to the parent - if (to->active && edge->data->isType(Edge::EDGE_TYPE_USAGE) && to->data->isParentOf(from->data)) - { - from->connected = false; - to->connected = false; - } } else { @@ -1729,7 +1723,7 @@ void GraphController::groupTrailNodes(GroupType groupType) std::shared_ptr groupNode = std::make_shared(DummyNode::DUMMY_GROUP); groupNode->visible = true; groupNode->groupType = groupType; - groupNode->groupLayout = GroupLayout::SKEWED; + groupNode->groupLayout = GroupLayout::SQUARE; // Use token Id of first node and make first 2 bits 1 groupNode->tokenId = ~(~Id(0) >> 2) + node.nodeId; @@ -1825,7 +1819,7 @@ void GraphController::layoutNesting() for (const std::shared_ptr& node : m_dummyNodes) { - layoutNestingRecursive(node.get()); + layoutNestingRecursive(node.get(), -1); } for (const std::shared_ptr& node : m_dummyNodes) @@ -1868,11 +1862,11 @@ void GraphController::extendEqualFunctionNames(const std::vectorvisible) { - return; + return Vec4i(0, 0, 0, 0); } GraphViewStyle::NodeMargins margins; @@ -1904,7 +1898,7 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const } else if (node->isQualifierNode()) { - return; + return Vec4i(0, 0, 0, 0); } else if (node->isTextNode()) { @@ -1940,6 +1934,8 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const width += margins.iconWidth; width = std::max(width, margins.minWidth); + int maxAccessWidth = 0; + for (const std::shared_ptr& subNode : node->subNodes) { if (!subNode->visible) @@ -1953,47 +1949,75 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const continue; } - layoutNestingRecursive(subNode.get()); + Vec4i rect = layoutNestingRecursive(subNode.get(), maxWidth); if (subNode->isExpandToggleNode()) { width += margins.spacingX + subNode->size.x; } - } - - if (node->isGroupNode()) - { - Vec2i viewSize = getView()->getViewSize(); - - switch (node->groupLayout) + else if (subNode->isAccessNode()) { - 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() || !m_activeNodeIds.size() /* aggregations */) - { - BucketLayouter grid(viewSize); - grid.createBuckets(node->subNodes, m_dummyEdges); - grid.layoutBuckets(m_activeNodeIds.size()); - node->subNodes = grid.getSortedNodes(); - } - else - { - ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); - } - break; + maxAccessWidth = std::max(maxAccessWidth, rect.z()); } } - else + + if (maxAccessWidth > 0) { - ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); + for (const std::shared_ptr& subNode : node->subNodes) + { + if (!subNode->visible || !subNode->isAccessNode()) + { + continue; + } + + layoutNestingRecursive(subNode.get(), maxAccessWidth); + } + } + + if (node->subNodes.size()) + { + if (node->isGroupNode()) + { + 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() || !m_activeNodeIds.size() /* aggregations */) + { + BucketLayouter grid(viewSize); + grid.createBuckets(node->subNodes, m_dummyEdges); + grid.layoutBuckets(m_activeNodeIds.size()); + node->subNodes = grid.getSortedNodes(); + } + else + { + ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); + } + break; + + case GroupLayout::SQUARE: + ListLayouter::layoutSquare(&node->subNodes, -1); + break; + } + } + else if (node->isAccessNode() && !node->hasConnectedSubNode()) + { + ListLayouter::layoutSquare(&node->subNodes, maxWidth); + } + else + { + ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); + } } Vec2i size = ListLayouter::offsetNodes( @@ -2022,6 +2046,8 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const subNode->position.y = 6; } } + + return ListLayouter::boundingRect(node->subNodes); } void GraphController::addExpandToggleNode(DummyNode* node) const diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h index ecd797e7..04b36573 100644 --- a/src/lib/component/controller/GraphController.h +++ b/src/lib/component/controller/GraphController.h @@ -120,7 +120,7 @@ private: void layoutNesting(); void extendEqualFunctionNames(const std::vector>& nodes) const; - void layoutNestingRecursive(DummyNode* node) const; + Vec4i layoutNestingRecursive(DummyNode* node, int maxWidth) const; void addExpandToggleNode(DummyNode* node) const; void layoutToGrid(DummyNode* node) const; diff --git a/src/lib/component/controller/helper/ListLayouter.cpp b/src/lib/component/controller/helper/ListLayouter.cpp index 1bdb77d0..114c2f39 100644 --- a/src/lib/component/controller/helper/ListLayouter.cpp +++ b/src/lib/component/controller/helper/ListLayouter.cpp @@ -128,6 +128,85 @@ void ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector>* nodes, int maxWidth) +{ + int gapX = GraphViewStyle::s_gridCellSize + 2 * GraphViewStyle::s_gridCellPadding; + int gapY = GraphViewStyle::s_gridCellPadding; + + std::vector> visibleNodes; + for (auto node : *nodes) + { + if (node->getsLayouted()) + { + visibleNodes.push_back(node); + } + } + + int totalHeight = 0; + for (size_t i = 0; i < visibleNodes.size(); i++) + { + totalHeight += visibleNodes[i]->size.y() + gapY; + } + + int diff = -1; + size_t cols = 1; + + for (size_t i = cols; i < 100; i++) + { + if (layoutSquareInternal(visibleNodes, Vec2i(maxWidth, totalHeight * i / 100), Vec2i(gapX, gapY))) + { + Vec4i rect = boundingRect(visibleNodes); + + int newDiff = rect.z() * rect.w() + (rect.z() - rect.w()) * (rect.z() - rect.w()) / 4; + if (maxWidth >= 0) + { + newDiff = rect.w(); + } + + if (diff < 0 || newDiff <= diff) + { + diff = newDiff; + cols = i; + } + } + } + + layoutSquareInternal(visibleNodes, Vec2i(maxWidth, totalHeight * cols / 100), Vec2i(gapX, gapY)); +} + +bool ListLayouter::layoutSquareInternal( + std::vector>& visibleNodes, const Vec2i& maxSize, const Vec2i& gap) +{ + int x = 0; + int y = 0; + + int width = 0; + + for (std::shared_ptr node : visibleNodes) + { + node->position.x() = x; + node->position.y() = y; + + y += node->size.y() + gap.y(); + width = std::max(width, node->size.x()); + + if (maxSize.x > 0 && x + width > maxSize.x) + { + return false; + } + + if (y >= maxSize.y) + { + y = 0; + x += width + gap.x(); + + width = 0; + } + } + + return true; +} + void ListLayouter::layoutSkewed(std::vector>* nodes, int gapX, int gapY, int maxWidth) { std::vector> visibleNodes; diff --git a/src/lib/component/controller/helper/ListLayouter.h b/src/lib/component/controller/helper/ListLayouter.h index 5711fb08..f6f47640 100644 --- a/src/lib/component/controller/helper/ListLayouter.h +++ b/src/lib/component/controller/helper/ListLayouter.h @@ -16,6 +16,7 @@ public: static void layoutColumn(std::vector>* nodes, int gap); static void layoutMultiColumn(Vec2i viewSize, std::vector>* nodes); + static void layoutSquare(std::vector>* nodes, int maxWidth); static void layoutSkewed(std::vector>* nodes, int gapX, int gapY, int maxWidth); static Vec4i boundingRect(const std::vector>& nodes); @@ -23,6 +24,7 @@ public: private: static void layoutSimple(std::vector>* nodes, int gapX, int gapY, bool horizontal); + static bool layoutSquareInternal(std::vector>& visibleNodes, const Vec2i& maxSize, const Vec2i& gap); }; #endif // LIST_LAYOUTER_H diff --git a/src/lib/data/GroupType.h b/src/lib/data/GroupType.h index 0ad2582c..6004d26c 100644 --- a/src/lib/data/GroupType.h +++ b/src/lib/data/GroupType.h @@ -20,7 +20,8 @@ enum class GroupLayout { LIST, SKEWED, - BUCKET + BUCKET, + SQUARE }; #endif // GROUP_TYPE_H diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp index a0855d08..26e1ed2b 100644 --- a/src/lib/data/storage/PersistentStorage.cpp +++ b/src/lib/data/storage/PersistentStorage.cpp @@ -1046,8 +1046,8 @@ std::shared_ptr PersistentStorage::getGraphForActiveTokenIds( { m_hierarchyCache.addFirstChildIdsForNodeId(elementId, &nodeIds, &edgeIds); - // don't expand active node if it has more than 20 child nodes - if (nodeIds.size() > 20 && nodeType.isCollapsible()) + // don't expand active node if it has too many child nodes + if (nodeIds.size() > 100 && nodeType.isCollapsible()) { nodeIds.clear(); }