From 39256fd6f9108ded1d79c5a3eb19b5de05ec4691 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 17 Jul 2019 00:14:49 -0400 Subject: [PATCH] made node edge changes undoable --- app/main.cpp | 8 ++++ app/node/CMakeLists.txt | 1 + app/node/generator/solid/solid.cpp | 2 +- app/node/input/CMakeLists.txt | 22 ++++++++++ app/node/input/image/CMakeLists.txt | 22 ++++++++++ app/node/input/image/image.cpp | 46 +++++++++++++++++++ app/node/input/image/image.h | 29 ++++++++++++ app/node/param.cpp | 29 ++++++++++-- app/node/param.h | 3 ++ app/widget/nodeview/CMakeLists.txt | 2 + app/widget/nodeview/nodeviewitem.cpp | 54 +++++++++++++++++++---- app/widget/nodeview/nodeviewitem.h | 5 +++ app/widget/nodeview/nodeviewundo.cpp | 66 ++++++++++++++++++++++++++++ app/widget/nodeview/nodeviewundo.h | 38 ++++++++++++++++ app/window/mainwindow/mainwindow.cpp | 3 ++ 15 files changed, 318 insertions(+), 12 deletions(-) create mode 100644 app/node/input/CMakeLists.txt create mode 100644 app/node/input/image/CMakeLists.txt create mode 100644 app/node/input/image/image.cpp create mode 100644 app/node/input/image/image.h create mode 100644 app/widget/nodeview/nodeviewundo.cpp create mode 100644 app/widget/nodeview/nodeviewundo.h diff --git a/app/main.cpp b/app/main.cpp index e049b1415..a05769982 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -45,6 +45,14 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationDomain("olivevideoeditor.org"); QCoreApplication::setApplicationName("Olive"); + QString app_version = "0.2.0"; +#ifdef GITHASH + app_version.append("-"); + app_version.append(GITHASH); +#endif + + QCoreApplication::setApplicationVersion(app_version); + #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) QGuiApplication::setDesktopFileName("org.olivevideoeditor.Olive"); #endif diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 662a8e73f..82920dc0d 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(generator) +add_subdirectory(input) add_subdirectory(output) add_subdirectory(processor) diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 8c8fe9268..2a785ce9c 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -34,9 +34,9 @@ NodeOutput *SolidGenerator::texture_output() void SolidGenerator::Process(const rational &time) { + // FIXME: Test code Q_UNUSED(time) - // FIXME: Test code if (texture_ == nullptr) { QImage img(1920, 1080, QImage::Format_RGBA8888_Premultiplied); img.fill(Qt::red); diff --git a/app/node/input/CMakeLists.txt b/app/node/input/CMakeLists.txt new file mode 100644 index 000000000..2a8c2fc3a --- /dev/null +++ b/app/node/input/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +add_subdirectory(image) + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + PARENT_SCOPE +) diff --git a/app/node/input/image/CMakeLists.txt b/app/node/input/image/CMakeLists.txt new file mode 100644 index 000000000..48e815787 --- /dev/null +++ b/app/node/input/image/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/input/image/image.h + node/input/image/image.cpp + PARENT_SCOPE +) diff --git a/app/node/input/image/image.cpp b/app/node/input/image/image.cpp new file mode 100644 index 000000000..9c8efb7f2 --- /dev/null +++ b/app/node/input/image/image.cpp @@ -0,0 +1,46 @@ +#include "image.h" + +ImageInput::ImageInput() : + texture_(nullptr) +{ + texture_output_ = new NodeOutput(); + texture_output_->set_data_type(NodeOutput::kTexture); + AddParameter(texture_output_); +} + +QString ImageInput::Name() +{ + return tr("Image"); +} + +QString ImageInput::Category() +{ + return tr("Input"); +} + +QString ImageInput::Description() +{ + return tr("Import an image file."); +} + +NodeOutput *ImageInput::texture_output() +{ + return texture_output_; +} + +void ImageInput::Process(const rational &time) +{ + // FIXME: Use OIIO and OCIO here + + // FIXME: Test code + Q_UNUSED(time) + + if (texture_ == nullptr) { + QImage img("/home/matt/Desktop/oof.png"); + + texture_ = new QOpenGLTexture(img); + } + + texture_output_->set_value(texture_->textureId()); + // End test code +} diff --git a/app/node/input/image/image.h b/app/node/input/image/image.h new file mode 100644 index 000000000..e64a541c8 --- /dev/null +++ b/app/node/input/image/image.h @@ -0,0 +1,29 @@ +#ifndef IMAGE_H +#define IMAGE_H + +#include + +#include "node/node.h" + +class ImageInput : public Node +{ + Q_OBJECT +public: + ImageInput(); + + virtual QString Name() override; + virtual QString Category() override; + virtual QString Description() override; + + NodeOutput* texture_output(); + +public slots: + virtual void Process(const rational &time) override; + +private: + NodeOutput* texture_output_; + + QOpenGLTexture* texture_; +}; + +#endif // IMAGE_H diff --git a/app/node/param.cpp b/app/node/param.cpp index 683013410..553b978ad 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -113,9 +113,7 @@ bool NodeParam::AreDataTypesCompatible(const DataType &output_type, const QList< NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) { // If the input can only accept one input (the default) and has one already, disconnect it - if (!input->edges_.isEmpty() && !input->can_accept_multiple_inputs()) { - DisconnectEdge(input->edges_.first()); - } + FreeSpaceForEdgeFromInput(input); // Make sure it's not a duplicate of an edge that already exists foreach (NodeEdgePtr existing, input->edges()) { @@ -146,6 +144,31 @@ void NodeParam::DisconnectEdge(NodeEdgePtr edge) emit output->EdgeRemoved(edge); } +void NodeParam::DisconnectEdge(NodeOutput *output, NodeInput *input) +{ + for (int i=0;iedges_.size();i++) { + NodeEdgePtr edge = output->edges_.at(i); + if (edge->input() == input) { + DisconnectEdge(edge); + break; + } + } +} + +NodeEdgePtr NodeParam::FreeSpaceForEdgeFromInput(NodeInput *input) +{ + // If the input can only accept one input (the default) and has one already, disconnect it + if (!input->edges_.isEmpty() && !input->can_accept_multiple_inputs()) { + NodeEdgePtr edge = input->edges_.first(); + + DisconnectEdge(edge); + + return edge; + } + + return nullptr; +} + QString NodeParam::GetDefaultDataTypeName(const DataType& type) { switch (type) { diff --git a/app/node/param.h b/app/node/param.h index 5f6f5a734..7ae269af8 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -72,6 +72,9 @@ public: static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input); static void DisconnectEdge(NodeEdgePtr edge); + static void DisconnectEdge(NodeOutput* output, NodeInput* input); + + static NodeEdgePtr FreeSpaceForEdgeFromInput(NodeInput* input); static QString GetDefaultDataTypeName(const DataType &type); diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 9103c574f..9f28bd1b1 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -24,5 +24,7 @@ set(OLIVE_SOURCES widget/nodeview/nodeviewitem.cpp widget/nodeview/nodeviewitemwidgetproxy.h widget/nodeview/nodeviewitemwidgetproxy.cpp + widget/nodeview/nodeviewundo.h + widget/nodeview/nodeviewundo.cpp PARENT_SCOPE ) diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index 242b2f369..ea1b04c16 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -28,7 +28,9 @@ #include "core.h" #include "nodeview.h" +#include "nodeviewundo.h" #include "ui/icons/icons.h" +#include "undo/undostack.h" #include "window/mainwindow/mainwindow.h" NodeViewItem::NodeViewItem(QGraphicsItem *parent) : @@ -36,8 +38,10 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : node_(nullptr), font_metrics(font), dragging_edge_(nullptr), + drag_expanded_item_(nullptr), expanded_(false), - standard_click_(false) + standard_click_(false), + node_edge_change_command_(nullptr) { // Set flags for this widget setFlag(QGraphicsItem::ItemIsMovable); @@ -131,7 +135,6 @@ QRectF NodeViewItem::GetParameterConnectorRect(int index) connector_rect.translate(0, font_metrics.height() * index); } - // FIXME: I don't know how this will work with NodeParam::kBidirectional if (param->type() == NodeParam::kOutput) { connector_rect.translate(rect().width() - node_connector_size_, 0); } @@ -147,7 +150,6 @@ QPointF NodeViewItem::GetParameterTextPoint(int index) NodeParam* param = node_->ParamAt(index); - // FIXME: I don't know how this will work with NodeParam::kBidirectional if (param->type() == NodeParam::kOutput) { return content_rect_.topRight() + QPointF(-(node_connector_size_ + node_text_padding_), node_text_padding_ + font_metrics.ascent() + font_metrics.height()*index); @@ -198,7 +200,6 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti // Draw text QPointF text_pt = GetParameterTextPoint(i); - // FIXME: I don't know how this will work for kBidirectional if (param->type() == NodeParam::kOutput) { #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) text_pt -= QPointF(font_metrics.width(param->name()), 0); @@ -281,6 +282,12 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) // Create draggable object dragging_edge_ = new NodeViewEdge(); + // Clear any existing node edge command + // FIXME: Is this ever necessary? + delete node_edge_change_command_; + + node_edge_change_command_ = new QUndoCommand(); + if (param->type() == NodeParam::kOutput || param->edges().isEmpty()) { // For an output param (or an input param with no connections), we default to creating a new edge drag_source_ = this; @@ -295,7 +302,8 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) NodeEdgePtr edge = param->edges().last(); // Remove old edge - NodeParam::DisconnectEdge(edge); + NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(), edge->input(), node_edge_change_command_); + remove_command->redo(); // The starting position will be the OPPOSING parameter's rectangle @@ -335,8 +343,18 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) // See if the mouse is currently inside a node NodeViewItem* drop_item = dynamic_cast(scene()->itemAt(event->scenePos(), sceneTransform())); + + // If we expanded an item below but are no longer dragging over it, re-collapse it + if (drag_expanded_item_ != nullptr && drop_item != drag_expanded_item_) { + drag_expanded_item_->SetExpanded(false); + drag_expanded_item_ = nullptr; + } + if (drop_item != nullptr && drop_item != drag_source_) { - if (drop_item->IsExpanded()) { + + // If the item we're dragging over is collapsed, expand it + if (!drop_item->IsExpanded()) { + drag_expanded_item_ = drop_item; drop_item->SetExpanded(true); } @@ -382,21 +400,41 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) // FIXME: Make this undoable + // Remove the drag object scene()->removeItem(dragging_edge_); + // If we expanded an item in the drag, re-collapse it now + if (drag_expanded_item_ != nullptr) { + drag_expanded_item_->SetExpanded(false); + drag_expanded_item_ = nullptr; + } + if (drag_dest_param_ != nullptr) { // We dragged to somewhere, so we'll make a new connection NodeEdgePtr new_edge; + // Connecting will automatically add an edge UI object through the signal/slot system if (drag_dest_param_->type() == NodeParam::kOutput) { - new_edge = NodeParam::ConnectEdge(static_cast(drag_dest_param_), static_cast(drag_src_param_)); + new NodeEdgeAddCommand(static_cast(drag_dest_param_), + static_cast(drag_src_param_), + node_edge_change_command_); + } else { - new_edge = NodeParam::ConnectEdge(static_cast(drag_src_param_), static_cast(drag_dest_param_)); + new NodeEdgeAddCommand(static_cast(drag_src_param_), + static_cast(drag_dest_param_), + node_edge_change_command_); } } dragging_edge_ = nullptr; + + if (node_edge_change_command_->childCount() > 0) { + olive::undo_stack.push(node_edge_change_command_); + } else { + delete node_edge_change_command_; + } + node_edge_change_command_ = nullptr; return; } diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index bee61490e..47b10632c 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "node/node.h" @@ -73,6 +74,7 @@ private: NodeParam* drag_src_param_; NodeParam* drag_dest_param_; NodeViewItem* drag_source_; + NodeViewItem* drag_expanded_item_; int node_connector_size_; int node_text_padding_; @@ -82,6 +84,9 @@ private: bool expanded_; bool standard_click_; + + QUndoCommand* node_edge_change_command_; + }; #endif // NODEVIEWITEM_H diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp new file mode 100644 index 000000000..9836bc97a --- /dev/null +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -0,0 +1,66 @@ +#include "nodeviewundo.h" + +NodeEdgeAddCommand::NodeEdgeAddCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : + QUndoCommand(parent), + output_(output), + input_(input), + old_edge_(nullptr), + done_(false) +{ +} + +void NodeEdgeAddCommand::redo() +{ + if (done_) { + return; + } + + old_edge_ = NodeParam::FreeSpaceForEdgeFromInput(input_); + + NodeParam::ConnectEdge(output_, input_); + + done_ = true; +} + +void NodeEdgeAddCommand::undo() +{ + if (!done_) { + return; + } + + NodeParam::DisconnectEdge(output_, input_); + + if (old_edge_ != nullptr) { + NodeParam::ConnectEdge(old_edge_->output(), old_edge_->input()); + } + + done_ = false; +} + +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : + QUndoCommand(parent), + output_(output), + input_(input), + done_(false) +{ +} + +void NodeEdgeRemoveCommand::redo() +{ + if (done_) { + return; + } + + NodeParam::DisconnectEdge(output_, input_); + done_ = true; +} + +void NodeEdgeRemoveCommand::undo() +{ + if (!done_) { + return; + } + + NodeParam::ConnectEdge(output_, input_); + done_ = false; +} diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h new file mode 100644 index 000000000..256a24537 --- /dev/null +++ b/app/widget/nodeview/nodeviewundo.h @@ -0,0 +1,38 @@ +#ifndef NODEVIEWUNDO_H +#define NODEVIEWUNDO_H + +#include + +#include "node/node.h" + +class NodeEdgeAddCommand : public QUndoCommand { +public: + NodeEdgeAddCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); + + virtual void redo() override; + virtual void undo() override; + +private: + NodeOutput* output_; + NodeInput* input_; + + NodeEdgePtr old_edge_; + + bool done_; +}; + +class NodeEdgeRemoveCommand : public QUndoCommand { +public: + NodeEdgeRemoveCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); + + virtual void redo() override; + virtual void undo() override; + +private: + NodeOutput* output_; + NodeInput* input_; + + bool done_; +}; + +#endif // NODEVIEWUNDO_H diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 427bf03ba..c8f80e78c 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -33,6 +33,7 @@ #include "mainmenu.h" // FIXME: Test code +#include "node/input/image/image.h" #include "node/output/viewer/viewer.h" #include "node/generator/solid/solid.h" // End test code @@ -89,6 +90,8 @@ void olive::MainWindow::ProjectOpen(Project* p) SolidGenerator* sg = new SolidGenerator(); NodeInput::ConnectEdge(sg->texture_output(), vo->texture_input()); graph->AddNode(sg); + ImageInput* ii = new ImageInput(); + graph->AddNode(ii); task_panel->SetGraph(graph); // End test code }