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:
@@ -13,7 +13,7 @@ public:
|
||||
|
||||
protected:
|
||||
template <typename ViewType>
|
||||
ViewType* getView();
|
||||
ViewType* getView() const;
|
||||
|
||||
private:
|
||||
Component* m_component;
|
||||
@@ -22,7 +22,7 @@ private:
|
||||
|
||||
|
||||
template <typename ViewType>
|
||||
ViewType* Controller::getView()
|
||||
ViewType* Controller::getView() const
|
||||
{
|
||||
if (m_component)
|
||||
return m_component->getView<ViewType>();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
#include "utility/messaging/MessageListener.h"
|
||||
#include "utility/messaging/type/MessageActivateToken.h"
|
||||
#include "utility/messaging/type/MessageActivateTokens.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeExpand.h"
|
||||
#include "utility/messaging/type/MessageGraphNodeMove.h"
|
||||
|
||||
#include "component/controller/Controller.h"
|
||||
#include "component/controller/GraphLayouter.h"
|
||||
#include "component/view/GraphView.h"
|
||||
|
||||
struct DummyNode;
|
||||
struct DummyEdge;
|
||||
class Graph;
|
||||
class GraphView;
|
||||
class GraphAccess;
|
||||
class Node;
|
||||
|
||||
@@ -21,21 +23,59 @@ class GraphController
|
||||
: public Controller
|
||||
, public MessageListener<MessageActivateToken>
|
||||
, public MessageListener<MessageActivateTokens>
|
||||
, public MessageListener<MessageGraphNodeExpand>
|
||||
, public MessageListener<MessageGraphNodeMove>
|
||||
{
|
||||
public:
|
||||
struct Margins
|
||||
{
|
||||
Margins();
|
||||
|
||||
int left;
|
||||
int right;
|
||||
|
||||
int top;
|
||||
int bottom;
|
||||
|
||||
int betweenX;
|
||||
int betweenY;
|
||||
|
||||
int minWidth;
|
||||
float charWidth;
|
||||
};
|
||||
|
||||
GraphController(GraphAccess* graphAccess);
|
||||
~GraphController();
|
||||
|
||||
private:
|
||||
virtual void handleMessage(MessageActivateToken* message);
|
||||
virtual void handleMessage(MessageActivateTokens* message);
|
||||
virtual void handleMessage(MessageGraphNodeExpand* message);
|
||||
virtual void handleMessage(MessageGraphNodeMove* message);
|
||||
|
||||
GraphView* getView();
|
||||
GraphView* getView() const;
|
||||
|
||||
void createDummyGraphForTokenIds(const std::vector<Id>& tokenIds);
|
||||
DummyNode createDummyNodeTopDown(Node* node, std::vector<DummyEdge>* dummyEdges) const;
|
||||
DummyNode createDummyNodeTopDown(Node* node);
|
||||
|
||||
void setActiveAndVisibility(const std::vector<Id>& activeTokenIds);
|
||||
bool setNodeActiveAndVisibilityRecursiveBottomUp(DummyNode& node, const std::vector<Id>& activeTokenIds) const;
|
||||
void setNodeVisibilityRecursiveTopDown(DummyNode& node) const;
|
||||
|
||||
void layoutNesting();
|
||||
void layoutNestingRecursive(DummyNode& node) const;
|
||||
|
||||
Margins getMarginsForDummyNode(DummyNode& node) const;
|
||||
DummyNode* findDummyNodeRecursive(std::vector<DummyNode>& nodes, Id tokenId);
|
||||
DummyNode* findDummyNodeAccessRecursive(std::vector<DummyNode>& nodes, Id parentId, TokenComponentAccess::AccessType type);
|
||||
|
||||
GraphAccess* m_graphAccess;
|
||||
GraphView::Metrics m_viewMetrics;
|
||||
|
||||
std::vector<DummyNode> m_dummyNodes;
|
||||
std::vector<DummyEdge> m_dummyEdges;
|
||||
|
||||
std::vector<Id> m_activeTokenIds;
|
||||
};
|
||||
|
||||
#endif // GRAPH_CONTROLLER_H
|
||||
|
||||
Reference in New Issue
Block a user