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
+2
View File
@@ -193,6 +193,8 @@ add_files(
utility/messaging/type/MessageActivateToken.h
utility/messaging/type/MessageFind.h
utility/messaging/type/MessageFinishedParsing.h
utility/messaging/type/MessageGraphNodeExpand.h
utility/messaging/type/MessageGraphNodeMove.h
utility/messaging/type/MessageLoadProject.h
utility/messaging/type/MessageLoadSource.h
utility/messaging/type/MessageRefresh.h
+2 -2
View File
@@ -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>();
+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;
}
+43 -3
View File
@@ -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
+9
View File
@@ -1,5 +1,14 @@
#include "component/view/GraphView.h"
GraphView::Metrics::Metrics()
: width(0)
, height(0)
, typeNameCharWidth(0.0f)
, variableNameCharWidth(0.0f)
, functionNameCharWidth(0.0f)
{
}
GraphView::GraphView(ViewLayout* viewLayout)
: View(viewLayout, Vec2i(100, 100))
{
+15 -5
View File
@@ -15,18 +15,28 @@ class Graph;
class GraphView : public View
{
public:
struct Metrics
{
Metrics();
int width;
int height;
float typeNameCharWidth;
float variableNameCharWidth;
float functionNameCharWidth;
};
GraphView(ViewLayout* viewLayout);
virtual ~GraphView();
virtual std::string getName() const;
virtual void rebuildGraph(
std::shared_ptr<Graph> graph,
std::vector<Id> activeTokenIds,
const std::vector<DummyNode>& nodes,
const std::vector<DummyEdge>& edges) = 0;
std::shared_ptr<Graph> graph, const std::vector<DummyNode>& nodes, const std::vector<DummyEdge>& edges) = 0;
virtual void clear() = 0;
virtual Metrics getViewMetrics() const = 0;
};
#endif // GRAPH_VIEW_H
@@ -33,6 +33,8 @@ struct DummyEdge
: ownerId(o)
, targetId(t)
, data(data)
, visible(false)
, active(false)
{
}
@@ -40,6 +42,9 @@ struct DummyEdge
Id targetId;
const Edge* data;
bool visible;
bool active;
};
#endif // GRAPH_EDGE_H
@@ -26,9 +26,12 @@ public:
void setData(const Node* data);
virtual Vec2i getPosition() const = 0;
virtual void setPosition(const Vec2i& position) = 0;
virtual bool setPosition(const Vec2i& position) = 0;
virtual void moveTo(const Vec2i& position) = 0;
virtual Vec2i getSize() const = 0;
virtual void setSize(const Vec2i& size) = 0;
virtual Vec4i getBoundingRect() const = 0;
virtual Vec4i getParentBoundingRect() const = 0;
@@ -47,19 +50,39 @@ struct DummyNode
DummyNode(const Node* data)
: data(data)
, accessType(TokenComponentAccess::ACCESS_NONE)
, active(false)
, connected(false)
, expanded(false)
, invisibleSubNodeCount(0)
, visible(false)
{
}
DummyNode(TokenComponentAccess::AccessType accessType)
: data(nullptr)
, accessType(accessType)
, active(false)
, connected(false)
, expanded(false)
, invisibleSubNodeCount(0)
, visible(false)
{
}
const Node* data;
const TokenComponentAccess::AccessType accessType;
TokenComponentAccess::AccessType accessType;
Vec2i position;
Vec2i size;
bool active;
bool connected;
bool expanded;
size_t invisibleSubNodeCount;
bool visible;
std::vector<DummyNode> subNodes;
};
@@ -0,0 +1,26 @@
#ifndef MESSAGE_GRAPH_NODE_EXPAND_H
#define MESSAGE_GRAPH_NODE_EXPAND_H
#include "utility/messaging/Message.h"
#include "utility/types.h"
#include "data/graph/token_component/TokenComponentAccess.h"
class MessageGraphNodeExpand: public Message<MessageGraphNodeExpand>
{
public:
MessageGraphNodeExpand(Id tokenId, TokenComponentAccess::AccessType access)
: tokenId(tokenId)
, access(access)
{
}
static const std::string getStaticType()
{
return "MessageGraphNodeExpand";
}
const Id tokenId;
const TokenComponentAccess::AccessType access;
};
#endif // MESSAGE_GRAPH_NODE_EXPAND_H
@@ -0,0 +1,26 @@
#ifndef MESSAGE_GRAPH_NODE_MOVE_H
#define MESSAGE_GRAPH_NODE_MOVE_H
#include "utility/math/Vector2.h"
#include "utility/messaging/Message.h"
#include "utility/types.h"
class MessageGraphNodeMove: public Message<MessageGraphNodeMove>
{
public:
MessageGraphNodeMove(Id tokenId, Vec2i position)
: tokenId(tokenId)
, position(position)
{
}
static const std::string getStaticType()
{
return "MessageGraphNodeMove";
}
const Id tokenId;
const Vec2i position;
};
#endif // MESSAGE_GRAPH_NODE_MOVE_H