ui: Multi-column layout within access nodes if no child node has edges
* use multiple columns for access nodes if no child has edges * try to layout close to a square * layout neighboring accesses to the width of the largest * increase threshold to hide children on parent activation to 100 * use square layouting in inheritance trees as well
This commit is contained in:
@@ -845,18 +845,12 @@ bool GraphController::setActive(const std::vector<Id>& 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<DummyNode> groupNode = std::make_shared<DummyNode>(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<DummyNode>& node : m_dummyNodes)
|
||||
{
|
||||
layoutNestingRecursive(node.get());
|
||||
layoutNestingRecursive(node.get(), -1);
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<DummyNode>& node : m_dummyNodes)
|
||||
@@ -1868,11 +1862,11 @@ void GraphController::extendEqualFunctionNames(const std::vector<std::shared_ptr
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::layoutNestingRecursive(DummyNode* node) const
|
||||
Vec4i GraphController::layoutNestingRecursive(DummyNode* node, int maxWidth) const
|
||||
{
|
||||
if (!node->visible)
|
||||
{
|
||||
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<DummyNode>& 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<DummyNode>& 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
|
||||
|
||||
@@ -120,7 +120,7 @@ private:
|
||||
|
||||
void layoutNesting();
|
||||
void extendEqualFunctionNames(const std::vector<std::shared_ptr<DummyNode>>& nodes) const;
|
||||
void layoutNestingRecursive(DummyNode* node) const;
|
||||
Vec4i layoutNestingRecursive(DummyNode* node, int maxWidth) const;
|
||||
void addExpandToggleNode(DummyNode* node) const;
|
||||
void layoutToGrid(DummyNode* node) const;
|
||||
|
||||
|
||||
@@ -128,6 +128,85 @@ void ListLayouter::layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr
|
||||
}
|
||||
}
|
||||
|
||||
void ListLayouter::layoutSquare(std::vector<std::shared_ptr<DummyNode>>* nodes, int maxWidth)
|
||||
{
|
||||
int gapX = GraphViewStyle::s_gridCellSize + 2 * GraphViewStyle::s_gridCellPadding;
|
||||
int gapY = GraphViewStyle::s_gridCellPadding;
|
||||
|
||||
std::vector<std::shared_ptr<DummyNode>> 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<std::shared_ptr<DummyNode>>& visibleNodes, const Vec2i& maxSize, const Vec2i& gap)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
int width = 0;
|
||||
|
||||
for (std::shared_ptr<DummyNode> 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<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth)
|
||||
{
|
||||
std::vector<std::shared_ptr<DummyNode>> visibleNodes;
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
static void layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap);
|
||||
|
||||
static void layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes);
|
||||
static void layoutSquare(std::vector<std::shared_ptr<DummyNode>>* nodes, int maxWidth);
|
||||
static void layoutSkewed(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth);
|
||||
|
||||
static Vec4i boundingRect(const std::vector<std::shared_ptr<DummyNode>>& nodes);
|
||||
@@ -23,6 +24,7 @@ public:
|
||||
|
||||
private:
|
||||
static void layoutSimple(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, bool horizontal);
|
||||
static bool layoutSquareInternal(std::vector<std::shared_ptr<DummyNode>>& visibleNodes, const Vec2i& maxSize, const Vec2i& gap);
|
||||
};
|
||||
|
||||
#endif // LIST_LAYOUTER_H
|
||||
|
||||
@@ -20,7 +20,8 @@ enum class GroupLayout
|
||||
{
|
||||
LIST,
|
||||
SKEWED,
|
||||
BUCKET
|
||||
BUCKET,
|
||||
SQUARE
|
||||
};
|
||||
|
||||
#endif // GROUP_TYPE_H
|
||||
|
||||
@@ -1046,8 +1046,8 @@ std::shared_ptr<Graph> 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user