ui: moved subnode layouting from GraphView to GraphController

This change moves nested layouting of the Graph to GraphController. The DummyNodes get the correct size assigned, as
well as a visible flag. Mouse interaction with the Graph is now handled via the GraphController, which makes it
easier to undo them or reuse the changed state after graph updates.
This commit is contained in:
Eberhard Graether
2014-11-20 20:53:01 +01:00
parent 113aab8e60
commit 048fcb28b4
17 changed files with 831 additions and 459 deletions
+352 -19
View File
@@ -9,6 +9,19 @@
#include "component/view/GraphView.h"
#include "data/access/GraphAccess.h"
GraphController::Margins::Margins()
: left(0)
, right(0)
, top(0)
, bottom(0)
, betweenX(0)
, betweenY(0)
, minWidth(0)
, charWidth(0.0f)
{
}
GraphController::GraphController(GraphAccess* graphAccess)
: m_graphAccess(graphAccess)
{
@@ -29,7 +42,30 @@ void GraphController::handleMessage(MessageActivateTokens* message)
createDummyGraphForTokenIds(message->tokenIds);
}
GraphView* GraphController::getView()
void GraphController::handleMessage(MessageGraphNodeExpand* message)
{
DummyNode* node = findDummyNodeAccessRecursive(m_dummyNodes, message->tokenId, message->access);
if (node)
{
node->expanded = !node->expanded;
setActiveAndVisibility(m_activeTokenIds);
layoutNesting();
getView()->rebuildGraph(nullptr, m_dummyNodes, m_dummyEdges);
}
}
void GraphController::handleMessage(MessageGraphNodeMove* message)
{
DummyNode* node = findDummyNodeRecursive(m_dummyNodes, message->tokenId);
if (node)
{
node->position = message->position;
}
}
GraphView* GraphController::getView() const
{
return Controller::getView<GraphView>();
}
@@ -47,12 +83,14 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
std::shared_ptr<Graph> graph = m_graphAccess->getGraphForActiveTokenIds(tokenIds);
std::vector<DummyNode> dummyNodes;
std::vector<DummyEdge> dummyEdges;
m_dummyEdges.clear();
std::set<Id> addedNodes;
std::vector<DummyNode> dummyNodes;
graph->forEachNode(
[&dummyNodes, &dummyEdges, &addedNodes, this](Node* node)
[&addedNodes, &dummyNodes, this](Node* node)
{
Node* parent = node->getLastParentNode();
Id id = parent->getId();
@@ -62,23 +100,29 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
}
addedNodes.insert(id);
dummyNodes.push_back(createDummyNodeTopDown(parent, &dummyEdges));
dummyNodes.push_back(createDummyNodeTopDown(parent));
}
);
layoutFunction(dummyNodes);
m_dummyNodes = dummyNodes;
view->rebuildGraph(graph, tokenIds, dummyNodes, dummyEdges);
m_activeTokenIds = tokenIds;
setActiveAndVisibility(tokenIds);
layoutNesting();
layoutFunction(m_dummyNodes);
view->rebuildGraph(graph, m_dummyNodes, m_dummyEdges);
}
DummyNode GraphController::createDummyNodeTopDown(Node* node, std::vector<DummyEdge>* dummyEdges) const
DummyNode GraphController::createDummyNodeTopDown(Node* node)
{
DummyNode result(node);
node->forEachChildNode(
[&result, dummyEdges, this](Node* child)
[node, &result, this](Node* child)
{
DummyNode* container = nullptr;
DummyNode* parent = nullptr;
Edge* edge = child->getMemberEdge();
TokenComponentAccess* access = edge->getComponent<TokenComponentAccess>();
@@ -90,36 +134,43 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node, std::vector<DummyE
{
if (dummy.accessType == accessType)
{
container = &dummy;
parent = &dummy;
break;
}
}
if (!container)
if (!parent)
{
result.subNodes.push_back(DummyNode(accessType));
container = &result.subNodes.back();
}
parent = &result.subNodes.back();
DummyNode* oldParent = findDummyNodeAccessRecursive(m_dummyNodes, node->getId(), accessType);
if (oldParent)
{
parent->expanded = oldParent->expanded;
}
}
}
else
{
container = &result;
parent = &result;
}
container->subNodes.push_back(createDummyNodeTopDown(child, dummyEdges));
parent->subNodes.push_back(createDummyNodeTopDown(child));
}
);
node->forEachEdge(
[&result, node, dummyEdges](Edge* edge)
[&result, node, this](Edge* edge)
{
if (edge->isType(Edge::EDGE_MEMBER))
{
return;
}
for (const DummyEdge& dummy : *dummyEdges)
result.connected = true;
for (const DummyEdge& dummy : m_dummyEdges)
{
if (dummy.data->getId() == edge->getId())
{
@@ -127,9 +178,291 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node, std::vector<DummyE
}
}
dummyEdges->push_back(DummyEdge(edge->getFrom()->getId(), edge->getTo()->getId(), edge));
m_dummyEdges.push_back(DummyEdge(edge->getFrom()->getId(), edge->getTo()->getId(), edge));
}
);
return result;
}
void GraphController::setActiveAndVisibility(const std::vector<Id>& activeTokenIds)
{
for (DummyNode& node : m_dummyNodes)
{
setNodeActiveAndVisibilityRecursiveBottomUp(node, activeTokenIds);
}
for (DummyEdge& edge : m_dummyEdges)
{
edge.visible = true;
if (find(activeTokenIds.begin(), activeTokenIds.end(), edge.data->getId()) != activeTokenIds.end())
{
edge.active = true;
}
}
}
bool GraphController::setNodeActiveAndVisibilityRecursiveBottomUp(
DummyNode& node, const std::vector<Id>& activeTokenIds
) const {
node.visible = false;
node.active = false;
if (node.data)
{
node.active = find(activeTokenIds.begin(), activeTokenIds.end(), node.data->getId()) != activeTokenIds.end();
}
bool childVisible = false;
for (DummyNode& subNode : node.subNodes)
{
if (setNodeActiveAndVisibilityRecursiveBottomUp(subNode, activeTokenIds))
{
childVisible = true;
}
}
if (node.active || node.connected || childVisible)
{
setNodeVisibilityRecursiveTopDown(node);
}
return node.visible;
}
void GraphController::setNodeVisibilityRecursiveTopDown(DummyNode& node) const
{
node.visible = true;
for (DummyNode& subNode : node.subNodes)
{
if (subNode.accessType != TokenComponentAccess::ACCESS_NONE || node.expanded ||
(node.data && node.data->getType() == Node::NODE_ENUM))
{
setNodeVisibilityRecursiveTopDown(subNode);
}
}
}
void GraphController::layoutNesting()
{
if (!m_viewMetrics.width)
{
m_viewMetrics = getView()->getViewMetrics();
}
for (DummyNode& node : m_dummyNodes)
{
layoutNestingRecursive(node);
}
}
void GraphController::layoutNestingRecursive(DummyNode& node) const
{
Margins margins = getMarginsForDummyNode(node);
int y = 0;
int x = 0;
int width = margins.minWidth;
int height = 0;
if (node.data)
{
width = margins.charWidth * node.data->getName().size();
}
bool layoutHorizontal = true;
for (DummyNode& subNode : node.subNodes)
{
if (!subNode.visible)
{
continue;
}
layoutNestingRecursive(subNode);
if (subNode.data || subNode.expanded || subNode.invisibleSubNodeCount != subNode.subNodes.size())
{
layoutHorizontal = false;
}
}
for (DummyNode& subNode : node.subNodes)
{
if (!subNode.visible)
{
continue;
}
subNode.position.x = margins.left + x;
subNode.position.y = margins.top + y;
if (layoutHorizontal)
{
x += subNode.size.x + margins.betweenX;
if (subNode.size.y > height)
{
height = subNode.size.y;
}
}
else
{
y += subNode.size.y + margins.betweenY;
if (subNode.size.x > width)
{
width = subNode.size.x;
}
}
}
if (x > 0)
{
x -= margins.betweenX;
}
if (y > 0)
{
y -= margins.betweenY;
}
if (layoutHorizontal && x > width)
{
width = x;
}
node.size.x = margins.left + width + margins.right;
node.size.y = margins.top + y + height + margins.bottom;
}
GraphController::Margins GraphController::getMarginsForDummyNode(DummyNode& node) const
{
Margins margins;
margins.betweenX = margins.betweenY = 8;
if (node.data)
{
switch (node.data->getType())
{
case Node::NODE_UNDEFINED:
case Node::NODE_NAMESPACE:
margins.left = margins.right = 15;
margins.top = 28;
margins.bottom = 15;
margins.charWidth = m_viewMetrics.typeNameCharWidth;
break;
case Node::NODE_UNDEFINED_TYPE:
case Node::NODE_STRUCT:
case Node::NODE_CLASS:
case Node::NODE_ENUM:
case Node::NODE_TYPEDEF:
if (node.subNodes.size())
{
margins.left = margins.right = 15;
margins.top = 30;
margins.bottom = 10;
}
else
{
margins.left = margins.right = 8;
margins.top = margins.bottom = 13;
}
margins.charWidth = m_viewMetrics.typeNameCharWidth;
break;
case Node::NODE_UNDEFINED_FUNCTION:
case Node::NODE_FUNCTION:
case Node::NODE_METHOD:
margins.left = margins.right = 5;
margins.top = margins.bottom = 10;
margins.charWidth = m_viewMetrics.functionNameCharWidth;
break;
case Node::NODE_UNDEFINED_VARIABLE:
case Node::NODE_GLOBAL_VARIABLE:
case Node::NODE_FIELD:
margins.left = margins.right = 5;
margins.top = margins.bottom = 10;
margins.charWidth = m_viewMetrics.variableNameCharWidth;
break;
}
}
else
{
node.invisibleSubNodeCount = 0;
for (const DummyNode& subNode : node.subNodes)
{
if (!subNode.visible)
{
node.invisibleSubNodeCount++;
}
}
margins.left = margins.right = 10;
margins.top = 40;
if (node.invisibleSubNodeCount == node.subNodes.size())
{
margins.minWidth = 20;
margins.bottom = 10;
}
else
{
margins.minWidth = 82;
if (node.expanded)
{
margins.bottom = 15;
}
else if (node.invisibleSubNodeCount)
{
margins.bottom = 23;
}
else
{
margins.bottom = 10;
}
}
}
return margins;
}
DummyNode* GraphController::findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId)
{
for (DummyNode& node : nodes)
{
if (node.data && node.data->getId() == tokenId)
{
return &node;
}
DummyNode* result = findDummyNodeRecursive(node.subNodes, tokenId);
if (result != nullptr)
{
return result;
}
}
return nullptr;
}
DummyNode* GraphController::findDummyNodeAccessRecursive(
std::vector<DummyNode>& nodes, Id parentId, TokenComponentAccess::AccessType type
){
DummyNode* node = findDummyNodeRecursive(nodes, parentId);
if (node)
{
for (DummyNode& subNode : node->subNodes)
{
if (subNode.accessType == type)
{
return &subNode;
}
}
}
return nullptr;
}