more work
This commit is contained in:
@@ -42,15 +42,13 @@ const double NodeView::kMinimumScale = 0.1;
|
||||
|
||||
NodeView::NodeView(QWidget *parent) :
|
||||
HandMovableView(parent),
|
||||
graph_(nullptr),
|
||||
drop_edge_(nullptr),
|
||||
create_edge_(nullptr),
|
||||
create_edge_dst_(nullptr),
|
||||
create_edge_dst_temp_expanded_(false),
|
||||
paste_command_(nullptr),
|
||||
filter_mode_(kFilterShowSelective),
|
||||
scale_(1.0),
|
||||
queue_reposition_contexts_(false)
|
||||
first_show_(true)
|
||||
{
|
||||
setScene(&scene_);
|
||||
SetDefaultDragMode(RubberBandDrag);
|
||||
@@ -84,10 +82,10 @@ NodeView::~NodeView()
|
||||
ClearGraph();
|
||||
}
|
||||
|
||||
void NodeView::SetGraph(NodeGraph *graph, const QVector<Node*> &nodes)
|
||||
void NodeView::SetContexts(const QVector<Node*> &nodes)
|
||||
{
|
||||
// Remove contexts that are no longer in the list
|
||||
foreach (Node *n, filter_nodes_) {
|
||||
foreach (Node *n, contexts_) {
|
||||
if (!nodes.contains(n)) {
|
||||
scene_.RemoveContext(n);
|
||||
}
|
||||
@@ -95,86 +93,38 @@ void NodeView::SetGraph(NodeGraph *graph, const QVector<Node*> &nodes)
|
||||
|
||||
// Add contexts that are now in the list
|
||||
foreach (Node *n, nodes) {
|
||||
if (!filter_nodes_.contains(n)) {
|
||||
if (!contexts_.contains(n)) {
|
||||
scene_.AddContext(n);
|
||||
}
|
||||
}
|
||||
|
||||
filter_nodes_ = nodes;
|
||||
contexts_ = nodes;
|
||||
|
||||
/*bool graph_changed = graph_ != graph;
|
||||
bool context_changed = last_set_filter_nodes_ != nodes;
|
||||
CenterOnItemsBoundingRect();
|
||||
}
|
||||
|
||||
if (graph_changed || context_changed) {
|
||||
// Clear nodes if necessary
|
||||
bool refresh_required = (graph_changed && filter_mode_ == kFilterShowAll)
|
||||
|| (context_changed && filter_mode_ == kFilterShowSelective);
|
||||
bool nodes_visible = (graph && filter_mode_ == kFilterShowAll)
|
||||
|| (!nodes.isEmpty() && filter_mode_ == kFilterShowSelective);
|
||||
void NodeView::CloseContextsBelongingToProject(Project *project)
|
||||
{
|
||||
QVector<Node*> new_contexts = contexts_;
|
||||
|
||||
if (refresh_required) {
|
||||
DeselectAll();
|
||||
positions_.clear();
|
||||
scene_.clear();
|
||||
context_offsets_.clear();
|
||||
for (auto it = new_contexts.begin(); it != new_contexts.end(); ) {
|
||||
if ((*it)->project() == project) {
|
||||
it = new_contexts.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle graph change
|
||||
if (graph_changed) {
|
||||
if (graph_) {
|
||||
// Disconnect from current graph
|
||||
disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
|
||||
disconnect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
|
||||
disconnect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
|
||||
disconnect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
|
||||
disconnect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
|
||||
}
|
||||
|
||||
graph_ = graph;
|
||||
|
||||
if (graph_) {
|
||||
// Connect to new graph
|
||||
connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
|
||||
connect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
|
||||
connect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
|
||||
connect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
|
||||
connect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (context_changed) {
|
||||
last_set_filter_nodes_ = nodes;
|
||||
|
||||
if (filter_mode_ == kFilterShowSelective) {
|
||||
filter_nodes_ = nodes;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh_required && nodes_visible) {
|
||||
if (filter_mode_ == kFilterShowAll) {
|
||||
// Just make the filter nodes all of the graph's contexts
|
||||
filter_nodes_ = graph->GetPositionMap().keys().toVector();
|
||||
}
|
||||
|
||||
RepositionContexts();
|
||||
|
||||
// Center on something
|
||||
QMetaObject::invokeMethod(this, &NodeView::CenterOnItemsBoundingRect, Qt::QueuedConnection);
|
||||
}
|
||||
}*/
|
||||
SetContexts(new_contexts);
|
||||
}
|
||||
|
||||
void NodeView::ClearGraph()
|
||||
{
|
||||
SetGraph(nullptr, QVector<Node*>());
|
||||
SetContexts(QVector<Node*>());
|
||||
}
|
||||
|
||||
void NodeView::DeleteSelected()
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
{
|
||||
@@ -189,9 +139,6 @@ void NodeView::DeleteSelected()
|
||||
command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input()));
|
||||
removed_connections[i] = {edge->output(), edge->input()};
|
||||
}
|
||||
|
||||
// Update contexts
|
||||
UpdateContextsFromEdgeRemove(command, removed_connections);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,10 +166,6 @@ void NodeView::DeleteSelected()
|
||||
|
||||
void NodeView::SelectAll()
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: rather than respond to every single item being selected, ignore the signal and
|
||||
// then handle them all at the end.
|
||||
DisconnectSelectionChangedSignal();
|
||||
@@ -249,7 +192,7 @@ void NodeView::SelectAll()
|
||||
|
||||
void NodeView::DeselectAll()
|
||||
{
|
||||
if (!graph_ || selected_nodes_.isEmpty()) {
|
||||
if (selected_nodes_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -268,10 +211,6 @@ void NodeView::DeselectAll()
|
||||
|
||||
void NodeView::Select(QVector<Node *> nodes, bool center_view_on_item)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: rather than respond to every single item being selected, ignore the signal and
|
||||
// then handle them all at the end.
|
||||
DisconnectSelectionChangedSignal();
|
||||
@@ -331,39 +270,13 @@ void NodeView::Select(QVector<Node *> nodes, bool center_view_on_item)
|
||||
selected_nodes_ = nodes;
|
||||
}
|
||||
|
||||
void NodeView::SelectWithDependencies(QVector<Node *> nodes, bool center_view_on_item)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int original_length = nodes.size();
|
||||
for (int i=0;i<original_length;i++) {
|
||||
QVector<Node*> dependencies = nodes.at(i)->GetDependencies();
|
||||
|
||||
foreach (Node *d, dependencies) {
|
||||
if (scene_.item_map().contains(d) && !nodes.contains(d)) {
|
||||
nodes.append(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Select(nodes, center_view_on_item);
|
||||
}
|
||||
|
||||
void NodeView::CopySelected(bool cut)
|
||||
{
|
||||
if (!graph_) {
|
||||
if (selected_nodes_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Node*> selected = scene_.GetSelectedNodes();
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CopyNodesToClipboard(selected);
|
||||
CopyNodesToClipboard(selected_nodes_);
|
||||
|
||||
if (cut) {
|
||||
DeleteSelected();
|
||||
@@ -409,43 +322,41 @@ void NodeView::keyPressEvent(QKeyEvent *event)
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
{
|
||||
if (graph_) {
|
||||
MultiUndoCommand *pos_command = new MultiUndoCommand();
|
||||
for (Node *n : qAsConst(selected_nodes_)) {
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
if (graph_->GetNodesForContext(context).contains(n)) {
|
||||
QPointF old_pos = graph_->GetNodePosition(n, context);
|
||||
MultiUndoCommand *pos_command = new MultiUndoCommand();
|
||||
for (Node *n : qAsConst(selected_nodes_)) {
|
||||
for (Node *context : qAsConst(contexts_)) {
|
||||
if (context->ContextContainsNode(n)) {
|
||||
QPointF old_pos = context->GetNodePositionInContext(n);
|
||||
|
||||
// Determine one pixel in scene units
|
||||
double movement_amt = 1.0 / scale_;
|
||||
// Determine one pixel in scene units
|
||||
double movement_amt = 1.0 / scale_;
|
||||
|
||||
// Translate to 2D movement
|
||||
QPointF node_movement;
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Left:
|
||||
node_movement.setX(-movement_amt);
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
node_movement.setX(movement_amt);
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
node_movement.setY(-movement_amt);
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
node_movement.setY(movement_amt);
|
||||
break;
|
||||
}
|
||||
|
||||
// Translate from screen units into node units
|
||||
node_movement = NodeViewItem::ScreenToNodePoint(node_movement, scene_.GetFlowDirection());
|
||||
|
||||
// Move command
|
||||
pos_command->add_child(new NodeSetPositionCommand(n, context, old_pos + node_movement, false));
|
||||
// Translate to 2D movement
|
||||
QPointF node_movement;
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Left:
|
||||
node_movement.setX(-movement_amt);
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
node_movement.setX(movement_amt);
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
node_movement.setY(-movement_amt);
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
node_movement.setY(movement_amt);
|
||||
break;
|
||||
}
|
||||
|
||||
// Translate from screen units into node units
|
||||
node_movement = NodeViewItem::ScreenToNodePoint(node_movement, scene_.GetFlowDirection());
|
||||
|
||||
// Move command
|
||||
pos_command->add_child(new NodeSetPositionCommand(n, context, old_pos + node_movement));
|
||||
}
|
||||
}
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(pos_command);
|
||||
}
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(pos_command);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_Escape:
|
||||
@@ -664,15 +575,6 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
create_edge_dst_ = nullptr;
|
||||
}
|
||||
|
||||
// Update contexts
|
||||
/*if (!removed_edges.empty()) {
|
||||
UpdateContextsFromEdgeRemove(command, removed_edges);
|
||||
}
|
||||
|
||||
if (added_edge.first) {
|
||||
UpdateContextsFromEdgeAdd(command, added_edge, removed_edges);
|
||||
}*/
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
return;
|
||||
}
|
||||
@@ -689,38 +591,6 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// If any node positions changed, set them in their contexts now
|
||||
MultiUndoCommand *set_pos_command = new MultiUndoCommand();
|
||||
for (auto it=positions_.begin(); it!=positions_.end(); it++) {
|
||||
NodeViewItem *item = it.key();
|
||||
Position &pos_data = it.value();
|
||||
QPointF current_item_pos = item->GetNodePosition();
|
||||
Node *node = pos_data.node;
|
||||
|
||||
if (pos_data.original_item_pos != current_item_pos) {
|
||||
QPointF diff = current_item_pos - pos_data.original_item_pos;
|
||||
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
if (graph_->ContextContainsNode(node, context)) {
|
||||
QPointF current_node_pos_in_context = graph_->GetNodePosition(node, context);
|
||||
current_node_pos_in_context += diff;
|
||||
set_pos_command->add_child(new NodeSetPositionCommand(node, context, current_node_pos_in_context, false));
|
||||
}
|
||||
}
|
||||
|
||||
pos_data.original_item_pos = current_item_pos;
|
||||
}
|
||||
}
|
||||
if (set_pos_command->child_count()) {
|
||||
set_pos_command->redo_now();
|
||||
command->add_child(set_pos_command);
|
||||
} else {
|
||||
delete set_pos_command;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!attached_items_.isEmpty()) {
|
||||
{
|
||||
// Dropped attached item onto an edge, connect it between them
|
||||
@@ -757,7 +627,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
bool removed = false;
|
||||
QVector<Node*> relevant_contexts;
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
for (Node *context : qAsConst(contexts_)) {
|
||||
if (attached_node->OutputsTo(context, true)) {
|
||||
relevant_contexts.append(context);
|
||||
} else {
|
||||
@@ -768,7 +638,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
if (removed && !relevant_contexts.isEmpty()) {
|
||||
for (Node *relevant : qAsConst(relevant_contexts)) {
|
||||
remove_pos_subcommand->add_child(new NodeSetPositionCommand(attached_node, relevant, GetEstimatedPositionForContext(attached.item, relevant), false));
|
||||
remove_pos_subcommand->add_child(new NodeSetPositionCommand(attached_node, relevant, GetEstimatedPositionForContext(attached.item, relevant)));
|
||||
}
|
||||
|
||||
remove_pos_command->add_child(remove_pos_subcommand);
|
||||
@@ -844,7 +714,7 @@ void NodeView::UpdateSelectionCache()
|
||||
|
||||
void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
{
|
||||
if (filter_nodes_.isEmpty()) {
|
||||
if (contexts_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -896,17 +766,6 @@ void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
Menu* filter_menu = new Menu(tr("Filter"), &m);
|
||||
m.addMenu(filter_menu);
|
||||
|
||||
filter_menu->AddActionWithData(tr("Show All Nodes"), kFilterShowAll, filter_mode_);
|
||||
|
||||
filter_menu->AddActionWithData(tr("Show Selected"), kFilterShowSelective, filter_mode_);
|
||||
|
||||
connect(filter_menu, &Menu::triggered, this, &NodeView::ContextMenuFilterChanged);
|
||||
|
||||
|
||||
|
||||
Menu* direction_menu = new Menu(tr("Direction"), &m);
|
||||
m.addMenu(direction_menu);
|
||||
|
||||
@@ -940,23 +799,20 @@ void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
|
||||
void NodeView::CreateNodeSlot(QAction *action)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node* new_node = NodeFactory::CreateFromMenuAction(action);
|
||||
qDebug() << "STUB!";
|
||||
/*Node* new_node = NodeFactory::CreateFromMenuAction(action);
|
||||
|
||||
if (new_node) {
|
||||
paste_command_ = new MultiUndoCommand();
|
||||
paste_command_->add_child(new NodeAddCommand(graph_, new_node));
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
for (Node *context : qAsConst(contexts_)) {
|
||||
paste_command_->add_child(new NodeSetPositionCommand(new_node, context, QPointF(0, 0), false));
|
||||
}
|
||||
paste_command_->add_child(new NodeViewAttachNodesToCursor(this, {new_node}));
|
||||
paste_command_->redo_now();
|
||||
|
||||
this->setFocus();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuSetDirection(QAction *action)
|
||||
@@ -964,26 +820,6 @@ void NodeView::ContextMenuSetDirection(QAction *action)
|
||||
SetFlowDirection(static_cast<NodeViewCommon::FlowDirection>(action->data().toInt()));
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuFilterChanged(QAction *action)
|
||||
{
|
||||
FilterMode mode = static_cast<FilterMode>(action->data().toInt());
|
||||
|
||||
if (filter_mode_ != mode) {
|
||||
// Store temporary graph variables
|
||||
NodeGraph *graph = graph_;
|
||||
QVector<Node*> nodes = last_set_filter_nodes_;
|
||||
|
||||
// Unset graph with current filter mode
|
||||
ClearGraph();
|
||||
|
||||
// Change filter mode
|
||||
filter_mode_ = mode;
|
||||
|
||||
// Re-set graph with new filter mode
|
||||
SetGraph(graph, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::OpenSelectedNodeInViewer()
|
||||
{
|
||||
QVector<Node*> selected = scene_.GetSelectedNodes();
|
||||
@@ -994,100 +830,6 @@ void NodeView::OpenSelectedNodeInViewer()
|
||||
}
|
||||
}
|
||||
|
||||
// Commenting out because there shouldn't be any situations where a node would be added without
|
||||
// being in a context. We're keeping RemoveNode as a fail-safe because it could provide crash
|
||||
// resistance where RemoveNodePosition might be missed.
|
||||
//void NodeView::AddNode(Node *node)
|
||||
//{
|
||||
// if (filter_mode_ == kFilterShowAll) {
|
||||
// scene_.AddNode(node);
|
||||
// }
|
||||
//}
|
||||
|
||||
void NodeView::RemoveNode(Node *node)
|
||||
{
|
||||
for (auto it=attached_items_.begin(); it!=attached_items_.end(); ) {
|
||||
if (it->item->GetNode() == node) {
|
||||
it = attached_items_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (const Node::OutputConnection &oc : node->output_connections()) {
|
||||
scene_.RemoveEdge(oc.first, oc.second);
|
||||
}
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
scene_.RemoveEdge(it->second, it->first);
|
||||
}
|
||||
positions_.remove(scene_.item_map().value(node));
|
||||
}
|
||||
|
||||
void NodeView::AddEdge(Node *output, const NodeInput &input)
|
||||
{
|
||||
Node *output_node = output;
|
||||
Node *input_node = input.node();
|
||||
|
||||
if (scene_.item_map().contains(output_node) && scene_.item_map().contains(input_node)) {
|
||||
scene_.AddEdge(output, input);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::RemoveEdge(Node *output, const NodeInput &input)
|
||||
{
|
||||
scene_.RemoveEdge(output, input);
|
||||
}
|
||||
|
||||
void NodeView::AddNodePosition(Node *node, Node *relative)
|
||||
{
|
||||
bool listening_to_node = filter_nodes_.contains(relative);
|
||||
|
||||
if (!listening_to_node) {
|
||||
if (filter_mode_ == kFilterShowAll) {
|
||||
// We're not listening to this context, but because we're showing all, add it
|
||||
filter_nodes_.append(relative);
|
||||
} else {
|
||||
// Ignore signal
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reposition contexts because one of their heights may have changed or a new one may have been
|
||||
// added
|
||||
UpdateNodeItem(node);
|
||||
|
||||
if (filter_mode_ == kFilterShowAll) {
|
||||
queue_reposition_contexts_ = true;
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::RemoveNodePosition(Node *node, Node *relative)
|
||||
{
|
||||
if (filter_nodes_.contains(relative)) {
|
||||
NodeViewItem *item = scene_.item_map().value(node);
|
||||
|
||||
if (item && !item->GetPreventRemoving()) {
|
||||
// Determine if any other contexts have this node
|
||||
bool found = false;
|
||||
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
if (graph_->ContextContainsNode(node, context)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
RemoveNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter_mode_ == kFilterShowAll) {
|
||||
RepositionContexts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::UpdateSceneBoundingRect()
|
||||
{
|
||||
// Get current items bounding rect
|
||||
@@ -1229,13 +971,6 @@ bool NodeView::event(QEvent *event)
|
||||
|
||||
bool NodeView::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (object == viewport() && event->type() == QEvent::Paint) {
|
||||
if (queue_reposition_contexts_) {
|
||||
RepositionContexts();
|
||||
queue_reposition_contexts_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
return super::eventFilter(object, event);
|
||||
}
|
||||
|
||||
@@ -1259,7 +994,8 @@ void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVec
|
||||
|
||||
void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void *userdata)
|
||||
{
|
||||
NodeGraph::PositionMap *map = static_cast<NodeGraph::PositionMap *>(userdata);
|
||||
qDebug() << "STUB!";
|
||||
/*NodeGraph::PositionMap *map = static_cast<NodeGraph::PositionMap *>(userdata);
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("pos")) {
|
||||
@@ -1295,7 +1031,7 @@ void NodeView::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNode
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void NodeView::ZoomFromKeyboard(double multiplier)
|
||||
@@ -1310,148 +1046,6 @@ void NodeView::ZoomFromKeyboard(double multiplier)
|
||||
ZoomIntoCursorPosition(nullptr, multiplier, cursor_pos);
|
||||
}
|
||||
|
||||
bool NodeView::DetermineIfNodeIsFloatingInContext(Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge)
|
||||
{
|
||||
// Determines whether `node` outputs to another node in `context` besides `source`
|
||||
for (const Node::OutputConnection &conn : node->output_connections()) {
|
||||
Node *output_candidate = conn.second.node();
|
||||
|
||||
if (output_candidate == source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (graph_->ContextContainsNode(output_candidate, context)) {
|
||||
if (!output_candidate->OutputsTo(source, true, removed_edges, added_edge)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeView::UpdateContextsFromEdgeRemove(MultiUndoCommand *command, const Node::OutputConnections &remove_edges)
|
||||
{
|
||||
// For each edge we remove, determine if we should remove the node from a context as well
|
||||
for (const Node::OutputConnection &edge : remove_edges) {
|
||||
Node *output_node = edge.first;
|
||||
QVector<Node *> contexts_to_remove_from;
|
||||
int contexts_containing = 0;
|
||||
|
||||
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||
Node *context = it.key();
|
||||
|
||||
if (it.value().contains(output_node)) {
|
||||
bool currently_outputs = output_node->OutputsTo(context, true);
|
||||
bool will_output_after_operation = output_node->OutputsTo(context, true, remove_edges);
|
||||
|
||||
if (currently_outputs && !will_output_after_operation) {
|
||||
// Will remove
|
||||
contexts_to_remove_from.append(context);
|
||||
}
|
||||
|
||||
contexts_containing++;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing from all current contexts, convert to a floating node (i.e. don't remove from the context)
|
||||
if (contexts_to_remove_from.size() != contexts_containing) {
|
||||
// Not removing from all contexts, can remove
|
||||
bool removing_from_all_current_contexts = true;
|
||||
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
if (graph_->ContextContainsNode(output_node, context)) {
|
||||
if (!contexts_to_remove_from.contains(context)) {
|
||||
removing_from_all_current_contexts = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Node *context : qAsConst(contexts_to_remove_from)) {
|
||||
RecursivelyRemoveFloatingNodeFromContext(command, output_node, context, output_node, remove_edges, Node::OutputConnection(), removing_from_all_current_contexts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::RecursivelyRemoveFloatingNodeFromContext(MultiUndoCommand *command, Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge, bool prevent_removing)
|
||||
{
|
||||
if (prevent_removing) {
|
||||
command->add_child(new NodeViewItemPreventRemovingCommand(this, node, true));
|
||||
}
|
||||
|
||||
command->add_child(new NodeRemovePositionFromContextCommand(node, context));
|
||||
|
||||
// Remove any dependency from the context that's also floating
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
Node *dependency = it->second;
|
||||
|
||||
// Determine if this node happens to output to anything else in the context (which may be
|
||||
// another floating node that won't be removed by this operation)
|
||||
if (!DetermineIfNodeIsFloatingInContext(dependency, context, source, removed_edges, added_edge)) {
|
||||
RecursivelyRemoveFloatingNodeFromContext(command, dependency, context, source, removed_edges, added_edge, prevent_removing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::RecursivelyAddNodeToContext(MultiUndoCommand *command, Node *node, Node *context)
|
||||
{
|
||||
NodeViewItem *item = scene_.item_map().value(node);
|
||||
|
||||
if (item) {
|
||||
command->add_child(new NodeSetPositionCommand(node, context, GetEstimatedPositionForContext(item, context), false));
|
||||
|
||||
// Add dependency
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
Node *dependency = it->second;
|
||||
RecursivelyAddNodeToContext(command, dependency, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::UpdateContextsFromEdgeAdd(MultiUndoCommand *command, const Node::OutputConnection &added_edge, const Node::OutputConnections &removed_edges)
|
||||
{
|
||||
// Determine if node currently does NOT output to a context that it WILL after this operation
|
||||
QVector<Node*> contexts_to_add_to;
|
||||
Node *connecting_node = added_edge.first;
|
||||
Node *input_node = added_edge.second.node();
|
||||
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||
if (it.value().contains(input_node)) {
|
||||
contexts_to_add_to.append(it.key());
|
||||
}
|
||||
}
|
||||
|
||||
if (!contexts_to_add_to.isEmpty()) {
|
||||
// Determine whether the node is currently "floating", i.e. it outputs to none of the contexts
|
||||
// that it currently belongs to. If so, we will take ownership of it with this node.
|
||||
bool node_is_floating = true;
|
||||
QVector<Node*> current_contexts;
|
||||
for (auto it=graph_->GetPositionMap().cbegin(); it!=graph_->GetPositionMap().cend(); it++) {
|
||||
if (it.value().contains(connecting_node)) {
|
||||
if (connecting_node->OutputsTo(it.key(), true, removed_edges)) {
|
||||
node_is_floating = false;
|
||||
break;
|
||||
} else {
|
||||
current_contexts.append(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node_is_floating) {
|
||||
// This action will unfloat this node, so remove it from all current contexts
|
||||
for (Node *context : qAsConst(current_contexts)) {
|
||||
RecursivelyRemoveFloatingNodeFromContext(command, connecting_node, context, connecting_node, removed_edges, added_edge, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Add nodes to contexts
|
||||
for (Node *context : qAsConst(contexts_to_add_to)) {
|
||||
RecursivelyAddNodeToContext(command, connecting_node, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPointF NodeView::GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const
|
||||
{
|
||||
return item->GetNodePosition() - context_offsets_.value(context);
|
||||
@@ -1545,86 +1139,6 @@ void NodeView::PositionNewEdge(const QPoint &pos)
|
||||
create_edge_->SetConnected(create_edge_dst_input_.IsValid());
|
||||
}
|
||||
|
||||
void NodeView::RepositionContexts()
|
||||
{
|
||||
// Determine which contexts are root-level
|
||||
QVector<Node*> processing_filters = filter_nodes_;
|
||||
|
||||
// Level counter as we iterate through the list a few times
|
||||
int level = 0;
|
||||
|
||||
// Root-level positioning variables
|
||||
qreal last_offset = 0;
|
||||
int additional_spacing = 0;
|
||||
|
||||
while (!processing_filters.isEmpty()) {
|
||||
QVector<Node*> contexts_on_this_level;
|
||||
|
||||
for (int i=0; i<processing_filters.size(); i++) {
|
||||
Node *context = processing_filters.at(i);
|
||||
|
||||
// Determine if this context is on an upper level or not
|
||||
bool this_level = true;
|
||||
for (int j=0; j<processing_filters.size(); j++) {
|
||||
Node *other_context = processing_filters.at(j);
|
||||
|
||||
if (i != j && graph_->ContextContainsNode(context, other_context)) {
|
||||
this_level = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this_level) {
|
||||
contexts_on_this_level.append(context);
|
||||
}
|
||||
}
|
||||
|
||||
for (Node *context : qAsConst(contexts_on_this_level)) {
|
||||
if (level == 0) {
|
||||
const NodeGraph::PositionMap &map = graph_->GetNodesForContext(context);
|
||||
|
||||
// First determine the total "height" of this graph and how much we need to offset it
|
||||
qreal top = 0;
|
||||
qreal bottom = 0;
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
const QPointF &node_pos_in_context = it.value();
|
||||
top = qMin(node_pos_in_context.y(), top);
|
||||
bottom = qMax(node_pos_in_context.y(), bottom);
|
||||
}
|
||||
|
||||
last_offset += (additional_spacing + (bottom - top));
|
||||
additional_spacing = 1;
|
||||
context_offsets_.insert(context, QPointF(0, last_offset));
|
||||
} else {
|
||||
// Create/update item
|
||||
NodeViewItem *item = UpdateNodeItem(context, true);
|
||||
|
||||
// Get position generated by UpdateNodeItem
|
||||
QPointF context_pos = item->GetNodePosition();
|
||||
|
||||
// Adjust by the context node's position in its own context (this will usually be 0,0)
|
||||
context_pos -= graph_->GetNodesForContext(context).value(context);
|
||||
|
||||
// Insert this context's offset
|
||||
context_offsets_.insert(context, context_pos);
|
||||
}
|
||||
|
||||
// Remove from list so we don't process again
|
||||
processing_filters.removeOne(context);
|
||||
}
|
||||
|
||||
level++;
|
||||
}
|
||||
|
||||
// Now that we've positioned all the contexts, position all other nodes relative to those contexts
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
const NodeGraph::PositionMap &map = graph_->GetNodesForContext(context);
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
UpdateNodeItem(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::GroupNodes()
|
||||
{
|
||||
/*NodeGroup *group = new NodeGroup();
|
||||
@@ -1636,54 +1150,11 @@ void NodeView::UngroupNodes()
|
||||
//static_cast<NodeGroup*>(selected_nodes_.first());
|
||||
}
|
||||
|
||||
NodeViewItem *NodeView::UpdateNodeItem(Node *node, bool ignore_own_context)
|
||||
{
|
||||
// Get UI item or create if it doesn't exist
|
||||
NodeViewItem *item = scene_.item_map().value(node);
|
||||
if (!item) {
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
if (scene_.item_map().contains(it->second)) {
|
||||
scene_.AddEdge(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
if (scene_.item_map().contains(it->second.node())) {
|
||||
scene_.AddEdge(it->first, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine "view" position by averaging the Y value and "min"ing the X value of all contexts
|
||||
QPointF item_pos(std::numeric_limits<qreal>::max(), 0.0);
|
||||
int average_count = 0;
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
if (context == node && ignore_own_context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (graph_->GetNodesForContext(context).contains(node)) {
|
||||
QPointF this_context_pos = graph_->GetNodePosition(node, context);
|
||||
this_context_pos += context_offsets_.value(context);
|
||||
|
||||
item_pos.setX(qMin(item_pos.x(), this_context_pos.x()));
|
||||
item_pos.setY(item_pos.y() + this_context_pos.y());
|
||||
average_count++;
|
||||
}
|
||||
}
|
||||
item_pos.setY(item_pos.y() / average_count);
|
||||
|
||||
// Set position
|
||||
item->SetNodePosition(item_pos);
|
||||
positions_.insert(item, {node, item_pos});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
{
|
||||
/*
|
||||
// If no graph, do nothing
|
||||
if (!graph_) {
|
||||
if (contexts_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1696,7 +1167,7 @@ void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
new_nodes = PasteNodesFromClipboard(graph_, paste_command_, &map);
|
||||
|
||||
for (auto it=new_nodes.cbegin(); it!=new_nodes.cend(); it++) {
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
for (Node *context : qAsConst(contexts_)) {
|
||||
paste_command_->add_child(new NodeSetPositionCommand(*it, context, map.value(*it), false));
|
||||
}
|
||||
}
|
||||
@@ -1707,7 +1178,7 @@ void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
Node *src = duplicate_nodes.at(i);
|
||||
Node *copy = new_nodes.at(i);
|
||||
|
||||
for (Node *context : qAsConst(filter_nodes_)) {
|
||||
for (Node *context : qAsConst(contexts_)) {
|
||||
QPointF p = scene_.item_map().value(src)->GetNodePosition();
|
||||
paste_command_->add_child(new NodeSetPositionCommand(copy, context, p, false));
|
||||
}
|
||||
@@ -1725,6 +1196,7 @@ void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
paste_command_->add_child(new NodeViewAttachNodesToCursor(this, new_nodes));
|
||||
|
||||
paste_command_->redo_now();
|
||||
*/
|
||||
}
|
||||
|
||||
NodeView::NodeViewAttachNodesToCursor::NodeViewAttachNodesToCursor(NodeView *view, const QVector<Node *> &nodes) :
|
||||
@@ -1745,8 +1217,7 @@ void NodeView::NodeViewAttachNodesToCursor::undo()
|
||||
|
||||
Project *NodeView::NodeViewAttachNodesToCursor::GetRelevantProject() const
|
||||
{
|
||||
// Will either return a project or a nullptr which is also acceptable
|
||||
return dynamic_cast<Project*>(view_->graph_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeView::NodeViewItemPreventRemovingCommand::redo()
|
||||
|
||||
@@ -49,12 +49,9 @@ public:
|
||||
|
||||
virtual ~NodeView() override;
|
||||
|
||||
NodeGraph* GetGraph() const
|
||||
{
|
||||
return graph_;
|
||||
}
|
||||
void SetContexts(const QVector<Node *> &nodes);
|
||||
|
||||
void SetGraph(NodeGraph *graph, const QVector<Node *> &nodes);
|
||||
void CloseContextsBelongingToProject(Project *project);
|
||||
|
||||
void ClearGraph();
|
||||
|
||||
@@ -67,7 +64,6 @@ public:
|
||||
void DeselectAll();
|
||||
|
||||
void Select(QVector<Node *> nodes, bool center_view_on_item);
|
||||
void SelectWithDependencies(QVector<Node *> nodes, bool center_view_on_item);
|
||||
|
||||
void CopySelected(bool cut);
|
||||
void Paste();
|
||||
@@ -82,7 +78,7 @@ public:
|
||||
|
||||
const QVector<Node*> &GetCurrentContexts() const
|
||||
{
|
||||
return filter_nodes_;
|
||||
return contexts_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
@@ -98,6 +94,8 @@ public slots:
|
||||
delete m;
|
||||
}
|
||||
|
||||
void CenterOnItemsBoundingRect();
|
||||
|
||||
signals:
|
||||
void NodesSelected(const QVector<Node*>& nodes);
|
||||
|
||||
@@ -137,12 +135,6 @@ private:
|
||||
|
||||
void ZoomFromKeyboard(double multiplier);
|
||||
|
||||
bool DetermineIfNodeIsFloatingInContext(Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge);
|
||||
void UpdateContextsFromEdgeRemove(MultiUndoCommand *command, const Node::OutputConnections &remove_edges);
|
||||
void UpdateContextsFromEdgeAdd(MultiUndoCommand *command, const Node::OutputConnection &added_edge, const Node::OutputConnections &removed_edges = Node::OutputConnections());
|
||||
void RecursivelyAddNodeToContext(MultiUndoCommand *command, Node *node, Node *context);
|
||||
void RecursivelyRemoveFloatingNodeFromContext(MultiUndoCommand *command, Node *node, Node *context, Node *source, const Node::OutputConnections &removed_edges, const Node::OutputConnection &added_edge, bool prevent_removing);
|
||||
|
||||
QPointF GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const;
|
||||
|
||||
Menu *CreateAddMenu(Menu *parent);
|
||||
@@ -151,8 +143,6 @@ private:
|
||||
|
||||
void PositionNewEdge(const QPoint &pos);
|
||||
|
||||
NodeViewItem *UpdateNodeItem(Node *node, bool ignore_own_context = false);
|
||||
|
||||
void PasteNodesInternal(const QVector<Node*> &duplicate_nodes = QVector<Node *>());
|
||||
|
||||
class NodeViewAttachNodesToCursor : public UndoCommand
|
||||
@@ -176,8 +166,6 @@ private:
|
||||
|
||||
NodeViewMiniMap *minimap_;
|
||||
|
||||
NodeGraph* graph_;
|
||||
|
||||
struct AttachedItem {
|
||||
NodeViewItem* item;
|
||||
QPointF original_pos;
|
||||
@@ -227,21 +215,7 @@ private:
|
||||
|
||||
QVector<Node*> selected_nodes_;
|
||||
|
||||
enum FilterMode {
|
||||
kFilterShowAll,
|
||||
kFilterShowSelective
|
||||
};
|
||||
|
||||
struct Position {
|
||||
Node *node;
|
||||
QPointF original_item_pos;
|
||||
};
|
||||
|
||||
QMap<NodeViewItem *, Position> positions_;
|
||||
|
||||
FilterMode filter_mode_;
|
||||
|
||||
QVector<Node*> filter_nodes_;
|
||||
QVector<Node*> contexts_;
|
||||
QVector<Node*> last_set_filter_nodes_;
|
||||
QMap<Node*, QPointF> context_offsets_;
|
||||
|
||||
@@ -249,7 +223,7 @@ private:
|
||||
|
||||
bool create_edge_already_exists_;
|
||||
|
||||
bool queue_reposition_contexts_;
|
||||
bool first_show_;
|
||||
|
||||
static const double kMinimumScale;
|
||||
|
||||
@@ -274,36 +248,19 @@ private slots:
|
||||
*/
|
||||
void ContextMenuSetDirection(QAction* action);
|
||||
|
||||
/**
|
||||
* @brief Receiver for the user changing the filter
|
||||
*/
|
||||
void ContextMenuFilterChanged(QAction* action);
|
||||
|
||||
/**
|
||||
* @brief Opens the selected node in a Viewer
|
||||
*/
|
||||
void OpenSelectedNodeInViewer();
|
||||
|
||||
//void AddNode(Node *node);
|
||||
void RemoveNode(Node *node);
|
||||
void AddEdge(Node *output, const NodeInput& input);
|
||||
void RemoveEdge(Node *output, const NodeInput& input);
|
||||
|
||||
void AddNodePosition(Node *node, Node *relative);
|
||||
void RemoveNodePosition(Node *node, Node *relative);
|
||||
|
||||
void UpdateSceneBoundingRect();
|
||||
|
||||
void CenterOnItemsBoundingRect();
|
||||
|
||||
void RepositionMiniMap();
|
||||
|
||||
void UpdateViewportOnMiniMap();
|
||||
|
||||
void MoveToScenePoint(const QPointF &pos);
|
||||
|
||||
void RepositionContexts();
|
||||
|
||||
void GroupNodes();
|
||||
|
||||
void UngroupNodes();
|
||||
|
||||
@@ -19,37 +19,29 @@ namespace olive {
|
||||
|
||||
#define super QGraphicsRectItem
|
||||
|
||||
NodeViewContext::NodeViewContext(QGraphicsItem *item) :
|
||||
super(item)
|
||||
NodeViewContext::NodeViewContext(Node *context, QGraphicsItem *item) :
|
||||
super(item),
|
||||
context_(context)
|
||||
{
|
||||
// Set default label text
|
||||
SetContext(nullptr);
|
||||
}
|
||||
|
||||
void NodeViewContext::SetContext(Node *node)
|
||||
{
|
||||
context_ = node;
|
||||
|
||||
if (context_) {
|
||||
if (Block *block = dynamic_cast<Block*>(node)) {
|
||||
rational timebase = block->track()->sequence()->GetVideoParams().frame_rate_as_time_base();
|
||||
lbl_ = QCoreApplication::translate("NodeViewContext",
|
||||
"%1 [%2] :: %3 - %4").arg(block->GetLabelAndName(),
|
||||
Track::Reference::TypeToTranslatedString(block->track()->type()),
|
||||
Timecode::time_to_timecode(block->in(), timebase, Core::instance()->GetTimecodeDisplay()),
|
||||
Timecode::time_to_timecode(block->out(), timebase, Core::instance()->GetTimecodeDisplay()));
|
||||
} else {
|
||||
lbl_ = node->GetLabelAndName();
|
||||
}
|
||||
|
||||
Color c = node->color();
|
||||
setPen(QPen(c.toQColor(), 2));
|
||||
|
||||
c.set_alpha(0.5f);
|
||||
setBrush(c.toQColor());
|
||||
if (Block *block = dynamic_cast<Block*>(context_)) {
|
||||
rational timebase = block->track()->sequence()->GetVideoParams().frame_rate_as_time_base();
|
||||
lbl_ = QCoreApplication::translate("NodeViewContext",
|
||||
"%1 [%2] :: %3 - %4").arg(block->GetLabelAndName(),
|
||||
Track::Reference::TypeToTranslatedString(block->track()->type()),
|
||||
Timecode::time_to_timecode(block->in(), timebase, Core::instance()->GetTimecodeDisplay()),
|
||||
Timecode::time_to_timecode(block->out(), timebase, Core::instance()->GetTimecodeDisplay()));
|
||||
} else {
|
||||
lbl_ = QCoreApplication::translate("NodeViewContext", "(None)");
|
||||
lbl_ = context_->GetLabelAndName();
|
||||
}
|
||||
|
||||
const Node::PositionMap &map = context_->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
AddChild(it.key());
|
||||
}
|
||||
|
||||
connect(context_, &Node::NodeAddedToContext, this, &NodeViewContext::AddChild, Qt::DirectConnection);
|
||||
connect(context_, &Node::NodePositionInContextChanged, this, &NodeViewContext::SetChildPosition, Qt::DirectConnection);
|
||||
connect(context_, &Node::NodeRemovedFromContext, this, &NodeViewContext::RemoveChild, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
void NodeViewContext::AddChild(Node *node)
|
||||
@@ -59,33 +51,73 @@ void NodeViewContext::AddChild(Node *node)
|
||||
}
|
||||
|
||||
NodeViewItem *item = new NodeViewItem(this);
|
||||
item->SetNode(node);
|
||||
item->SetNodePosition(context_->parent()->GetNodesForContext(context_).value(node));
|
||||
item->SetNode(node, context_);
|
||||
item->SetNodePosition(context_->GetNodePositionInContext(node));
|
||||
item->SetFlowDirection(flow_dir_);
|
||||
|
||||
connect(node, &Node::InputConnected, this, &NodeViewContext::ChildInputConnected);
|
||||
connect(node, &Node::InputDisconnected, this, &NodeViewContext::ChildInputDisconnected);
|
||||
|
||||
item_map_.insert(node, item);
|
||||
|
||||
if (node == context_) {
|
||||
item->SetLabelAsOutput(true);
|
||||
}
|
||||
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
foreach (auto child, childItems()) {
|
||||
if (NodeViewItem *other_item = dynamic_cast<NodeViewItem*>(child)) {
|
||||
if (other_item->GetNode() == it->second.node()) {
|
||||
AddEdgeInternal(node, it->second, item, other_item);
|
||||
}
|
||||
if (!it->second.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second.node())) {
|
||||
AddEdgeInternal(node, it->second, item, other_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
foreach (auto child, childItems()) {
|
||||
if (NodeViewItem *other_item = dynamic_cast<NodeViewItem*>(child)) {
|
||||
if (it->second == other_item->GetNode()) {
|
||||
AddEdgeInternal(it->second, it->first, other_item, item);
|
||||
}
|
||||
if (!it->first.IsHidden()) {
|
||||
if (NodeViewItem *other_item = item_map_.value(it->second)) {
|
||||
AddEdgeInternal(it->second, it->first, other_item, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
void NodeViewContext::SetChildPosition(Node *node, const QPointF &pos)
|
||||
{
|
||||
item_map_.value(node)->SetNodePosition(pos);
|
||||
}
|
||||
|
||||
void NodeViewContext::RemoveChild(Node *node)
|
||||
{
|
||||
disconnect(node, &Node::InputConnected, this, &NodeViewContext::ChildInputConnected);
|
||||
disconnect(node, &Node::InputDisconnected, this, &NodeViewContext::ChildInputDisconnected);
|
||||
|
||||
delete item_map_.take(node);
|
||||
}
|
||||
|
||||
void NodeViewContext::ChildInputConnected(Node *output, const NodeInput &input)
|
||||
{
|
||||
// Add edge
|
||||
if (!input.IsHidden()) {
|
||||
if (NodeViewItem* output_item = item_map_.value(output)) {
|
||||
AddEdgeInternal(output, input, output_item, item_map_.value(input.node()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeViewContext::ChildInputDisconnected(Node *output, const NodeInput &input)
|
||||
{
|
||||
// Remove edge
|
||||
for (int i=0; i<edges_.size(); i++) {
|
||||
if (edges_.at(i)->output() == output && edges_.at(i)->input() == input) {
|
||||
delete edges_.at(i);
|
||||
edges_.removeAt(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
qreal GetTextOffset(const QFontMetricsF &fm)
|
||||
@@ -105,23 +137,19 @@ void NodeViewContext::UpdateRect()
|
||||
rect.adjust(-pad, - lbl_offset*2 - fm.height() - pad, pad, pad);
|
||||
setRect(rect);
|
||||
|
||||
last_titlebar_height_ = rect.y() + (cbr.y() - rect.y());
|
||||
last_titlebar_height_ = rect.y() + (cbr.y() - rect.y()) - pad;
|
||||
}
|
||||
|
||||
void NodeViewContext::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
|
||||
foreach (auto child, childItems()) {
|
||||
if (NodeViewItem *item = dynamic_cast<NodeViewItem*>(child)) {
|
||||
item->SetFlowDirection(dir);
|
||||
}
|
||||
foreach (NodeViewItem *item, item_map_) {
|
||||
item->SetFlowDirection(dir);
|
||||
}
|
||||
|
||||
foreach (auto child, childItems()) {
|
||||
if (NodeViewEdge *edge = dynamic_cast<NodeViewEdge*>(child)) {
|
||||
edge->SetFlowDirection(dir);
|
||||
}
|
||||
foreach (NodeViewEdge *edge, edges_) {
|
||||
edge->SetFlowDirection(dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,29 +157,40 @@ void NodeViewContext::SetCurvedEdges(bool e)
|
||||
{
|
||||
curved_edges_ = e;
|
||||
|
||||
const QList<QGraphicsItem*> &children = childItems();
|
||||
foreach (auto child, children) {
|
||||
if (NodeViewEdge *edge = dynamic_cast<NodeViewEdge*>(child)) {
|
||||
edge->SetCurved(e);
|
||||
}
|
||||
foreach (NodeViewEdge *edge, edges_) {
|
||||
edge->SetCurved(e);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewContext::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
QPen p = pen();
|
||||
|
||||
// Set pen and brush
|
||||
Color color = context_->color();
|
||||
QColor c = color.toQColor();
|
||||
QPen pen(c, 2);
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
p.setStyle(Qt::DotLine);
|
||||
pen.setStyle(Qt::DotLine);
|
||||
}
|
||||
painter->setPen(pen);
|
||||
|
||||
painter->setPen(p);
|
||||
painter->setBrush(brush());
|
||||
QColor bg = c;
|
||||
bg.setAlpha(128);
|
||||
painter->setBrush(bg);
|
||||
|
||||
// Draw semi-transparent rect for whole item
|
||||
int rounded = painter->fontMetrics().height();
|
||||
painter->drawRoundedRect(rect(), rounded, rounded);
|
||||
|
||||
painter->setPen(widget->palette().text().color());
|
||||
// Draw solid background for titlebar
|
||||
QRectF titlebar_rect = rect();
|
||||
titlebar_rect.setHeight(last_titlebar_height_ - rect().top());
|
||||
painter->setClipRect(titlebar_rect);
|
||||
painter->setBrush(c);
|
||||
painter->drawRoundedRect(rect(), rounded, rounded);
|
||||
painter->setClipping(false);
|
||||
|
||||
// Draw titlebar text
|
||||
painter->setPen(ColorCoding::GetUISelectorColor(color));
|
||||
|
||||
int offset = GetTextOffset(painter->fontMetrics());
|
||||
|
||||
@@ -182,8 +221,7 @@ NodeViewEdge* NodeViewContext::AddEdgeInternal(Node *output, const NodeInput& in
|
||||
edge_ui->SetFlowDirection(flow_dir_);
|
||||
edge_ui->SetCurved(curved_edges_);
|
||||
|
||||
from->AddEdge(edge_ui);
|
||||
to->AddEdge(edge_ui);
|
||||
edges_.append(edge_ui);
|
||||
|
||||
return edge_ui;
|
||||
}
|
||||
|
||||
@@ -10,14 +10,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeViewContext : public QGraphicsRectItem
|
||||
class NodeViewContext : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeViewContext(QGraphicsItem *item = nullptr);
|
||||
|
||||
void SetContext(Node *node);
|
||||
|
||||
void AddChild(Node *node);
|
||||
NodeViewContext(Node *context, QGraphicsItem *item = nullptr);
|
||||
|
||||
void UpdateRect();
|
||||
|
||||
@@ -27,6 +24,17 @@ public:
|
||||
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
public slots:
|
||||
void AddChild(Node *node);
|
||||
|
||||
void SetChildPosition(Node *node, const QPointF &pos);
|
||||
|
||||
void RemoveChild(Node *node);
|
||||
|
||||
void ChildInputConnected(Node *output, const NodeInput& input);
|
||||
|
||||
bool ChildInputDisconnected(Node *output, const NodeInput& input);
|
||||
|
||||
protected:
|
||||
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||
|
||||
@@ -45,6 +53,10 @@ private:
|
||||
|
||||
int last_titlebar_height_;
|
||||
|
||||
QMap<Node*, NodeViewItem*> item_map_;
|
||||
|
||||
QVector<NodeViewEdge*> edges_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ NodeViewEdge::NodeViewEdge(Node *output, const NodeInput &input,
|
||||
{
|
||||
Init();
|
||||
SetConnected(true);
|
||||
|
||||
from_item_->AddEdge(this);
|
||||
to_item_->AddEdge(this);
|
||||
}
|
||||
|
||||
NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) :
|
||||
@@ -56,6 +59,17 @@ NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) :
|
||||
Init();
|
||||
}
|
||||
|
||||
NodeViewEdge::~NodeViewEdge()
|
||||
{
|
||||
if (from_item_) {
|
||||
from_item_->RemoveEdge(this);
|
||||
}
|
||||
|
||||
if (to_item_) {
|
||||
to_item_->RemoveEdge(this);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewEdge::Adjust()
|
||||
{
|
||||
// Draw a line between the two
|
||||
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
|
||||
NodeViewEdge(QGraphicsItem* parent = nullptr);
|
||||
|
||||
virtual ~NodeViewEdge() override;
|
||||
|
||||
Node *output() const
|
||||
{
|
||||
return output_;
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace olive {
|
||||
NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
node_(nullptr),
|
||||
context_(nullptr),
|
||||
expanded_(false),
|
||||
hide_titlebar_(false),
|
||||
highlighted_index_(-1),
|
||||
@@ -207,7 +208,7 @@ int NodeViewItem::GetIndexAt(QPointF pt) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NodeViewItem::SetNode(Node *n)
|
||||
void NodeViewItem::SetNode(Node *n, Node *context)
|
||||
{
|
||||
if (node_) {
|
||||
disconnect(n, &Node::LabelChanged, this, &NodeViewItem::NodeAppearanceChanged);
|
||||
@@ -215,6 +216,7 @@ void NodeViewItem::SetNode(Node *n)
|
||||
}
|
||||
|
||||
node_ = n;
|
||||
context_ = context;
|
||||
|
||||
node_inputs_.clear();
|
||||
input_connectors_.clear();
|
||||
@@ -223,7 +225,7 @@ void NodeViewItem::SetNode(Node *n)
|
||||
node_->Retranslate();
|
||||
|
||||
foreach (const QString& input, node_->inputs()) {
|
||||
if (node_->IsInputConnectable(input)) {
|
||||
if (node_->IsInputConnectable(input) && !node_->IsInputHidden(input)) {
|
||||
node_inputs_.append(input);
|
||||
}
|
||||
}
|
||||
@@ -327,9 +329,11 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
|
||||
int icon_size = painter->fontMetrics().height()/2;
|
||||
|
||||
bool draw_arrow = !node_inputs_.isEmpty();
|
||||
|
||||
if (node_label.isEmpty()) {
|
||||
// Draw shortname only
|
||||
DrawNodeTitle(painter, node_shortname, title_bar_rect_, Qt::AlignVCenter, icon_size, true);
|
||||
DrawNodeTitle(painter, node_shortname, title_bar_rect_, Qt::AlignVCenter, icon_size, draw_arrow);
|
||||
} else {
|
||||
int text_pad = DefaultTextPadding()/2;
|
||||
QRectF safe_label_bounds = title_bar_rect_.adjusted(text_pad, text_pad, -text_pad, -text_pad);
|
||||
@@ -337,7 +341,7 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
qreal font_sz = f.pointSizeF();
|
||||
f.setPointSizeF(font_sz * 0.8);
|
||||
painter->setFont(f);
|
||||
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, icon_size, true);
|
||||
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, icon_size, draw_arrow);
|
||||
f.setPointSizeF(font_sz * 0.6);
|
||||
painter->setFont(f);
|
||||
DrawNodeTitle(painter, node_shortname, safe_label_bounds, Qt::AlignBottom, icon_size, false);
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
/**
|
||||
* @brief Set the Node to correspond to this widget
|
||||
*/
|
||||
void SetNode(Node* n);
|
||||
void SetNode(Node* n, Node *context);
|
||||
|
||||
/**
|
||||
* @brief Get currently attached node
|
||||
@@ -174,6 +174,8 @@ private:
|
||||
*/
|
||||
Node* node_;
|
||||
|
||||
Node *context_;
|
||||
|
||||
/**
|
||||
* @brief Cached list of node inputs
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,7 @@ NodeViewMiniMap::NodeViewMiniMap(NodeViewScene *scene, QWidget *parent) :
|
||||
setViewportUpdateMode(FullViewportUpdate);
|
||||
setFrameShape(QFrame::Panel);
|
||||
setFrameShadow(QFrame::Plain);
|
||||
setMouseTracking(true);
|
||||
|
||||
QMetaObject::invokeMethod(this, &NodeViewMiniMap::SetDefaultSize, Qt::QueuedConnection);
|
||||
|
||||
@@ -87,7 +88,7 @@ void NodeViewMiniMap::resizeEvent(QResizeEvent *event)
|
||||
void NodeViewMiniMap::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (event->pos().x() <= resize_triangle_sz_ && event->pos().y() <= resize_triangle_sz_) {
|
||||
if (MouseInsideResizeTriangle(event)) {
|
||||
// Resizing!
|
||||
resizing_ = true;
|
||||
resize_anchor_ = QCursor::pos();
|
||||
@@ -107,6 +108,8 @@ void NodeViewMiniMap::mouseMoveEvent(QMouseEvent *event)
|
||||
} else {
|
||||
EmitMoveSignal(event);
|
||||
}
|
||||
} else {
|
||||
setCursor(MouseInsideResizeTriangle(event) ? Qt::SizeFDiagCursor : Qt::ArrowCursor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +138,11 @@ void NodeViewMiniMap::SetDefaultSize()
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeViewMiniMap::MouseInsideResizeTriangle(QMouseEvent *event)
|
||||
{
|
||||
return event->pos().x() <= resize_triangle_sz_ && event->pos().y() <= resize_triangle_sz_;
|
||||
}
|
||||
|
||||
void NodeViewMiniMap::EmitMoveSignal(QMouseEvent *event)
|
||||
{
|
||||
emit MoveToScenePoint(mapToScene(event->pos()));
|
||||
|
||||
@@ -57,6 +57,8 @@ private slots:
|
||||
void SetDefaultSize();
|
||||
|
||||
private:
|
||||
bool MouseInsideResizeTriangle(QMouseEvent *event);
|
||||
|
||||
void EmitMoveSignal(QMouseEvent *event);
|
||||
|
||||
int resize_triangle_sz_;
|
||||
|
||||
@@ -143,45 +143,25 @@ QVector<NodeViewEdge *> NodeViewScene::GetSelectedEdges() const
|
||||
return edges;
|
||||
}
|
||||
|
||||
NodeViewEdge* NodeViewScene::AddEdge(Node *output, const NodeInput &input)
|
||||
{
|
||||
NodeViewEdge *edge = EdgeToUIObject(output, input);
|
||||
|
||||
if (!edge) {
|
||||
edge = AddEdgeInternal(output, input, NodeToUIObject(output), NodeToUIObject(input.node()));
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
void NodeViewScene::RemoveEdge(Node *output, const NodeInput &input)
|
||||
{
|
||||
NodeViewEdge* edge = EdgeToUIObject(output, input);
|
||||
if (edge) {
|
||||
edge->from_item()->RemoveEdge(edge);
|
||||
edge->to_item()->RemoveEdge(edge);
|
||||
edges_.removeOne(edge);
|
||||
delete edge;
|
||||
}
|
||||
}
|
||||
|
||||
NodeViewContext *NodeViewScene::AddContext(Node *node)
|
||||
{
|
||||
NodeViewContext *context_item = context_map_.value(node);
|
||||
|
||||
if (!context_item) {
|
||||
context_item = new NodeViewContext();
|
||||
context_item->SetContext(node);
|
||||
context_item->setPos(0, 0);
|
||||
context_item = new NodeViewContext(node);
|
||||
|
||||
context_item->SetFlowDirection(GetFlowDirection());
|
||||
context_item->SetCurvedEdges(GetEdgesAreCurved());
|
||||
addItem(context_item);
|
||||
|
||||
const NodeGraph::PositionMap &map = node->parent()->GetNodesForContext(node);
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
context_item->AddChild(it.key());
|
||||
QPointF pos(0, 0);
|
||||
QRectF item_rect = context_item->rect();
|
||||
while (!items(item_rect).isEmpty()) {
|
||||
pos.setY(pos.y() + item_rect.height());
|
||||
item_rect = context_item->rect().translated(pos);
|
||||
}
|
||||
context_item->UpdateRect();
|
||||
context_item->setPos(pos);
|
||||
|
||||
addItem(context_item);
|
||||
|
||||
context_map_.insert(node, context_item);
|
||||
}
|
||||
@@ -209,22 +189,6 @@ int NodeViewScene::DetermineWeight(Node *n)
|
||||
return qMax(1, weight);
|
||||
}
|
||||
|
||||
NodeViewEdge* NodeViewScene::AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to)
|
||||
{
|
||||
NodeViewEdge* edge_ui = new NodeViewEdge(output, input, from, to);
|
||||
|
||||
edge_ui->SetFlowDirection(direction_);
|
||||
edge_ui->SetCurved(curved_edges_);
|
||||
|
||||
from->AddEdge(edge_ui);
|
||||
to->AddEdge(edge_ui);
|
||||
|
||||
addItem(edge_ui);
|
||||
edges_.append(edge_ui);
|
||||
|
||||
return edge_ui;
|
||||
}
|
||||
|
||||
Qt::Orientation NodeViewScene::GetFlowOrientation() const
|
||||
{
|
||||
return NodeViewCommon::GetFlowOrientation(direction_);
|
||||
@@ -240,8 +204,8 @@ void NodeViewScene::SetEdgesAreCurved(bool curved)
|
||||
if (curved_edges_ != curved) {
|
||||
curved_edges_ = curved;
|
||||
|
||||
foreach (NodeViewEdge* e, edges_) {
|
||||
e->SetCurved(curved_edges_);
|
||||
foreach (NodeViewContext *ctx, context_map_) {
|
||||
ctx->SetCurvedEdges(curved_edges_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +81,6 @@ public:
|
||||
}
|
||||
|
||||
public slots:
|
||||
NodeViewEdge *AddEdge(Node *output, const NodeInput& input);
|
||||
void RemoveEdge(Node *output, const NodeInput& input);
|
||||
|
||||
NodeViewContext *AddContext(Node *node);
|
||||
void RemoveContext(Node *node);
|
||||
|
||||
@@ -95,8 +92,6 @@ public slots:
|
||||
private:
|
||||
static int DetermineWeight(Node* n);
|
||||
|
||||
NodeViewEdge* AddEdgeInternal(Node *output, const NodeInput &input, NodeViewItem* from, NodeViewItem* to);
|
||||
|
||||
QHash<Node*, NodeViewContext*> context_map_;
|
||||
|
||||
QHash<Node*, NodeViewItem*> item_map_;
|
||||
|
||||
Reference in New Issue
Block a user