made node edge changes undoable
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(output)
|
||||
add_subdirectory(processor)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(image)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/input/image/image.h
|
||||
node/input/image/image.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
#include <QOpenGLTexture>
|
||||
|
||||
#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
|
||||
+26
-3
@@ -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;i<output->edges_.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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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<NodeViewItem*>(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<NodeOutput*>(drag_dest_param_), static_cast<NodeInput*>(drag_src_param_));
|
||||
new NodeEdgeAddCommand(static_cast<NodeOutput*>(drag_dest_param_),
|
||||
static_cast<NodeInput*>(drag_src_param_),
|
||||
node_edge_change_command_);
|
||||
|
||||
} else {
|
||||
new_edge = NodeParam::ConnectEdge(static_cast<NodeOutput*>(drag_src_param_), static_cast<NodeInput*>(drag_dest_param_));
|
||||
new NodeEdgeAddCommand(static_cast<NodeOutput*>(drag_src_param_),
|
||||
static_cast<NodeInput*>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QFontMetrics>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QLinearGradient>
|
||||
#include <QUndoCommand>
|
||||
#include <QWidget>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef NODEVIEWUNDO_H
|
||||
#define NODEVIEWUNDO_H
|
||||
|
||||
#include <QUndoCommand>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user