nodeview: implement dragging items from project explorer into node view

This commit is contained in:
itsmattkc
2022-05-11 14:55:07 -07:00
parent 83e7707025
commit c0ad244397
8 changed files with 281 additions and 145 deletions
+260 -136
View File
@@ -22,6 +22,7 @@
#include <QInputDialog>
#include <QMessageBox>
#include <QMimeData>
#include <QMouseEvent>
#include <QPushButton>
#include <QScrollBar>
@@ -539,78 +540,7 @@ void NodeView::mouseMoveEvent(QMouseEvent *event)
// See if there are any items attached
if (!attached_items_.isEmpty()) {
// Move those items to the cursor
MoveAttachedNodesToCursor(event->pos());
// See if the user clicked on an edge (only when dropping single nodes)
if (attached_items_.size() == 1) {
Node* attached_node = attached_items_.first().item->GetNode();
QRect edge_detect_rect(event->pos(), event->pos());
int edge_detect_radius = fontMetrics().height();
edge_detect_rect.adjust(-edge_detect_radius, -edge_detect_radius, edge_detect_radius, edge_detect_radius);
QList<QGraphicsItem*> items = this->items(edge_detect_rect);
NodeViewEdge* new_drop_edge = nullptr;
// See if there is an edge here
for (QGraphicsItem* item : qAsConst(items)) {
new_drop_edge = dynamic_cast<NodeViewEdge*>(item);
if (new_drop_edge) {
drop_input_.Reset();
NodeValue::Type drop_edge_data_type = new_drop_edge->input().GetDataType();
// Determine best input to connect to our new node
if (attached_node->GetEffectInput().IsValid()) {
// If node specifies an effect input, use that immediately
drop_input_ = attached_node->GetEffectInput();
} else {
// Otherwise, we may have to iterate to find a valid one
for (const QString& input : attached_node->inputs()) {
if (input == Node::kEnabledInput) {
// Ignore enabled input
continue;
}
NodeInput i(attached_node, input);
if (attached_node->IsInputConnectable(input)) {
if (attached_node->GetInputDataType(input) == drop_edge_data_type) {
// Found exactly the type we're looking for, set and break this loop
drop_input_ = i;
break;
} else if (!drop_input_.IsValid()) {
// Default to first connectable input
drop_input_ = i;
}
}
}
}
if (drop_input_.IsValid()) {
break;
} else {
new_drop_edge = nullptr;
}
}
}
if (drop_edge_ != new_drop_edge) {
if (drop_edge_) {
drop_edge_->SetHighlighted(false);
}
drop_edge_ = new_drop_edge;
if (drop_edge_) {
drop_edge_->SetHighlighted(true);
}
}
}
ProcessMovingAttachedNodes(event->pos());
}
}
@@ -630,72 +560,10 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
bool had_attached_items = !attached_items_.isEmpty();
if (!attached_items_.isEmpty()) {
select_context = nullptr;
QList<QGraphicsItem*> items_at_cursor = this->items(event->pos());
foreach (QGraphicsItem *i, items_at_cursor) {
if (NodeViewContext *context_item = dynamic_cast<NodeViewContext*>(i)) {
select_context = context_item->GetContext();
break;
}
}
select_context = GetContextAtMousePos(event->pos());
if (select_context) {
{
MultiUndoCommand *add_command = new MultiUndoCommand();
foreach (const AttachedItem &ai, attached_items_) {
// Add node to the same graph that the context is in
add_command->add_child(new NodeAddCommand(select_context->parent(), ai.node));
// Add node to the context
if (ai.item) {
add_command->add_child(new NodeSetPositionCommand(ai.node, select_context, scene_.context_map().value(select_context)->MapScenePosToNodePosInContext(ai.item->pos())));
select_nodes.append(ai.node);
}
}
if (add_command->child_count()) {
add_command->redo_now();
command->add_child(add_command);
} else {
delete add_command;
}
}
{
// Dropped attached item onto an edge, connect it between them
MultiUndoCommand *drop_edge_command = new MultiUndoCommand();
if (attached_items_.size() == 1) {
Node* dropping_node = nullptr;
foreach (const AttachedItem &ai, attached_items_) {
if (ai.item) {
dropping_node = ai.node;
break;
}
}
if (dropping_node && drop_edge_) {
// Remove old edge
drop_edge_command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input()));
// Place new edges
drop_edge_command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
drop_edge_command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
}
drop_edge_ = nullptr;
}
if (drop_edge_command->child_count()) {
drop_edge_command->redo_now();
command->add_child(drop_edge_command);
} else {
delete drop_edge_command;
}
}
DetachItemsFromCursor(false);
select_nodes = ProcessDroppingAttachedNodes(command, select_context, event->pos());
} else {
QToolTip::showText(QCursor::pos(), tr("Nodes must be placed inside a context."));
}
@@ -733,6 +601,96 @@ void NodeView::mouseDoubleClickEvent(QMouseEvent *event)
}
}
void NodeView::dragEnterEvent(QDragEnterEvent *event)
{
if (contexts_.empty()) {
event->ignore();
return;
}
QStringList mime_fmts = event->mimeData()->formats();
if (mime_fmts.contains(Project::kItemMimeType)) {
QByteArray model_data = event->mimeData()->data(Project::kItemMimeType);
QDataStream stream(&model_data, QIODevice::ReadOnly);
// Variables to deserialize into
quintptr item_ptr;
QVector<Track::Reference> enabled_streams;
QVector<AttachedItem> new_attached;
int y = 0;
while (!stream.atEnd()) {
stream >> enabled_streams >> item_ptr;
// Get Item object
Node* item = reinterpret_cast<Node*>(item_ptr);
if (ViewerOutput* f = dynamic_cast<ViewerOutput*>(item)) {
NodeViewItem *new_item;
new_item = new NodeViewItem(f, nullptr);
new_item->SetFlowDirection(scene_.GetFlowDirection());
y++;
scene_.addItem(new_item);
new_attached.append({new_item, f, QPointF(0, y)});
}
}
if (new_attached.empty()) {
event->ignore();
} else {
SetAttachedItems(new_attached);
event->accept();
}
}
}
void NodeView::dragMoveEvent(QDragMoveEvent *event)
{
if (attached_items_.empty()) {
event->ignore();
} else {
ProcessMovingAttachedNodes(event->pos());
if (GetContextAtMousePos(event->pos())) {
event->accept();
} else {
event->ignore();
}
}
}
void NodeView::dropEvent(QDropEvent *event)
{
if (Node *drop_ctx = GetContextAtMousePos(event->pos())) {
MultiUndoCommand *command = new MultiUndoCommand();
QVector<Node*> select_nodes = ProcessDroppingAttachedNodes(command, drop_ctx, event->pos());
Core::instance()->undo_stack()->pushIfHasChildren(command);
scene_.context_map().value(drop_ctx)->Select(select_nodes);
event->accept();
} else {
DetachItemsFromCursor(false);
event->ignore();
}
}
void NodeView::dragLeaveEvent(QDragLeaveEvent *event)
{
if (attached_items_.empty()) {
event->ignore();
} else {
DetachItemsFromCursor(false);
event->accept();
}
}
void NodeView::resizeEvent(QResizeEvent *event)
{
super::resizeEvent(event);
@@ -1030,6 +988,172 @@ void NodeView::MoveAttachedNodesToCursor(const QPoint& p)
}
}
void NodeView::ProcessMovingAttachedNodes(const QPoint &pos)
{
// Move those items to the cursor
MoveAttachedNodesToCursor(pos);
// See if the user clicked on an edge (only when dropping single nodes)
if (attached_items_.size() == 1) {
Node* attached_node = attached_items_.first().item->GetNode();
QRect edge_detect_rect(pos, pos);
int edge_detect_radius = fontMetrics().height();
edge_detect_rect.adjust(-edge_detect_radius, -edge_detect_radius, edge_detect_radius, edge_detect_radius);
QList<QGraphicsItem*> items = this->items(edge_detect_rect);
NodeViewEdge* new_drop_edge = nullptr;
// See if there is an edge here
for (QGraphicsItem* item : qAsConst(items)) {
new_drop_edge = dynamic_cast<NodeViewEdge*>(item);
if (new_drop_edge) {
drop_input_.Reset();
NodeValue::Type drop_edge_data_type = new_drop_edge->input().GetDataType();
// Determine best input to connect to our new node
if (attached_node->GetEffectInput().IsValid()) {
// If node specifies an effect input, use that immediately
drop_input_ = attached_node->GetEffectInput();
} else {
// Otherwise, we may have to iterate to find a valid one
for (const QString& input : attached_node->inputs()) {
if (input == Node::kEnabledInput) {
// Ignore enabled input
continue;
}
NodeInput i(attached_node, input);
if (attached_node->IsInputConnectable(input)) {
if (attached_node->GetInputDataType(input) == drop_edge_data_type) {
// Found exactly the type we're looking for, set and break this loop
drop_input_ = i;
break;
} else if (!drop_input_.IsValid()) {
// Default to first connectable input
drop_input_ = i;
}
}
}
}
if (new_drop_edge->input().node()->OutputsTo(attached_node, true)) {
drop_input_.Reset();
}
if (drop_input_.IsValid()) {
break;
} else {
new_drop_edge = nullptr;
}
}
}
if (drop_edge_ != new_drop_edge) {
if (drop_edge_) {
drop_edge_->SetHighlighted(false);
}
drop_edge_ = new_drop_edge;
if (drop_edge_) {
drop_edge_->SetHighlighted(true);
}
}
}
}
QVector<Node*> NodeView::ProcessDroppingAttachedNodes(MultiUndoCommand *command, Node *select_context, const QPoint &pos)
{
QVector<Node*> select_nodes;
{
MultiUndoCommand *add_command = new MultiUndoCommand();
foreach (const AttachedItem &ai, attached_items_) {
if (select_context->OutputsTo(ai.node, true)) {
continue;
}
if (ai.item) {
select_nodes.append(ai.node);
}
if (select_context->ContextContainsNode(ai.node)) {
continue;
}
// Add node to the same graph that the context is in
add_command->add_child(new NodeAddCommand(select_context->parent(), ai.node));
// Add node to the context
if (ai.item) {
add_command->add_child(new NodeSetPositionCommand(ai.node, select_context, scene_.context_map().value(select_context)->MapScenePosToNodePosInContext(ai.item->pos())));
}
}
if (add_command->child_count()) {
add_command->redo_now();
command->add_child(add_command);
} else {
delete add_command;
}
}
{
// Dropped attached item onto an edge, connect it between them
MultiUndoCommand *drop_edge_command = new MultiUndoCommand();
if (attached_items_.size() == 1) {
Node* dropping_node = nullptr;
foreach (const AttachedItem &ai, attached_items_) {
if (ai.item && !select_context->ContextContainsNode(ai.node) && !select_context->OutputsTo(ai.node, true)) {
dropping_node = ai.node;
break;
}
}
if (dropping_node && drop_edge_) {
// Remove old edge
drop_edge_command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input()));
// Place new edges
drop_edge_command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_));
drop_edge_command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input()));
}
drop_edge_ = nullptr;
}
if (drop_edge_command->child_count()) {
drop_edge_command->redo_now();
command->add_child(drop_edge_command);
} else {
delete drop_edge_command;
}
}
DetachItemsFromCursor(false);
return select_nodes;
}
Node *NodeView::GetContextAtMousePos(const QPoint &p)
{
QList<QGraphicsItem*> items_at_cursor = this->items(p);
foreach (QGraphicsItem *i, items_at_cursor) {
if (NodeViewContext *context_item = dynamic_cast<NodeViewContext*>(i)) {
return context_item->GetContext();
}
}
return nullptr;
}
void NodeView::ConnectSelectionChangedSignal()
{
connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::UpdateSelectionCache);