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