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:
Eberhard Graether
2019-04-16 16:24:38 +02:00
parent 92192659d9
commit 4dd032dcff
6 changed files with 157 additions and 49 deletions
@@ -845,18 +845,12 @@ bool GraphController::setActive(const std::vector<Id>& activeTokenIds, bool show
bool isInheritance = edge->data->isType(Edge::EDGE_INHERITANCE); bool isInheritance = edge->data->isType(Edge::EDGE_INHERITANCE);
if (from && to && !edge->hidden && 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; edge->visible = true;
from->connected = true; from->connected = true;
to->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 else
{ {
@@ -1729,7 +1723,7 @@ void GraphController::groupTrailNodes(GroupType groupType)
std::shared_ptr<DummyNode> groupNode = std::make_shared<DummyNode>(DummyNode::DUMMY_GROUP); std::shared_ptr<DummyNode> groupNode = std::make_shared<DummyNode>(DummyNode::DUMMY_GROUP);
groupNode->visible = true; groupNode->visible = true;
groupNode->groupType = groupType; groupNode->groupType = groupType;
groupNode->groupLayout = GroupLayout::SKEWED; groupNode->groupLayout = GroupLayout::SQUARE;
// Use token Id of first node and make first 2 bits 1 // Use token Id of first node and make first 2 bits 1
groupNode->tokenId = ~(~Id(0) >> 2) + node.nodeId; groupNode->tokenId = ~(~Id(0) >> 2) + node.nodeId;
@@ -1825,7 +1819,7 @@ void GraphController::layoutNesting()
for (const std::shared_ptr<DummyNode>& node : m_dummyNodes) 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) 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) if (!node->visible)
{ {
return; return Vec4i(0, 0, 0, 0);
} }
GraphViewStyle::NodeMargins margins; GraphViewStyle::NodeMargins margins;
@@ -1904,7 +1898,7 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
} }
else if (node->isQualifierNode()) else if (node->isQualifierNode())
{ {
return; return Vec4i(0, 0, 0, 0);
} }
else if (node->isTextNode()) else if (node->isTextNode())
{ {
@@ -1940,6 +1934,8 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
width += margins.iconWidth; width += margins.iconWidth;
width = std::max(width, margins.minWidth); width = std::max(width, margins.minWidth);
int maxAccessWidth = 0;
for (const std::shared_ptr<DummyNode>& subNode : node->subNodes) for (const std::shared_ptr<DummyNode>& subNode : node->subNodes)
{ {
if (!subNode->visible) if (!subNode->visible)
@@ -1953,14 +1949,33 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
continue; continue;
} }
layoutNestingRecursive(subNode.get()); Vec4i rect = layoutNestingRecursive(subNode.get(), maxWidth);
if (subNode->isExpandToggleNode()) if (subNode->isExpandToggleNode())
{ {
width += margins.spacingX + subNode->size.x; width += margins.spacingX + subNode->size.x;
} }
else if (subNode->isAccessNode())
{
maxAccessWidth = std::max(maxAccessWidth, rect.z());
}
} }
if (maxAccessWidth > 0)
{
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()) if (node->isGroupNode())
{ {
Vec2i viewSize = getView()->getViewSize(); Vec2i viewSize = getView()->getViewSize();
@@ -1989,12 +2004,21 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); ListLayouter::layoutColumn(&node->subNodes, margins.spacingY);
} }
break; break;
case GroupLayout::SQUARE:
ListLayouter::layoutSquare(&node->subNodes, -1);
break;
} }
} }
else if (node->isAccessNode() && !node->hasConnectedSubNode())
{
ListLayouter::layoutSquare(&node->subNodes, maxWidth);
}
else else
{ {
ListLayouter::layoutColumn(&node->subNodes, margins.spacingY); ListLayouter::layoutColumn(&node->subNodes, margins.spacingY);
} }
}
Vec2i size = ListLayouter::offsetNodes( Vec2i size = ListLayouter::offsetNodes(
node->subNodes, margins.top + margins.charHeight + margins.spacingA, margins.left); node->subNodes, margins.top + margins.charHeight + margins.spacingA, margins.left);
@@ -2022,6 +2046,8 @@ void GraphController::layoutNestingRecursive(DummyNode* node) const
subNode->position.y = 6; subNode->position.y = 6;
} }
} }
return ListLayouter::boundingRect(node->subNodes);
} }
void GraphController::addExpandToggleNode(DummyNode* node) const void GraphController::addExpandToggleNode(DummyNode* node) const
@@ -120,7 +120,7 @@ private:
void layoutNesting(); void layoutNesting();
void extendEqualFunctionNames(const std::vector<std::shared_ptr<DummyNode>>& nodes) const; 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 addExpandToggleNode(DummyNode* node) const;
void layoutToGrid(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) void ListLayouter::layoutSkewed(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth)
{ {
std::vector<std::shared_ptr<DummyNode>> visibleNodes; 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 layoutColumn(std::vector<std::shared_ptr<DummyNode>>* nodes, int gap);
static void layoutMultiColumn(Vec2i viewSize, std::vector<std::shared_ptr<DummyNode>>* nodes); 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 void layoutSkewed(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, int maxWidth);
static Vec4i boundingRect(const std::vector<std::shared_ptr<DummyNode>>& nodes); static Vec4i boundingRect(const std::vector<std::shared_ptr<DummyNode>>& nodes);
@@ -23,6 +24,7 @@ public:
private: private:
static void layoutSimple(std::vector<std::shared_ptr<DummyNode>>* nodes, int gapX, int gapY, bool horizontal); 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 #endif // LIST_LAYOUTER_H
+2 -1
View File
@@ -20,7 +20,8 @@ enum class GroupLayout
{ {
LIST, LIST,
SKEWED, SKEWED,
BUCKET BUCKET,
SQUARE
}; };
#endif // GROUP_TYPE_H #endif // GROUP_TYPE_H
+2 -2
View File
@@ -1046,8 +1046,8 @@ std::shared_ptr<Graph> PersistentStorage::getGraphForActiveTokenIds(
{ {
m_hierarchyCache.addFirstChildIdsForNodeId(elementId, &nodeIds, &edgeIds); m_hierarchyCache.addFirstChildIdsForNodeId(elementId, &nodeIds, &edgeIds);
// don't expand active node if it has more than 20 child nodes // don't expand active node if it has too many child nodes
if (nodeIds.size() > 20 && nodeType.isCollapsible()) if (nodeIds.size() > 100 && nodeType.isCollapsible())
{ {
nodeIds.clear(); nodeIds.clear();
} }