From 4378fb74232052d9da5bcb1440dad92ba7264ef9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 09:47:08 +1100 Subject: [PATCH 01/32] changed img seq debug message --- io/loadthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/loadthread.cpp b/io/loadthread.cpp index b7688a1a6..65bdc50f4 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -237,7 +237,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (m->url.contains('%')) { // hack for image sequences (qt won't be able to find the URL with %, but ffmpeg may) m->url = internal_proj_dir_test; - dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's current directory"; + dout << "[INFO] Guess image sequence" << attr.value().toString() << "path to project's internal directory"; } else { dout << "[INFO] Failed to match" << attr.value().toString() << "to file"; } From 1ee9451343313e38b199b2a21d14df8815f9e706 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 10:29:02 +1100 Subject: [PATCH 02/32] implemented hold keyframe and began new panel --- mainwindow.cpp | 21 +++++---------------- olive.pro | 8 ++++++-- panels/grapheditor.cpp | 31 +++++++++++++++++++++++++++++++ panels/grapheditor.h | 16 ++++++++++++++++ panels/panels.cpp | 35 ++++++++++++++++++++++++++++++----- panels/panels.h | 6 ++++++ project/effect.h | 4 ++-- project/effectfield.cpp | 13 +++++++++---- ui/graphview.cpp | 13 +++++++++++++ ui/graphview.h | 13 +++++++++++++ ui/keyframeview.cpp | 31 ++++++++++++++++++++++--------- 11 files changed, 153 insertions(+), 38 deletions(-) create mode 100644 panels/grapheditor.cpp create mode 100644 panels/grapheditor.h create mode 100644 ui/graphview.cpp create mode 100644 ui/graphview.h diff --git a/mainwindow.cpp b/mainwindow.cpp index 738e7c9b6..8cd2f0972 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -18,6 +18,7 @@ #include "panels/effectcontrols.h" #include "panels/viewer.h" #include "panels/timeline.h" +#include "panels/grapheditor.h" #include "dialogs/aboutdialog.h" #include "dialogs/newsequencedialog.h" @@ -57,6 +58,7 @@ void MainWindow::setup_layout(bool reset) { panel_footage_viewer->show(); panel_sequence_viewer->show(); panel_timeline->show(); + panel_graph_editor->hide(); bool load_default = true; @@ -77,6 +79,7 @@ void MainWindow::setup_layout(bool reset) { panel_footage_viewer->raise(); addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); + panel_graph_editor->setFloating(true); // workaround for strange Qt dock bug (see https://bugreports.qt.io/browse/QTBUG-65592) #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) @@ -194,12 +197,7 @@ MainWindow::MainWindow(QWidget *parent) : } } - // TODO maybe replace these with non-pointers later on? - panel_sequence_viewer = new Viewer(this); - panel_footage_viewer = new Viewer(this); - panel_project = new Project(this); - panel_effect_controls = new EffectControls(this); - panel_timeline = new Timeline(this); + alloc_panels(this); if (!data_dir.isEmpty()) { // detect auto-recovery file @@ -259,16 +257,7 @@ MainWindow::~MainWindow() { delete ui; - delete panel_sequence_viewer; - panel_sequence_viewer = NULL; - delete panel_footage_viewer; - panel_footage_viewer = NULL; - delete panel_project; - panel_project = NULL; - delete panel_effect_controls; - panel_effect_controls = NULL; - delete panel_timeline; - panel_timeline = NULL; + free_panels(); close_debug(); } diff --git a/olive.pro b/olive.pro index 650aedb28..eab6b3b19 100644 --- a/olive.pro +++ b/olive.pro @@ -105,7 +105,9 @@ SOURCES += \ ui/resizablescrollbar.cpp \ ui/sourceiconview.cpp \ project/sourcescommon.cpp \ - ui/keyframenavigator.cpp + ui/keyframenavigator.cpp \ + panels/grapheditor.cpp \ + ui/graphview.cpp HEADERS += \ mainwindow.h \ @@ -185,7 +187,9 @@ HEADERS += \ ui/resizablescrollbar.h \ ui/sourceiconview.h \ project/sourcescommon.h \ - ui/keyframenavigator.h + ui/keyframenavigator.h \ + panels/grapheditor.h \ + ui/graphview.h FORMS += \ mainwindow.ui \ diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp new file mode 100644 index 000000000..ef2f1fdd2 --- /dev/null +++ b/panels/grapheditor.cpp @@ -0,0 +1,31 @@ +#include "grapheditor.h" + +#include +#include +#include + +#include "ui/graphview.h" + +GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { + setWindowTitle("Graph Editor"); + + QWidget* central_widget = new QWidget(); + setWidget(central_widget); + + QVBoxLayout* layout = new QVBoxLayout(); + central_widget->setLayout(layout); + + QHBoxLayout* tools = new QHBoxLayout(); + tools->addWidget(new QPushButton("HECK!")); + layout->addLayout(tools); + + view = new GraphView(); + layout->addWidget(view); + + QHBoxLayout* values = new QHBoxLayout(); + values->addStretch(); + values->addWidget(new QLabel("1920.0")); + values->addWidget(new QLabel("1080.0")); + values->addStretch(); + layout->addLayout(values); +} diff --git a/panels/grapheditor.h b/panels/grapheditor.h new file mode 100644 index 000000000..5e4a919cf --- /dev/null +++ b/panels/grapheditor.h @@ -0,0 +1,16 @@ +#ifndef GRAPHEDITOR_H +#define GRAPHEDITOR_H + +#include + +class GraphView; + +class GraphEditor : public QDockWidget { + Q_OBJECT +public: + GraphEditor(QWidget* parent = 0); +private: + GraphView* view; +}; + +#endif // GRAPHEDITOR_H diff --git a/panels/panels.cpp b/panels/panels.cpp index dd8206ccc..fcc9e0008 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -8,6 +8,7 @@ #include "project/clip.h" #include "project/transition.h" #include "io/config.h" +#include "grapheditor.h" #include "debug.h" Project* panel_project = 0; @@ -15,6 +16,7 @@ EffectControls* panel_effect_controls = 0; Viewer* panel_sequence_viewer = 0; Viewer* panel_footage_viewer = 0; Timeline* panel_timeline = 0; +GraphEditor* panel_graph_editor = 0; void update_effect_controls() { // SEND CLIPS TO EFFECT CONTROLS @@ -110,15 +112,15 @@ void update_ui(bool modified) { QDockWidget *get_focused_panel() { QDockWidget* w = NULL; if (config.hover_focus) { - if (panel_project->rect().contains(panel_project->mapFromGlobal(QCursor::pos()))) { + if (panel_project->underMouse()) { w = panel_project; - } else if (panel_effect_controls->rect().contains(panel_effect_controls->mapFromGlobal(QCursor::pos()))) { + } else if (panel_effect_controls->underMouse()) { w = panel_effect_controls; - } else if (panel_sequence_viewer->rect().contains(panel_sequence_viewer->mapFromGlobal(QCursor::pos()))) { + } else if (panel_sequence_viewer->underMouse()) { w = panel_sequence_viewer; - } else if (panel_footage_viewer->rect().contains(panel_footage_viewer->mapFromGlobal(QCursor::pos()))) { + } else if (panel_footage_viewer->underMouse()) { w = panel_footage_viewer; - } else if (panel_timeline->rect().contains(panel_timeline->mapFromGlobal(QCursor::pos()))) { + } else if (panel_timeline->underMouse()) { w = panel_timeline; } } @@ -137,3 +139,26 @@ QDockWidget *get_focused_panel() { } return w; } + +void alloc_panels(QWidget* parent) { + // TODO maybe replace these with non-pointers later on? + panel_sequence_viewer = new Viewer(parent); + panel_footage_viewer = new Viewer(parent); + panel_project = new Project(parent); + panel_effect_controls = new EffectControls(parent); + panel_timeline = new Timeline(parent); + panel_graph_editor = new GraphEditor(parent); +} + +void free_panels() { + delete panel_sequence_viewer; + panel_sequence_viewer = NULL; + delete panel_footage_viewer; + panel_footage_viewer = NULL; + delete panel_project; + panel_project = NULL; + delete panel_effect_controls; + panel_effect_controls = NULL; + delete panel_timeline; + panel_timeline = NULL; +} diff --git a/panels/panels.h b/panels/panels.h index 042a652d8..691736b82 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -5,6 +5,9 @@ class Project; class EffectControls; class Viewer; class Timeline; +class GraphEditor; + +class QWidget; class QDockWidget; extern Project* panel_project; @@ -12,8 +15,11 @@ extern EffectControls* panel_effect_controls; extern Viewer* panel_sequence_viewer; extern Viewer* panel_footage_viewer; extern Timeline* panel_timeline; +extern GraphEditor* panel_graph_editor; void update_ui(bool modified); QDockWidget* get_focused_panel(); +void alloc_panels(QWidget *parent); +void free_panels(); #endif // PANELS_H diff --git a/project/effect.h b/project/effect.h index 08fcc13b4..572baf7a9 100644 --- a/project/effect.h +++ b/project/effect.h @@ -69,8 +69,8 @@ extern QMutex effects_loaded; #define EFFECT_INTERNAL_COUNT 13 #define KEYFRAME_TYPE_LINEAR 0 -#define KEYFRAME_TYPE_SMOOTH 1 -#define KEYFRAME_TYPE_BEZIER 2 +#define KEYFRAME_TYPE_BEZIER 1 +#define KEYFRAME_TYPE_HOLD 2 struct GLTextureCoords { int grid_size; diff --git a/project/effectfield.cpp b/project/effectfield.cpp index d2166a835..2caaf2991 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -140,7 +140,12 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do before = before_keyframe_index; after = after_keyframe_index; - progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + if (parent_row->keyframe_types.at(before) == KEYFRAME_TYPE_HOLD) { + progress = 0; + } else { + // TODO replace with bezier function + progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + } } else if (before_keyframe_index > -1) { before = before_keyframe_index; after = before_keyframe_index; @@ -161,13 +166,13 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { double progress; get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); - int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); - if (kf_type == KEYFRAME_TYPE_SMOOTH) { + /*int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); + if (kf_type == KEYFRAME_TYPE_BEZIER) { double x = (8.0 * progress) - 4.0; progress = 1.0 / (1.0 + qPow(M_E, -x)); progress *= 1.0373; progress -= 0.01865; - } + }*/ const QVariant& before_data = keyframe_data.at(before_keyframe); switch (type) { diff --git a/ui/graphview.cpp b/ui/graphview.cpp new file mode 100644 index 000000000..405663889 --- /dev/null +++ b/ui/graphview.cpp @@ -0,0 +1,13 @@ +#include "graphview.h" + +#include + +GraphView::GraphView(QWidget* parent) {} + +void GraphView::paintEvent(QPaintEvent *event) { + QPainter p(this); + + p.setPen(Qt::white); + + p.drawRect(rect()); +} diff --git a/ui/graphview.h b/ui/graphview.h new file mode 100644 index 000000000..b28d80bf9 --- /dev/null +++ b/ui/graphview.h @@ -0,0 +1,13 @@ +#ifndef GRAPHVIEW_H +#define GRAPHVIEW_H + +#include + +class GraphView : public QWidget { +public: + GraphView(QWidget* parent = 0); + + void paintEvent(QPaintEvent *event); +}; + +#endif // GRAPHVIEW_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index c46dcb0c2..431d9ab03 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -11,6 +11,7 @@ #include "panels/viewer.h" #include "ui/viewerwidget.h" #include "project/sequence.h" +#include "panels/grapheditor.h" #include "ui_effectcontrols.h" #include @@ -47,24 +48,33 @@ KeyframeView::KeyframeView(QWidget *parent) : void KeyframeView::show_context_menu(const QPoint& pos) { if (selected_rows.size() > 0) { QMenu menu(this); + QAction* linear = menu.addAction("Linear"); linear->setData(KEYFRAME_TYPE_LINEAR); QAction* smooth = menu.addAction("Smooth"); - smooth->setData(KEYFRAME_TYPE_SMOOTH); - /*QAction* bezier = menu.addAction("Bezier"); - bezier->setData(KEYFRAME_TYPE_BEZIER);*/ + smooth->setData(KEYFRAME_TYPE_BEZIER); + QAction* hold = menu.addAction("Hold"); + hold->setData(KEYFRAME_TYPE_HOLD); + menu.addSeparator(); + menu.addAction("Graph Editor"); + connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_set_key_type(QAction*))); menu.exec(mapToGlobal(pos)); } } void KeyframeView::menu_set_key_type(QAction* a) { - ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + if (a->data().isNull()) { + // load graph editor + panel_graph_editor->show(); + } else { + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + } + undo_stack.push(ca); + update(); } - undo_stack.push(ca); - update(); } void KeyframeView::paintEvent(QPaintEvent*) { @@ -184,9 +194,12 @@ void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darke p.drawPolygon(points, KEYFRAME_POINT_COUNT); } break; - case KEYFRAME_TYPE_SMOOTH: + case KEYFRAME_TYPE_BEZIER: p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); break; + case KEYFRAME_TYPE_HOLD: + p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); + break; } } From 8a6242e36fde034d77b2bd5f8b4b6b836af0f760 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 27 Dec 2018 11:51:35 +1100 Subject: [PATCH 03/32] reimplemented panel state restoration --- mainwindow.cpp | 42 +++++++++++++++++++++--------------------- mainwindow.h | 2 ++ mainwindow.ui | 9 +++++++++ panels/grapheditor.cpp | 21 +++++++++++++++++++-- panels/panels.cpp | 6 ++++++ playback/audio.cpp | 6 ++++-- ui/graphview.cpp | 5 ++++- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 8cd2f0972..f8be11aff 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -60,31 +60,26 @@ void MainWindow::setup_layout(bool reset) { panel_timeline->show(); panel_graph_editor->hide(); - bool load_default = true; - - /*if (!reset) { - QFile panel_config(get_data_path() + "/layout"); - if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { - if (restoreState(panel_config.readAll(), 0)) { - load_default = false; - } - panel_config.close(); - } - }*/ - - if (load_default) { - addDockWidget(Qt::TopDockWidgetArea, panel_project); - addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); - tabifyDockWidget(panel_footage_viewer, panel_effect_controls); - panel_footage_viewer->raise(); - addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); - addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); - panel_graph_editor->setFloating(true); + addDockWidget(Qt::TopDockWidgetArea, panel_project); + addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); + tabifyDockWidget(panel_footage_viewer, panel_effect_controls); + panel_footage_viewer->raise(); + addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); + addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); + panel_graph_editor->setFloating(true); // workaround for strange Qt dock bug (see https://bugreports.qt.io/browse/QTBUG-65592) #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - resizeDocks({panel_project}, {40}, Qt::Horizontal); + resizeDocks({panel_project}, {40}, Qt::Horizontal); #endif + + // load panels from file + if (!reset) { + QFile panel_config(get_data_path() + "/layout"); + if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { + restoreState(panel_config.readAll(), 0); + panel_config.close(); + } } layout()->update(); @@ -662,6 +657,7 @@ void MainWindow::windowMenu_About_To_Be_Shown() { ui->actionTimeline->setChecked(panel_timeline->isVisible()); ui->actionViewer->setChecked(panel_sequence_viewer->isVisible()); ui->actionFootage_Viewer->setChecked(panel_footage_viewer->isVisible()); + ui->actionGraph_Editor->setChecked(panel_graph_editor->isVisible()); } void MainWindow::viewMenu_About_To_Be_Shown() { @@ -1064,3 +1060,7 @@ void MainWindow::on_actionHand_Tool_triggered() { || panel_sequence_viewer->is_focused()) panel_timeline->ui->toolHandButton->click(); } + +void MainWindow::on_actionGraph_Editor_triggered() { + panel_graph_editor->setVisible(!panel_graph_editor->isVisible()); +} diff --git a/mainwindow.h b/mainwindow.h index a1b380542..76d70263a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -215,6 +215,8 @@ private slots: void on_actionHand_Tool_triggered(); + void on_actionGraph_Editor_triggered(); + private: Ui::MainWindow *ui; void setup_layout(bool reset); diff --git a/mainwindow.ui b/mainwindow.ui index fe24d5e23..1daae80cf 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -111,6 +111,7 @@ + @@ -957,6 +958,14 @@ H + + + true + + + Graph Editor + + diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index ef2f1fdd2..4128b2474 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -5,9 +5,11 @@ #include #include "ui/graphview.h" +#include "ui/keyframenavigator.h" GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { setWindowTitle("Graph Editor"); + resize(720, 480); QWidget* central_widget = new QWidget(); setWidget(central_widget); @@ -15,17 +17,32 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); central_widget->setLayout(layout); + QWidget* tool_widget = new QWidget(); + tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* tools = new QHBoxLayout(); + tool_widget->setLayout(tools); + + KeyframeNavigator* keyframe_nav = new KeyframeNavigator(); + tools->addWidget(keyframe_nav); + tools->addStretch(); + tools->addWidget(new QPushButton("HECK!")); - layout->addLayout(tools); + tools->addStretch(); + + tools->addWidget(new QPushButton("Hand")); + + layout->addWidget(tool_widget); view = new GraphView(); layout->addWidget(view); + QWidget* value_widget = new QWidget(); + value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* values = new QHBoxLayout(); + value_widget->setLayout(values); values->addStretch(); values->addWidget(new QLabel("1920.0")); values->addWidget(new QLabel("1080.0")); values->addStretch(); - layout->addLayout(values); + layout->addWidget(value_widget); } diff --git a/panels/panels.cpp b/panels/panels.cpp index fcc9e0008..be40e402b 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -143,11 +143,17 @@ QDockWidget *get_focused_panel() { void alloc_panels(QWidget* parent) { // TODO maybe replace these with non-pointers later on? panel_sequence_viewer = new Viewer(parent); + panel_sequence_viewer->setObjectName("seq_viewer"); panel_footage_viewer = new Viewer(parent); + panel_footage_viewer->setObjectName("footage_viewer"); panel_project = new Project(parent); + panel_project->setObjectName("proj_root"); panel_effect_controls = new EffectControls(parent); + panel_effect_controls->setObjectName("fx_controls"); panel_timeline = new Timeline(parent); + panel_timeline->setObjectName("timeline"); panel_graph_editor = new GraphEditor(parent); + panel_graph_editor->setObjectName("graph_editor"); } void free_panels() { diff --git a/playback/audio.cpp b/playback/audio.cpp index 8d04cedce..9f80c7c6c 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -99,8 +99,10 @@ void stop_audio() { } void clear_audio_ibuffer() { + if (audio_thread != NULL) audio_thread->lock.lock(); memset(audio_ibuffer, 0, audio_ibuffer_size); audio_ibuffer_read = 0; + if (audio_thread != NULL) audio_thread->lock.unlock(); } int get_buffer_offset_from_frame(double framerate, long frame) { @@ -130,7 +132,7 @@ void AudioSenderThread::run() { // start data loop send_audio_to_output(0, audio_ibuffer_size); - lock.lock(); + lock.lock(); while (true) { cond.wait(&lock); if (close) { @@ -149,7 +151,7 @@ void AudioSenderThread::run() { audio_scrub = false; } - } + } lock.unlock(); } diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 405663889..7b1bad15f 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -9,5 +9,8 @@ void GraphView::paintEvent(QPaintEvent *event) { p.setPen(Qt::white); - p.drawRect(rect()); + QRect border = rect(); + border.setWidth(border.width()-1); + border.setHeight(border.height()-1); + p.drawRect(border); } From e101739214607fbb5e3ffcd5f751f34c4b792e9c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Dec 2018 22:06:38 +1100 Subject: [PATCH 04/32] big progress on graph editor --- olive.pro | 10 +- panels/effectcontrols.cpp | 4 + panels/grapheditor.cpp | 190 ++++++++++++++++++++++++++++++---- panels/grapheditor.h | 20 +++- panels/panels.cpp | 1 + project/effect.cpp | 2 + project/effectfield.cpp | 1 + project/effectfield.h | 1 + project/effectrow.cpp | 24 ++++- project/effectrow.h | 14 ++- project/keyframe.cpp | 2 + project/keyframe.h | 17 ++++ ui/clickablelabel.cpp | 13 +++ ui/clickablelabel.h | 16 +++ ui/graphview.cpp | 207 ++++++++++++++++++++++++++++++++++++-- ui/graphview.h | 30 ++++++ ui/keyframedrawing.cpp | 26 +++++ ui/keyframedrawing.h | 10 ++ ui/keyframenavigator.cpp | 27 +++-- ui/keyframenavigator.h | 4 +- ui/keyframeview.cpp | 53 ++++------ ui/keyframeview.h | 5 +- ui/labelslider.cpp | 10 +- ui/labelslider.h | 2 + ui/timelineheader.h | 2 +- 25 files changed, 607 insertions(+), 84 deletions(-) create mode 100644 project/keyframe.cpp create mode 100644 project/keyframe.h create mode 100644 ui/clickablelabel.cpp create mode 100644 ui/clickablelabel.h create mode 100644 ui/keyframedrawing.cpp create mode 100644 ui/keyframedrawing.h diff --git a/olive.pro b/olive.pro index eab6b3b19..3cfcd8ff8 100644 --- a/olive.pro +++ b/olive.pro @@ -107,7 +107,10 @@ SOURCES += \ project/sourcescommon.cpp \ ui/keyframenavigator.cpp \ panels/grapheditor.cpp \ - ui/graphview.cpp + ui/graphview.cpp \ + ui/keyframedrawing.cpp \ + ui/clickablelabel.cpp \ + project/keyframe.cpp HEADERS += \ mainwindow.h \ @@ -189,7 +192,10 @@ HEADERS += \ project/sourcescommon.h \ ui/keyframenavigator.h \ panels/grapheditor.h \ - ui/graphview.h + ui/graphview.h \ + ui/keyframedrawing.h \ + ui/clickablelabel.h \ + project/keyframe.h FORMS += \ mainwindow.ui \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 4809aa482..aa576ec64 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -16,6 +16,7 @@ #include "panels/project.h" #include "panels/timeline.h" #include "panels/viewer.h" +#include "panels/grapheditor.h" #include "ui/viewerwidget.h" #include "io/clipboard.h" #include "debug.h" @@ -204,6 +205,9 @@ void EffectControls::clear_effects(bool clear_cache) { // clear existing clips deselect_all_effects(NULL); + // clear graph editor + if (panel_graph_editor != NULL) panel_graph_editor->set_row(NULL); + QVBoxLayout* video_layout = static_cast(ui->video_effect_area->layout()); QVBoxLayout* audio_layout = static_cast(ui->audio_effect_area->layout()); QLayoutItem* item; diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 4128b2474..71dd5142f 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -3,46 +3,200 @@ #include #include #include +#include -#include "ui/graphview.h" #include "ui/keyframenavigator.h" +#include "ui/timelineheader.h" +#include "ui/timelinetools.h" +#include "ui/labelslider.h" +#include "ui/graphview.h" +#include "project/effect.h" +#include "project/effectfield.h" +#include "project/effectrow.h" +#include "project/clip.h" +#include "panels.h" +#include "debug.h" -GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent) { +GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { setWindowTitle("Graph Editor"); resize(720, 480); - QWidget* central_widget = new QWidget(); - setWidget(central_widget); - + QWidget* main_widget = new QWidget(); + setWidget(main_widget); QVBoxLayout* layout = new QVBoxLayout(); - central_widget->setLayout(layout); + main_widget->setLayout(layout); QWidget* tool_widget = new QWidget(); tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* tools = new QHBoxLayout(); tool_widget->setLayout(tools); - KeyframeNavigator* keyframe_nav = new KeyframeNavigator(); - tools->addWidget(keyframe_nav); - tools->addStretch(); + QWidget* left_tool_widget = new QWidget(); + QHBoxLayout* left_tool_layout = new QHBoxLayout(); + left_tool_layout->setSpacing(0); + left_tool_layout->setMargin(0); + left_tool_widget->setLayout(left_tool_layout); + tools->addWidget(left_tool_widget); + QWidget* center_tool_widget = new QWidget(); + QHBoxLayout* center_tool_layout = new QHBoxLayout(); + center_tool_layout->setSpacing(0); + center_tool_layout->setMargin(0); + center_tool_widget->setLayout(center_tool_layout); + tools->addWidget(center_tool_widget); + QWidget* right_tool_widget = new QWidget(); + QHBoxLayout* right_tool_layout = new QHBoxLayout(); + right_tool_layout->setSpacing(0); + right_tool_layout->setMargin(0); + right_tool_widget->setLayout(right_tool_layout); + tools->addWidget(right_tool_widget); - tools->addWidget(new QPushButton("HECK!")); - tools->addStretch(); + keyframe_nav = new KeyframeNavigator(); + keyframe_nav->enable_keyframes(true); + keyframe_nav->enable_keyframe_toggle(false); + left_tool_layout->addWidget(keyframe_nav); + left_tool_layout->addStretch(); - tools->addWidget(new QPushButton("Hand")); + QPushButton* linear_button = new QPushButton("Linear"); + linear_button->setCheckable(true); + QPushButton* bezier_button = new QPushButton("Bezier"); + bezier_button->setCheckable(true); + QPushButton* hold_button = new QPushButton("Hold"); + hold_button->setCheckable(true); + + center_tool_layout->addStretch(); + center_tool_layout->addWidget(linear_button); + center_tool_layout->addWidget(bezier_button); + center_tool_layout->addWidget(hold_button); + + /*QPushButton* tool_arrow_button = new QPushButton(); + tool_arrow_button->setCheckable(true); + tool_arrow_button->setIcon(QIcon(":/icons/arrow.png")); + tool_arrow_button->setProperty("tool", TIMELINE_TOOL_POINTER); + tool_buttons.append(tool_arrow_button); + QPushButton* tool_hand_button = new QPushButton(); + tool_hand_button->setCheckable(true); + tool_hand_button->setIcon(QIcon(":/icons/hand.png")); + tool_hand_button->setProperty("tool", TIMELINE_TOOL_HAND); + tool_buttons.append(tool_hand_button); + QPushButton* tool_zoom_button = new QPushButton(); + tool_zoom_button->setCheckable(true); + tool_zoom_button->setIcon(QIcon(":/icons/zoomin.png")); + tool_zoom_button->setProperty("tool", TIMELINE_TOOL_ZOOM); + tool_buttons.append(tool_zoom_button); + + right_tool_layout->addStretch(); + for (int i=0;iaddWidget(tool_buttons.at(i)); + }*/ layout->addWidget(tool_widget); - view = new GraphView(); - layout->addWidget(view); + QWidget* central_widget = new QWidget(); + QVBoxLayout* central_layout = new QVBoxLayout(); + central_widget->setLayout(central_layout); + central_layout->setSpacing(0); + central_layout->setMargin(0); + header = new TimelineHeader(); + header->viewer = panel_sequence_viewer; + central_layout->addWidget(header); + view = new GraphView(); + central_layout->addWidget(view); + + layout->addWidget(central_widget); QWidget* value_widget = new QWidget(); value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QHBoxLayout* values = new QHBoxLayout(); + QHBoxLayout* values = new QHBoxLayout(); value_widget->setLayout(values); values->addStretch(); - values->addWidget(new QLabel("1920.0")); - values->addWidget(new QLabel("1080.0")); + + current_row_desc = new QLabel(); + values->addWidget(current_row_desc); + + QWidget* central_value_widget = new QWidget(); + value_layout = new QHBoxLayout(); + value_layout->setMargin(0); + central_value_widget->setLayout(value_layout); + values->addWidget(central_value_widget); + values->addStretch(); - layout->addWidget(value_widget); + layout->addWidget(value_widget); + + connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); + connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); +} + +void GraphEditor::update_panel() { + if (isVisible()) { + if (row != NULL) { + int slider_index = 0; + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + slider_proxies.at(slider_index)->set_value(row->field(i)->get_current_data().toDouble(), false); + slider_index++; + } + } + } + + header->update(); + view->update(); + } +} + +void GraphEditor::set_row(EffectRow *r) { + for (int i=0;iisKeyframing()) { + for (int i=0;ifieldCount();i++) { + EffectField* field = r->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + LabelSlider* slider = new LabelSlider(); + slider->set_color(get_curve_color(i, r->fieldCount()).name()); + connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value())); + + slider_proxies.append(slider); + value_layout->addWidget(slider); + + slider_proxy_sources.append(static_cast(field->ui_element)); + + found_vals = true; + } + } + } + + if (found_vals) { + row = r; + current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name()); + + connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key())); + connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key())); + connect(keyframe_nav, SIGNAL(goto_next_key()), row, SLOT(goto_next_key())); + } else { + row = NULL; + current_row_desc->setText(0); + } + view->set_row(row); + update_panel(); +} + +void GraphEditor::passthrough_slider_value() { + for (int i=0;iset_value(slider_proxies.at(i)->value(), true); + } + } } diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 5e4a919cf..87f8e0d14 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -4,13 +4,31 @@ #include class GraphView; +class TimelineHeader; +class QPushButton; +class EffectRow; +class QHBoxLayout; +class LabelSlider; +class QLabel; +class KeyframeNavigator; class GraphEditor : public QDockWidget { Q_OBJECT public: GraphEditor(QWidget* parent = 0); + void update_panel(); + void set_row(EffectRow* r); private: - GraphView* view; + GraphView* view; + TimelineHeader* header; + QHBoxLayout* value_layout; + QVector slider_proxies; + QVector slider_proxy_sources; + QLabel* current_row_desc; + EffectRow* row; + KeyframeNavigator* keyframe_nav; +private slots: + void passthrough_slider_value(); }; #endif // GRAPHEDITOR_H diff --git a/panels/panels.cpp b/panels/panels.cpp index be40e402b..35bed658a 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -107,6 +107,7 @@ void update_ui(bool modified) { panel_effect_controls->update_keyframes(); panel_timeline->repaint_timeline(); panel_sequence_viewer->update_viewer(); + panel_graph_editor->update_panel(); } QDockWidget *get_focused_panel() { diff --git a/project/effect.cpp b/project/effect.cpp index 153987319..8c049801e 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -10,6 +10,7 @@ #include "project/clip.h" #include "panels/timeline.h" #include "panels/effectcontrols.h" +#include "panels/grapheditor.h" #include "ui/checkboxex.h" #include "debug.h" #include "io/path.h" @@ -489,6 +490,7 @@ void Effect::refresh() {} void Effect::field_changed() { panel_sequence_viewer->viewer_widget->update(); + panel_graph_editor->update_panel(); } void Effect::show_context_menu(const QPoint& pos) { diff --git a/project/effectfield.cpp b/project/effectfield.cpp index 2caaf2991..f465e5e27 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -28,6 +28,7 @@ EffectField::EffectField(EffectRow *parent, int t, const QString &i) : LabelSlider* ls = new LabelSlider(); ui_element = ls; connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); + connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked())); } break; case EFFECT_FIELD_COLOR: diff --git a/project/effectfield.h b/project/effectfield.h index f2b99002e..485b43287 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -69,6 +69,7 @@ private slots: signals: void changed(); void toggled(bool); + void clicked(); }; #endif // EFFECTFIELD_H diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 1cfd81cb9..28d4cfaeb 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -1,6 +1,5 @@ #include "effectrow.h" -#include #include #include #include @@ -11,9 +10,11 @@ #include "panels/panels.h" #include "panels/effectcontrols.h" #include "panels/viewer.h" +#include "panels/grapheditor.h" #include "effect.h" #include "ui/viewerwidget.h" #include "ui/keyframenavigator.h" +#include "ui/clickablelabel.h" EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row) : parent_effect(parent), @@ -24,15 +25,19 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt ui_row(row), just_made_unsafe_keyframe(false) { - label = new QLabel(name); + label = new ClickableLabel(name); + ui->addWidget(label, row, 0); if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); + keyframe_nav = new KeyframeNavigator(); connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); - connect(keyframe_nav, SIGNAL(set_keyframe_enabled(bool)), this, SLOT(set_keyframe_enabled(bool))); + connect(keyframe_nav, SIGNAL(keyframe_enabled_changed(bool)), this, SLOT(set_keyframe_enabled(bool))); + connect(keyframe_nav, SIGNAL(clicked()), this, SLOT(focus_row())); ui->addWidget(keyframe_nav, row, 6); } } @@ -114,11 +119,16 @@ void EffectRow::goto_next_key() { key = qMin(comp, key); } } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + if (key != LONG_MAX) panel_sequence_viewer->seek(key); +} + +void EffectRow::focus_row() { + panel_graph_editor->set_row(this); } EffectField* EffectRow::add_field(int type, const QString& id, int colspan) { EffectField* field = new EffectField(this, type, id); + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row())); fields.append(field); QWidget* element = field->get_ui_element(); ui->addWidget(element, ui_row, fields.size(), 1, colspan); @@ -162,7 +172,11 @@ void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) { delete_keyframe(kd, i); break; } - } + } +} + +const QString &EffectRow::get_name() { + return name; } void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) { diff --git a/project/effectrow.h b/project/effectrow.h index 943ba709f..a18cd56af 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -13,6 +13,7 @@ class QPushButton; class ComboAction; class QHBoxLayout; class KeyframeNavigator; +class ClickableLabel; class EffectRow : public QObject { Q_OBJECT @@ -25,20 +26,23 @@ public: void set_keyframe_now(ComboAction *ca); void delete_keyframe(KeyframeDelete *kd, int index); void delete_keyframe_at_time(KeyframeDelete* kd, long time); - QLabel* label; + ClickableLabel* label; Effect* parent_effect; bool savable; + const QString& get_name(); bool isKeyframing(); void setKeyframing(bool); QVector keyframe_times; QVector keyframe_types; +public slots: + void goto_previous_key(); + void toggle_key(); + void goto_next_key(); + void focus_row(); private slots: - void set_keyframe_enabled(bool); - void goto_previous_key(); - void toggle_key(); - void goto_next_key(); + void set_keyframe_enabled(bool); private: bool keyframing; QGridLayout* ui; diff --git a/project/keyframe.cpp b/project/keyframe.cpp new file mode 100644 index 000000000..8068d5129 --- /dev/null +++ b/project/keyframe.cpp @@ -0,0 +1,2 @@ +#include "keyframe.h" + diff --git a/project/keyframe.h b/project/keyframe.h new file mode 100644 index 000000000..7cad20bfc --- /dev/null +++ b/project/keyframe.h @@ -0,0 +1,17 @@ +#ifndef KEYFRAME_H +#define KEYFRAME_H + + + +#include +#include + +class KeyframeData +{ +public: + QVariant data; + QPoint handle_pre; + QPoint handle_post; +}; + +#endif // KEYFRAME_H diff --git a/ui/clickablelabel.cpp b/ui/clickablelabel.cpp new file mode 100644 index 000000000..b5afb2327 --- /dev/null +++ b/ui/clickablelabel.cpp @@ -0,0 +1,13 @@ +#include "clickablelabel.h" + +ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) : + QLabel(parent, f) +{} + +ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : + QLabel(text, parent, f) +{} + +void ClickableLabel::mousePressEvent(QMouseEvent *) { + emit clicked(); +} diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h new file mode 100644 index 000000000..785c4c7c6 --- /dev/null +++ b/ui/clickablelabel.h @@ -0,0 +1,16 @@ +#ifndef CLICKABLELABEL_H +#define CLICKABLELABEL_H + +#include + +class ClickableLabel : public QLabel { + Q_OBJECT +public: + ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); + ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0); + void mousePressEvent(QMouseEvent *ev); +signals: + void clicked(); +}; + +#endif // CLICKABLELABEL_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 7b1bad15f..194139c7d 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -1,16 +1,209 @@ #include "graphview.h" #include +#include -GraphView::GraphView(QWidget* parent) {} +#include "panels/panels.h" +#include "panels/timeline.h" +#include "panels/viewer.h" +#include "project/sequence.h" +#include "project/effectrow.h" +#include "project/effectfield.h" +#include "ui/keyframedrawing.h" + +#include "debug.h" + +#define GRAPH_ZOOM_SPEED 0.05 +#define GRAPH_SIZE 100 + +QColor get_curve_color(int index, int length) { + QColor c; + int hue = (double(index)/double(length))*255; + c.setHsv(hue, 255, 255); + return c; +} + +GraphView::GraphView(QWidget* parent) : + QWidget(parent), + x_scroll(0), + y_scroll(0), + mousedown(false), + zoom(1.0), + row(NULL) +{ + setMouseTracking(true); +} void GraphView::paintEvent(QPaintEvent *event) { - QPainter p(this); + QPainter p(this); - p.setPen(Qt::white); + if (panel_sequence_viewer->seq != NULL) { + // draw lines + //int graph_size = GRAPH_SIZE*zoom; + bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); - QRect border = rect(); - border.setWidth(border.width()-1); - border.setHeight(border.height()-1); - p.drawRect(border); + p.setPen(Qt::gray); + int i = 0; + while (true) { + int line_x = (i*GRAPH_SIZE*zoom) - x_scroll; + if (line_x >= width()) { + break; + } + if (line_x >= 0) { + if (line_x > 0) p.drawLine(line_x, 0, line_x, height()); + if (draw_text) p.drawText(QRect(line_x, height()-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); + } + i++; + } + i = 0; + while (true) { + int line_y = height() - (i*GRAPH_SIZE*zoom) + y_scroll; + if (line_y <= 0) { + break; + } + if (line_y <= height()) { + if (line_y < height()) p.drawLine(0, line_y, width(), line_y); + if (draw_text) p.drawText(QRect(0, line_y-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); + } + i++; + } + + // draw keyframes + if (row != NULL) { + QPen line_pen; + line_pen.setWidth(2); + + // sort keyframes by time + QVector sorted_keys; + for (int i=0;ikeyframe_times.size();i++) { + bool inserted = false; + for (int j=0;jkeyframe_times.at(sorted_keys.at(j)) > row->keyframe_times.at(i)) { + sorted_keys.insert(j, i); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(i); + } + } + + int last_key_x, last_key_y; + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.at(key_index)); + int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + + line_pen.setColor(get_curve_color(i, row->fieldCount())); + p.setPen(line_pen); + if (key_index == 0) { + p.drawLine(0, key_y, key_x, key_y); + } else { + p.drawLine(last_key_x, last_key_y, key_x, key_y); + } + last_key_x = key_x; + last_key_y = key_y; + } + for (int j=0;jkeyframe_times.at(key_index)); + int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + + draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, false); + } + p.setBrush(Qt::NoBrush); + } + } + } + + // draw playhead + p.setPen(Qt::red); + int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead); + p.drawLine(playhead_x, 0, playhead_x, height()); + } + + p.setPen(Qt::white); + + QRect border = rect(); + border.setWidth(border.width()-1); + border.setHeight(border.height()-1); + p.drawRect(border); +} + +void GraphView::mousePressEvent(QMouseEvent *event) { + mousedown = true; + start_x = event->pos().x(); + start_y = event->pos().y(); +} + +void GraphView::mouseMoveEvent(QMouseEvent *event) { + if (mousedown) { + if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + set_scroll_x(x_scroll + start_x - event->pos().x()); + set_scroll_y(y_scroll + event->pos().y() - start_y); + start_x = event->pos().x(); + start_y = event->pos().y(); + update(); + } + } +} + +void GraphView::mouseReleaseEvent(QMouseEvent *event) { + mousedown = false; +} + +void GraphView::wheelEvent(QWheelEvent *event) { + bool redraw = false; + bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom + bool alt = (event->modifiers() & Qt::AltModifier); // horiz scroll instead of vert scroll + + if (shift) { + // scroll + set_scroll_x(x_scroll + event->angleDelta().x()/10); + set_scroll_y(y_scroll + event->angleDelta().y()/10); + redraw = true; + } else { + // set zoom + if (event->angleDelta().y() != 0) { + double zoom_diff = (GRAPH_ZOOM_SPEED*zoom); + if (event->angleDelta().y() < 0) zoom_diff = -zoom_diff; + zoom += zoom_diff; + emit zoom_changed(zoom); + redraw = true; + } + } + + if (redraw) { + update(); + } +} + +void GraphView::set_row(EffectRow *r) { + row = r; + update(); +} + +void GraphView::set_scroll_x(int s) { + x_scroll = s;//qMax(0, s); + dout << x_scroll; + emit x_scroll_changed(x_scroll); +} + +void GraphView::set_scroll_y(int s) { + y_scroll = s;//qMax(0, s); + emit y_scroll_changed(y_scroll); +} + +int GraphView::get_screen_x(double d) { + return (d*zoom) - x_scroll; +} + +int GraphView::get_screen_y(double d) { + return height() + y_scroll - d*zoom; } diff --git a/ui/graphview.h b/ui/graphview.h index b28d80bf9..ca1404c94 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -3,11 +3,41 @@ #include +class EffectRow; + +QColor get_curve_color(int index, int length); + class GraphView : public QWidget { + Q_OBJECT public: GraphView(QWidget* parent = 0); void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent *event); + + void set_row(EffectRow* r); +signals: + void x_scroll_changed(int); + void y_scroll_changed(int); + void zoom_changed(double); +private: + int x_scroll; + int y_scroll; + bool mousedown; + int start_x; + int start_y; + double zoom; + + void set_scroll_x(int s); + void set_scroll_y(int s); + + int get_screen_x(double); + int get_screen_y(double); + + EffectRow* row; }; #endif // GRAPHVIEW_H diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp new file mode 100644 index 000000000..693a4e299 --- /dev/null +++ b/ui/keyframedrawing.cpp @@ -0,0 +1,26 @@ +#include "keyframedrawing.h" + +#include "project/effect.h" + +#define KEYFRAME_POINT_COUNT 4 + +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { + int color = (darker) ? 100 : 160; + p.setPen(QColor(0, 0, 0)); + p.setBrush(QColor(color, color, color)); + + switch (type) { + case KEYFRAME_TYPE_LINEAR: + { + QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)}; + p.drawPolygon(points, KEYFRAME_POINT_COUNT); + } + break; + case KEYFRAME_TYPE_BEZIER: + p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); + break; + case KEYFRAME_TYPE_HOLD: + p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); + break; + } +} diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h new file mode 100644 index 000000000..c09ae2626 --- /dev/null +++ b/ui/keyframedrawing.h @@ -0,0 +1,10 @@ +#ifndef KEYFRAMEDRAWING_H +#define KEYFRAMEDRAWING_H + +#include + +#define KEYFRAME_SIZE 6 + +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker); + +#endif // KEYFRAMEDRAWING_H diff --git a/ui/keyframenavigator.cpp b/ui/keyframenavigator.cpp index a7022fe45..f5495a135 100644 --- a/ui/keyframenavigator.cpp +++ b/ui/keyframenavigator.cpp @@ -2,15 +2,22 @@ #include #include +#include +#include KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { - QSize icon_size(12, 12); - QSize button_size(20, 20); + int icon_size_val = 8*QApplication::desktop()->devicePixelRatio(); + int clock_size_val = 12*QApplication::desktop()->devicePixelRatio(); + int button_size_val = 20*QApplication::desktop()->devicePixelRatio(); + + QSize icon_size(icon_size_val, icon_size_val); + QSize clock_size(clock_size_val, clock_size_val); + QSize button_size(button_size_val, button_size_val); key_controls = new QHBoxLayout(); key_controls->setSpacing(0); key_controls->setMargin(0); - key_controls->addStretch(); + //key_controls->addStretch(); setLayout(key_controls); @@ -21,6 +28,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { left_key_nav->setVisible(false); key_controls->addWidget(left_key_nav); connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key())); + connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); key_addremove = new QPushButton(); key_addremove->setIcon(QIcon(":/icons/diamond.png")); @@ -29,6 +37,7 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { key_addremove->setVisible(false); key_controls->addWidget(key_addremove); connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key())); + connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); right_key_nav = new QPushButton(); right_key_nav->setIcon(QIcon(":/icons/tri-right.png")); @@ -37,20 +46,26 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) { right_key_nav->setVisible(false); key_controls->addWidget(right_key_nav); connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key())); + connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), ""); keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); keyframe_enable->setMaximumSize(button_size); - keyframe_enable->setIconSize(icon_size); + keyframe_enable->setIconSize(clock_size); keyframe_enable->setCheckable(true); keyframe_enable->setToolTip("Enable Keyframes"); - connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(set_keyframe_enabled(bool))); + connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool))); connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool))); + connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked())); key_controls->addWidget(keyframe_enable); } void KeyframeNavigator::enable_keyframes(bool b) { - keyframe_enable->setChecked(b); + keyframe_enable->setChecked(b); +} + +void KeyframeNavigator::enable_keyframe_toggle(bool b) { + keyframe_enable->setVisible(b); } void KeyframeNavigator::keyframe_ui_enabled(bool enabled) { diff --git a/ui/keyframenavigator.h b/ui/keyframenavigator.h index d41a7ac0d..6b6eaee85 100644 --- a/ui/keyframenavigator.h +++ b/ui/keyframenavigator.h @@ -12,11 +12,13 @@ class KeyframeNavigator : public QWidget public: KeyframeNavigator(QWidget* parent = 0); void enable_keyframes(bool); + void enable_keyframe_toggle(bool); signals: void goto_previous_key(); void toggle_key(); void goto_next_key(); - void set_keyframe_enabled(bool); + void keyframe_enabled_changed(bool); + void clicked(); private slots: void keyframe_ui_enabled(bool); private: diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 431d9ab03..0da66dada 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -12,16 +12,14 @@ #include "ui/viewerwidget.h" #include "project/sequence.h" #include "panels/grapheditor.h" +#include "ui/keyframedrawing.h" #include "ui_effectcontrols.h" +#include "ui/clickablelabel.h" -#include #include #include #include -#define KEYFRAME_SIZE 6 -#define KEYFRAME_POINT_COUNT 4 - long KeyframeView::adjust_row_keyframe(EffectRow* row, long time) { return time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); } @@ -36,7 +34,7 @@ KeyframeView::KeyframeView(QWidget *parent) : select_rect(false), x_scroll(0), y_scroll(0), - scroll_drag(false) + scroll_drag(false) { setFocusPolicy(Qt::ClickFocus); setMouseTracking(true); @@ -73,7 +71,7 @@ void KeyframeView::menu_set_key_type(QAction* a) { ca->append(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); } undo_stack.push(ca); - update(); + update_keys(); } } @@ -101,7 +99,7 @@ void KeyframeView::paintEvent(QPaintEvent*) { for (int j=0;jrow_count();j++) { EffectRow* row = e->row(j); - QLabel* label = row->label; + ClickableLabel* label = row->label; QWidget* contents = e->container->contents; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; @@ -153,6 +151,11 @@ bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) { return false; } +void KeyframeView::update_keys() { +// panel_graph_editor->update_panel(); + update(); +} + void KeyframeView::delete_selected_keyframes() { KeyframeDelete* kd = new KeyframeDelete(); bool del = false; @@ -165,7 +168,7 @@ void KeyframeView::delete_selected_keyframes() { selected_keyframes.clear(); selected_rows.clear(); - update(); + update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { delete kd; @@ -174,33 +177,12 @@ void KeyframeView::delete_selected_keyframes() { void KeyframeView::set_x_scroll(int s) { x_scroll = s; - update(); + update_keys(); } void KeyframeView::set_y_scroll(int s) { y_scroll = s; - update(); -} - -void KeyframeView::draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { - int color = (darker) ? 100 : 160; - p.setPen(QColor(0, 0, 0)); - p.setBrush(QColor(color, color, color)); - - switch (type) { - case KEYFRAME_TYPE_LINEAR: - { - QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)}; - p.drawPolygon(points, KEYFRAME_POINT_COUNT); - } - break; - case KEYFRAME_TYPE_BEZIER: - p.drawEllipse(QPoint(x, y), KEYFRAME_SIZE, KEYFRAME_SIZE); - break; - case KEYFRAME_TYPE_HOLD: - p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); - break; - } + update_keys(); } void KeyframeView::resize_move(double d) { @@ -227,6 +209,9 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { for (int i=0;i rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && mouse_y < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) { EffectRow* row = rows.at(i); + + row->focus_row(); + for (int j=0;jkeyframe_times.size();j++) { long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { @@ -259,7 +244,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { keys_selected = true; } - update(); + update_keys(); if (event->button() == Qt::LeftButton) { mousedown = true; @@ -345,7 +330,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { select_rect = true; } - update(); + update_keys(); } } @@ -363,5 +348,5 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent*) { mousedown = false; scroll_drag = false; panel_timeline->snapped = false; - update(); + update_ui(false); } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 6c3a72f31..c81186683 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -23,7 +23,7 @@ public: public slots: void set_x_scroll(int); void set_y_scroll(int); - void resize_move(double d); + void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); QVector selected_rows; @@ -35,7 +35,6 @@ private: void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); - void draw_keyframe(QPainter& p, int type, int x, int y, bool darker); bool mousedown; bool dragging; bool keys_selected; @@ -53,6 +52,8 @@ private: int x_scroll; int y_scroll; + + void update_keys(); private slots: void show_context_menu(const QPoint& pos); void menu_set_key_type(QAction*); diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 357a891db..7395ad752 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -16,7 +16,7 @@ LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { drag_proc = false; min_enabled = false; max_enabled = false; - setStyleSheet("QLabel{color:#ffc000;text-decoration:underline;}QLabel:disabled{color:#808080;}"); + set_color(); setCursor(Qt::SizeHorCursor); internal_value = -1; set = false; @@ -75,7 +75,12 @@ double LabelSlider::getPreviousValue() { } void LabelSlider::set_previous_value() { - previous_value = internal_value; + previous_value = internal_value; +} + +void LabelSlider::set_color(QString c) { + if (c.isEmpty()) c = "#ffc000"; + setStyleSheet("QLabel{color:" + c + ";text-decoration:underline;}QLabel:disabled{color:#808080;}"); } double LabelSlider::value() { @@ -115,6 +120,7 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) { drag_start_x = cursor().pos().x(); drag_start_y = cursor().pos().y(); } + emit clicked(); } void LabelSlider::mouseMoveEvent(QMouseEvent* event) { diff --git a/ui/labelslider.h b/ui/labelslider.h index 3476128ff..f671d18df 100644 --- a/ui/labelslider.h +++ b/ui/labelslider.h @@ -25,6 +25,7 @@ public: QString valueToString(double v); double getPreviousValue(); void set_previous_value(); + void set_color(QString c = 0); int decimal_places; protected: void mousePressEvent(QMouseEvent *ev); @@ -53,6 +54,7 @@ private: double frame_rate; signals: void valueChanged(); + void clicked(); }; #endif // LABELSLIDER_H diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 884a44adf..2950cb0fc 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -21,12 +21,12 @@ public: bool snapping; void show_text(bool enable); - void update_zoom(double z); double get_zoom(); void delete_markers(); void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset); public slots: + void update_zoom(double z); void set_scroll(int); void set_visible_in(long i); void show_context_menu(const QPoint &pos); From e46ba45bd65eb878188b271e2961246a066624b6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Dec 2018 22:45:53 +1100 Subject: [PATCH 05/32] fixed crash when deleting media of a selected clip --- panels/project.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panels/project.cpp b/panels/project.cpp index ce3264a6c..2c1a50dba 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -476,7 +476,7 @@ void Project::delete_selected_media() { remove = false; } } - if (confirm_delete) { + if (confirm_delete) { ca->append(new DeleteClipAction(s, k)); } } @@ -491,6 +491,9 @@ void Project::delete_selected_media() { // remove if (remove) { + panel_effect_controls->clear_effects(true); + sequence->selections.clear(); + // remove media and parents for (int m=0;m Date: Sat, 29 Dec 2018 19:56:20 +1100 Subject: [PATCH 06/32] rounded hue value --- ui/graphview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 194139c7d..4a62ed8fd 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -18,7 +18,7 @@ QColor get_curve_color(int index, int length) { QColor c; - int hue = (double(index)/double(length))*255; + int hue = qRound((double(index)/double(length))*255); c.setHsv(hue, 255, 255); return c; } From 7a4e655d2c1901f07e141a66b80128004681c9a3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 31 Dec 2018 22:18:42 +1100 Subject: [PATCH 07/32] keyframes are movable in graph view --- project/clip.cpp | 19 ++++++++++----- project/undo.cpp | 12 ++++++--- project/undo.h | 9 ++++--- ui/graphview.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++-- ui/graphview.h | 11 +++++++-- ui/keyframeview.cpp | 40 +++++++++++++++++++++--------- ui/keyframeview.h | 14 +++++------ 7 files changed, 128 insertions(+), 36 deletions(-) diff --git a/project/clip.cpp b/project/clip.cpp index 213a53858..88258441f 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -309,18 +309,25 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t track); } + QVector key_rows; + QVector key_indices; + QVector key_old; + QVector key_new; + for (int i=0;irow_count();j++) { EffectRow* r = e->row(j); for (int k=0;kkeyframe_times.size();k++) { - long new_pos = r->keyframe_times.at(k) * multiplier; - KeyframeMove* km = new KeyframeMove(); - km->movement = new_pos - r->keyframe_times.at(k); - km->rows.append(r); - km->keyframes.append(k); - ca->append(km); + key_rows.append(r); + key_indices.at(k); + key_old.append(r->keyframe_times.at(k)); + key_new.append(r->keyframe_times.at(k) * multiplier); } } } + + if (key_rows.size() > 0) { + ca->append(new KeyframeMove(key_rows, key_indices, key_old, key_new)); + } } diff --git a/project/undo.cpp b/project/undo.cpp index e67c38941..fc9f77d92 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,18 +737,24 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeMove::KeyframeMove() : old_project_changed(mainWindow->isWindowModified()) {} +KeyframeMove::KeyframeMove(const QVector& irows, const QVector& ikeyframes, const QVector& iold_values, const QVector& inew_values) : + rows(irows), + keyframes(ikeyframes), + old_values(iold_values), + new_values(inew_values), + old_project_changed(mainWindow->isWindowModified()) +{} void KeyframeMove::undo() { for (int i=0;ikeyframe_times[keyframes.at(i)] -= movement; + rows.at(i)->keyframe_times[keyframes.at(i)] = old_values.at(i); } mainWindow->setWindowModified(old_project_changed); } void KeyframeMove::redo() { for (int i=0;ikeyframe_times[keyframes.at(i)] += movement; + rows.at(i)->keyframe_times[keyframes.at(i)] = new_values.at(i); } mainWindow->setWindowModified(true); } diff --git a/project/undo.h b/project/undo.h index a1f79212f..f7aae7626 100644 --- a/project/undo.h +++ b/project/undo.h @@ -330,13 +330,14 @@ private: class KeyframeMove : public QUndoCommand { public: - KeyframeMove(); - QVector rows; - QVector keyframes; - long movement; + KeyframeMove(const QVector& rows, const QVector& keyframes, const QVector& old_values, const QVector& new_values); void undo(); void redo(); private: + QVector rows; + QVector keyframes; + QVector old_values; + QVector new_values; bool old_project_changed; }; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4a62ed8fd..4c26a081e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -10,6 +10,7 @@ #include "project/effectrow.h" #include "project/effectfield.h" #include "ui/keyframedrawing.h" +#include "project/undo.h" #include "debug.h" @@ -29,7 +30,8 @@ GraphView::GraphView(QWidget* parent) : y_scroll(0), mousedown(false), zoom(1.0), - row(NULL) + row(NULL), + moved_keys(false) { setMouseTracking(true); } @@ -115,7 +117,7 @@ void GraphView::paintEvent(QPaintEvent *event) { int key_x = get_screen_x(row->keyframe_times.at(key_index)); int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); - draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, false); + draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); } p.setBrush(Qt::NoBrush); } @@ -140,6 +142,42 @@ void GraphView::mousePressEvent(QMouseEvent *event) { mousedown = true; start_x = event->pos().x(); start_y = event->pos().y(); + + // selecting + int sel_key = -1; + int sel_key_field = -1; + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.size();j++) { + int key_x = get_screen_x(row->keyframe_times.at(j)); + int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { + sel_key = j; + sel_key_field = i; + break; + } + } + } + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } + + selected_keys_old_vals.clear(); + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + } + + update(); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -150,11 +188,28 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_x = event->pos().x(); start_y = event->pos().y(); update(); + } else { + for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); + } + moved_keys = true; + update_ui(false); } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { + if (moved_keys) { + QVector rows; + QVector new_vals; + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + } + + undo_stack.push(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + } + moved_keys = false; mousedown = false; } diff --git a/ui/graphview.h b/ui/graphview.h index ca1404c94..5706b48a6 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -2,6 +2,7 @@ #define GRAPHVIEW_H #include +#include class EffectRow; @@ -10,9 +11,9 @@ QColor get_curve_color(int index, int length); class GraphView : public QWidget { Q_OBJECT public: - GraphView(QWidget* parent = 0); + GraphView(QWidget* parent = 0); - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); @@ -37,6 +38,12 @@ private: int get_screen_x(double); int get_screen_y(double); + QVector selected_keys; + QVector selected_keys_fields; + QVector selected_keys_old_vals; + + bool moved_keys; + EffectRow* row; }; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index e12feb4f7..b37f71b17 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -106,7 +106,6 @@ void KeyframeView::paintEvent(QPaintEvent*) { for (int k=0;kkeyframe_times.size();k++) { bool keyframe_selected = keyframeIsSelected(row, k); long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k)); - if (dragging && keyframe_selected) keyframe_frame += frame_diff; draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); } @@ -198,6 +197,8 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { return; } + old_key_vals.clear(); + int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; @@ -241,6 +242,10 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } if (selected_rows.size() > 0) { + for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + } + keys_selected = true; } @@ -266,14 +271,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { int mouse_x = event->x() + x_scroll; if (keys_selected) { // move keyframes - frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; + long frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; // snapping to playhead + panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_effect->parent_clip; - long key_time = row->keyframe_times.at(selected_keyframes.at(i)) + frame_diff - c->clip_in + c->timeline_in; + long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { frame_diff += (key_eval - key_time); @@ -283,9 +289,9 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i)); + long eval_key = old_key_vals.at(i); for (int j=0;jkeyframe_times.size();j++) { while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { @@ -299,9 +305,17 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } } + // apply frame_diffs + for (int i=0;ikeyframe_times[selected_keyframes.at(i)] = old_key_vals.at(i) + frame_diff; + } + last_frame_diff = frame_diff; dragging = true; + + update_ui(false); } else { // do a rect select rect_select_w = event->x() - rect_select_x; @@ -329,18 +343,20 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } select_rect = true; + + update_keys(); } - update_keys(); } } void KeyframeView::mouseReleaseEvent(QMouseEvent*) { - if (dragging && frame_diff != 0) { - KeyframeMove* ka = new KeyframeMove(); - ka->movement = frame_diff; - ka->keyframes = selected_keyframes; - ka->rows = selected_rows; - undo_stack.push(ka); + if (dragging) { + QVector new_key_vals; + for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + } + + undo_stack.push(new KeyframeMove(selected_rows, selected_keyframes, old_key_vals, new_key_vals)); } select_rect = false; diff --git a/ui/keyframeview.h b/ui/keyframeview.h index c81186683..990bdef9e 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -27,19 +27,19 @@ public slots: private: long adjust_row_keyframe(EffectRow* row, long time); QVector selected_rows; - QVector selected_keyframes; + QVector selected_keyframes; QVector rowY; - long frame_diff; QVector rows; + QVector old_key_vals; void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); - bool mousedown; + bool mousedown; bool dragging; bool keys_selected; - bool select_rect; - bool scroll_drag; + bool select_rect; + bool scroll_drag; bool keyframeIsSelected(EffectRow* row, int keyframe); @@ -55,8 +55,8 @@ private: void update_keys(); private slots: - void show_context_menu(const QPoint& pos); - void menu_set_key_type(QAction*); + void show_context_menu(const QPoint& pos); + void menu_set_key_type(QAction*); }; #endif // KEYFRAMEVIEW_H From 1e1ed96bff9752d273d2f8352423316e081b487c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 31 Dec 2018 22:44:34 +1100 Subject: [PATCH 08/32] keyframe values can now be set in graph editor --- project/undo.cpp | 31 +++++++++++++++++++++++++++++++ project/undo.h | 23 +++++++++++++++++++++++ ui/graphview.cpp | 23 +++++++++++++++++++++-- ui/graphview.h | 1 + 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/project/undo.cpp b/project/undo.cpp index fc9f77d92..963b02c32 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1301,3 +1301,34 @@ void RippleAction::redo() { } ca->redo(); } + +SetDouble::SetDouble(double* pointer, double new_value) : + p(pointer), + oldval(*pointer), + newval(new_value), + old_project_changed(mainWindow->isWindowModified()) +{} + +void SetDouble::undo() { + *p = oldval; + mainWindow->setWindowModified(old_project_changed); +} + +void SetDouble::redo() { + *p = newval; + mainWindow->setWindowModified(true); +} + +SetQVariant::SetQVariant(QVariant *itarget, const QVariant &iold, const QVariant &inew) : + target(itarget), + old_val(iold), + new_val(inew) +{} + +void SetQVariant::undo() { + *target = old_val; +} + +void SetQVariant::redo() { + *target = new_val; +} diff --git a/project/undo.h b/project/undo.h index f7aae7626..e74d09a25 100644 --- a/project/undo.h +++ b/project/undo.h @@ -524,6 +524,18 @@ private: bool old_project_changed; }; +class SetDouble : public QUndoCommand { +public: + SetDouble(double* pointer, double new_value); + void undo(); + void redo(); +private: + double* p; + double oldval; + double newval; + bool old_project_changed; +}; + class SetString : public QUndoCommand { public: SetString(QString* pointer, QString new_value); @@ -606,4 +618,15 @@ public: void redo(); }; +class SetQVariant : public QUndoCommand { +public: + SetQVariant(QVariant* itarget, const QVariant& iold, const QVariant& inew); + void undo(); + void redo(); +private: + QVariant* target; + QVariant old_val; + QVariant new_val; +}; + #endif // UNDO_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4c26a081e..f76168677 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -173,8 +173,13 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + + for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)).toDouble()); + } } update(); @@ -191,6 +196,10 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } else { for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); + + for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)] = selected_keys_old_doubles.at((i*selected_keys_fields.size())+j) + (double(start_y - event->pos().y())/zoom); + } } moved_keys = true; update_ui(false); @@ -199,15 +208,25 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } void GraphView::mouseReleaseEvent(QMouseEvent *event) { - if (moved_keys) { + if (moved_keys && selected_keys.size() > 0) { + ComboAction* ca = new ComboAction(); QVector rows; QVector new_vals; + for (int i=0;ikeyframe_times.at(selected_keys.at(i))); + + for (int j=0;jappend(new SetQVariant(&row->field(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)], + selected_keys_old_doubles.at((i*selected_keys_fields.size())+j), + row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); + } + //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); } - undo_stack.push(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); + undo_stack.push(ca); } moved_keys = false; mousedown = false; diff --git a/ui/graphview.h b/ui/graphview.h index 5706b48a6..363303632 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -41,6 +41,7 @@ private: QVector selected_keys; QVector selected_keys_fields; QVector selected_keys_old_vals; + QVector selected_keys_old_doubles; bool moved_keys; From ef3e0754be392a70ac373fbe4505cbf3c39598a1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 08:36:47 +1100 Subject: [PATCH 09/32] fixed graph view bug --- ui/graphview.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f76168677..b87302824 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -150,19 +150,21 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys.clear(); selected_keys_fields.clear(); } - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframe_times.size();j++) { - int key_x = get_screen_x(row->keyframe_times.at(j)); - int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); - if (event->pos().x() > key_x-KEYFRAME_SIZE - && event->pos().x() < key_x+KEYFRAME_SIZE - && event->pos().y() > key_y-KEYFRAME_SIZE - && event->pos().y() < key_y+KEYFRAME_SIZE) { - sel_key = j; - sel_key_field = i; - break; + if (row != NULL) { + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE) { + for (int j=0;jkeyframe_times.size();j++) { + int key_x = get_screen_x(row->keyframe_times.at(j)); + int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { + sel_key = j; + sel_key_field = i; + break; + } } } } From 8d36921b3351a949ff2ad236324b2acd77d856df Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 09:04:33 +1100 Subject: [PATCH 10/32] panel tweaks --- mainwindow.cpp | 57 +++++++++++++++++++-------------------- panels/effectcontrols.cpp | 4 +-- panels/grapheditor.cpp | 28 ++++++++++--------- panels/project.cpp | 2 +- panels/timeline.cpp | 2 ++ panels/viewer.cpp | 4 +-- 6 files changed, 50 insertions(+), 47 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index b81ab17c4..1f4094bce 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -219,36 +219,7 @@ MainWindow::MainWindow(QWidget *parent) : } MainWindow::~MainWindow() { - panel_effect_controls->clear_effects(true); - panel_sequence_viewer->viewer_widget->delete_function(); - panel_footage_viewer->viewer_widget->delete_function(); - - set_sequence(NULL); - - QString data_dir = get_data_path(); - if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { - if (QFile::exists(autorecovery_filename)) { - QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); - } - } - if (!config_dir.isEmpty()) { - // save settings - config.save(config_dir); - - // save panel layout - QFile panel_config(data_dir + "/layout"); - if (panel_config.open(QFile::WriteOnly)) { - panel_config.write(saveState(0)); - panel_config.close(); - } else { - dout << "[ERROR] Failed to save layout"; - } - } - - stop_audio(); - free_panels(); - close_debug(); } @@ -790,6 +761,34 @@ void MainWindow::updateTitle(const QString& url) { void MainWindow::closeEvent(QCloseEvent *e) { if (can_close_project()) { + panel_effect_controls->clear_effects(true); + panel_sequence_viewer->viewer_widget->delete_function(); + panel_footage_viewer->viewer_widget->delete_function(); + + set_sequence(NULL); + + QString data_dir = get_data_path(); + if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { + if (QFile::exists(autorecovery_filename)) { + QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); + } + } + if (!config_dir.isEmpty()) { + // save settings + config.save(config_dir); + + // save panel layout + QFile panel_config(data_dir + "/layout"); + if (panel_config.open(QFile::WriteOnly)) { + panel_config.write(saveState(0)); + panel_config.close(); + } else { + dout << "[ERROR] Failed to save layout"; + } + } + + stop_audio(); + e->accept(); } else { e->ignore(); diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index a163ed549..de1726900 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -37,6 +37,8 @@ EffectControls::EffectControls(QWidget *parent) : panel_name("Effects: "), mode(TA_NO_TRANSITION) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); init_effects(); @@ -253,8 +255,6 @@ void EffectControls::open_effect(QVBoxLayout* layout, Effect* e) { } void EffectControls::setup_ui() { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QWidget* contents = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(contents); diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 71dd5142f..bc8bc48dc 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -18,18 +18,20 @@ #include "debug.h" GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { - setWindowTitle("Graph Editor"); - resize(720, 480); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + setWindowTitle("Graph Editor"); + resize(720, 480); QWidget* main_widget = new QWidget(); setWidget(main_widget); - QVBoxLayout* layout = new QVBoxLayout(); + QVBoxLayout* layout = new QVBoxLayout(); main_widget->setLayout(layout); - QWidget* tool_widget = new QWidget(); - tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - QHBoxLayout* tools = new QHBoxLayout(); - tool_widget->setLayout(tools); + QWidget* tool_widget = new QWidget(); + tool_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QHBoxLayout* tools = new QHBoxLayout(); + tool_widget->setLayout(tools); QWidget* left_tool_widget = new QWidget(); QHBoxLayout* left_tool_layout = new QHBoxLayout(); @@ -89,7 +91,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { right_tool_layout->addWidget(tool_buttons.at(i)); }*/ - layout->addWidget(tool_widget); + layout->addWidget(tool_widget); QWidget* central_widget = new QWidget(); QVBoxLayout* central_layout = new QVBoxLayout(); @@ -104,11 +106,11 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { layout->addWidget(central_widget); - QWidget* value_widget = new QWidget(); - value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QWidget* value_widget = new QWidget(); + value_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); QHBoxLayout* values = new QHBoxLayout(); - value_widget->setLayout(values); - values->addStretch(); + value_widget->setLayout(values); + values->addStretch(); current_row_desc = new QLabel(); values->addWidget(current_row_desc); @@ -119,7 +121,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { central_value_widget->setLayout(value_layout); values->addWidget(central_value_widget); - values->addStretch(); + values->addStretch(); layout->addWidget(value_widget); connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); diff --git a/panels/project.cpp b/panels/project.cpp index 31669a555..563f38c9b 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -60,7 +60,7 @@ QString recent_proj_file; Project::Project(QWidget *parent) : QDockWidget(parent) { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* dockWidgetContents = new QWidget(); QVBoxLayout* verticalLayout = new QVBoxLayout(dockWidgetContents); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 432fb6996..0e97fb22a 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -80,6 +80,8 @@ Timeline::Timeline(QWidget *parent) : last_frame(0), scroll(0) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); default_track_height = (QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96) * TRACK_DEFAULT_HEIGHT; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 01e488079..d09f53ae7 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -46,6 +46,8 @@ Viewer::Viewer(QWidget *parent) : panel_name("Viewer: "), minimum_zoom(1.0) { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setup_ui(); headers->viewer = this; @@ -449,8 +451,6 @@ void Viewer::set_sb_max() { } void Viewer::setup_ui() { - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QWidget* contents = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(contents); From 88bf0ff2be937423e27316a6a029dd76e549c58e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 09:49:11 +1100 Subject: [PATCH 11/32] buttons enabled by selection --- panels/grapheditor.cpp | 36 +++++++++++------------------------- panels/grapheditor.h | 8 ++++++-- ui/graphview.cpp | 7 +++++++ ui/graphview.h | 1 + 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index bc8bc48dc..259ade390 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -58,11 +58,11 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { left_tool_layout->addWidget(keyframe_nav); left_tool_layout->addStretch(); - QPushButton* linear_button = new QPushButton("Linear"); + linear_button = new QPushButton("Linear"); linear_button->setCheckable(true); - QPushButton* bezier_button = new QPushButton("Bezier"); + bezier_button = new QPushButton("Bezier"); bezier_button->setCheckable(true); - QPushButton* hold_button = new QPushButton("Hold"); + hold_button = new QPushButton("Hold"); hold_button->setCheckable(true); center_tool_layout->addStretch(); @@ -70,27 +70,6 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { center_tool_layout->addWidget(bezier_button); center_tool_layout->addWidget(hold_button); - /*QPushButton* tool_arrow_button = new QPushButton(); - tool_arrow_button->setCheckable(true); - tool_arrow_button->setIcon(QIcon(":/icons/arrow.png")); - tool_arrow_button->setProperty("tool", TIMELINE_TOOL_POINTER); - tool_buttons.append(tool_arrow_button); - QPushButton* tool_hand_button = new QPushButton(); - tool_hand_button->setCheckable(true); - tool_hand_button->setIcon(QIcon(":/icons/hand.png")); - tool_hand_button->setProperty("tool", TIMELINE_TOOL_HAND); - tool_buttons.append(tool_hand_button); - QPushButton* tool_zoom_button = new QPushButton(); - tool_zoom_button->setCheckable(true); - tool_zoom_button->setIcon(QIcon(":/icons/zoomin.png")); - tool_zoom_button->setProperty("tool", TIMELINE_TOOL_ZOOM); - tool_buttons.append(tool_zoom_button); - - right_tool_layout->addStretch(); - for (int i=0;iaddWidget(tool_buttons.at(i)); - }*/ - layout->addWidget(tool_widget); QWidget* central_widget = new QWidget(); @@ -114,7 +93,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { current_row_desc = new QLabel(); values->addWidget(current_row_desc); - +1 QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); @@ -126,6 +105,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); + connect(view, SIGNAL(selection_changed(bool)), this, SLOT(set_key_button_enabled(bool))); } void GraphEditor::update_panel() { @@ -195,6 +175,12 @@ void GraphEditor::set_row(EffectRow *r) { update_panel(); } +void GraphEditor::set_key_button_enabled(bool e) { + linear_button->setEnabled(e); + bezier_button->setEnabled(e); + hold_button->setEnabled(e); +} + void GraphEditor::passthrough_slider_value() { for (int i=0;i 0); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -261,6 +263,11 @@ void GraphView::wheelEvent(QWheelEvent *event) { } void GraphView::set_row(EffectRow *r) { + selected_keys.clear(); + selected_keys_fields.clear(); + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + emit selection_changed(false); row = r; update(); } diff --git a/ui/graphview.h b/ui/graphview.h index 363303632..97b6e1e2c 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -24,6 +24,7 @@ signals: void x_scroll_changed(int); void y_scroll_changed(int); void zoom_changed(double); + void selection_changed(bool); private: int x_scroll; int y_scroll; From 9934ec577717ba855de0100a11e4c83dbceaa625 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 11:29:37 +1100 Subject: [PATCH 12/32] changed effect field struct --- panels/grapheditor.cpp | 2 +- project/effectfield.h | 93 +++++++++++++++++++++++------------------- project/effectrow.h | 43 +++++++++---------- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 259ade390..9b84dd8d0 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -93,7 +93,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { current_row_desc = new QLabel(); values->addWidget(current_row_desc); -1 + QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); diff --git a/project/effectfield.h b/project/effectfield.h index 485b43287..f95c896cd 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -15,60 +15,71 @@ class EffectRow; class ComboAction; -class EffectField : public QObject { - Q_OBJECT +class EffectKeyframe { public: - EffectField(EffectRow* parent, int t, const QString& i); - EffectRow* parent_row; - int type; - QString id; + long time; + int type; + QVariant data; - QVariant get_previous_data(); - QVariant get_current_data(); - double frameToTimecode(long frame); - long timecodeToFrame(double timecode); - void set_current_data(const QVariant&); - void get_keyframe_data(double timecode, int& before, int& after, double& d); - QVariant validate_keyframe_data(double timecode, bool async = false); + // only for bezier type + QPointF pre_handle; + QPointF post_handle; +}; - double get_double_value(double timecode, bool async = false); - void set_double_value(double v); - void set_double_default_value(double v); - void set_double_minimum_value(double v); - void set_double_maximum_value(double v); +class EffectField : public QObject { + Q_OBJECT +public: + EffectField(EffectRow* parent, int t, const QString& i); + EffectRow* parent_row; + int type; + QString id; - const QString get_string_value(double timecode, bool async = false); - void set_string_value(const QString &s); + QVariant get_previous_data(); + QVariant get_current_data(); + double frameToTimecode(long frame); + long timecodeToFrame(double timecode); + void set_current_data(const QVariant&); + void get_keyframe_data(double timecode, int& before, int& after, double& d); + QVariant validate_keyframe_data(double timecode, bool async = false); - void add_combo_item(const QString& name, const QVariant &data); - int get_combo_index(double timecode, bool async = false); - const QVariant get_combo_data(double timecode); - const QString get_combo_string(double timecode); - void set_combo_index(int index); - void set_combo_string(const QString& s); + double get_double_value(double timecode, bool async = false); + void set_double_value(double v); + void set_double_default_value(double v); + void set_double_minimum_value(double v); + void set_double_maximum_value(double v); - bool get_bool_value(double timecode, bool async = false); - void set_bool_value(bool b); + const QString get_string_value(double timecode, bool async = false); + void set_string_value(const QString &s); - const QString get_font_name(double timecode, bool async = false); - void set_font_name(const QString& s); + void add_combo_item(const QString& name, const QVariant &data); + int get_combo_index(double timecode, bool async = false); + const QVariant get_combo_data(double timecode); + const QString get_combo_string(double timecode); + void set_combo_index(int index); + void set_combo_string(const QString& s); - QColor get_color_value(double timecode, bool async = false); - void set_color_value(QColor color); + bool get_bool_value(double timecode, bool async = false); + void set_bool_value(bool b); - QWidget* get_ui_element(); - void set_enabled(bool e); - QVector keyframe_data; - QWidget* ui_element; + const QString get_font_name(double timecode, bool async = false); + void set_font_name(const QString& s); - void make_key_from_change(ComboAction* ca); + QColor get_color_value(double timecode, bool async = false); + void set_color_value(QColor color); + + QWidget* get_ui_element(); + void set_enabled(bool e); + QVector keyframes; + QWidget* ui_element; + + void make_key_from_change(ComboAction* ca); private: - bool hasKeyframes(); + bool hasKeyframes(); private slots: - void ui_element_change(); + void ui_element_change(); signals: - void changed(); - void toggled(bool); + void changed(); + void toggled(bool); void clicked(); }; diff --git a/project/effectrow.h b/project/effectrow.h index a18cd56af..672c00b16 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -16,26 +16,23 @@ class KeyframeNavigator; class ClickableLabel; class EffectRow : public QObject { - Q_OBJECT + Q_OBJECT public: - EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row); - ~EffectRow(); - EffectField* add_field(int type, const QString &id, int colspan = 1); - EffectField* field(int i); - int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe(KeyframeDelete *kd, int index); - void delete_keyframe_at_time(KeyframeDelete* kd, long time); + EffectRow(Effect* parent, bool save, QGridLayout* uilayout, const QString& n, int row); + ~EffectRow(); + EffectField* add_field(int type, const QString &id, int colspan = 1); + EffectField* field(int i); + int fieldCount(); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe(KeyframeDelete *kd, int index); + void delete_keyframe_at_time(KeyframeDelete* kd, long time); ClickableLabel* label; - Effect* parent_effect; - bool savable; + Effect* parent_effect; + bool savable; const QString& get_name(); - bool isKeyframing(); - void setKeyframing(bool); - - QVector keyframe_times; - QVector keyframe_types; + bool isKeyframing(); + void setKeyframing(bool); public slots: void goto_previous_key(); void toggle_key(); @@ -44,15 +41,15 @@ public slots: private slots: void set_keyframe_enabled(bool); private: - bool keyframing; - QGridLayout* ui; - QString name; - int ui_row; - QVector fields; + bool keyframing; + QGridLayout* ui; + QString name; + int ui_row; + QVector fields; - KeyframeNavigator* keyframe_nav; + KeyframeNavigator* keyframe_nav; - bool just_made_unsafe_keyframe; + bool just_made_unsafe_keyframe; }; #endif // EFFECTROW_H From 05524153ab8fef7550c86a994a3b4ad636eb47ad Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 16:12:10 +1100 Subject: [PATCH 13/32] began refactoring keyframe times --- project/clip.cpp | 20 ++++++-------------- project/undo.cpp | 39 +++++++++++++++++---------------------- project/undo.h | 30 ++++++++++++++---------------- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/project/clip.cpp b/project/clip.cpp index 88258441f..883705b2f 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -309,25 +309,17 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t track); } - QVector key_rows; - QVector key_indices; - QVector key_old; - QVector key_new; - + // move keyframes for (int i=0;irow_count();j++) { EffectRow* r = e->row(j); - for (int k=0;kkeyframe_times.size();k++) { - key_rows.append(r); - key_indices.at(k); - key_old.append(r->keyframe_times.at(k)); - key_new.append(r->keyframe_times.at(k) * multiplier); + for (int l=0;lfieldCount();l++) { + EffectField* f = r->field(l); + for (int k=0;kkeyframes.size();k++) { + ca->append(new SetLong(&f->keyframes[k].time, f->keyframes[k].time, f->keyframes[k].time * multiplier)); + } } } } - - if (key_rows.size() > 0) { - ca->append(new KeyframeMove(key_rows, key_indices, key_old, key_new)); - } } diff --git a/project/undo.cpp b/project/undo.cpp index 963b02c32..b1a7caf04 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,28 +737,6 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeMove::KeyframeMove(const QVector& irows, const QVector& ikeyframes, const QVector& iold_values, const QVector& inew_values) : - rows(irows), - keyframes(ikeyframes), - old_values(iold_values), - new_values(inew_values), - old_project_changed(mainWindow->isWindowModified()) -{} - -void KeyframeMove::undo() { - for (int i=0;ikeyframe_times[keyframes.at(i)] = old_values.at(i); - } - mainWindow->setWindowModified(old_project_changed); -} - -void KeyframeMove::redo() { - for (int i=0;ikeyframe_times[keyframes.at(i)] = new_values.at(i); - } - mainWindow->setWindowModified(true); -} - KeyframeDelete::KeyframeDelete() : disable_keyframes_on_row(NULL), old_project_changed(mainWindow->isWindowModified()), @@ -1332,3 +1310,20 @@ void SetQVariant::undo() { void SetQVariant::redo() { *target = new_val; } + +SetLong::SetLong(long *pointer, long old_value, long new_value) : + p(pointer), + oldval(old_value), + newval(new_value), + old_project_changed(mainWindow->isWindowModified()) +{} + +void SetLong::undo() { + *p = oldval; + mainWindow->setWindowModified(old_project_changed); +} + +void SetLong::redo() { + *p = newval; + mainWindow->setWindowModified(true); +} diff --git a/project/undo.h b/project/undo.h index e74d09a25..1e9fde486 100644 --- a/project/undo.h +++ b/project/undo.h @@ -17,6 +17,7 @@ struct EffectMeta; #include "project/marker.h" #include "project/selection.h" +#include "project/effectfield.h" #include #include @@ -328,19 +329,6 @@ private: QString to; }; -class KeyframeMove : public QUndoCommand { -public: - KeyframeMove(const QVector& rows, const QVector& keyframes, const QVector& old_values, const QVector& new_values); - void undo(); - void redo(); -private: - QVector rows; - QVector keyframes; - QVector old_values; - QVector new_values; - bool old_project_changed; -}; - class KeyframeDelete : public QUndoCommand { public: KeyframeDelete(); @@ -351,9 +339,7 @@ public: void redo(); private: bool old_project_changed; - QVector deleted_keyframe_times; - QVector deleted_keyframe_types; - QVector deleted_keyframe_data; + QVector deleted_keyframe_times; bool sorted; }; @@ -524,6 +510,18 @@ private: bool old_project_changed; }; +class SetLong : public QUndoCommand { +public: + SetLong(long* pointer, long old_value, long new_value); + void undo(); + void redo(); +private: + long* p; + long oldval; + long newval; + bool old_project_changed; +}; + class SetDouble : public QUndoCommand { public: SetDouble(double* pointer, double new_value); From 37f21d2ff732359a823ffc39886249741b41c767 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 22:30:32 +1100 Subject: [PATCH 14/32] refactored keyframes to fields rather than rows --- project/effect.cpp | 38 ++++++------ project/effectfield.cpp | 22 +++---- project/effectrow.cpp | 101 ++++++++++++++++++-------------- project/effectrow.h | 5 +- project/undo.cpp | 89 ++++++---------------------- project/undo.h | 15 +++-- ui/graphview.cpp | 78 +++++++++++++------------ ui/keyframedrawing.cpp | 2 + ui/keyframeview.cpp | 126 +++++++++++++++++++++++----------------- ui/keyframeview.h | 5 +- 10 files changed, 236 insertions(+), 245 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 0f75405b2..1be29a308 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -456,13 +456,11 @@ void Effect::copy_field_keyframes(Effect* e) { for (int i=0;irows.at(i); - copy_row->setKeyframing(row->isKeyframing()); - copy_row->keyframe_times = row->keyframe_times; - copy_row->keyframe_types = row->keyframe_types; + copy_row->setKeyframing(row->isKeyframing()); for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - copy_row->field(j)->set_current_data(field->get_current_data()); - copy_row->field(j)->keyframe_data = field->keyframe_data; + EffectField* field = row->field(j); + EffectField* copy_field = copy_row->field(j); + copy_field->keyframes = field->keyframes; } } } @@ -633,8 +631,13 @@ void Effect::load(QXmlStreamReader& stream) { keyframe_type = attr.value().toInt(); } } - row->keyframe_times.append(keyframe_frame); - row->keyframe_types.append(keyframe_type); + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + EffectKeyframe key; + key.time = keyframe_frame; + key.type = keyframe_type; + field->keyframes.append(key); + } } stream.readNext(); } @@ -677,13 +680,15 @@ void Effect::load(QXmlStreamReader& stream) { } } + int field_index = 0; while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { stream.readNext(); // read all keyframes if (stream.name() == "key" && stream.isStartElement()) { stream.readNext(); - field->keyframe_data.append(load_data_from_string(field->type, stream.text().toString())); + field->keyframes[field_index].data = load_data_from_string(field->type, stream.text().toString()); + field_index++; } } } else { @@ -711,20 +716,19 @@ void Effect::save(QXmlStreamWriter& stream) { stream.writeStartElement("row"); // row stream.writeStartElement("keyframes"); // keyframes stream.writeAttribute("enabled", QString::number(row->isKeyframing())); - for (int j=0;jkeyframe_times.size();j++) { - stream.writeStartElement("key"); // key - stream.writeAttribute("frame", QString::number(row->keyframe_times.at(j))); - stream.writeAttribute("type", QString::number(row->keyframe_types.at(j))); - stream.writeEndElement(); // key - } stream.writeEndElement(); // keyframes for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); stream.writeStartElement("field"); // field stream.writeAttribute("id", field->id); stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); - for (int k=0;kkeyframe_data.size();k++) { - stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k))); + for (int k=0;kkeyframes.size();k++) { + const EffectKeyframe& key = field->keyframes.at(k); + stream.writeStartElement("key"); + stream.writeAttribute("value", save_data_to_string(field->type, key.data)); + stream.writeAttribute("frame", QString::number(key.time)); + stream.writeAttribute("type", QString::number(key.type)); + stream.writeEndElement(); // key } stream.writeEndElement(); // field } diff --git a/project/effectfield.cpp b/project/effectfield.cpp index f465e5e27..00435b91d 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -121,8 +121,8 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do long after_keyframe_time = LONG_MAX; long frame = timecodeToFrame(timecode); - for (int i=0;ikeyframe_times.size();i++) { - long eval_keyframe_time = parent_row->keyframe_times.at(i); + for (int i=0;ikeyframe_types.at(before) == KEYFRAME_TYPE_HOLD) { + if (keyframes.at(before).type == KEYFRAME_TYPE_HOLD) { progress = 0; } else { // TODO replace with bezier function @@ -157,7 +157,7 @@ void EffectField::get_keyframe_data(double timecode, int &before, int &after, do } bool EffectField::hasKeyframes() { - return (parent_row->isKeyframing() && keyframe_data.size() > 0); + return (parent_row->isKeyframing() && keyframes.size() > 0); } QVariant EffectField::validate_keyframe_data(double timecode, bool async) { @@ -175,16 +175,16 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { progress -= 0.01865; }*/ - const QVariant& before_data = keyframe_data.at(before_keyframe); + const QVariant& before_data = keyframes.at(before_keyframe).data; switch (type) { case EFFECT_FIELD_DOUBLE: { double value; if (before_keyframe == after_keyframe) { - value = keyframe_data.at(before_keyframe).toDouble(); + value = keyframes.at(before_keyframe).data.toDouble(); } else { - double before_dbl = keyframe_data.at(before_keyframe).toDouble(); - double after_dbl = keyframe_data.at(after_keyframe).toDouble(); + double before_dbl = keyframes.at(before_keyframe).data.toDouble(); + double after_dbl = keyframes.at(after_keyframe).data.toDouble(); value = double_lerp(before_dbl, after_dbl, progress); } if (async) { @@ -197,10 +197,10 @@ QVariant EffectField::validate_keyframe_data(double timecode, bool async) { { QColor value; if (before_keyframe == after_keyframe) { - value = keyframe_data.at(before_keyframe).value(); + value = keyframes.at(before_keyframe).data.value(); } else { - QColor before_data = keyframe_data.at(before_keyframe).value(); - QColor after_data = keyframe_data.at(after_keyframe).value(); + QColor before_data = keyframes.at(before_keyframe).data.value(); + QColor after_data = keyframes.at(after_keyframe).data.value(); value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); } if (async) { diff --git a/project/effectrow.cpp b/project/effectrow.cpp index 28d4cfaeb..db86e75f3 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -61,12 +61,14 @@ void EffectRow::set_keyframe_enabled(bool enabled) { } else { if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { // clear - KeyframeDelete* kd = new KeyframeDelete(); - for (int i=keyframe_times.size()-1;i>=0;i--) { - delete_keyframe(kd, i); + ComboAction* ca = new ComboAction(); + for (int i=0;ikeyframes.size();j++) { + ca->append(new KeyframeDelete(f, 0)); + } } - kd->disable_keyframes_on_row = this; - undo_stack.push(kd); + undo_stack.push(ca); panel_effect_controls->update_keyframes(); } else { setKeyframing(true); @@ -77,49 +79,61 @@ void EffectRow::set_keyframe_enabled(bool enabled) { void EffectRow::goto_previous_key() { long key = LONG_MIN; Clip* c = parent_effect->parent_clip; - for (int i=0;iclip_in + c->timeline_in; - if (comp < sequence->playhead) { - key = qMax(comp, key); + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; + if (comp < sequence->playhead) { + key = qMax(comp, key); + } } } if (key != LONG_MIN) panel_sequence_viewer->seek(key); } void EffectRow::toggle_key() { - int index = -1; + QVector key_fields; + QVector key_field_index; Clip* c = parent_effect->parent_clip; - for (int i=0;itimeline_in - c->clip_in + keyframe_times.at(i); - if (comp == sequence->playhead) { - index = i; - break; + for (int j=0;jkeyframes.size();i++) { + long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; + if (comp == sequence->playhead) { + key_fields.append(f); + key_field_index.append(i); + } } } - if (index < 0) { + + ComboAction* ca = new ComboAction(); + if (key_fields.size() == 0) { // keyframe doesn't exist, set one - ComboAction* ca = new ComboAction(); set_keyframe_now(ca); - undo_stack.push(ca); } else { - KeyframeDelete* kd = new KeyframeDelete(); - delete_keyframe(kd, index); - undo_stack.push(kd); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); + } } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + panel_sequence_viewer->viewer_widget->update(); } void EffectRow::goto_next_key() { long key = LONG_MAX; Clip* c = parent_effect->parent_clip; - for (int i=0;itimeline_in - c->clip_in + keyframe_times.at(i); - if (comp > sequence->playhead) { - key = qMin(comp, key); + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; + if (comp > sequence->playhead) { + key = qMax(comp, key); + } } } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + if (key != LONG_MAX) panel_sequence_viewer->seek(key); } void EffectRow::focus_row() { @@ -145,10 +159,13 @@ EffectRow::~EffectRow() { void EffectRow::set_keyframe_now(ComboAction* ca) { int index = -1; long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; - for (int i=0;ikeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + index = i; + break; + } } } @@ -166,24 +183,22 @@ void EffectRow::set_keyframe_now(ComboAction* ca) { panel_effect_controls->update_keyframes(); } -void EffectRow::delete_keyframe_at_time(KeyframeDelete* kd, long time) { - for (int i=0;ikeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + ca->append(new KeyframeDelete(f, i)); + break; + } } - } + } } const QString &EffectRow::get_name() { return name; } -void EffectRow::delete_keyframe(KeyframeDelete* kd, int index) { - kd->rows.append(this); - kd->keyframes.append(index); -} - EffectField* EffectRow::field(int i) { return fields.at(i); } diff --git a/project/effectrow.h b/project/effectrow.h index 672c00b16..a91171545 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -23,9 +23,8 @@ public: EffectField* add_field(int type, const QString &id, int colspan = 1); EffectField* field(int i); int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe(KeyframeDelete *kd, int index); - void delete_keyframe_at_time(KeyframeDelete* kd, long time); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe_at_time(ComboAction *ca, long time); ClickableLabel* label; Effect* parent_effect; bool savable; diff --git a/project/undo.cpp b/project/undo.cpp index b1a7caf04..5187d2a6e 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -737,69 +737,21 @@ void MediaRename::redo() { mainWindow->setWindowModified(true); } -KeyframeDelete::KeyframeDelete() : - disable_keyframes_on_row(NULL), - old_project_changed(mainWindow->isWindowModified()), - sorted(false) +KeyframeDelete::KeyframeDelete(EffectField *ifield, int iindex) : + field(ifield), + index(iindex), + old_project_changed(mainWindow->isWindowModified()) {} void KeyframeDelete::undo() { - if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(true); - - int data_index = deleted_keyframe_data.size()-1; - for (int i=rows.size()-1;i>=0;i--) { - EffectRow* row = rows.at(i); - int keyframe_index = keyframes.at(i); - - row->keyframe_times.insert(keyframe_index, deleted_keyframe_times.at(i)); - row->keyframe_types.insert(keyframe_index, deleted_keyframe_types.at(i)); - - for (int j=row->fieldCount()-1;j>=0;j--) { - row->field(j)->keyframe_data.insert(keyframe_index, deleted_keyframe_data.at(data_index)); - data_index--; - } - } - + field->keyframes.insert(index, deleted_key); mainWindow->setWindowModified(old_project_changed); } void KeyframeDelete::redo() { - if (!sorted) { - deleted_keyframe_times.resize(rows.size()); - deleted_keyframe_types.resize(rows.size()); - deleted_keyframe_data.clear(); - } - - for (int i=0;ikeyframe_times.at(keyframe_index)); - deleted_keyframe_types[i] = (row->keyframe_types.at(keyframe_index)); - } - - row->keyframe_times.removeAt(keyframe_index); - row->keyframe_types.removeAt(keyframe_index); - - for (int j=0;jfieldCount();j++) { - if (!sorted) deleted_keyframe_data.append(row->field(j)->keyframe_data.at(keyframe_index)); - row->field(j)->keyframe_data.removeAt(keyframe_index); - } - - // correct following indices - if (!sorted) { - for (int j=i+1;j keyframe_index) { - keyframes[j]--; - } - } - } - } - - if (disable_keyframes_on_row != NULL) disable_keyframes_on_row->setKeyframing(false); - mainWindow->setWindowModified(true); - sorted = true; + deleted_key = field->keyframes.at(index); + field->keyframes.removeAt(index); + mainWindow->setWindowModified(true); } KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : @@ -819,7 +771,7 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : if (field->type == EFFECT_FIELD_DOUBLE) { old_values[i] = static_cast(field->ui_element)->getPreviousValue(); } else { - old_values[i] = field->keyframe_data.at(index); + old_values[i] = field->keyframes.at(index).data; } } new_values[i] = field->get_current_data(); @@ -829,16 +781,12 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : void KeyframeSet::undo() { if (enable_keyframes) row->setKeyframing(false); - bool append = (index == -1 || just_made_keyframe); - if (append) { - row->keyframe_times.removeLast(); - row->keyframe_types.removeLast(); - } + bool append = (index == -1 || just_made_keyframe); for (int i=0;ifieldCount();i++) { if (append) { - row->field(i)->keyframe_data.removeLast(); + row->field(i)->keyframes.removeLast(); } else { - row->field(i)->keyframe_data[index] = old_values.at(i); + row->field(i)->keyframes[index].data = old_values.at(i); } } @@ -848,15 +796,16 @@ void KeyframeSet::undo() { void KeyframeSet::redo() { bool append = (index == -1 || (just_made_keyframe && !done)); - if (append) { - row->keyframe_times.append(time); - row->keyframe_types.append((row->keyframe_types.size() > 0) ? row->keyframe_types.last() : EFFECT_KEYFRAME_LINEAR); - } for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); if (append) { - row->field(i)->keyframe_data.append(new_values.at(i)); + EffectKeyframe k; + k.data = new_values.at(i); + k.time = time; + k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; + f->keyframes.append(k); } else { - row->field(i)->keyframe_data[index] = new_values.at(i); + f->keyframes[index].data = new_values.at(i); } } row->setKeyframing(true); diff --git a/project/undo.h b/project/undo.h index 1e9fde486..9ef3a1e3b 100644 --- a/project/undo.h +++ b/project/undo.h @@ -331,22 +331,21 @@ private: class KeyframeDelete : public QUndoCommand { public: - KeyframeDelete(); - QVector rows; - EffectRow* disable_keyframes_on_row; - QVector keyframes; + KeyframeDelete(EffectField* ifield, int iindex); void undo(); void redo(); private: - bool old_project_changed; - QVector deleted_keyframe_times; - bool sorted; + EffectField* field; + int index; + bool done; + EffectKeyframe deleted_key; + bool old_project_changed; }; class KeyframeSet : public QUndoCommand { public: - KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); + KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); void undo(); void redo(); QVector old_values; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 4fc859645..5accaacad 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -75,31 +75,34 @@ void GraphView::paintEvent(QPaintEvent *event) { QPen line_pen; line_pen.setWidth(2); - // sort keyframes by time - QVector sorted_keys; - for (int i=0;ikeyframe_times.size();i++) { - bool inserted = false; - for (int j=0;jkeyframe_times.at(sorted_keys.at(j)) > row->keyframe_times.at(i)) { - sorted_keys.insert(j, i); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(i); - } - } + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + + if (field->type == EFFECT_FIELD_DOUBLE) { + // sort keyframes by time + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(i); + } + } + + int last_key_x, last_key_y; - int last_key_x, last_key_y; - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { for (int j=0;jkeyframe_times.at(key_index)); - int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + int key_x = get_screen_x(field->keyframes.at(key_index).time); + int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); line_pen.setColor(get_curve_color(i, row->fieldCount())); p.setPen(line_pen); @@ -112,14 +115,14 @@ void GraphView::paintEvent(QPaintEvent *event) { last_key_y = key_y; } for (int j=0;jkeyframe_times.at(key_index)); - int key_y = get_screen_y(field->keyframe_data.at(key_index).toDouble()); + int key_x = get_screen_x(field->keyframes.at(key_index).time); + int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); - draw_keyframe(p, row->keyframe_types.at(key_index), key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); + draw_keyframe(p, field->keyframes.at(key_index).type, key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); } - p.setBrush(Qt::NoBrush); } } } @@ -154,9 +157,9 @@ void GraphView::mousePressEvent(QMouseEvent *event) { for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframe_times.size();j++) { - int key_x = get_screen_x(row->keyframe_times.at(j)); - int key_y = get_screen_y(field->keyframe_data.at(j).toDouble()); + for (int j=0;jkeyframes.size();j++) { + int key_x = get_screen_x(field->keyframes.at(j).time); + int key_y = get_screen_y(field->keyframes.at(j).data.toDouble()); if (event->pos().x() > key_x-KEYFRAME_SIZE && event->pos().x() < key_x+KEYFRAME_SIZE && event->pos().y() > key_y-KEYFRAME_SIZE @@ -177,10 +180,9 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); for (int i=0;ikeyframe_times.at(selected_keys.at(i))); - for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)).toDouble()); + selected_keys_old_vals.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).time); + selected_keys_old_doubles.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).data.toDouble()); } } @@ -199,10 +201,10 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { update(); } else { for (int i=0;ikeyframe_times[selected_keys.at(i)] = selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom); - for (int j=0;jfield(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)] = selected_keys_old_doubles.at((i*selected_keys_fields.size())+j) + (double(start_y - event->pos().y())/zoom); + int index = (i*selected_keys_fields.size())+j; + row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].time = selected_keys_old_vals.at(index) + (double(event->pos().x() - start_x)/zoom); + row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(index) + (double(start_y - event->pos().y())/zoom); } } moved_keys = true; @@ -213,11 +215,11 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { void GraphView::mouseReleaseEvent(QMouseEvent *event) { if (moved_keys && selected_keys.size() > 0) { - ComboAction* ca = new ComboAction(); + /*ComboAction* ca = new ComboAction(); QVector rows; QVector new_vals; - for (int i=0;ikeyframe_times.at(selected_keys.at(i))); @@ -227,10 +229,10 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); } //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); - } + } ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); - undo_stack.push(ca); + undo_stack.push(ca);*/ } moved_keys = false; mousedown = false; diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 693a4e299..07d515957 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -23,4 +23,6 @@ void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { p.drawRect(QRect(x - KEYFRAME_SIZE, y - KEYFRAME_SIZE, KEYFRAME_SIZE*2, KEYFRAME_SIZE*2)); break; } + + p.setBrush(Qt::NoBrush); } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index b37f71b17..7518c4575 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -44,7 +44,7 @@ KeyframeView::KeyframeView(QWidget *parent) : } void KeyframeView::show_context_menu(const QPoint& pos) { - if (selected_rows.size() > 0) { + if (selected_fields.size() > 0) { QMenu menu(this); QAction* linear = menu.addAction("Linear"); @@ -67,8 +67,9 @@ void KeyframeView::menu_set_key_type(QAction* a) { panel_graph_editor->show(); } else { ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&selected_rows.at(i)->keyframe_types[selected_keyframes.at(i)], a->data().toInt())); + for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); } undo_stack.push(ca); update_keys(); @@ -102,12 +103,19 @@ void KeyframeView::paintEvent(QPaintEvent*) { ClickableLabel* label = row->label; QWidget* contents = e->container->contents; + QVector key_times; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; - for (int k=0;kkeyframe_times.size();k++) { - bool keyframe_selected = keyframeIsSelected(row, k); - long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(k)); - draw_keyframe(p, row->keyframe_types.at(k), getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); - } + for (int l=0;lfieldCount();l++) { + EffectField* f = row->field(l); + for (int k=0;kkeyframes.size();k++) { + if (!key_times.contains(f->keyframes.at(k).time)) { + bool keyframe_selected = keyframeIsSelected(f, k); + long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + key_times.append(f->keyframes.at(k).time); + } + } + } rows.append(row); rowY.append(keyframe_y); @@ -141,9 +149,9 @@ void KeyframeView::paintEvent(QPaintEvent*) { }*/ } -bool KeyframeView::keyframeIsSelected(EffectRow *row, int keyframe) { - for (int i=0;idelete_keyframe(kd, selected_keyframes.at(i)); + // TODO these need to be sorted + ca->append(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); del = true; } if (del) { - undo_stack.push(kd); + undo_stack.push(ca); selected_keyframes.clear(); - selected_rows.clear(); + selected_fields.clear(); update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { - delete kd; + delete ca; } } @@ -202,6 +211,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; + int field_index = -1; int keyframe_index = -1; long frame_diff = 0; long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE); @@ -213,37 +223,41 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { row->focus_row(); - for (int j=0;jkeyframe_times.size();j++) { - long eval_keyframe_time = row->keyframe_times.at(j)-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); - if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { - long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); - if (keyframe_index == -1 || eval_frame_diff < frame_diff) { - row_index = i; - keyframe_index = j; - frame_diff = eval_frame_diff; - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* f = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); + if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { + long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); + if (keyframe_index == -1 || eval_frame_diff < frame_diff) { + row_index = i; + field_index = k; + keyframe_index = j; + frame_diff = eval_frame_diff; + } + } + } + } break; } } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index), keyframe_index); + if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_rows.clear(); + selected_fields.clear(); selected_keyframes.clear(); } if (keyframe_index > -1) { - selected_rows.append(rows.at(row_index)); + selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); } } - if (selected_rows.size() > 0) { - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + if (selected_fields.size() > 0) { + for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } keys_selected = true; @@ -277,8 +291,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_effect->parent_clip; + EffectField* field = selected_fields.at(i); + Clip* c = field->parent_row->parent_effect->parent_clip; long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { @@ -289,11 +303,11 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframe_times.size();j++) { - while (!keyframeIsSelected(row, j) && row->keyframe_times.at(j) == eval_key + frame_diff) { + for (int j=0;jkeyframes.size();j++) { + while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { frame_diff++; panel_timeline->snapped = false; @@ -307,8 +321,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { // apply frame_diffs for (int i=0;ikeyframe_times[selected_keyframes.at(i)] = old_key_vals.at(i) + frame_diff; + EffectField* field = selected_fields.at(i); + field->keyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; } last_frame_diff = frame_diff; @@ -332,13 +346,16 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { for (int i=0;i= min_row && rowY.at(i) <= max_row) { EffectRow* row = rows.at(i); - for (int j=0;jkeyframe_times.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, row->keyframe_times.at(j)); - if (!keyframeIsSelected(row, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_rows.append(rows.at(i)); - selected_keyframes.append(j); - } - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } } } @@ -351,12 +368,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { void KeyframeView::mouseReleaseEvent(QMouseEvent*) { if (dragging) { - QVector new_key_vals; - for (int i=0;ikeyframe_times.at(selected_keyframes.at(i))); + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetLong( + &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, + old_key_vals.at(i), + selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time + )); } - - undo_stack.push(new KeyframeMove(selected_rows, selected_keyframes, old_key_vals, new_key_vals)); + undo_stack.push(ca); } select_rect = false; diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 990bdef9e..59a328ed9 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -7,6 +7,7 @@ struct Clip; class Effect; class EffectRow; +class EffectField; class TimelineHeader; class KeyframeView : public QWidget { @@ -26,7 +27,7 @@ public slots: void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); - QVector selected_rows; + QVector selected_fields; QVector selected_keyframes; QVector rowY; QVector rows; @@ -41,7 +42,7 @@ private: bool select_rect; bool scroll_drag; - bool keyframeIsSelected(EffectRow* row, int keyframe); + bool keyframeIsSelected(EffectField *field, int keyframe); long drag_frame_start; long last_frame_diff; From 215c784f8ed280c968bdd90b86e6067ad949c81e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 2 Jan 2019 22:43:35 +1100 Subject: [PATCH 15/32] fixed keyframe save/load and goto next key --- mainwindow.cpp | 4 ++-- project/effect.cpp | 27 +++++++++++++++++---------- project/effectrow.cpp | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 1f4094bce..e1cb1764e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,13 +80,13 @@ void MainWindow::setup_layout(bool reset) { #endif // load panels from file - if (!reset) { + if (!reset) { QFile panel_config(get_data_path() + "/layout"); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { restoreState(panel_config.readAll(), 0); panel_config.close(); } - } + } layout()->update(); } diff --git a/project/effect.cpp b/project/effect.cpp index 1be29a308..bf20bd12a 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -610,7 +610,7 @@ void Effect::load(QXmlStreamReader& stream) { stream.readNext(); // read keyframes - if (stream.name() == "keyframes" && stream.isStartElement()) { + /*if (stream.name() == "keyframes" && stream.isStartElement()) { for (int k=0;kkeyframes[field_index].data = load_data_from_string(field->type, stream.text().toString()); - field_index++; + row->setKeyframing(true); + + EffectKeyframe key; + for (int k=0;ktype, attr.value().toString()); + } else if (attr.name() == "frame") { + key.time = attr.value().toLong(); + } else if (attr.name() == "type") { + key.type = attr.value().toInt(); + } + } + field->keyframes.append(key); } } } else { @@ -714,9 +724,6 @@ void Effect::save(QXmlStreamWriter& stream) { EffectRow* row = rows.at(i); if (row->savable) { stream.writeStartElement("row"); // row - stream.writeStartElement("keyframes"); // keyframes - stream.writeAttribute("enabled", QString::number(row->isKeyframing())); - stream.writeEndElement(); // keyframes for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); stream.writeStartElement("field"); // field diff --git a/project/effectrow.cpp b/project/effectrow.cpp index db86e75f3..c980c37f3 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -129,7 +129,7 @@ void EffectRow::goto_next_key() { for (int j=0;jkeyframes.size();j++) { long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; if (comp > sequence->playhead) { - key = qMax(comp, key); + key = qMin(comp, key); } } } From 30933cbfcd6eeae5c1853cd9bcf1469dc775b05c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 21:52:01 +1100 Subject: [PATCH 16/32] implemented full bezier interpolation for keyframes --- effects/internal/audionoiseeffect.cpp | 8 +- effects/internal/cornerpineffect.cpp | 78 ++-- effects/internal/paneffect.cpp | 8 +- effects/internal/shakeeffect.cpp | 14 +- effects/internal/solideffect.cpp | 90 ++--- effects/internal/texteffect.cpp | 50 +-- effects/internal/timecodeeffect.cpp | 102 +++--- effects/internal/toneeffect.cpp | 10 +- effects/internal/transformeffect.cpp | 230 ++++++------ effects/internal/volumeeffect.cpp | 6 +- io/math.cpp | 45 ++- io/math.h | 5 + panels/grapheditor.cpp | 47 ++- panels/grapheditor.h | 5 +- project/effect.cpp | 386 +++++++++---------- project/effectfield.cpp | 509 +++++++++++++------------- project/effectfield.h | 13 +- project/effectrow.cpp | 264 ++++++------- project/keyframe.cpp | 7 + project/keyframe.h | 18 +- project/undo.cpp | 40 +- project/undo.h | 16 +- ui/graphview.cpp | 269 ++++++++++---- ui/graphview.h | 12 +- ui/keyframeview.cpp | 150 ++++---- 25 files changed, 1311 insertions(+), 1071 deletions(-) diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index bd0283a5b..effd65f6e 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -4,15 +4,15 @@ #include AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount"); + amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount"); amount_val->set_double_minimum_value(0); amount_val->set_double_maximum_value(100); - amount_val->set_double_default_value(20); + amount_val->set_double_default_value(20); - mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix"); + mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix"); mix_val->set_bool_value(true); - srand(QDateTime::currentMSecsSinceEpoch()); + srand(QDateTime::currentMSecsSinceEpoch()); } void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) { diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 96069b0bf..5be93b671 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -5,59 +5,59 @@ #include "debug.h" CornerPinEffect::CornerPinEffect(Clip *c, const EffectMeta *em) : Effect(c, em) { - enable_coords = true; + enable_coords = true; enable_shader = true; - EffectRow* top_left = add_row("Top Left:"); - top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx"); - top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty"); + EffectRow* top_left = add_row("Top Left"); + top_left_x = top_left->add_field(EFFECT_FIELD_DOUBLE, "topleftx"); + top_left_y = top_left->add_field(EFFECT_FIELD_DOUBLE, "toplefty"); - EffectRow* top_right = add_row("Top Right:"); - top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx"); - top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty"); + EffectRow* top_right = add_row("Top Right"); + top_right_x = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprightx"); + top_right_y = top_right->add_field(EFFECT_FIELD_DOUBLE, "toprighty"); - EffectRow* bottom_left = add_row("Bottom Left:"); - bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx"); - bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty"); + EffectRow* bottom_left = add_row("Bottom Left"); + bottom_left_x = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomleftx"); + bottom_left_y = bottom_left->add_field(EFFECT_FIELD_DOUBLE, "bottomlefty"); - EffectRow* bottom_right = add_row("Bottom Right:"); - bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx"); - bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty"); + EffectRow* bottom_right = add_row("Bottom Right"); + bottom_right_x = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrightx"); + bottom_right_y = bottom_right->add_field(EFFECT_FIELD_DOUBLE, "bottomrighty"); - perspective = add_row("Perspective:")->add_field(EFFECT_FIELD_BOOL, "perspective"); - perspective->set_bool_value(true); + perspective = add_row("Perspective")->add_field(EFFECT_FIELD_BOOL, "perspective"); + perspective->set_bool_value(true); - top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->x_field1 = top_left_x; - top_left_gizmo->y_field1 = top_left_y; + top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_left_gizmo->x_field1 = top_left_x; + top_left_gizmo->y_field1 = top_left_y; - top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->x_field1 = top_right_x; - top_right_gizmo->y_field1 = top_right_y; + top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_right_gizmo->x_field1 = top_right_x; + top_right_gizmo->y_field1 = top_right_y; - bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->x_field1 = bottom_left_x; - bottom_left_gizmo->y_field1 = bottom_left_y; + bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_left_gizmo->x_field1 = bottom_left_x; + bottom_left_gizmo->y_field1 = bottom_left_y; - bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->x_field1 = bottom_right_x; - bottom_right_gizmo->y_field1 = bottom_right_y; + bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_right_gizmo->x_field1 = bottom_right_x; + bottom_right_gizmo->y_field1 = bottom_right_y; vertPath = "cornerpin.vert"; fragPath = "cornerpin.frag"; } void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int data) { - coords.vertexTopLeftX += top_left_x->get_double_value(timecode); - coords.vertexTopLeftY += top_left_y->get_double_value(timecode); + coords.vertexTopLeftX += top_left_x->get_double_value(timecode); + coords.vertexTopLeftY += top_left_y->get_double_value(timecode); - coords.vertexTopRightX += top_right_x->get_double_value(timecode); - coords.vertexTopRightY += top_right_y->get_double_value(timecode); + coords.vertexTopRightX += top_right_x->get_double_value(timecode); + coords.vertexTopRightY += top_right_y->get_double_value(timecode); - coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode); - coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode); + coords.vertexBottomLeftX += bottom_left_x->get_double_value(timecode); + coords.vertexBottomLeftY += bottom_left_y->get_double_value(timecode); - coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode); + coords.vertexBottomRightX += bottom_right_x->get_double_value(timecode); coords.vertexBottomRightY += bottom_right_y->get_double_value(timecode); } @@ -66,12 +66,12 @@ void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords) { glslProgram->setUniformValue("p1", (GLfloat) coords.vertexBottomRightX, (GLfloat) coords.vertexBottomRightY); glslProgram->setUniformValue("p2", (GLfloat) coords.vertexTopLeftX, (GLfloat) coords.vertexTopLeftY); glslProgram->setUniformValue("p3", (GLfloat) coords.vertexTopRightX, (GLfloat) coords.vertexTopRightY); - glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode)); + glslProgram->setUniformValue("perspective", perspective->get_bool_value(timecode)); } void CornerPinEffect::gizmo_draw(double timecode, GLTextureCoords &coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); } diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 0a7b25a58..fcb862192 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -9,13 +9,13 @@ #include "ui/collapsiblewidget.h" PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* pan_row = add_row("Pan:"); - pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan"); + EffectRow* pan_row = add_row("Pan"); + pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE, "pan"); pan_val->set_double_minimum_value(-100); - pan_val->set_double_maximum_value(100); + pan_val->set_double_maximum_value(100); // set defaults - pan_val->set_double_default_value(0); + pan_val->set_double_default_value(0); } void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 0687a71ad..92975fe8a 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -16,19 +16,19 @@ ShakeEffect::ShakeEffect(Clip *c, const EffectMeta *em) : Effect(c, em) { enable_coords = true; - EffectRow* intensity_row = add_row("Intensity:"); - intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity"); + EffectRow* intensity_row = add_row("Intensity"); + intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE, "intensity"); intensity_val->set_double_minimum_value(0); - EffectRow* rotation_row = add_row("Rotation:"); - rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); + EffectRow* rotation_row = add_row("Rotation"); + rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); rotation_val->set_double_minimum_value(0); - EffectRow* frequency_row = add_row("Frequency:"); - frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency"); + EffectRow* frequency_row = add_row("Frequency"); + frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE, "frequency"); frequency_val->set_double_minimum_value(0); - // set defaults + // set defaults intensity_val->set_double_default_value(25); rotation_val->set_double_default_value(10); frequency_val->set_double_default_value(5); diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 0f888da61..512dccda6 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -21,36 +21,36 @@ SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { enable_superimpose = true; - solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type"); + solid_type = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type"); solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR); solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS); - solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD); + solid_type->add_combo_item("Checkerboard", SOLID_TYPE_CHECKERBOARD); - opacity_field = add_row("Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "opacity"); - opacity_field->set_double_minimum_value(0); - opacity_field->set_double_maximum_value(100); - opacity_field->set_double_default_value(100); + opacity_field = add_row("Opacity")->add_field(EFFECT_FIELD_DOUBLE, "opacity"); + opacity_field->set_double_minimum_value(0); + opacity_field->set_double_maximum_value(100); + opacity_field->set_double_default_value(100); - solid_color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color"); - solid_color_field->set_color_value(Qt::red); + solid_color_field = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color"); + solid_color_field->set_color_value(Qt::red); - checkerboard_size_field = add_row("Checkerboard Size:")->add_field(EFFECT_FIELD_DOUBLE, "checker_size"); - checkerboard_size_field->set_double_minimum_value(1); - checkerboard_size_field->set_double_default_value(10); + checkerboard_size_field = add_row("Checkerboard Size")->add_field(EFFECT_FIELD_DOUBLE, "checker_size"); + checkerboard_size_field->set_double_minimum_value(1); + checkerboard_size_field->set_double_default_value(10); - // hacky but eh - QComboBox* solid_type_combo = static_cast(solid_type->get_ui_element()); - connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int))); - ui_update(solid_type_combo->currentIndex()); + // hacky but eh + QComboBox* solid_type_combo = static_cast(solid_type->get_ui_element()); + connect(solid_type_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(ui_update(int))); + ui_update(solid_type_combo->currentIndex()); - /*vertPath = ":/shaders/common.vert"; - fragPath = ":/shaders/solideffect.frag";*/ + /*vertPath = ":/shaders/common.vert"; + fragPath = ":/shaders/solideffect.frag";*/ } void SolidEffect::redraw(double timecode) { int w = img.width(); int h = img.height(); - int alpha = qRound(opacity_field->get_double_value(timecode)*2.55); + int alpha = qRound(opacity_field->get_double_value(timecode)*2.55); switch (solid_type->get_combo_data(timecode).toInt()) { case SOLID_TYPE_COLOR: { @@ -141,37 +141,37 @@ void SolidEffect::redraw(double timecode) { third_color.setAlpha(alpha); p.fillRect(QRect(bar_x, third_bar_y, third_bar_width, third_bar_height), third_color); } - } - break; - case SOLID_TYPE_CHECKERBOARD: - { - // draw checkboard - QPainter p(&img); - img.fill(Qt::transparent); + } + break; + case SOLID_TYPE_CHECKERBOARD: + { + // draw checkboard + QPainter p(&img); + img.fill(Qt::transparent); - int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode)); - int checker_x, checker_y; - int checkerboard_size_w = qCeil(double(w)/checker_width); - int checkerboard_size_h = qCeil(double(h)/checker_width); + int checker_width = qCeil(checkerboard_size_field->get_double_value(timecode)); + int checker_x, checker_y; + int checkerboard_size_w = qCeil(double(w)/checker_width); + int checkerboard_size_h = qCeil(double(h)/checker_width); - QColor checker_odd(QColor(0, 0, 0, alpha)); - QColor checker_even(solid_color_field->get_color_value(timecode)); - checker_even.setAlpha(alpha); - QVector checker_color{checker_odd, checker_even}; + QColor checker_odd(QColor(0, 0, 0, alpha)); + QColor checker_even(solid_color_field->get_color_value(timecode)); + checker_even.setAlpha(alpha); + QVector checker_color{checker_odd, checker_even}; - for(int i = 0; i < checkerboard_size_w; i++){ - checker_x = checker_width*i; - for(int j = 0; j < checkerboard_size_h; j++){ - checker_y = checker_width*j; - p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]); - } - } - } - break; - } + for(int i = 0; i < checkerboard_size_w; i++){ + checker_x = checker_width*i; + for(int j = 0; j < checkerboard_size_h; j++){ + checker_y = checker_width*j; + p.fillRect(QRect(checker_x, checker_y, checker_width, checker_width), checker_color[(i + j)%2]); + } + } + } + break; + } } void SolidEffect::ui_update(int i) { - solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD); - checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD); + solid_color_field->set_enabled(i == SOLID_TYPE_COLOR || i == SOLID_TYPE_CHECKERBOARD); + checkerboard_size_field->set_enabled(i == SOLID_TYPE_CHECKERBOARD); } diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 66a6a6108..cb3ea3155 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -24,43 +24,43 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) : Effect(c, em) { enable_superimpose = true; - enable_shader = true; + enable_shader = true; - text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, "text", 2); + text_val = add_row("Text")->add_field(EFFECT_FIELD_STRING, "text", 2); - set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, "font", 2); + set_font_combobox = add_row("Font")->add_field(EFFECT_FIELD_FONT, "font", 2); - size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, "size", 2); - size_val->set_double_minimum_value(0); + size_val = add_row("Size")->add_field(EFFECT_FIELD_DOUBLE, "size", 2); + size_val->set_double_minimum_value(0); - set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2); + set_color_button = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2); - EffectRow* alignment_row = add_row("Alignment:"); - halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign"); + EffectRow* alignment_row = add_row("Alignment"); + halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign"); halign_field->add_combo_item("Left", Qt::AlignLeft); halign_field->add_combo_item("Center", Qt::AlignHCenter); halign_field->add_combo_item("Right", Qt::AlignRight); - halign_field->add_combo_item("Justify", Qt::AlignJustify); + halign_field->add_combo_item("Justify", Qt::AlignJustify); - valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign"); + valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign"); valign_field->add_combo_item("Top", Qt::AlignTop); valign_field->add_combo_item("Center", Qt::AlignVCenter); - valign_field->add_combo_item("Bottom", Qt::AlignBottom); + valign_field->add_combo_item("Bottom", Qt::AlignBottom); - word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2); + word_wrap_field = add_row("Word Wrap")->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2); - outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, "outline", 2); - outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2); - outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2); + outline_bool = add_row("Outline")->add_field(EFFECT_FIELD_BOOL, "outline", 2); + outline_color = add_row("Outline Color")->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2); + outline_width = add_row("Outline Width")->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2); outline_width->set_double_minimum_value(0); - shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, "shadow", 2); - shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2); - shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2); + shadow_bool = add_row("Shadow")->add_field(EFFECT_FIELD_BOOL, "shadow", 2); + shadow_color = add_row("Shadow Color")->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2); + shadow_distance = add_row("Shadow Distance")->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2); shadow_distance->set_double_minimum_value(0); - shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2); + shadow_softness = add_row("Shadow Softness")->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2); shadow_softness->set_double_minimum_value(0); - shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2); + shadow_opacity = add_row("Shadow Opacity")->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2); shadow_opacity->set_double_minimum_value(0); shadow_opacity->set_double_maximum_value(100); @@ -78,13 +78,13 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) : outline_width->set_double_default_value(20); outline_enable(false); - shadow_enable(false); + shadow_enable(false); connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool))); connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool))); - vertPath = "common.vert"; - fragPath = "dropshadow.frag"; + vertPath = "common.vert"; + fragPath = "dropshadow.frag"; } void TextEffect::redraw(double timecode) { @@ -170,7 +170,7 @@ void TextEffect::redraw(double timecode) { } path.addText(text_x, text_y, font, lines.at(i)); - } + } // draw outline int outline_width_val = outline_width->get_double_value(timecode); @@ -185,7 +185,7 @@ void TextEffect::redraw(double timecode) { // draw "master" text p.setPen(Qt::NoPen); p.setBrush(set_color_button->get_color_value(timecode)); - p.drawPath(path); + p.drawPath(path); } void TextEffect::shadow_enable(bool e) { diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index f09b7fecc..baba8c50d 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -24,82 +24,82 @@ #include "playback/playback.h" TimecodeEffect::TimecodeEffect(Clip *c, const EffectMeta* em) : - Effect(c, em) + Effect(c, em) { - enable_always_update = true; + enable_always_update = true; enable_superimpose = true; - EffectRow* tc_row = add_row("Timecode:"); - tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector"); - tc_select->add_combo_item("Sequence", true); - tc_select->add_combo_item("Media", false); - tc_select->set_combo_index(0); + EffectRow* tc_row = add_row("Timecode"); + tc_select = tc_row->add_field(EFFECT_FIELD_COMBO, "tc_selector"); + tc_select->add_combo_item("Sequence", true); + tc_select->add_combo_item("Media", false); + tc_select->set_combo_index(0); - scale_val = add_row("Scale:")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2); - scale_val->set_double_minimum_value(1); - scale_val->set_double_default_value(100); - scale_val->set_double_maximum_value(1000); + scale_val = add_row("Scale")->add_field(EFFECT_FIELD_DOUBLE, "scale", 2); + scale_val->set_double_minimum_value(1); + scale_val->set_double_default_value(100); + scale_val->set_double_maximum_value(1000); - color_val = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, "color", 2); - color_val->set_color_value(Qt::white); + color_val = add_row("Color")->add_field(EFFECT_FIELD_COLOR, "color", 2); + color_val->set_color_value(Qt::white); - color_bg_val = add_row("Background Color:")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2); - color_bg_val->set_color_value(Qt::black); + color_bg_val = add_row("Background Color")->add_field(EFFECT_FIELD_COLOR, "bgcolor", 2); + color_bg_val->set_color_value(Qt::black); - bg_alpha = add_row("Background Opacity:")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2); - bg_alpha->set_double_minimum_value(0); - bg_alpha->set_double_maximum_value(100); - bg_alpha->set_double_default_value(50); + bg_alpha = add_row("Background Opacity")->add_field(EFFECT_FIELD_DOUBLE, "bgalpha", 2); + bg_alpha->set_double_minimum_value(0); + bg_alpha->set_double_maximum_value(100); + bg_alpha->set_double_default_value(50); - EffectRow* offset = add_row("Offset:"); - offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx"); - offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety"); + EffectRow* offset = add_row("Offset"); + offset_x_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsetx"); + offset_y_val = offset->add_field(EFFECT_FIELD_DOUBLE, "offsety"); - prepend_text = add_row("Prepend:")->add_field(EFFECT_FIELD_STRING, "prepend", 2); + prepend_text = add_row("Prepend")->add_field(EFFECT_FIELD_STRING, "prepend", 2); } void TimecodeEffect::redraw(double timecode) { - if (tc_select->get_combo_data(timecode).toBool()){ - display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);} - else { - double media_rate = parent_clip->getMediaFrameRate(); - display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);} - img.fill(Qt::transparent); + if (tc_select->get_combo_data(timecode).toBool()){ + display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(sequence->playhead, config.timecode_view, sequence->frame_rate);} + else { + double media_rate = parent_clip->getMediaFrameRate(); + display_timecode = prepend_text->get_string_value(timecode) + frame_to_timecode(timecode * media_rate, config.timecode_view, media_rate);} + img.fill(Qt::transparent); QPainter p(&img); - p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::Antialiasing); int width = img.width(); int height = img.height(); // set font font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias); - font.setFamily("Helvetica"); - font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10))); - p.setFont(font); + font.setFamily("Helvetica"); + font.setPixelSize(qCeil(scale_val->get_double_value(timecode)*.01*(height/10))); + p.setFont(font); QFontMetrics fm(font); - QPainterPath path; + QPainterPath path; - int text_x, text_y, rect_y, offset_x, offset_y; - int text_height = fm.height(); - int text_width = fm.width(display_timecode); - QColor background_color = color_bg_val->get_color_value(timecode); - int alpha_val = bg_alpha->get_double_value(timecode)*2.55; - background_color.setAlpha(alpha_val); + int text_x, text_y, rect_y, offset_x, offset_y; + int text_height = fm.height(); + int text_width = fm.width(display_timecode); + QColor background_color = color_bg_val->get_color_value(timecode); + int alpha_val = bg_alpha->get_double_value(timecode)*2.55; + background_color.setAlpha(alpha_val); - offset_x = int(offset_x_val->get_double_value(timecode)); - offset_y = int(offset_y_val->get_double_value(timecode)); + offset_x = int(offset_x_val->get_double_value(timecode)); + offset_y = int(offset_y_val->get_double_value(timecode)); - text_x = offset_x + (width/2) - (text_width/2); - text_y = offset_y + height - height/10; - rect_y = text_y + fm.descent()/2 - text_height; + text_x = offset_x + (width/2) - (text_width/2); + text_y = offset_y + height - height/10; + rect_y = text_y + fm.descent()/2 - text_height; - path.addText(text_x, text_y, font, display_timecode); + path.addText(text_x, text_y, font, display_timecode); - p.setPen(Qt::NoPen); - p.setBrush(background_color); - p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height)); - p.setBrush(color_val->get_color_value(timecode)); - p.drawPath(path); + p.setPen(Qt::NoPen); + p.setBrush(background_color); + p.drawRect(QRect(text_x-fm.descent()/2, rect_y, text_width+fm.descent(), text_height)); + p.setBrush(color_val->get_color_value(timecode)); + p.drawPath(path); } diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index bb0fb7ac8..b17478e40 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -9,21 +9,21 @@ #include "debug.h" ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) { - type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO, "type"); + type_val = add_row("Type")->add_field(EFFECT_FIELD_COMBO, "type"); type_val->add_combo_item("Sine", TONE_TYPE_SINE); - freq_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE, "frequency"); + freq_val = add_row("Frequency")->add_field(EFFECT_FIELD_DOUBLE, "frequency"); freq_val->set_double_minimum_value(20); freq_val->set_double_maximum_value(20000); freq_val->set_double_default_value(1000); - amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE, "amount"); + amount_val = add_row("Amount")->add_field(EFFECT_FIELD_DOUBLE, "amount"); amount_val->set_double_minimum_value(0); amount_val->set_double_maximum_value(100); amount_val->set_double_default_value(25); - mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL, "mix"); - mix_val->set_bool_value(true); + mix_val = add_row("Mix")->add_field(EFFECT_FIELD_BOOL, "mix"); + mix_val->set_bool_value(true); } void ToneEffect::process_audio(double timecode_start, double timecode_end, quint8 *samples, int nb_bytes, int) { diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 42b54497c..0b7466dd5 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -28,104 +28,104 @@ #define BLEND_MODE_MULTIPLY 2 #define BLEND_MODE_OVERLAY 3 -TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { - enable_coords = true; +TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { + enable_coords = true; - EffectRow* position_row = add_row("Position:"); - position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X - position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y + EffectRow* position_row = add_row("Position"); + position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X + position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y - EffectRow* scale_row = add_row("Scale:"); - scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) + EffectRow* scale_row = add_row("Scale"); + scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) scale_x->set_double_minimum_value(0); scale_x->set_double_maximum_value(3000); - scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) + scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) scale_y->set_double_minimum_value(0); scale_y->set_double_maximum_value(3000); - EffectRow* uniform_scale_row = add_row("Uniform Scale:"); - uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option + EffectRow* uniform_scale_row = add_row("Uniform Scale"); + uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option - EffectRow* rotation_row = add_row("Rotation:"); - rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); + EffectRow* rotation_row = add_row("Rotation"); + rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); - EffectRow* anchor_point_row = add_row("Anchor Point:"); - anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X - anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y + EffectRow* anchor_point_row = add_row("Anchor Point"); + anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X + anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y - EffectRow* opacity_row = add_row("Opacity:"); - opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity + EffectRow* opacity_row = add_row("Opacity"); + opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity opacity->set_double_minimum_value(0); opacity->set_double_maximum_value(100); - EffectRow* blend_mode_row = add_row("Blend Mode:"); - blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode + EffectRow* blend_mode_row = add_row("Blend Mode"); + blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode"); // blend mode blend_mode_box->add_combo_item("Normal", BLEND_MODE_NORMAL); blend_mode_box->add_combo_item("Overlay", BLEND_MODE_OVERLAY); blend_mode_box->add_combo_item("Screen", BLEND_MODE_SCREEN); blend_mode_box->add_combo_item("Multiply", BLEND_MODE_MULTIPLY); - // set up gizmos - top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); - top_left_gizmo->x_field1 = scale_x; + // set up gizmos + top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); + top_left_gizmo->x_field1 = scale_x; - top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_center_gizmo->set_cursor(Qt::SizeVerCursor); - top_center_gizmo->y_field1 = scale_x; + top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_center_gizmo->set_cursor(Qt::SizeVerCursor); + top_center_gizmo->y_field1 = scale_x; - top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); - top_right_gizmo->x_field1 = scale_x; + top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); + top_right_gizmo->x_field1 = scale_x; - bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); - bottom_left_gizmo->x_field1 = scale_x; + bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); + bottom_left_gizmo->x_field1 = scale_x; - bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); - bottom_center_gizmo->y_field1 = scale_x; + bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); + bottom_center_gizmo->y_field1 = scale_x; - bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); - bottom_right_gizmo->x_field1 = scale_x; + bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); + bottom_right_gizmo->x_field1 = scale_x; - left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - left_center_gizmo->set_cursor(Qt::SizeHorCursor); - left_center_gizmo->x_field1 = scale_x; + left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + left_center_gizmo->set_cursor(Qt::SizeHorCursor); + left_center_gizmo->x_field1 = scale_x; - right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - right_center_gizmo->set_cursor(Qt::SizeHorCursor); - right_center_gizmo->x_field1 = scale_x; + right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + right_center_gizmo->set_cursor(Qt::SizeHorCursor); + right_center_gizmo->x_field1 = scale_x; - anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); - anchor_gizmo->set_cursor(Qt::SizeAllCursor); - anchor_gizmo->x_field1 = anchor_x_box; - anchor_gizmo->y_field1 = anchor_y_box; - anchor_gizmo->x_field2 = position_x; - anchor_gizmo->y_field2 = position_y; + anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); + anchor_gizmo->set_cursor(Qt::SizeAllCursor); + anchor_gizmo->x_field1 = anchor_x_box; + anchor_gizmo->y_field1 = anchor_y_box; + anchor_gizmo->x_field2 = position_x; + anchor_gizmo->y_field2 = position_y; - rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); - rotate_gizmo->color = Qt::green; - rotate_gizmo->set_cursor(Qt::SizeAllCursor); - rotate_gizmo->x_field1 = rotation; + rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); + rotate_gizmo->color = Qt::green; + rotate_gizmo->set_cursor(Qt::SizeAllCursor); + rotate_gizmo->x_field1 = rotation; - rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); - rect_gizmo->x_field1 = position_x; - rect_gizmo->y_field1 = position_y; + rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); + rect_gizmo->x_field1 = position_x; + rect_gizmo->y_field1 = position_y; - connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); + connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); - // set defaults + // set defaults uniform_scale_field->set_bool_value(true); blend_mode_box->set_combo_index(0); refresh(); } void TransformEffect::refresh() { - if (parent_clip != NULL && parent_clip->sequence != NULL) { - double default_pos_x = parent_clip->sequence->width/2; - double default_pos_y = parent_clip->sequence->height/2; + if (parent_clip != NULL && parent_clip->sequence != NULL) { + double default_pos_x = parent_clip->sequence->width/2; + double default_pos_y = parent_clip->sequence->height/2; position_x->set_double_default_value(default_pos_x); position_y->set_double_default_value(default_pos_y); @@ -140,35 +140,35 @@ void TransformEffect::refresh() { anchor_x_box->set_double_default_value(default_anchor_x); anchor_y_box->set_double_default_value(default_anchor_y); - opacity->set_double_default_value(100); + opacity->set_double_default_value(100); - double x_percent_multipler = 200.0 / parent_clip->sequence->width; - double y_percent_multipler = 200.0 / parent_clip->sequence->height; - top_left_gizmo->x_field_multi1 = -x_percent_multipler; - top_left_gizmo->y_field_multi1 = -y_percent_multipler; - top_center_gizmo->y_field_multi1 = -y_percent_multipler; - top_right_gizmo->x_field_multi1 = x_percent_multipler; - top_right_gizmo->y_field_multi1 = -y_percent_multipler; - bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; - bottom_left_gizmo->y_field_multi1 = y_percent_multipler; - bottom_center_gizmo->y_field_multi1 = y_percent_multipler; - bottom_right_gizmo->x_field_multi1 = x_percent_multipler; - bottom_right_gizmo->y_field_multi1 = y_percent_multipler; - left_center_gizmo->x_field_multi1 = -x_percent_multipler; - right_center_gizmo->x_field_multi1 = x_percent_multipler; - rotate_gizmo->x_field_multi1 = x_percent_multipler; + double x_percent_multipler = 200.0 / parent_clip->sequence->width; + double y_percent_multipler = 200.0 / parent_clip->sequence->height; + top_left_gizmo->x_field_multi1 = -x_percent_multipler; + top_left_gizmo->y_field_multi1 = -y_percent_multipler; + top_center_gizmo->y_field_multi1 = -y_percent_multipler; + top_right_gizmo->x_field_multi1 = x_percent_multipler; + top_right_gizmo->y_field_multi1 = -y_percent_multipler; + bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; + bottom_left_gizmo->y_field_multi1 = y_percent_multipler; + bottom_center_gizmo->y_field_multi1 = y_percent_multipler; + bottom_right_gizmo->x_field_multi1 = x_percent_multipler; + bottom_right_gizmo->y_field_multi1 = y_percent_multipler; + left_center_gizmo->x_field_multi1 = -x_percent_multipler; + right_center_gizmo->x_field_multi1 = x_percent_multipler; + rotate_gizmo->x_field_multi1 = x_percent_multipler; } } void TransformEffect::toggle_uniform_scale(bool enabled) { scale_y->set_enabled(!enabled); - top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - top_left_gizmo->y_field1 = enabled ? NULL : scale_y; - top_right_gizmo->y_field1 = enabled ? NULL : scale_y; - bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y; - bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y; + top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + top_left_gizmo->y_field1 = enabled ? NULL : scale_y; + top_right_gizmo->y_field1 = enabled ? NULL : scale_y; + bottom_left_gizmo->y_field1 = enabled ? NULL : scale_y; + bottom_right_gizmo->y_field1 = enabled ? NULL : scale_y; } void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int data) { @@ -188,51 +188,51 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i coords.vertexBottomRightY -= anchor_y_offset; // rotation - glRotatef(rotation->get_double_value(timecode), 0, 0, 1); + glRotatef(rotation->get_double_value(timecode), 0, 0, 1); // scale float sx = scale_x->get_double_value(timecode)*0.01; float sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01; - glScalef(sx, sy, 1); + glScalef(sx, sy, 1); - // blend mode + // blend mode switch (blend_mode_box->get_combo_data(timecode).toInt()) { - case BLEND_MODE_NORMAL: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case BLEND_MODE_OVERLAY: + case BLEND_MODE_NORMAL: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case BLEND_MODE_OVERLAY: glBlendFunc(GL_SRC_ALPHA, GL_ONE); - break; - case BLEND_MODE_SCREEN: - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); - break; - case BLEND_MODE_MULTIPLY: + break; + case BLEND_MODE_SCREEN: + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); + break; + case BLEND_MODE_MULTIPLY: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); - break; - default: + break; + default: dout << "[ERROR] Invalid blend mode. This is a bug - please contact developers"; - } + } // opacity - float color[4]; - glGetFloatv(GL_CURRENT_COLOR, color); - glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01)); + float color[4]; + glGetFloatv(GL_CURRENT_COLOR, color); + glColor4f(1.0, 1.0, 1.0, color[3]*(opacity->get_double_value(timecode)*0.01)); } void TransformEffect::gizmo_draw(double timecode, GLTextureCoords& coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); - left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); + top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); + top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); + bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); + bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); - rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); + rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); - rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); } diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index 6a71d06a7..b7b18017b 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -9,12 +9,12 @@ #include "ui/collapsiblewidget.h" VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* volume_row = add_row("Volume:"); - volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume"); + EffectRow* volume_row = add_row("Volume"); + volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE, "volume"); volume_val->set_double_minimum_value(0); // set defaults - volume_val->set_double_default_value(100); + volume_val->set_double_default_value(100); } void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) { diff --git a/io/math.cpp b/io/math.cpp index c26bb5cad..4d71229a4 100644 --- a/io/math.cpp +++ b/io/math.cpp @@ -1,15 +1,54 @@ #include "math.h" #include +#include + +#include "debug.h" int lerp(int a, int b, double t) { - return qRound(((1.0 - t) * a) + (t * b)); + return qRound(((1.0 - t) * a) + (t * b)); } float float_lerp(float a, float b, float t) { - return ((1.0F - t) * a) + (t * b); + return ((1.0F - t) * a) + (t * b); } double double_lerp(double a, double b, double t) { - return ((1.0 - t) * a) + (t * b); + return ((1.0 - t) * a) + (t * b); +} + +double quad_from_t(double a, double b, double c, double t) { + return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c; +} + +double quad_t_from_x(double x, double a, double b, double c) { + return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); + // alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); +} + +double cubic_from_t(double a, double b, double c, double d, double t) { + return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d; +} + +double cubic_t_from_x(double x_target, double a, double b, double c, double d) { + double tolerance = 0.0001; + + double lower = 0.0; + double upper = 1.0; + + double percent = 0.5; + double x = cubic_from_t(a, b, c, d, percent); + + while (qAbs(x_target - x) > tolerance) { + if (x_target > x) { + lower = percent; + } else { + upper = percent; + } + + percent = (upper + lower) / 2.0; + x = cubic_from_t(a, b, c, d, percent); + } + + return percent; } diff --git a/io/math.h b/io/math.h index 927e988ac..ba1e7ed9e 100644 --- a/io/math.h +++ b/io/math.h @@ -4,5 +4,10 @@ int lerp(int a, int b, double t); float float_lerp(float a, float b, float t); double double_lerp(double a, double b, double t); +double quad_from_t(double a, double b, double c, double t); +double quad_t_from_x(double x, double a, double b, double c); +double cubic_from_t(double a, double b, double c, double d, double t); +double cubic_t_from_x(double x_target, double a, double b, double c, double d); +double solveCubicBezier(double p0, double p1, double p2, double p3, double x); #endif // MATH_H diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 9b84dd8d0..7dc981fb4 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -59,10 +59,13 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { left_tool_layout->addStretch(); linear_button = new QPushButton("Linear"); + linear_button->setProperty("type", KEYFRAME_TYPE_LINEAR); linear_button->setCheckable(true); bezier_button = new QPushButton("Bezier"); + bezier_button->setProperty("type", KEYFRAME_TYPE_BEZIER); bezier_button->setCheckable(true); hold_button = new QPushButton("Hold"); + hold_button->setProperty("type", KEYFRAME_TYPE_HOLD); hold_button->setCheckable(true); center_tool_layout->addStretch(); @@ -91,9 +94,6 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { value_widget->setLayout(values); values->addStretch(); - current_row_desc = new QLabel(); - values->addWidget(current_row_desc); - QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); @@ -103,9 +103,18 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { values->addStretch(); layout->addWidget(value_widget); + current_row_desc = new QLabel(); + current_row_desc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + current_row_desc->setAlignment(Qt::AlignCenter); + layout->addWidget(current_row_desc); + connect(view, SIGNAL(zoom_changed(double)), header, SLOT(update_zoom(double))); connect(view, SIGNAL(x_scroll_changed(int)), header, SLOT(set_scroll(int))); - connect(view, SIGNAL(selection_changed(bool)), this, SLOT(set_key_button_enabled(bool))); + connect(view, SIGNAL(selection_changed(bool, int)), this, SLOT(set_key_button_enabled(bool, int))); + + connect(linear_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); + connect(bezier_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); + connect(hold_button, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_type())); } void GraphEditor::update_panel() { @@ -129,8 +138,10 @@ void GraphEditor::update_panel() { void GraphEditor::set_row(EffectRow *r) { for (int i=0;ifieldCount();i++) { EffectField* field = r->field(i); if (field->type == EFFECT_FIELD_DOUBLE) { + QPushButton* slider_button = new QPushButton(); + slider_button->setCheckable(true); + slider_button->setChecked(true); + slider_button->setIcon(QIcon(":/icons/record.png")); + slider_button->setProperty("field", i); + slider_button->setIconSize(QSize(8, 8)); + slider_button->setMaximumSize(QSize(12, 12)); + connect(slider_button, SIGNAL(toggled(bool)), this, SLOT(set_field_visibility(bool))); + slider_proxy_buttons.append(slider_button); + value_layout->addWidget(slider_button); + LabelSlider* slider = new LabelSlider(); slider->set_color(get_curve_color(i, r->fieldCount()).name()); connect(slider, SIGNAL(valueChanged()), this, SLOT(passthrough_slider_value())); - slider_proxies.append(slider); value_layout->addWidget(slider); @@ -175,10 +196,13 @@ void GraphEditor::set_row(EffectRow *r) { update_panel(); } -void GraphEditor::set_key_button_enabled(bool e) { +void GraphEditor::set_key_button_enabled(bool e, int type) { linear_button->setEnabled(e); + linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR); bezier_button->setEnabled(e); + bezier_button->setChecked(type == KEYFRAME_TYPE_BEZIER); hold_button->setEnabled(e); + hold_button->setChecked(type == KEYFRAME_TYPE_HOLD); } void GraphEditor::passthrough_slider_value() { @@ -188,3 +212,14 @@ void GraphEditor::passthrough_slider_value() { } } } + +void GraphEditor::set_keyframe_type() { + linear_button->setChecked(linear_button == sender()); + bezier_button->setChecked(bezier_button == sender()); + hold_button->setChecked(hold_button == sender()); + view->set_selected_keyframe_type(sender()->property("type").toInt()); +} + +void GraphEditor::set_field_visibility(bool b) { + view->set_field_visibility(sender()->property("field").toInt(), b); +} diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 79329342b..467de9ec3 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -23,6 +23,7 @@ private: TimelineHeader* header; QHBoxLayout* value_layout; QVector slider_proxies; + QVector slider_proxy_buttons; QVector slider_proxy_sources; QLabel* current_row_desc; EffectRow* row; @@ -31,8 +32,10 @@ private: QPushButton* bezier_button; QPushButton* hold_button; private slots: - void set_key_button_enabled(bool e); + void set_key_button_enabled(bool e, int type); void passthrough_slider_value(); + void set_keyframe_type(); + void set_field_visibility(bool b); }; #endif // GRAPHEDITOR_H diff --git a/project/effect.cpp b/project/effect.cpp index bf20bd12a..40fc2b6b0 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -52,14 +52,14 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { switch (em->internal) { case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em); case EFFECT_INTERNAL_TEXT: return new TextEffect(c, em); - case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em); + case EFFECT_INTERNAL_TIMECODE: return new TimecodeEffect(c, em); case EFFECT_INTERNAL_SOLID: return new SolidEffect(c, em); case EFFECT_INTERNAL_NOISE: return new AudioNoiseEffect(c, em); case EFFECT_INTERNAL_VOLUME: return new VolumeEffect(c, em); case EFFECT_INTERNAL_PAN: return new PanEffect(c, em); case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em); - case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em); - case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em); + case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em); + case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em); } } else { dout << "[ERROR] Invalid effect data"; @@ -69,12 +69,12 @@ Effect* create_effect(Clip* c, const EffectMeta* em) { } const EffectMeta* get_internal_meta(int internal_id, int type) { - for (int i=0;ienabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); - ui = new QWidget(); - ui_layout = new QGridLayout(); + // set up base UI + container = new CollapsibleWidget(); + connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); + ui = new QWidget(); + ui_layout = new QGridLayout(); ui_layout->setSpacing(4); - ui->setLayout(ui_layout); + ui->setLayout(ui_layout); container->setContents(ui); connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); - // set up UI from effect file + // set up UI from effect file container->setText(em->name); if (!em->filename.isEmpty()) { @@ -277,7 +277,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : } } if (!row_name.isEmpty()) { - EffectRow* row = add_row(row_name + ":"); + EffectRow* row = add_row(row_name); while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) { reader.readNext(); if (reader.name() == "field" && reader.isStartElement()) { @@ -311,7 +311,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : if (id.isEmpty()) { dout << "[ERROR] Couldn't load field from" << em->filename << "- ID cannot be empty."; } else if (type > -1) { - EffectField* field = row->add_field(type, id); + EffectField* field = row->add_field(type, id); connect(field, SIGNAL(changed()), this, SLOT(field_changed())); switch (type) { case EFFECT_FIELD_DOUBLE: @@ -447,27 +447,27 @@ Effect::~Effect() { for (int i=0;irows.at(i); - copy_row->setKeyframing(row->isKeyframing()); + copy_row->setKeyframing(row->isKeyframing()); for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - EffectField* copy_field = copy_row->field(j); - copy_field->keyframes = field->keyframes; + EffectField* field = row->field(j); + EffectField* copy_field = copy_row->field(j); + copy_field->keyframes = field->keyframes; } } } EffectRow* Effect::add_row(const QString& name, bool savable) { - EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size()); - rows.append(row); + EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size()); + rows.append(row); return row; } @@ -476,53 +476,53 @@ EffectRow* Effect::row(int i) { } int Effect::row_count() { - return rows.size(); + return rows.size(); } EffectGizmo *Effect::add_gizmo(int type) { - EffectGizmo* gizmo = new EffectGizmo(type); - gizmos.append(gizmo); - return gizmo; + EffectGizmo* gizmo = new EffectGizmo(type); + gizmos.append(gizmo); + return gizmo; } EffectGizmo *Effect::gizmo(int i) { - return gizmos.at(i); + return gizmos.at(i); } int Effect::gizmo_count(){ - return gizmos.size(); + return gizmos.size(); } void Effect::refresh() {} void Effect::field_changed() { - panel_sequence_viewer->viewer_widget->update(); + panel_sequence_viewer->viewer_widget->update(); panel_graph_editor->update_panel(); } void Effect::show_context_menu(const QPoint& pos) { - if (meta->type == EFFECT_TYPE_EFFECT) { - QMenu menu(mainWindow); + if (meta->type == EFFECT_TYPE_EFFECT) { + QMenu menu(mainWindow); - int index = get_index_in_clip(); + int index = get_index_in_clip(); - if (index > 0) { - QAction* move_up = menu.addAction("Move &Up"); - connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up())); - } + if (index > 0) { + QAction* move_up = menu.addAction("Move &Up"); + connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up())); + } - if (index < parent_clip->effects.size() - 1) { - QAction* move_down = menu.addAction("Move &Down"); - connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down())); - } + if (index < parent_clip->effects.size() - 1) { + QAction* move_down = menu.addAction("Move &Down"); + connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down())); + } - menu.addSeparator(); + menu.addSeparator(); - QAction* del_action = menu.addAction("D&elete"); - connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self())); + QAction* del_action = menu.addAction("D&elete"); + connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self())); - menu.exec(container->title_bar->mapToGlobal(pos)); - } + menu.exec(container->title_bar->mapToGlobal(pos)); + } } void Effect::delete_self() { @@ -572,24 +572,24 @@ void Effect::set_enabled(bool b) { QVariant load_data_from_string(int type, const QString& string) { switch (type) { - case EFFECT_FIELD_DOUBLE: return string.toDouble(); - case EFFECT_FIELD_COLOR: return QColor(string); - case EFFECT_FIELD_STRING: return string; - case EFFECT_FIELD_BOOL: return (string == "1"); - case EFFECT_FIELD_COMBO: return string.toInt(); - case EFFECT_FIELD_FONT: return string; + case EFFECT_FIELD_DOUBLE: return string.toDouble(); + case EFFECT_FIELD_COLOR: return QColor(string); + case EFFECT_FIELD_STRING: return string; + case EFFECT_FIELD_BOOL: return (string == "1"); + case EFFECT_FIELD_COMBO: return string.toInt(); + case EFFECT_FIELD_FONT: return string; } return QVariant(); } QString save_data_to_string(int type, const QVariant& data) { switch (type) { - case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); - case EFFECT_FIELD_COLOR: return data.value().name(); - case EFFECT_FIELD_STRING: return data.toString(); - case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); - case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); - case EFFECT_FIELD_FONT: return data.toString(); + case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); + case EFFECT_FIELD_COLOR: return data.value().name(); + case EFFECT_FIELD_STRING: return data.toString(); + case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); + case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); + case EFFECT_FIELD_FONT: return data.toString(); } return QString(); } @@ -610,7 +610,7 @@ void Effect::load(QXmlStreamReader& stream) { stream.readNext(); // read keyframes - /*if (stream.name() == "keyframes" && stream.isStartElement()) { + /*if (stream.name() == "keyframes" && stream.isStartElement()) { for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - EffectKeyframe key; - key.time = keyframe_frame; - key.type = keyframe_type; - field->keyframes.append(key); - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + EffectKeyframe key; + key.time = keyframe_frame; + key.type = keyframe_type; + field->keyframes.append(key); + } } stream.readNext(); } } stream.readNext(); - }*/ + }*/ // read field if (stream.name() == "field" && stream.isStartElement()) { @@ -683,22 +683,22 @@ void Effect::load(QXmlStreamReader& stream) { while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { stream.readNext(); - // read keyframes + // read keyframes if (stream.name() == "key" && stream.isStartElement()) { - row->setKeyframing(true); + row->setKeyframing(true); - EffectKeyframe key; - for (int k=0;ktype, attr.value().toString()); - } else if (attr.name() == "frame") { - key.time = attr.value().toLong(); - } else if (attr.name() == "type") { - key.type = attr.value().toInt(); - } - } - field->keyframes.append(key); + EffectKeyframe key; + for (int k=0;ktype, attr.value().toString()); + } else if (attr.name() == "frame") { + key.time = attr.value().toLong(); + } else if (attr.name() == "type") { + key.type = attr.value().toInt(); + } + } + field->keyframes.append(key); } } } else { @@ -718,29 +718,29 @@ void Effect::load(QXmlStreamReader& stream) { void Effect::save(QXmlStreamWriter& stream) { stream.writeAttribute("name", meta->name); - stream.writeAttribute("enabled", QString::number(is_enabled())); + stream.writeAttribute("enabled", QString::number(is_enabled())); for (int i=0;isavable) { - stream.writeStartElement("row"); // row - for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - stream.writeStartElement("field"); // field - stream.writeAttribute("id", field->id); - stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); - for (int k=0;kkeyframes.size();k++) { - const EffectKeyframe& key = field->keyframes.at(k); - stream.writeStartElement("key"); - stream.writeAttribute("value", save_data_to_string(field->type, key.data)); - stream.writeAttribute("frame", QString::number(key.time)); - stream.writeAttribute("type", QString::number(key.type)); - stream.writeEndElement(); // key - } - stream.writeEndElement(); // field - } - stream.writeEndElement(); // row - } + if (row->savable) { + stream.writeStartElement("row"); // row + for (int j=0;jfieldCount();j++) { + EffectField* field = row->field(j); + stream.writeStartElement("field"); // field + stream.writeAttribute("id", field->id); + stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data())); + for (int k=0;kkeyframes.size();k++) { + const EffectKeyframe& key = field->keyframes.at(k); + stream.writeStartElement("key"); + stream.writeAttribute("value", save_data_to_string(field->type, key.data)); + stream.writeAttribute("frame", QString::number(key.time)); + stream.writeAttribute("type", QString::number(key.type)); + stream.writeEndElement(); // key + } + stream.writeEndElement(); // field + } + stream.writeEndElement(); // row + } } } @@ -790,16 +790,16 @@ void Effect::startEffect() { void Effect::endEffect() { if (bound) glslProgram->release(); - bound = false; + bound = false; } void Effect::process_image(double, uint8_t *, int) {} Effect* Effect::copy(Clip* c) { Effect* copy = create_effect(c, meta); - copy->set_enabled(is_enabled()); - copy_field_keyframes(copy); - return copy; + copy->set_enabled(is_enabled()); + copy_field_keyframes(copy); + return copy; } void Effect::process_shader(double timecode, GLTextureCoords&) { @@ -844,7 +844,7 @@ GLuint Effect::process_superimpose(double timecode) { recreate_texture = true; } - if (valueHasChanged(timecode) || recreate_texture || enable_always_update) { + if (valueHasChanged(timecode) || recreate_texture || enable_always_update) { redraw(timecode); } @@ -865,78 +865,78 @@ GLuint Effect::process_superimpose(double timecode) { void Effect::process_audio(double, double, quint8*, int, int) { // only volume/pan, hand off to AU and VST for all other cases - /*double interval = (timecode_end-timecode_start)/nb_bytes; + /*double interval = (timecode_end-timecode_start)/nb_bytes; for (int i=0;ifield(0)->get_double_value(timecode_start+(interval*i), true)); QJSValue result = eval.call(); - samp = result.toInt(); - QJSValueList args; - args << samples << nb_bytes; + samp = result.toInt(); + QJSValueList args; + args << samples << nb_bytes; samples[i+1] = (quint8) (samp >> 8); samples[i] = (quint8) samp; - }*/ + }*/ } void Effect::gizmo_draw(double, GLTextureCoords &) {} void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) { - for (int i=0;ix_field1 != NULL) { - gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); - gizmo->x_field1->make_key_from_change(ca); - } - if (gizmo->y_field1 != NULL) { - gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); - gizmo->y_field1->make_key_from_change(ca); - } - if (gizmo->x_field2 != NULL) { - gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); - gizmo->x_field2->make_key_from_change(ca); - } - if (gizmo->y_field2 != NULL) { - gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); - gizmo->y_field2->make_key_from_change(ca); - } - if (done) undo_stack.push(ca); - break; - } - } + for (int i=0;ix_field1 != NULL) { + gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); + gizmo->x_field1->make_key_from_change(ca); + } + if (gizmo->y_field1 != NULL) { + gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); + gizmo->y_field1->make_key_from_change(ca); + } + if (gizmo->x_field2 != NULL) { + gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); + gizmo->x_field2->make_key_from_change(ca); + } + if (gizmo->y_field2 != NULL) { + gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); + gizmo->y_field2->make_key_from_change(ca); + } + if (done) undo_stack.push(ca); + break; + } + } } void Effect::gizmo_world_to_screen() { - GLfloat view_val[16]; - GLfloat projection_val[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, view_val); - glGetFloatv(GL_PROJECTION_MATRIX, projection_val); + GLfloat view_val[16]; + GLfloat projection_val[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, view_val); + glGetFloatv(GL_PROJECTION_MATRIX, projection_val); - QMatrix4x4 view_matrix(view_val); - QMatrix4x4 projection_matrix(projection_val); + QMatrix4x4 view_matrix(view_val); + QMatrix4x4 projection_matrix(projection_val); - for (int i=0;iget_point_count();j++) { - QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); + for (int j=0;jget_point_count();j++) { + QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); - int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); - int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); + int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); + int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); - g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); - } - } + g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); + } + } } bool Effect::are_gizmos_enabled() { - return (gizmos.size() > 0); + return (gizmos.size() > 0); } void Effect::redraw(double) { diff --git a/project/effectfield.cpp b/project/effectfield.cpp index 00435b91d..6eda75505 100644 --- a/project/effectfield.cpp +++ b/project/effectfield.cpp @@ -15,353 +15,366 @@ #include "project/sequence.h" #include "io/math.h" -#include +#include + +#include "debug.h" EffectField::EffectField(EffectRow *parent, int t, const QString &i) : - parent_row(parent), - type(t), - id(i) + parent_row(parent), + type(t), + id(i) { - switch (t) { - case EFFECT_FIELD_DOUBLE: - { - LabelSlider* ls = new LabelSlider(); - ui_element = ls; - connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); + switch (t) { + case EFFECT_FIELD_DOUBLE: + { + LabelSlider* ls = new LabelSlider(); + ui_element = ls; + connect(ls, SIGNAL(valueChanged()), this, SLOT(ui_element_change())); connect(ls, SIGNAL(clicked()), this, SIGNAL(clicked())); - } - break; - case EFFECT_FIELD_COLOR: - { - ColorButton* cb = new ColorButton(); - ui_element = cb; - connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_STRING: - { - TextEditEx* edit = new TextEditEx(); - edit->setUndoRedoEnabled(true); - ui_element = edit; - connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_BOOL: - { - CheckboxEx* cb = new CheckboxEx(); - ui_element = cb; - connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change())); - connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); - } - break; - case EFFECT_FIELD_COMBO: - { - ComboBoxEx* cb = new ComboBoxEx(); - ui_element = cb; - connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); - } - break; - case EFFECT_FIELD_FONT: - { - FontCombobox* fcb = new FontCombobox(); - ui_element = fcb; - connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); - } - break; - } + } + break; + case EFFECT_FIELD_COLOR: + { + ColorButton* cb = new ColorButton(); + ui_element = cb; + connect(cb, SIGNAL(color_changed()), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_STRING: + { + TextEditEx* edit = new TextEditEx(); + edit->setUndoRedoEnabled(true); + ui_element = edit; + connect(edit, SIGNAL(textChanged()), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_BOOL: + { + CheckboxEx* cb = new CheckboxEx(); + ui_element = cb; + connect(cb, SIGNAL(clicked(bool)), this, SLOT(ui_element_change())); + connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); + } + break; + case EFFECT_FIELD_COMBO: + { + ComboBoxEx* cb = new ComboBoxEx(); + ui_element = cb; + connect(cb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); + } + break; + case EFFECT_FIELD_FONT: + { + FontCombobox* fcb = new FontCombobox(); + ui_element = fcb; + connect(fcb, SIGNAL(activated(int)), this, SLOT(ui_element_change())); + } + break; + } } QVariant EffectField::get_previous_data() { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); - case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPreviousValue(); + case EFFECT_FIELD_BOOL: return !static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->getPreviousIndex(); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->getPreviousValue(); + } + return QVariant(); } QVariant EffectField::get_current_data() { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->getPlainTextEx(); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); + } + return QVariant(); } double EffectField::frameToTimecode(long frame) { - return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); + return ((double) frame / parent_row->parent_effect->parent_clip->sequence->frame_rate); } long EffectField::timecodeToFrame(double timecode) { - return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate); + return qRound(timecode * parent_row->parent_effect->parent_clip->sequence->frame_rate); } void EffectField::set_current_data(const QVariant& data) { - switch (type) { - case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); - case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); - case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); - case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); - case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); - case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); - } + switch (type) { + case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); + case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); + case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainTextEx(data.toString()); + case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); + case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); + case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); + } } void EffectField::get_keyframe_data(double timecode, int &before, int &after, double &progress) { - int before_keyframe_index = -1; - int after_keyframe_index = -1; - long before_keyframe_time = LONG_MIN; - long after_keyframe_time = LONG_MAX; - long frame = timecodeToFrame(timecode); + int before_keyframe_index = -1; + int after_keyframe_index = -1; + long before_keyframe_time = LONG_MIN; + long after_keyframe_time = LONG_MAX; + long frame = timecodeToFrame(timecode); - for (int i=0;i before_keyframe_time) { - before_keyframe_index = i; - before_keyframe_time = eval_keyframe_time; - } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { - after_keyframe_index = i; - after_keyframe_time = eval_keyframe_time; - } - } + for (int i=0;i before_keyframe_time) { + before_keyframe_index = i; + before_keyframe_time = eval_keyframe_time; + } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { + after_keyframe_index = i; + after_keyframe_time = eval_keyframe_time; + } + } - if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { - // interpolate - before = before_keyframe_index; - after = after_keyframe_index; - - if (keyframes.at(before).type == KEYFRAME_TYPE_HOLD) { - progress = 0; - } else { - // TODO replace with bezier function - progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); - } - } else if (before_keyframe_index > -1) { - before = before_keyframe_index; - after = before_keyframe_index; - } else { - before = after_keyframe_index; - after = after_keyframe_index; - } + if ((type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { + // interpolate + before = before_keyframe_index; + after = after_keyframe_index; + progress = (timecode-frameToTimecode(before_keyframe_time))/(frameToTimecode(after_keyframe_time)-frameToTimecode(before_keyframe_time)); + } else if (before_keyframe_index > -1) { + before = before_keyframe_index; + after = before_keyframe_index; + } else { + before = after_keyframe_index; + after = after_keyframe_index; + } } bool EffectField::hasKeyframes() { - return (parent_row->isKeyframing() && keyframes.size() > 0); + return (parent_row->isKeyframing() && keyframes.size() > 0); } QVariant EffectField::validate_keyframe_data(double timecode, bool async) { - if (hasKeyframes()) { - int before_keyframe; - int after_keyframe; - double progress; - get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); + if (hasKeyframes()) { + int before_keyframe; + int after_keyframe; + double progress; + get_keyframe_data(timecode, before_keyframe, after_keyframe, progress); - /*int kf_type = (progress < 0.5) ? parent_row->keyframe_types.at(before_keyframe) : parent_row->keyframe_types.at(after_keyframe); - if (kf_type == KEYFRAME_TYPE_BEZIER) { - double x = (8.0 * progress) - 4.0; - progress = 1.0 / (1.0 + qPow(M_E, -x)); - progress *= 1.0373; - progress -= 0.01865; - }*/ + const QVariant& before_data = keyframes.at(before_keyframe).data; + switch (type) { + case EFFECT_FIELD_DOUBLE: + { + double value; + if (before_keyframe == after_keyframe) { + value = keyframes.at(before_keyframe).data.toDouble(); + } else { + const EffectKeyframe& before_key = keyframes.at(before_keyframe); + const EffectKeyframe& after_key = keyframes.at(after_keyframe); - const QVariant& before_data = keyframes.at(before_keyframe).data; - switch (type) { - case EFFECT_FIELD_DOUBLE: - { - double value; - if (before_keyframe == after_keyframe) { - value = keyframes.at(before_keyframe).data.toDouble(); - } else { - double before_dbl = keyframes.at(before_keyframe).data.toDouble(); - double after_dbl = keyframes.at(after_keyframe).data.toDouble(); - value = double_lerp(before_dbl, after_dbl, progress); - } - if (async) { - return value; - } - static_cast(ui_element)->set_value(value, false); - } - break; - case EFFECT_FIELD_COLOR: - { - QColor value; - if (before_keyframe == after_keyframe) { - value = keyframes.at(before_keyframe).data.value(); - } else { - QColor before_data = keyframes.at(before_keyframe).data.value(); - QColor after_data = keyframes.at(after_keyframe).data.value(); - value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); - } - if (async) { - return value; - } - static_cast(ui_element)->set_color(value); - } - break; - case EFFECT_FIELD_STRING: - if (async) { - return before_data; - } - static_cast(ui_element)->setPlainTextEx(before_data.toString()); - break; - case EFFECT_FIELD_BOOL: - if (async) { - return before_data; - } - static_cast(ui_element)->setChecked(before_data.toBool()); - break; - case EFFECT_FIELD_COMBO: - if (async) { - return before_data; - } - static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); - break; - case EFFECT_FIELD_FONT: - if (async) { - return before_data; - } - static_cast(ui_element)->setCurrentTextEx(before_data.toString()); - break; - } - } - return QVariant(); + double before_dbl = before_key.data.toDouble(); + double after_dbl = after_key.data.toDouble(); + + if (before_key.type == KEYFRAME_TYPE_HOLD) { + // hold + value = before_dbl; + } else if (before_key.type == KEYFRAME_TYPE_BEZIER || after_key.type == KEYFRAME_TYPE_BEZIER) { + // bezier interpolation + if (before_key.type == KEYFRAME_TYPE_BEZIER && after_key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + double t = cubic_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time+after_key.pre_handle_x, after_key.time); + value = cubic_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl+after_key.pre_handle_y, after_dbl, t); + } else if (after_key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, before_key.time+before_key.post_handle_x, after_key.time); + value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t); + } else { + // this keyframe is the bezier one + double t = quad_t_from_x(timecode*parent_row->parent_effect->parent_clip->sequence->frame_rate, before_key.time, after_key.time+after_key.pre_handle_x, after_key.time); + value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t); + } + } else { + // linear + value = double_lerp(before_dbl, after_dbl, progress); + } + } + if (async) { + return value; + } + static_cast(ui_element)->set_value(value, false); + } + break; + case EFFECT_FIELD_COLOR: + { + QColor value; + if (before_keyframe == after_keyframe) { + value = keyframes.at(before_keyframe).data.value(); + } else { + QColor before_data = keyframes.at(before_keyframe).data.value(); + QColor after_data = keyframes.at(after_keyframe).data.value(); + value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); + } + if (async) { + return value; + } + static_cast(ui_element)->set_color(value); + } + break; + case EFFECT_FIELD_STRING: + if (async) { + return before_data; + } + static_cast(ui_element)->setPlainTextEx(before_data.toString()); + break; + case EFFECT_FIELD_BOOL: + if (async) { + return before_data; + } + static_cast(ui_element)->setChecked(before_data.toBool()); + break; + case EFFECT_FIELD_COMBO: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); + break; + case EFFECT_FIELD_FONT: + if (async) { + return before_data; + } + static_cast(ui_element)->setCurrentTextEx(before_data.toString()); + break; + } + } + return QVariant(); } void EffectField::ui_element_change() { - bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast(ui_element)->is_dragging()); - ComboAction* ca = NULL; - if (!dragging_double) ca = new ComboAction(); - make_key_from_change(ca); - if (!dragging_double) undo_stack.push(ca); - emit changed(); + bool dragging_double = (type == EFFECT_FIELD_DOUBLE && static_cast(ui_element)->is_dragging()); + ComboAction* ca = NULL; + if (!dragging_double) ca = new ComboAction(); + make_key_from_change(ca); + if (!dragging_double) undo_stack.push(ca); + emit changed(); } void EffectField::make_key_from_change(ComboAction* ca) { - if (parent_row->isKeyframing()) { - parent_row->set_keyframe_now(ca); - } else if (ca != NULL) { - // set undo - ca->append(new EffectFieldUndo(this)); - } + if (parent_row->isKeyframing()) { + parent_row->set_keyframe_now(ca); + } else if (ca != NULL) { + // set undo + ca->append(new EffectFieldUndo(this)); + } } QWidget* EffectField::get_ui_element() { - return ui_element; + return ui_element; } void EffectField::set_enabled(bool e) { - ui_element->setEnabled(e); + ui_element->setEnabled(e); } double EffectField::get_double_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toDouble(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->value(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toDouble(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->value(); } void EffectField::set_double_value(double v) { - static_cast(ui_element)->set_value(v, false); + static_cast(ui_element)->set_value(v, false); } void EffectField::set_double_default_value(double v) { - static_cast(ui_element)->set_default_value(v); + static_cast(ui_element)->set_default_value(v); } void EffectField::set_double_minimum_value(double v) { - static_cast(ui_element)->set_minimum_value(v); + static_cast(ui_element)->set_minimum_value(v); } void EffectField::set_double_maximum_value(double v) { - static_cast(ui_element)->set_maximum_value(v); + static_cast(ui_element)->set_maximum_value(v); } void EffectField::add_combo_item(const QString& name, const QVariant& data) { - static_cast(ui_element)->addItem(name, data); + static_cast(ui_element)->addItem(name, data); } int EffectField::get_combo_index(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toInt(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentIndex(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toInt(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentIndex(); } const QVariant EffectField::get_combo_data(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentData(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentData(); } const QString EffectField::get_combo_string(double timecode) { - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentText(); + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_combo_index(int index) { - static_cast(ui_element)->setCurrentIndexEx(index); + static_cast(ui_element)->setCurrentIndexEx(index); } void EffectField::set_combo_string(const QString& s) { - static_cast(ui_element)->setCurrentTextEx(s); + static_cast(ui_element)->setCurrentTextEx(s); } bool EffectField::get_bool_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toBool(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->isChecked(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toBool(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->isChecked(); } void EffectField::set_bool_value(bool b) { - return static_cast(ui_element)->setChecked(b); + return static_cast(ui_element)->setChecked(b); } const QString EffectField::get_string_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toString(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->getPlainTextEx(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->getPlainTextEx(); } void EffectField::set_string_value(const QString& s) { - static_cast(ui_element)->setPlainTextEx(s); + static_cast(ui_element)->setPlainTextEx(s); } const QString EffectField::get_font_name(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).toString(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->currentText(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).toString(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->currentText(); } void EffectField::set_font_name(const QString& s) { - static_cast(ui_element)->setCurrentText(s); + static_cast(ui_element)->setCurrentText(s); } QColor EffectField::get_color_value(double timecode, bool async) { - if (async && hasKeyframes()) { - return validate_keyframe_data(timecode, true).value(); - } - validate_keyframe_data(timecode); - return static_cast(ui_element)->get_color(); + if (async && hasKeyframes()) { + return validate_keyframe_data(timecode, true).value(); + } + validate_keyframe_data(timecode); + return static_cast(ui_element)->get_color(); } void EffectField::set_color_value(QColor color) { - static_cast(ui_element)->set_color(color); + static_cast(ui_element)->set_color(color); } diff --git a/project/effectfield.h b/project/effectfield.h index f95c896cd..c946c475b 100644 --- a/project/effectfield.h +++ b/project/effectfield.h @@ -12,20 +12,11 @@ #include #include +#include "keyframe.h" + class EffectRow; class ComboAction; -class EffectKeyframe { -public: - long time; - int type; - QVariant data; - - // only for bezier type - QPointF pre_handle; - QPointF post_handle; -}; - class EffectField : public QObject { Q_OBJECT public: diff --git a/project/effectrow.cpp b/project/effectrow.cpp index c980c37f3..a9b7fd0f1 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -17,123 +17,123 @@ #include "ui/clickablelabel.h" EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QString &n, int row) : - parent_effect(parent), - savable(save), - keyframing(false), - ui(uilayout), - name(n), - ui_row(row), - just_made_unsafe_keyframe(false) + parent_effect(parent), + savable(save), + keyframing(false), + ui(uilayout), + name(n), + ui_row(row), + just_made_unsafe_keyframe(false) { - label = new ClickableLabel(name); + label = new ClickableLabel(name + ":"); - ui->addWidget(label, row, 0); + ui->addWidget(label, row, 0); - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { connect(label, SIGNAL(clicked()), this, SLOT(focus_row())); - keyframe_nav = new KeyframeNavigator(); - connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); - connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); - connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); + keyframe_nav = new KeyframeNavigator(); + connect(keyframe_nav, SIGNAL(goto_previous_key()), this, SLOT(goto_previous_key())); + connect(keyframe_nav, SIGNAL(toggle_key()), this, SLOT(toggle_key())); + connect(keyframe_nav, SIGNAL(goto_next_key()), this, SLOT(goto_next_key())); connect(keyframe_nav, SIGNAL(keyframe_enabled_changed(bool)), this, SLOT(set_keyframe_enabled(bool))); connect(keyframe_nav, SIGNAL(clicked()), this, SLOT(focus_row())); - ui->addWidget(keyframe_nav, row, 6); - } + ui->addWidget(keyframe_nav, row, 6); + } } bool EffectRow::isKeyframing() { - return keyframing; + return keyframing; } void EffectRow::setKeyframing(bool b) { - if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { - keyframing = b; - keyframe_nav->enable_keyframes(b); - } + if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) { + keyframing = b; + keyframe_nav->enable_keyframes(b); + } } void EffectRow::set_keyframe_enabled(bool enabled) { - if (enabled) { - ComboAction* ca = new ComboAction(); - set_keyframe_now(ca); - undo_stack.push(ca); - } else { - if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - // clear - ComboAction* ca = new ComboAction(); - for (int i=0;ikeyframes.size();j++) { - ca->append(new KeyframeDelete(f, 0)); - } - } - undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - } else { - setKeyframing(true); - } - } + if (enabled) { + ComboAction* ca = new ComboAction(); + set_keyframe_now(ca); + undo_stack.push(ca); + } else { + if (QMessageBox::question(panel_effect_controls, "Disable Keyframes", "Disabling keyframes will delete all current keyframes. Are you sure you want to do this?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + // clear + ComboAction* ca = new ComboAction(); + for (int i=0;ikeyframes.size();j++) { + ca->append(new KeyframeDelete(f, 0)); + } + } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + } else { + setKeyframing(true); + } + } } void EffectRow::goto_previous_key() { - long key = LONG_MIN; - Clip* c = parent_effect->parent_clip; - for (int i=0;ikeyframes.size();j++) { - long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; - if (comp < sequence->playhead) { - key = qMax(comp, key); - } - } - } - if (key != LONG_MIN) panel_sequence_viewer->seek(key); + long key = LONG_MIN; + Clip* c = parent_effect->parent_clip; + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in; + if (comp < sequence->playhead) { + key = qMax(comp, key); + } + } + } + if (key != LONG_MIN) panel_sequence_viewer->seek(key); } void EffectRow::toggle_key() { - QVector key_fields; - QVector key_field_index; - Clip* c = parent_effect->parent_clip; - for (int j=0;jkeyframes.size();i++) { - long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; - if (comp == sequence->playhead) { - key_fields.append(f); - key_field_index.append(i); - } - } - } + QVector key_fields; + QVector key_field_index; + Clip* c = parent_effect->parent_clip; + for (int j=0;jkeyframes.size();i++) { + long comp = c->timeline_in - c->clip_in + f->keyframes.at(i).time; + if (comp == sequence->playhead) { + key_fields.append(f); + key_field_index.append(i); + } + } + } - ComboAction* ca = new ComboAction(); - if (key_fields.size() == 0) { - // keyframe doesn't exist, set one - set_keyframe_now(ca); - } else { - for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); - } - } - undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + ComboAction* ca = new ComboAction(); + if (key_fields.size() == 0) { + // keyframe doesn't exist, set one + set_keyframe_now(ca); + } else { + for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); + } + } + undo_stack.push(ca); + panel_effect_controls->update_keyframes(); + panel_sequence_viewer->viewer_widget->update(); } void EffectRow::goto_next_key() { - long key = LONG_MAX; - Clip* c = parent_effect->parent_clip; - for (int i=0;ikeyframes.size();j++) { - long comp = f->keyframes.at(i).time - c->clip_in + c->timeline_in; - if (comp > sequence->playhead) { - key = qMin(comp, key); - } - } - } - if (key != LONG_MAX) panel_sequence_viewer->seek(key); + long key = LONG_MAX; + Clip* c = parent_effect->parent_clip; + for (int i=0;ikeyframes.size();j++) { + long comp = f->keyframes.at(j).time - c->clip_in + c->timeline_in; + if (comp > sequence->playhead) { + key = qMin(comp, key); + } + } + } + if (key != LONG_MAX) panel_sequence_viewer->seek(key); } void EffectRow::focus_row() { @@ -141,58 +141,58 @@ void EffectRow::focus_row() { } EffectField* EffectRow::add_field(int type, const QString& id, int colspan) { - EffectField* field = new EffectField(this, type, id); + EffectField* field = new EffectField(this, type, id); if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) connect(field, SIGNAL(clicked()), this, SLOT(focus_row())); - fields.append(field); - QWidget* element = field->get_ui_element(); - ui->addWidget(element, ui_row, fields.size(), 1, colspan); - connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed())); - return field; + fields.append(field); + QWidget* element = field->get_ui_element(); + ui->addWidget(element, ui_row, fields.size(), 1, colspan); + connect(field, SIGNAL(changed()), parent_effect, SLOT(field_changed())); + return field; } EffectRow::~EffectRow() { - for (int i=0;iplayhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; - for (int j=0;jkeyframes.size();i++) { - if (f->keyframes.at(i).time == time) { - index = i; - break; - } - } - } + int index = -1; + long time = sequence->playhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in; + for (int j=0;jkeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + index = i; + break; + } + } + } - KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe); + KeyframeSet* ks = new KeyframeSet(this, index, time, just_made_unsafe_keyframe); - if (ca != NULL) { - just_made_unsafe_keyframe = false; - ca->append(ks); - } else { - if (index == -1) just_made_unsafe_keyframe = true; - ks->redo(); - delete ks; - } + if (ca != NULL) { + just_made_unsafe_keyframe = false; + ca->append(ks); + } else { + if (index == -1) just_made_unsafe_keyframe = true; + ks->redo(); + delete ks; + } - panel_effect_controls->update_keyframes(); + panel_effect_controls->update_keyframes(); } void EffectRow::delete_keyframe_at_time(ComboAction* ca, long time) { - for (int j=0;jkeyframes.size();i++) { - if (f->keyframes.at(i).time == time) { - ca->append(new KeyframeDelete(f, i)); - break; - } - } - } + for (int j=0;jkeyframes.size();i++) { + if (f->keyframes.at(i).time == time) { + ca->append(new KeyframeDelete(f, i)); + break; + } + } + } } const QString &EffectRow::get_name() { @@ -200,9 +200,9 @@ const QString &EffectRow::get_name() { } EffectField* EffectRow::field(int i) { - return fields.at(i); + return fields.at(i); } int EffectRow::fieldCount() { - return fields.size(); + return fields.size(); } diff --git a/project/keyframe.cpp b/project/keyframe.cpp index 8068d5129..3d12220df 100644 --- a/project/keyframe.cpp +++ b/project/keyframe.cpp @@ -1,2 +1,9 @@ #include "keyframe.h" + +EffectKeyframe::EffectKeyframe() { + pre_handle_x = -40; + pre_handle_y = 0; + post_handle_x = 40; + post_handle_y = 0; +} diff --git a/project/keyframe.h b/project/keyframe.h index 7cad20bfc..7974a8a1b 100644 --- a/project/keyframe.h +++ b/project/keyframe.h @@ -1,17 +1,21 @@ #ifndef KEYFRAME_H #define KEYFRAME_H - - #include -#include -class KeyframeData -{ +class EffectKeyframe { public: + EffectKeyframe(); + + long time; + int type; QVariant data; - QPoint handle_pre; - QPoint handle_post; + + // only for bezier type + double pre_handle_x; + double pre_handle_y; + double post_handle_x; + double post_handle_y; }; #endif // KEYFRAME_H diff --git a/project/undo.cpp b/project/undo.cpp index 5187d2a6e..3e2b445d0 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -738,20 +738,20 @@ void MediaRename::redo() { } KeyframeDelete::KeyframeDelete(EffectField *ifield, int iindex) : - field(ifield), - index(iindex), - old_project_changed(mainWindow->isWindowModified()) + field(ifield), + index(iindex), + old_project_changed(mainWindow->isWindowModified()) {} void KeyframeDelete::undo() { - field->keyframes.insert(index, deleted_key); + field->keyframes.insert(index, deleted_key); mainWindow->setWindowModified(old_project_changed); } void KeyframeDelete::redo() { - deleted_key = field->keyframes.at(index); - field->keyframes.removeAt(index); - mainWindow->setWindowModified(true); + deleted_key = field->keyframes.at(index); + field->keyframes.removeAt(index); + mainWindow->setWindowModified(true); } KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : @@ -771,7 +771,7 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : if (field->type == EFFECT_FIELD_DOUBLE) { old_values[i] = static_cast(field->ui_element)->getPreviousValue(); } else { - old_values[i] = field->keyframes.at(index).data; + old_values[i] = field->keyframes.at(index).data; } } new_values[i] = field->get_current_data(); @@ -781,12 +781,12 @@ KeyframeSet::KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe) : void KeyframeSet::undo() { if (enable_keyframes) row->setKeyframing(false); - bool append = (index == -1 || just_made_keyframe); + bool append = (index == -1 || just_made_keyframe); for (int i=0;ifieldCount();i++) { if (append) { - row->field(i)->keyframes.removeLast(); + row->field(i)->keyframes.removeLast(); } else { - row->field(i)->keyframes[index].data = old_values.at(i); + row->field(i)->keyframes[index].data = old_values.at(i); } } @@ -797,15 +797,15 @@ void KeyframeSet::undo() { void KeyframeSet::redo() { bool append = (index == -1 || (just_made_keyframe && !done)); for (int i=0;ifieldCount();i++) { - EffectField* f = row->field(i); + EffectField* f = row->field(i); if (append) { - EffectKeyframe k; - k.data = new_values.at(i); - k.time = time; - k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; - f->keyframes.append(k); + EffectKeyframe k; + k.data = new_values.at(i); + k.time = time; + k.type = (f->keyframes.size() > 0) ? f->keyframes.last().type : EFFECT_KEYFRAME_LINEAR; + f->keyframes.append(k); } else { - f->keyframes[index].data = new_values.at(i); + f->keyframes[index].data = new_values.at(i); } } row->setKeyframing(true); @@ -1229,9 +1229,9 @@ void RippleAction::redo() { ca->redo(); } -SetDouble::SetDouble(double* pointer, double new_value) : +SetDouble::SetDouble(double* pointer, double old_value, double new_value) : p(pointer), - oldval(*pointer), + oldval(old_value), newval(new_value), old_project_changed(mainWindow->isWindowModified()) {} diff --git a/project/undo.h b/project/undo.h index 9ef3a1e3b..fcb44f500 100644 --- a/project/undo.h +++ b/project/undo.h @@ -331,21 +331,21 @@ private: class KeyframeDelete : public QUndoCommand { public: - KeyframeDelete(EffectField* ifield, int iindex); + KeyframeDelete(EffectField* ifield, int iindex); void undo(); void redo(); private: - EffectField* field; - int index; - bool done; - EffectKeyframe deleted_key; - bool old_project_changed; + EffectField* field; + int index; + bool done; + EffectKeyframe deleted_key; + bool old_project_changed; }; class KeyframeSet : public QUndoCommand { public: - KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); + KeyframeSet(EffectRow* r, int i, long t, bool justMadeKeyframe); void undo(); void redo(); QVector old_values; @@ -523,7 +523,7 @@ private: class SetDouble : public QUndoCommand { public: - SetDouble(double* pointer, double new_value); + SetDouble(double* pointer, double old_value, double new_value); void undo(); void redo(); private: diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 5accaacad..84a87cebe 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -11,11 +11,17 @@ #include "project/effectfield.h" #include "ui/keyframedrawing.h" #include "project/undo.h" +#include "project/effect.h" #include "debug.h" #define GRAPH_ZOOM_SPEED 0.05 #define GRAPH_SIZE 100 +#define BEZIER_HANDLE_SIZE 3 + +#define BEZIER_HANDLE_NONE 1 +#define BEZIER_HANDLE_PRE 2 +#define BEZIER_HANDLE_POST 3 QColor get_curve_color(int index, int length) { QColor c; @@ -31,7 +37,8 @@ GraphView::GraphView(QWidget* parent) : mousedown(false), zoom(1.0), row(NULL), - moved_keys(false) + moved_keys(false), + current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); } @@ -40,7 +47,8 @@ void GraphView::paintEvent(QPaintEvent *event) { QPainter p(this); if (panel_sequence_viewer->seq != NULL) { - // draw lines + // draw grid lines + //int graph_size = GRAPH_SIZE*zoom; bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); @@ -75,53 +83,100 @@ void GraphView::paintEvent(QPaintEvent *event) { QPen line_pen; line_pen.setWidth(2); - for (int i=0;ifieldCount();i++) { - EffectField* field = row->field(i); + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - // sort keyframes by time - QVector sorted_keys; - for (int k=0;kkeyframes.size();k++) { - bool inserted = false; - for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { - sorted_keys.insert(j, k); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(i); - } - } + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + // sort keyframes by time + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(k); + } + } - int last_key_x, last_key_y; + int last_key_x, last_key_y; + // draw lines for (int j=0;jkeyframes.at(sorted_keys.at(j)); - int key_x = get_screen_x(field->keyframes.at(key_index).time); - int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); line_pen.setColor(get_curve_color(i, row->fieldCount())); p.setPen(line_pen); - if (key_index == 0) { + if (j == 0) { p.drawLine(0, key_y, key_x, key_y); } else { - p.drawLine(last_key_x, last_key_y, key_x, key_y); + const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1)); + if (last_key.type == KEYFRAME_TYPE_HOLD) { + // hold + p.drawLine(last_key_x, last_key_y, key_x, last_key_y); + p.drawLine(key_x, last_key_y, key_x, key_y); + } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + QPainterPath bezier_path; + bezier_path.moveTo(last_key_x, last_key_y); + if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + bezier_path.cubicTo( + QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x, key_y) + ); + } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + bezier_path.quadTo( + QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(key_x, key_y) + ); + } else { + // this keyframe is the bezier one + bezier_path.quadTo( + QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x, key_y) + ); + } + p.drawPath(bezier_path); + } else { + // linear + p.drawLine(last_key_x, last_key_y, key_x, key_y); + } } last_key_x = key_x; last_key_y = key_y; } + + // draw keys for (int j=0;jkeyframes.at(sorted_keys.at(j)); - int key_x = get_screen_x(field->keyframes.at(key_index).time); - int key_y = get_screen_y(field->keyframes.at(key_index).data.toDouble()); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); - draw_keyframe(p, field->keyframes.at(key_index).type, key_x, key_y, (selected_keys.contains(key_index) && selected_keys_fields.contains(i))); + if (key.type == KEYFRAME_TYPE_BEZIER) { + p.setPen(Qt::gray); + + // pre handle line + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + p.drawLine(pre_point, QPointF(key_x, key_y)); + p.drawEllipse(pre_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); + + // post handle line + QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + p.drawLine(post_point, QPointF(key_x, key_y)); + p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); + } + + draw_keyframe(p, key.type, key_x, key_y, (selected_keys.contains(sorted_keys.at(j)) && selected_keys_fields.contains(i))); } } } @@ -153,13 +208,15 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys.clear(); selected_keys_fields.clear(); } + current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE) { - for (int j=0;jkeyframes.size();j++) { - int key_x = get_screen_x(field->keyframes.at(j).time); - int key_y = get_screen_y(field->keyframes.at(j).data.toDouble()); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + for (int j=0;jkeyframes.size();j++) { + const EffectKeyframe& key = field->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); if (event->pos().x() > key_x-KEYFRAME_SIZE && event->pos().x() < key_x+KEYFRAME_SIZE && event->pos().y() > key_y-KEYFRAME_SIZE @@ -167,6 +224,33 @@ void GraphView::mousePressEvent(QMouseEvent *event) { sel_key = j; sel_key_field = i; break; + } else { + // selecting a handle + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + sel_key = j; + sel_key_field = i; + old_handle_x = key.pre_handle_x; + old_handle_y = key.pre_handle_y; + current_handle = BEZIER_HANDLE_PRE; + break; + } else { + QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + sel_key = j; + sel_key_field = i; + old_handle_x = key.post_handle_x; + old_handle_y = key.post_handle_y; + current_handle = BEZIER_HANDLE_POST; + break; + } + } } } } @@ -179,16 +263,24 @@ void GraphView::mousePressEvent(QMouseEvent *event) { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); + + int selected_key_type = -1; + for (int i=0;ifield(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).time); - selected_keys_old_doubles.append(row->field(selected_keys_fields.at(j))->keyframes.at(selected_keys.at(i)).data.toDouble()); + const EffectKeyframe& key = row->field(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + selected_keys_old_vals.append(key.time); + selected_keys_old_doubles.append(key.data.toDouble()); + + if (selected_key_type == -1) { + selected_key_type = key.type; + } else if (selected_key_type != key.type) { + selected_key_type = -2; } } update(); - emit selection_changed(selected_keys.size() > 0); + emit selection_changed(selected_keys.size() > 0, selected_key_type); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -200,39 +292,60 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_y = event->pos().y(); update(); } else { - for (int i=0;ifield(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].time = selected_keys_old_vals.at(index) + (double(event->pos().x() - start_x)/zoom); - row->field(selected_keys_fields.at(j))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(index) + (double(start_y - event->pos().y())/zoom); + switch (current_handle) { + case BEZIER_HANDLE_NONE: + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); } + moved_keys = true; + update_ui(false); + break; + case BEZIER_HANDLE_PRE: + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + moved_keys = true; + update_ui(false); + break; + case BEZIER_HANDLE_POST: + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + moved_keys = true; + update_ui(false); + break; } - moved_keys = true; - update_ui(false); + } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { if (moved_keys && selected_keys.size() > 0) { - /*ComboAction* ca = new ComboAction(); - QVector rows; - QVector new_vals; - - for (int i=0;ikeyframe_times.at(selected_keys.at(i))); - - for (int j=0;jappend(new SetQVariant(&row->field(selected_keys_fields.at(j))->keyframe_data[selected_keys.at(i)], - selected_keys_old_doubles.at((i*selected_keys_fields.size())+j), - row->field(selected_keys_fields.at(j))->keyframe_data.at(selected_keys.at(i)))); + ComboAction* ca = new ComboAction(); + switch (current_handle) { + case BEZIER_HANDLE_NONE: + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)]; + ca->append(new SetLong(&key.time, selected_keys_old_vals.at(i), key.time)); + ca->append(new SetQVariant(&key.data, selected_keys_old_doubles.at(i), key.data)); } - //ca->append(new KeyframeSet(row, selected_keys.at(i), 0, false)); - } - - ca->append(new KeyframeMove(rows, selected_keys, selected_keys_old_vals, new_vals)); - undo_stack.push(ca);*/ + break; + case BEZIER_HANDLE_PRE: + { + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + ca->append(new SetDouble(&key.pre_handle_x, old_handle_x, key.pre_handle_x)); + ca->append(new SetDouble(&key.pre_handle_y, old_handle_y, key.pre_handle_y)); + } + break; + case BEZIER_HANDLE_POST: + { + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + ca->append(new SetDouble(&key.post_handle_x, old_handle_x, key.post_handle_x)); + ca->append(new SetDouble(&key.post_handle_y, old_handle_y, key.post_handle_y)); + } + break; + } + undo_stack.push(ca); } moved_keys = false; mousedown = false; @@ -269,19 +382,39 @@ void GraphView::set_row(EffectRow *r) { selected_keys_fields.clear(); selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); - emit selection_changed(false); + emit selection_changed(false, -1); row = r; + if (row != NULL) { + field_visibility.resize(row->fieldCount()); + field_visibility.fill(true); + } + update(); +} + +void GraphView::set_selected_keyframe_type(int type) { + if (selected_keys.size() > 0) { + ComboAction* ca = new ComboAction(); + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)]; + ca->append(new SetInt(&key.type, type)); + } + undo_stack.push(ca); + update_ui(false); + } +} + +void GraphView::set_field_visibility(int field, bool b) { + field_visibility[field] = b; update(); } void GraphView::set_scroll_x(int s) { - x_scroll = s;//qMax(0, s); - dout << x_scroll; + x_scroll = s; emit x_scroll_changed(x_scroll); } void GraphView::set_scroll_y(int s) { - y_scroll = s;//qMax(0, s); + y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index 97b6e1e2c..d7511184b 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -20,11 +20,14 @@ public: void wheelEvent(QWheelEvent *event); void set_row(EffectRow* r); + + void set_selected_keyframe_type(int type); + void set_field_visibility(int field, bool b); signals: void x_scroll_changed(int); void y_scroll_changed(int); void zoom_changed(double); - void selection_changed(bool); + void selection_changed(bool, int); private: int x_scroll; int y_scroll; @@ -39,13 +42,20 @@ private: int get_screen_x(double); int get_screen_y(double); + QVector field_visibility; + QVector selected_keys; QVector selected_keys_fields; QVector selected_keys_old_vals; QVector selected_keys_old_doubles; + double old_handle_x; + double old_handle_y; + bool moved_keys; + int current_handle; + EffectRow* row; }; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 7518c4575..4137f68e2 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -44,13 +44,13 @@ KeyframeView::KeyframeView(QWidget *parent) : } void KeyframeView::show_context_menu(const QPoint& pos) { - if (selected_fields.size() > 0) { + if (selected_fields.size() > 0) { QMenu menu(this); QAction* linear = menu.addAction("Linear"); linear->setData(KEYFRAME_TYPE_LINEAR); - QAction* smooth = menu.addAction("Smooth"); - smooth->setData(KEYFRAME_TYPE_BEZIER); + QAction* bezier = menu.addAction("Bezier"); + bezier->setData(KEYFRAME_TYPE_BEZIER); QAction* hold = menu.addAction("Hold"); hold->setData(KEYFRAME_TYPE_HOLD); menu.addSeparator(); @@ -67,12 +67,12 @@ void KeyframeView::menu_set_key_type(QAction* a) { panel_graph_editor->show(); } else { ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); + for (int i=0;iappend(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); } undo_stack.push(ca); - update_keys(); + update_ui(false); } } @@ -103,19 +103,19 @@ void KeyframeView::paintEvent(QPaintEvent*) { ClickableLabel* label = row->label; QWidget* contents = e->container->contents; - QVector key_times; + QVector key_times; int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height()/* - y_scroll*/; - for (int l=0;lfieldCount();l++) { - EffectField* f = row->field(l); - for (int k=0;kkeyframes.size();k++) { - if (!key_times.contains(f->keyframes.at(k).time)) { - bool keyframe_selected = keyframeIsSelected(f, k); - long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); - draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); - key_times.append(f->keyframes.at(k).time); - } - } - } + for (int l=0;lfieldCount();l++) { + EffectField* f = row->field(l); + for (int k=0;kkeyframes.size();k++) { + if (!key_times.contains(f->keyframes.at(k).time)) { + bool keyframe_selected = keyframeIsSelected(f, k); + long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + key_times.append(f->keyframes.at(k).time); + } + } + } rows.append(row); rowY.append(keyframe_y); @@ -150,8 +150,8 @@ void KeyframeView::paintEvent(QPaintEvent*) { } bool KeyframeView::keyframeIsSelected(EffectField *field, int keyframe) { - for (int i=0;iappend(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); + // TODO these need to be sorted + ca->append(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); del = true; } if (del) { - undo_stack.push(ca); + undo_stack.push(ca); selected_keyframes.clear(); - selected_fields.clear(); + selected_fields.clear(); update_keys(); panel_sequence_viewer->viewer_widget->update(); } else { - delete ca; + delete ca; } } @@ -211,7 +211,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { int mouse_x = event->x() + x_scroll; int mouse_y = event->y(); int row_index = -1; - int field_index = -1; + int field_index = -1; int keyframe_index = -1; long frame_diff = 0; long frame_min = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x-KEYFRAME_SIZE); @@ -223,41 +223,41 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { row->focus_row(); - for (int k=0;kfieldCount();k++) { - EffectField* f = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); - if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { - long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); - if (keyframe_index == -1 || eval_frame_diff < frame_diff) { - row_index = i; - field_index = k; - keyframe_index = j; - frame_diff = eval_frame_diff; - } - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* f = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long eval_keyframe_time = f->keyframes.at(j).time-row->parent_effect->parent_clip->clip_in+(row->parent_effect->parent_clip->timeline_in-visible_in); + if (eval_keyframe_time >= frame_min && eval_keyframe_time <= frame_max) { + long eval_frame_diff = qAbs(eval_keyframe_time - drag_frame_start); + if (keyframe_index == -1 || eval_frame_diff < frame_diff) { + row_index = i; + field_index = k; + keyframe_index = j; + frame_diff = eval_frame_diff; + } + } + } + } break; } } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_fields.clear(); + selected_fields.clear(); selected_keyframes.clear(); } if (keyframe_index > -1) { - selected_fields.append(rows.at(row_index)->field(field_index)); + selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); } } - if (selected_fields.size() > 0) { - for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); + if (selected_fields.size() > 0) { + for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } keys_selected = true; @@ -291,8 +291,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { panel_timeline->snapped = false; if (panel_timeline->snapping) { for (int i=0;iparent_row->parent_effect->parent_clip; + EffectField* field = selected_fields.at(i); + Clip* c = field->parent_row->parent_effect->parent_clip; long key_time = old_key_vals.at(i) + frame_diff - c->clip_in + c->timeline_in; long key_eval = key_time; if (panel_timeline->snap_to_point(sequence->playhead, &key_eval)) { @@ -303,11 +303,11 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { } // validate frame_diff (make sure no keyframes overlap each other) - for (int i=0;ikeyframes.size();j++) { - while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { + for (int j=0;jkeyframes.size();j++) { + while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { frame_diff++; panel_timeline->snapped = false; @@ -321,8 +321,8 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { // apply frame_diffs for (int i=0;ikeyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; + EffectField* field = selected_fields.at(i); + field->keyframes[selected_keyframes.at(i)].time = old_key_vals.at(i) + frame_diff; } last_frame_diff = frame_diff; @@ -346,16 +346,16 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { for (int i=0;i= min_row && rowY.at(i) <= max_row) { EffectRow* row = rows.at(i); - for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); - if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_fields.append(field); - selected_keyframes.append(j); - } - } - } + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } } } @@ -368,15 +368,15 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { void KeyframeView::mouseReleaseEvent(QMouseEvent*) { if (dragging) { - ComboAction* ca = new ComboAction(); - for (int i=0;iappend(new SetLong( - &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, - old_key_vals.at(i), - selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time - )); + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new SetLong( + &selected_fields.at(i)->keyframes[selected_keyframes.at(i)].time, + old_key_vals.at(i), + selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time + )); } - undo_stack.push(ca); + undo_stack.push(ca); } select_rect = false; From e8b2f3129cf0d6fafdc8fcf684187221a2554ca0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 23:20:41 +1100 Subject: [PATCH 17/32] anchor points now recenter when the media is replaced (#226) --- effects/internal/transformeffect.cpp | 42 ++++++++++++++++++++++++---- effects/internal/transformeffect.h | 3 ++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 0b7466dd5..352e0a6e6 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -119,24 +119,52 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) // set defaults uniform_scale_field->set_bool_value(true); blend_mode_box->set_combo_index(0); + set = false; refresh(); } +void adjust_field(EffectField* field, double old_offset, double new_offset) { + if (field->keyframes.size() > 0) { + for (int i=0;ikeyframes.size();i++) { + field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; + } + } else { + field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); + } +} + void TransformEffect::refresh() { if (parent_clip != NULL && parent_clip->sequence != NULL) { - double default_pos_x = parent_clip->sequence->width/2; - double default_pos_y = parent_clip->sequence->height/2; + double new_default_pos_x = parent_clip->sequence->width/2; + double new_default_pos_y = parent_clip->sequence->height/2; + + /*if (set) { + adjust_field(position_x, default_pos_x, new_default_pos_x); + adjust_field(position_y, default_pos_y, new_default_pos_y); + }*/ + + default_pos_x = new_default_pos_x; + default_pos_y = new_default_pos_y; position_x->set_double_default_value(default_pos_x); position_y->set_double_default_value(default_pos_y); scale_x->set_double_default_value(100); scale_y->set_double_default_value(100); - default_anchor_x = parent_clip->getWidth()/2; - default_anchor_y = parent_clip->getHeight()/2; + int new_default_anchor_x = parent_clip->getWidth()/2; + int new_default_anchor_y = parent_clip->getHeight()/2; - if (default_anchor_x == 0) default_anchor_x = default_pos_x; - if (default_anchor_y == 0) default_anchor_y = default_pos_y; + if (new_default_anchor_x == 0) new_default_anchor_x = default_pos_x; + if (new_default_anchor_y == 0) new_default_anchor_y = default_pos_y; + + // adjust anchors for new size + if (set) { + adjust_field(anchor_x_box, default_anchor_x, new_default_anchor_x); + adjust_field(anchor_y_box, default_anchor_y, new_default_anchor_y); + } + + default_anchor_x = new_default_anchor_x; + default_anchor_y = new_default_anchor_y; anchor_x_box->set_double_default_value(default_anchor_x); anchor_y_box->set_double_default_value(default_anchor_y); @@ -157,6 +185,8 @@ void TransformEffect::refresh() { left_center_gizmo->x_field_multi1 = -x_percent_multipler; right_center_gizmo->x_field_multi1 = x_percent_multipler; rotate_gizmo->x_field_multi1 = x_percent_multipler; + + set = true; } } diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index c198ffb0a..c20ff6a6d 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -39,6 +39,9 @@ private: int default_anchor_x; int default_anchor_y; + double default_pos_x; + double default_pos_y; + bool set; }; #endif // TRANSFORMEFFECT_H From 9e912ef10356b1897b12b575a1750355d28143eb Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 3 Jan 2019 23:26:27 +1100 Subject: [PATCH 18/32] added playback control shortcuts to graph editor --- mainwindow.cpp | 27 +++++++++++++++++++++------ panels/grapheditor.cpp | 6 +++++- panels/grapheditor.h | 1 + ui/graphview.cpp | 1 + 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e1cb1764e..42afa55d2 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -727,7 +727,7 @@ void MainWindow::setup_menus() { tools_menu->addAction("Preferences", this, SLOT(preferences()), QKeySequence("Ctrl+.")); #ifdef QT_DEBUG - tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack()), QKeySequence("Ctrl+.")); + tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); #endif // INITIALIZE HELP MENU @@ -824,7 +824,10 @@ void MainWindow::reset_layout() { } void MainWindow::go_to_start() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_start(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_start(); @@ -832,7 +835,10 @@ void MainWindow::go_to_start() { } void MainWindow::prev_frame() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->previous_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->previous_frame(); @@ -840,7 +846,10 @@ void MainWindow::prev_frame() { } void MainWindow::next_frame() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->next_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->next_frame(); @@ -848,7 +857,10 @@ void MainWindow::next_frame() { } void MainWindow::go_to_end() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_end(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_end(); @@ -856,7 +868,10 @@ void MainWindow::go_to_end() { } void MainWindow::playpause() { - if (panel_timeline->focused() || panel_sequence_viewer->is_focused() || panel_effect_controls->keyframe_focus()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->toggle_play(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->toggle_play(); diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 7dc981fb4..961acac5e 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -193,7 +193,11 @@ void GraphEditor::set_row(EffectRow *r) { current_row_desc->setText(0); } view->set_row(row); - update_panel(); + update_panel(); +} + +bool GraphEditor::view_is_focused() { + return view->hasFocus() || header->hasFocus(); } void GraphEditor::set_key_button_enabled(bool e, int type) { diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 467de9ec3..16c71d23a 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -18,6 +18,7 @@ public: GraphEditor(QWidget* parent = 0); void update_panel(); void set_row(EffectRow* r); + bool view_is_focused(); private: GraphView* view; TimelineHeader* header; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 84a87cebe..1c7b2e92c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -41,6 +41,7 @@ GraphView::GraphView(QWidget* parent) : current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); } void GraphView::paintEvent(QPaintEvent *event) { From dabed8ceec43c874092cab2a8cdad4da2071a394 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 10:40:02 +1100 Subject: [PATCH 19/32] fixed regression with internal shaders and multiple effects paths --- project/effect.cpp | 25 +++++++++++++- project/effect.h | 83 +++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/project/effect.cpp b/project/effect.cpp index 40fc2b6b0..fa6c4b878 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -155,12 +155,17 @@ void load_internal_effects() { effects.append(em); } -void load_shader_effects() { +QList get_effects_paths() { QList effects_paths; effects_paths.append(get_app_dir() + "/effects"); effects_paths.append(get_app_dir() + "/../share/olive-editor/effects"); QString env_path(qgetenv("OLIVE_EFFECTS_PATH")); if (!env_path.isEmpty()) effects_paths.append(env_path); + return effects_paths; +} + +void load_shader_effects() { + QList effects_paths = get_effects_paths(); for (int h=0;hpath.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; + QList effects_paths = get_effects_paths(); + const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; + for (int i=0;iaddShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath); if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath); glslProgram->link(); diff --git a/project/effect.h b/project/effect.h index 6f8b35fc1..208ee18a0 100644 --- a/project/effect.h +++ b/project/effect.h @@ -26,13 +26,13 @@ class CheckboxEx; class KeyframeDelete; struct EffectMeta { - QString name; - QString category; - QString filename; + QString name; + QString category; + QString filename; QString path; int internal; int type; - int subtype; + int subtype; }; extern QVector effects; @@ -74,33 +74,33 @@ extern QMutex effects_loaded; #define KEYFRAME_TYPE_HOLD 2 struct GLTextureCoords { - int grid_size; + int grid_size; int vertexTopLeftX; int vertexTopLeftY; - int vertexTopLeftZ; + int vertexTopLeftZ; int vertexTopRightX; int vertexTopRightY; - int vertexTopRightZ; + int vertexTopRightZ; int vertexBottomLeftX; int vertexBottomLeftY; - int vertexBottomLeftZ; + int vertexBottomLeftZ; int vertexBottomRightX; int vertexBottomRightY; - int vertexBottomRightZ; + int vertexBottomRightZ; - float textureTopLeftX; - float textureTopLeftY; - float textureTopLeftQ; - float textureTopRightX; - float textureTopRightY; - float textureTopRightQ; - float textureBottomRightX; - float textureBottomRightY; - float textureBottomRightQ; - float textureBottomLeftX; - float textureBottomLeftY; - float textureBottomLeftQ; + float textureTopLeftX; + float textureTopLeftY; + float textureTopLeftQ; + float textureTopRightX; + float textureTopRightY; + float textureTopRightQ; + float textureBottomRightX; + float textureBottomRightY; + float textureBottomRightQ; + float textureBottomLeftX; + float textureBottomLeftY; + float textureBottomLeftQ; }; qint16 mix_audio_sample(qint16 a, qint16 b); @@ -114,21 +114,21 @@ class Effect : public QObject { public: Effect(Clip* c, const EffectMeta* em); ~Effect(); - Clip* parent_clip; + Clip* parent_clip; const EffectMeta* meta; - int id; + int id; QString name; CollapsibleWidget* container; - EffectRow* add_row(const QString &name, bool savable = true); + EffectRow* add_row(const QString &name, bool savable = true); EffectRow* row(int i); int row_count(); - EffectGizmo* add_gizmo(int type); - EffectGizmo* gizmo(int i); - int gizmo_count(); + EffectGizmo* add_gizmo(int type); + EffectGizmo* gizmo(int i); + int gizmo_count(); - bool is_enabled(); + bool is_enabled(); void set_enabled(bool b); virtual void refresh(); @@ -137,7 +137,7 @@ public: void copy_field_keyframes(Effect *e); void load(QXmlStreamReader& stream); - void save(QXmlStreamWriter& stream); + void save(QXmlStreamWriter& stream); // glsl handling void open(); @@ -147,24 +147,24 @@ public: bool enable_shader; bool enable_coords; - bool enable_superimpose; - bool enable_image; + bool enable_superimpose; + bool enable_image; int getIterations(); void setIterations(int i); const char* ffmpeg_filter; - virtual void process_image(double timecode, uint8_t* data, int size); + virtual void process_image(double timecode, uint8_t* data, int size); virtual void process_shader(double timecode, GLTextureCoords&); - virtual void process_coords(double timecode, GLTextureCoords& coords, int data); + virtual void process_coords(double timecode, GLTextureCoords& coords, int data); virtual GLuint process_superimpose(double timecode); virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); - virtual void gizmo_draw(double timecode, GLTextureCoords& coords); - void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); - void gizmo_world_to_screen(); - bool are_gizmos_enabled(); + virtual void gizmo_draw(double timecode, GLTextureCoords& coords); + void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); + void gizmo_world_to_screen(); + bool are_gizmos_enabled(); public slots: void field_changed(); private slots: @@ -182,17 +182,17 @@ protected: QImage img; QOpenGLTexture* texture; - // enable effect to update constantly - bool enable_always_update; + // enable effect to update constantly + bool enable_always_update; private: // superimpose effect QString script; bool isOpen; QVector rows; - QVector gizmos; + QVector gizmos; QGridLayout* ui_layout; - QWidget* ui; + QWidget* ui; bool bound; // superimpose functions @@ -201,6 +201,7 @@ private: QVector cachedValues; void delete_texture(); int get_index_in_clip(); + void validate_meta_path(); }; class EffectInit : public QThread { From 4157e4d432600b0bf7e3de9e381751154303e512 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 11:54:18 +1100 Subject: [PATCH 20/32] potential fix for #240 --- dialogs/exportdialog.cpp | 88 ++++++++++---------- dialogs/speeddialog.cpp | 2 +- mainwindow.cpp | 50 ++++++------ panels/viewer.cpp | 22 ++--- playback/playback.cpp | 171 +++++++++++++++++++-------------------- playback/playback.h | 4 +- project/clip.cpp | 20 ++--- project/effect.cpp | 4 + project/effect.h | 1 + project/undo.cpp | 12 ++- ui/viewerwidget.cpp | 8 +- 11 files changed, 192 insertions(+), 190 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 39e788e12..7f4c433e0 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -57,7 +57,7 @@ enum ExportFormats { ExportDialog::ExportDialog(QWidget *parent) : QDialog(parent) { - setWindowTitle("Export \"" + sequence->name + "\""); + setWindowTitle("Export \"" + sequence->name + "\""); setup_ui(); rangeCombobox->setCurrentIndex(0); @@ -479,7 +479,7 @@ void ExportDialog::export_action() { connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished())); connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64))); - closeActiveClips(sequence, true); + closeActiveClips(sequence); mainWindow->autorecover_interval(); @@ -603,40 +603,40 @@ void ExportDialog::setup_ui() { videoGroupbox->setFlat(false); videoGroupbox->setCheckable(true); - QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox); + QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox); - videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); - vcodecCombobox = new QComboBox(videoGroupbox); - videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + vcodecCombobox = new QComboBox(videoGroupbox); + videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); - widthSpinbox = new QSpinBox(videoGroupbox); - widthSpinbox->setMaximum(16777216); - videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Width:"), 1, 0, 1, 1); + widthSpinbox = new QSpinBox(videoGroupbox); + widthSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); - heightSpinbox = new QSpinBox(videoGroupbox); - heightSpinbox->setMaximum(16777216); - videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Height:"), 2, 0, 1, 1); + heightSpinbox = new QSpinBox(videoGroupbox); + heightSpinbox->setMaximum(16777216); + videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); - framerateSpinbox = new QDoubleSpinBox(videoGroupbox); - framerateSpinbox->setMaximum(60); - framerateSpinbox->setValue(0); - videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); + videoGridLayout->addWidget(new QLabel("Frame Rate:"), 3, 0, 1, 1); + framerateSpinbox = new QDoubleSpinBox(videoGroupbox); + framerateSpinbox->setMaximum(60); + framerateSpinbox->setValue(0); + videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1); - videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); + videoGridLayout->addWidget(new QLabel("Compression Type:"), 4, 0, 1, 1); compressionTypeCombobox = new QComboBox(videoGroupbox); compressionTypeCombobox->addItem("Quality-based (Constant Rate Factor)"); compressionTypeCombobox->addItem("File size-based (Two-Pass)"); - videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); + videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1); - videoBitrateLabel = new QLabel(videoGroupbox); - videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); - videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); - videobitrateSpinbox->setMaximum(100); - videobitrateSpinbox->setValue(2); - videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); + videoBitrateLabel = new QLabel(videoGroupbox); + videoGridLayout->addWidget(videoBitrateLabel, 5, 0, 1, 1); + videobitrateSpinbox = new QDoubleSpinBox(videoGroupbox); + videobitrateSpinbox->setMaximum(100); + videobitrateSpinbox->setValue(2); + videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); verticalLayout->addWidget(videoGroupbox); @@ -644,60 +644,60 @@ void ExportDialog::setup_ui() { audioGroupbox->setTitle("Audio"); audioGroupbox->setCheckable(true); - QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox); + QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox); - audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Codec:"), 0, 0, 1, 1); acodecCombobox = new QComboBox(audioGroupbox); - audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1); + audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1); - audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Sampling Rate:"), 1, 0, 1, 1); samplingRateSpinbox = new QSpinBox(audioGroupbox); samplingRateSpinbox->setMaximum(96000); samplingRateSpinbox->setValue(0); - audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1); + audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1); - audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); + audioGridLayout->addWidget(new QLabel("Bitrate (Kbps/CBR):"), 3, 0, 1, 1); audiobitrateSpinbox = new QSpinBox(audioGroupbox); audiobitrateSpinbox->setMaximum(320); audiobitrateSpinbox->setValue(256); - audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); + audioGridLayout->addWidget(audiobitrateSpinbox, 3, 1, 1, 1); verticalLayout->addWidget(audioGroupbox); - QHBoxLayout* progressLayout = new QHBoxLayout(); + QHBoxLayout* progressLayout = new QHBoxLayout(); progressBar = new QProgressBar(this); progressBar->setFormat("%p% (ETA: 0:00:00)"); progressBar->setEnabled(false); progressBar->setValue(0); - progressLayout->addWidget(progressBar); + progressLayout->addWidget(progressBar); renderCancel = new QPushButton(this); renderCancel->setText("x"); renderCancel->setEnabled(false); renderCancel->setMaximumSize(QSize(20, 16777215)); connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render())); - progressLayout->addWidget(renderCancel); + progressLayout->addWidget(renderCancel); - verticalLayout->addLayout(progressLayout); + verticalLayout->addLayout(progressLayout); - QHBoxLayout* buttonLayout = new QHBoxLayout(); - buttonLayout->addStretch(); + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->addStretch(); export_button = new QPushButton(this); export_button->setText("Export"); connect(export_button, SIGNAL(clicked(bool)), this, SLOT(export_action())); - buttonLayout->addWidget(export_button); + buttonLayout->addWidget(export_button); cancel_button = new QPushButton(this); cancel_button->setText("Cancel"); connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject())); - buttonLayout->addWidget(cancel_button); + buttonLayout->addWidget(cancel_button); - buttonLayout->addStretch(); + buttonLayout->addStretch(); - verticalLayout->addLayout(buttonLayout); + verticalLayout->addLayout(buttonLayout); connect(formatCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(format_changed(int))); connect(compressionTypeCombobox, SIGNAL(currentIndexChanged(int)), this, SLOT(comp_type_changed(int))); diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index c48baacd3..ff4c52a14 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -335,7 +335,7 @@ void SpeedDialog::accept() { for (int i=0;iopen) close_clip(c); + if (c->open) close_clip(c, true); if (c->track >= 0 && maintain_pitch->checkState() != Qt::PartiallyChecked diff --git a/mainwindow.cpp b/mainwindow.cpp index 42afa55d2..bffdf2169 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,13 +80,13 @@ void MainWindow::setup_layout(bool reset) { #endif // load panels from file - if (!reset) { + if (!reset) { QFile panel_config(get_data_path() + "/layout"); if (panel_config.exists() && panel_config.open(QFile::ReadOnly)) { restoreState(panel_config.readAll(), 0); panel_config.close(); } - } + } layout()->update(); } @@ -727,7 +727,7 @@ void MainWindow::setup_menus() { tools_menu->addAction("Preferences", this, SLOT(preferences()), QKeySequence("Ctrl+.")); #ifdef QT_DEBUG - tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); + tools_menu->addAction("Clear Undo", this, SLOT(clear_undo_stack())); #endif // INITIALIZE HELP MENU @@ -762,11 +762,11 @@ void MainWindow::updateTitle(const QString& url) { void MainWindow::closeEvent(QCloseEvent *e) { if (can_close_project()) { panel_effect_controls->clear_effects(true); - panel_sequence_viewer->viewer_widget->delete_function(); - panel_footage_viewer->viewer_widget->delete_function(); set_sequence(NULL); + panel_footage_viewer->set_main_sequence(); + QString data_dir = get_data_path(); if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { if (QFile::exists(autorecovery_filename)) { @@ -824,10 +824,10 @@ void MainWindow::reset_layout() { } void MainWindow::go_to_start() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_start(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_start(); @@ -835,10 +835,10 @@ void MainWindow::go_to_start() { } void MainWindow::prev_frame() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->previous_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->previous_frame(); @@ -846,10 +846,10 @@ void MainWindow::prev_frame() { } void MainWindow::next_frame() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->next_frame(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->next_frame(); @@ -857,10 +857,10 @@ void MainWindow::next_frame() { } void MainWindow::go_to_end() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->go_to_end(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->go_to_end(); @@ -868,10 +868,10 @@ void MainWindow::go_to_end() { } void MainWindow::playpause() { - if (panel_timeline->focused() - || panel_sequence_viewer->is_focused() - || panel_effect_controls->keyframe_focus() - || panel_graph_editor->view_is_focused()) { + if (panel_timeline->focused() + || panel_sequence_viewer->is_focused() + || panel_effect_controls->keyframe_focus() + || panel_graph_editor->view_is_focused()) { panel_sequence_viewer->toggle_play(); } else if (panel_footage_viewer->is_focused()) { panel_footage_viewer->toggle_play(); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 866c940dc..4d5a98d32 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -573,14 +573,14 @@ void Viewer::set_media(Media* m) { seq->frame_rate = 30; if (footage->video_tracks.size() > 0) { - const FootageStream& video_stream = footage->video_tracks.at(0); - seq->width = video_stream.video_width; - seq->height = video_stream.video_height; - if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; + const FootageStream& video_stream = footage->video_tracks.at(0); + seq->width = video_stream.video_width; + seq->height = video_stream.video_height; + if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate; Clip* c = new Clip(seq); c->media = media; - c->media_stream = video_stream.file_index; + c->media_stream = video_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); if (c->timeline_out <= 0) c->timeline_out = 150; @@ -594,12 +594,12 @@ void Viewer::set_media(Media* m) { } if (footage->audio_tracks.size() > 0) { - const FootageStream& audio_stream = footage->audio_tracks.at(0); - seq->audio_frequency = audio_stream.audio_frequency; + const FootageStream& audio_stream = footage->audio_tracks.at(0); + seq->audio_frequency = audio_stream.audio_frequency; Clip* c = new Clip(seq); c->media = media; - c->media_stream = audio_stream.file_index; + c->media_stream = audio_stream.file_index; c->timeline_in = 0; c->timeline_out = footage->get_length_in_frames(seq->frame_rate); c->track = 0; @@ -610,7 +610,7 @@ void Viewer::set_media(Media* m) { if (footage->video_tracks.size() == 0) { viewer_widget->waveform = true; viewer_widget->waveform_clip = c; - viewer_widget->waveform_ms = &audio_stream; + viewer_widget->waveform_ms = &audio_stream; viewer_widget->update(); } } else { @@ -698,6 +698,10 @@ void Viewer::clean_created_seq() { void Viewer::set_sequence(bool main, Sequence *s) { reset_all_audio(); + if (seq != NULL) { + closeActiveClips(seq); + } + main_sequence = main; seq = (main) ? sequence : s; diff --git a/playback/playback.cpp b/playback/playback.cpp index 9dd4a399d..dfb67461a 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -37,39 +37,39 @@ bool texture_failed = false; bool rendering = false; bool clip_uses_cacher(Clip* clip) { - return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE); + return (clip->media == NULL && clip->track >= 0) || (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_FOOTAGE); } void open_clip(Clip* clip, bool multithreaded) { - if (clip_uses_cacher(clip)) { - clip->multithreaded = multithreaded; - if (multithreaded) { - if (clip->open_lock.tryLock()) { - // maybe keep cacher instance in memory while clip exists for performance? - clip->cacher = new Cacher(clip); - QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater())); - clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority); - } - } else { - clip->finished_opening = false; - clip->open = true; + if (clip_uses_cacher(clip)) { + clip->multithreaded = multithreaded; + if (multithreaded) { + if (clip->open_lock.tryLock()) { + // maybe keep cacher instance in memory while clip exists for performance? + clip->cacher = new Cacher(clip); + QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater())); + clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority); + } + } else { + clip->finished_opening = false; + clip->open = true; - open_clip_worker(clip); - } - } else { - clip->open = true; - } + open_clip_worker(clip); + } + } else { + clip->open = true; + } } -void close_clip(Clip* clip) { +void close_clip(Clip* clip, bool wait) { // destroy opengl texture in main thread - if (clip->texture != NULL) { + if (clip->texture != NULL) { delete clip->texture; clip->texture = NULL; } for (int i=0;ieffects.size();i++) { - clip->effects.at(i)->close(); + if (clip->effects.at(i)->is_open()) clip->effects.at(i)->close(); } if (clip->fbo != NULL) { @@ -79,29 +79,30 @@ void close_clip(Clip* clip) { clip->fbo = NULL; } - if (clip_uses_cacher(clip)) { - if (clip->multithreaded) { - clip->cacher->caching = false; - clip->can_cache.wakeAll(); - } else { - close_clip_worker(clip); - } - } else { - if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE) - closeActiveClips(clip->media->to_sequence(), false); + if (clip_uses_cacher(clip)) { + if (clip->multithreaded) { + clip->cacher->caching = false; + clip->can_cache.wakeAll(); + if (wait) clip->cacher->wait(); + } else { + close_clip_worker(clip); + } + } else { + if (clip->media != NULL && clip->media->get_type() == MEDIA_TYPE_SEQUENCE) + closeActiveClips(clip->media->to_sequence()); clip->open = false; - } + } } void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector& nests) { - if (clip_uses_cacher(clip)) { + if (clip_uses_cacher(clip)) { if (clip->multithreaded) { clip->cacher->playhead = playhead; clip->cacher->reset = reset; clip->cacher->nests = nests; - clip->cacher->scrubbing = scrubbing; - if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true; + clip->cacher->scrubbing = scrubbing; + if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true; clip->can_cache.wakeAll(); } else { @@ -111,16 +112,16 @@ void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVectorget_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate); + return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate); } void get_clip_frame(Clip* c, long playhead) { if (c->finished_opening) { - const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); - int64_t target_pts = qMax(static_cast(0), playhead_to_timestamp(c, playhead)); - int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base))); - if (ms->video_interlacing != VIDEO_PROGRESSIVE) { + int64_t target_pts = qMax(static_cast(0), playhead_to_timestamp(c, playhead)); + int64_t second_pts = qRound64(av_q2d(av_inv_q(c->stream->time_base))); + if (ms->video_interlacing != VIDEO_PROGRESSIVE) { target_pts *= 2; second_pts *= 2; } @@ -132,7 +133,7 @@ void get_clip_frame(Clip* c, long playhead) { c->queue_lock.lock(); if (c->queue.size() > 0) { - if (ms->infinite_length) { + if (ms->infinite_length) { target_frame = c->queue.at(0); #ifdef GCF_DEBUG dout << "GCF ==> USE PRECISE (INFINITE)"; @@ -192,14 +193,14 @@ void get_clip_frame(Clip* c, long playhead) { #endif c->reached_end = false; cache = false; - } else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) { + } else if (target_pts != c->last_invalid_ts && (target_pts < target_frame->pts || pts_diff > second_pts)) { #ifdef GCF_DEBUG dout << "GCF ==> RESET" << target_pts << "(" << target_frame->pts << "-" << target_frame->pts+target_frame->pkt_duration << ")"; #endif - if (!config.fast_seeking) target_frame = NULL; + if (!config.fast_seeking) target_frame = NULL; reset = true; - c->last_invalid_ts = target_pts; + c->last_invalid_ts = target_pts; } else { #ifdef GCF_DEBUG dout << "GCF ==> WAIT - target pts:" << target_pts << "closest frame:" << target_frame->pts; @@ -225,26 +226,26 @@ void get_clip_frame(Clip* c, long playhead) { int nb_components = av_pix_fmt_desc_get(static_cast(c->pix_fmt))->nb_components; glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/nb_components); - bool copied = false; - uint8_t* data = target_frame->data[0]; - int frame_size; + bool copied = false; + uint8_t* data = target_frame->data[0]; + int frame_size; - for (int i=0;ieffects.size();i++) { - Effect* e = c->effects.at(i); - if (e->enable_image) { - if (!copied) { - frame_size = target_frame->linesize[0]*target_frame->height; - data = new uint8_t[frame_size]; - memcpy(data, target_frame->data[0], frame_size); - copied = true; - } - e->process_image(get_timecode(c, playhead), data, frame_size); - } - } + for (int i=0;ieffects.size();i++) { + Effect* e = c->effects.at(i); + if (e->enable_image) { + if (!copied) { + frame_size = target_frame->linesize[0]*target_frame->height; + data = new uint8_t[frame_size]; + memcpy(data, target_frame->data[0], frame_size); + copied = true; + } + e->process_image(get_timecode(c, playhead), data, frame_size); + } + } c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, data); - if (copied) delete [] data; + if (copied) delete [] data; glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } @@ -252,13 +253,13 @@ void get_clip_frame(Clip* c, long playhead) { c->queue_lock.unlock(); // get more frames - QVector empty; - if (cache) cache_clip(c, playhead, reset, false, empty); + QVector empty; + if (cache) cache_clip(c, playhead, reset, false, empty); } } long playhead_to_clip_frame(Clip* c, long playhead) { - return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition()); + return (qMax(0L, playhead - c->get_timeline_in_with_transition()) + c->get_clip_in_with_transition()); } double playhead_to_clip_seconds(Clip* c, long playhead) { @@ -278,11 +279,11 @@ int64_t playhead_to_timestamp(Clip* c, long playhead) { int retrieve_next_frame(Clip* c, AVFrame* f) { int result = 0; - int receive_ret; + int receive_ret; // do we need to retrieve a new packet for a new frame? av_frame_unref(f); - while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) { + while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) { int read_ret = 0; do { if (c->pkt_written) { @@ -293,15 +294,15 @@ int retrieve_next_frame(Clip* c, AVFrame* f) { if (read_ret >= 0) { c->pkt_written = true; } - } while (read_ret >= 0 && c->pkt->stream_index != c->media_stream); + } while (read_ret >= 0 && c->pkt->stream_index != c->media_stream); if (read_ret >= 0) { int send_ret = avcodec_send_packet(c->codecCtx, c->pkt); if (send_ret < 0) { dout << "[ERROR] Failed to send packet to decoder." << send_ret; - return send_ret; + return send_ret; } - } else { + } else { if (read_ret == AVERROR_EOF) { int send_ret = avcodec_send_packet(c->codecCtx, NULL); if (send_ret < 0) { @@ -323,32 +324,30 @@ int retrieve_next_frame(Clip* c, AVFrame* f) { } bool is_clip_active(Clip* c, long playhead) { - return c->enabled - && c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2) - && c->get_timeline_out_with_transition() > playhead - && playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength(); + return c->enabled + && c->get_timeline_in_with_transition() < playhead + ceil(c->sequence->frame_rate*2) + && c->get_timeline_out_with_transition() > playhead + && playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition() < c->getMaximumLength(); } void set_sequence(Sequence* s) { - closeActiveClips(sequence, true); panel_effect_controls->clear_effects(true); - sequence = s; + sequence = s; panel_sequence_viewer->set_main_sequence(); - panel_timeline->update_sequence(); - panel_timeline->setFocus(); + panel_timeline->update_sequence(); + panel_timeline->setFocus(); } -void closeActiveClips(Sequence *s, bool wait) { +void closeActiveClips(Sequence *s) { if (s != NULL) { - for (int i=0;iclips.size();i++) { - Clip* c = s->clips.at(i); - if (c != NULL) { - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) { - closeActiveClips(c->media->to_sequence(), wait); - if (c->open) close_clip(c); - } else if (clip_uses_cacher(c) && c->open) { - close_clip(c); - if (c->multithreaded && wait) c->cacher->wait(); + for (int i=0;iclips.size();i++) { + Clip* c = s->clips.at(i); + if (c != NULL) { + if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_SEQUENCE) { + closeActiveClips(c->media->to_sequence()); + if (c->open) close_clip(c, true); + } else if (c->open) { + close_clip(c, true); } } } diff --git a/playback/playback.h b/playback/playback.h index 67a18c6d4..db4a80600 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -15,7 +15,7 @@ extern bool rendering; bool clip_uses_cacher(Clip* clip); void open_clip(Clip* clip, bool multithreaded); void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector &nests); -void close_clip(Clip* clip); +void close_clip(Clip* clip, bool wait); void cache_audio_worker(Clip* c, bool write_A); void cache_video_worker(Clip* c, long playhead); void handle_media(Sequence* sequence, long playhead, bool multithreaded); @@ -32,6 +32,6 @@ int retrieve_next_frame(Clip* c, AVFrame* f); bool is_clip_active(Clip* c, long playhead); void get_next_audio(Clip* c, bool mix); void set_sequence(Sequence* s); -void closeActiveClips(Sequence* s, bool wait); +void closeActiveClips(Sequence* s); #endif // PLAYBACK_H diff --git a/project/clip.cpp b/project/clip.cpp index 2253b1ba4..05758928c 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -12,6 +12,7 @@ #include "project/media.h" #include "io/clipboard.h" #include "undo.h" +#include "debug.h" extern "C" { #include @@ -113,9 +114,9 @@ void Clip::refresh() { Footage* m = media->to_footage(); if (track < 0 && m->video_tracks.size() > 0) { - media_stream = m->video_tracks.at(0).file_index; + media_stream = m->video_tracks.at(0).file_index; } else if (track >= 0 && m->audio_tracks.size() > 0) { - media_stream = m->audio_tracks.at(0).file_index; + media_stream = m->audio_tracks.at(0).file_index; } } replaced = false; @@ -171,12 +172,7 @@ Transition* Clip::get_closing_transition() { Clip::~Clip() { if (open) { - close_clip(this); - - // make sure clip has closed before clip is destroyed - if (multithreaded && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) { - cacher->wait(); - } + close_clip(this, true); } if (opening_transition != -1) this->sequence->hard_delete_transition(this, TA_OPENING_TRANSITION); @@ -241,8 +237,8 @@ void Clip::recalculateMaxLength() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media->to_footage(); - const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); - if (ms != NULL && ms->infinite_length) { + const FootageStream* ms = m->get_stream_from_file_index(track < 0, media_stream); + if (ms != NULL && ms->infinite_length) { calculated_length = LONG_MAX; } else { calculated_length = m->get_length_in_frames(fr); @@ -269,7 +265,7 @@ int Clip::getWidth() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_width; if (sequence != NULL) return sequence->width; } @@ -287,7 +283,7 @@ int Clip::getHeight() { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { - const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); + const FootageStream* ms = media->to_footage()->get_stream_from_file_index(track < 0, media_stream); if (ms != NULL) return ms->video_height; if (sequence != NULL) return sequence->height; } diff --git a/project/effect.cpp b/project/effect.cpp index fa6c4b878..d91019d79 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -749,6 +749,10 @@ void Effect::save(QXmlStreamWriter& stream) { } } +bool Effect::is_open() { + return isOpen; +} + void Effect::validate_meta_path() { if (!meta->path.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; QList effects_paths = get_effects_paths(); diff --git a/project/effect.h b/project/effect.h index 208ee18a0..a7210a3de 100644 --- a/project/effect.h +++ b/project/effect.h @@ -140,6 +140,7 @@ public: void save(QXmlStreamWriter& stream); // glsl handling + bool is_open(); void open(); void close(); virtual void startEffect(); diff --git a/project/undo.cpp b/project/undo.cpp index 010561b53..fae0bfc10 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -150,7 +150,7 @@ void DeleteClipAction::redo() { // remove ref to clip ref = seq->clips.at(index); if (ref->open) { - close_clip(ref); + close_clip(ref, true); } seq->clips[index] = NULL; @@ -493,7 +493,7 @@ void AddClipCommand::undo() { Clip* c = seq->clips.last(); panel_timeline->deselect_area(c->timeline_in, c->timeline_out, c->track); undone_clips.prepend(c); - if (c->open) close_clip(c); + if (c->open) close_clip(c, true); seq->clips.removeLast(); } mainWindow->setWindowModified(old_project_changed); @@ -587,8 +587,7 @@ void ReplaceMediaCommand::replace(QString& filename) { for (int j=0;jclips.size();j++) { Clip* c = s->clips.at(j); if (c != NULL && c->media == item && c->open) { - close_clip(c); - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait(); + close_clip(c, true); c->replaced = true; } } @@ -628,8 +627,7 @@ void ReplaceClipMediaCommand::replace(bool undo) { for (int i=0;iopen) { - close_clip(c); - if (c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) c->cacher->wait(); + close_clip(c, true); } if (undo) { @@ -1102,7 +1100,7 @@ void CloseAllClipsCommand::undo() { } void CloseAllClipsCommand::redo() { - closeActiveClips(sequence, true); + closeActiveClips(sequence); } UpdateFootageTooltip::UpdateFootageTooltip(Media *i) : diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 9a1681dc7..8481f90fa 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -72,7 +72,7 @@ void ViewerWidget::delete_function() { // destroy all textures as well if (viewer->seq != NULL) { makeCurrent(); - closeActiveClips(viewer->seq, true); + closeActiveClips(viewer->seq); doneCurrent(); } } @@ -485,7 +485,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) Footage* m = c->media->to_footage(); if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) { if (m->ready) { - const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); + const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream); if (ms != NULL && is_clip_active(c, playhead)) { // if thread is already working, we don't want to touch this, // but we also don't want to hang the UI thread @@ -495,7 +495,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) clip_is_active = true; if (c->track >= 0) audio_track_count++; } else if (c->open) { - close_clip(c); + close_clip(c, false); } } else { //dout << "[WARNING] Media '" + m->name + "' was not ready, retrying..."; @@ -507,7 +507,7 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) if (!c->open) open_clip(c, !rendering); clip_is_active = true; } else if (c->open) { - close_clip(c); + close_clip(c, false); } } if (clip_is_active) { From 50a64c3bf506f08dfd112f3033e4e6b85d049530 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 12:31:14 +1100 Subject: [PATCH 21/32] corrected bezier handles inverting Y values --- ui/graphview.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 1c7b2e92c..7bbd9b85e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -41,7 +41,7 @@ GraphView::GraphView(QWidget* parent) : current_handle(BEZIER_HANDLE_NONE) { setMouseTracking(true); - setFocusPolicy(Qt::ClickFocus); + setFocusPolicy(Qt::ClickFocus); } void GraphView::paintEvent(QPaintEvent *event) { @@ -129,20 +129,20 @@ void GraphView::paintEvent(QPaintEvent *event) { if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { // cubic bezier bezier_path.cubicTo( - QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), - QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( - QPointF(last_key_x+last_key.post_handle_x, last_key_y+last_key.post_handle_y), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), QPointF(key_x, key_y) ); } else { // this keyframe is the bezier one bezier_path.quadTo( - QPointF(key_x+key.pre_handle_x, key_y+key.pre_handle_y), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } @@ -167,12 +167,12 @@ void GraphView::paintEvent(QPaintEvent *event) { p.setPen(Qt::gray); // pre handle line - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); p.drawLine(pre_point, QPointF(key_x, key_y)); p.drawEllipse(pre_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); // post handle line - QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); p.drawLine(post_point, QPointF(key_x, key_y)); p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); } @@ -304,13 +304,13 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { break; case BEZIER_HANDLE_PRE: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_POST: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(event->pos().y() - start_y)/zoom; + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; moved_keys = true; update_ui(false); break; From 6c8ec756d89631236aa6f78dffc680ba14a03c21 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 15:26:31 +1100 Subject: [PATCH 22/32] various graph editor improvements --- panels/grapheditor.cpp | 5 +- project/effectrow.cpp | 4 +- ui/graphview.cpp | 180 ++++++++++++++++++++++++++++------------- ui/graphview.h | 7 ++ ui/keyframeview.cpp | 14 ++++ 5 files changed, 150 insertions(+), 60 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 961acac5e..b73bf2438 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -97,6 +97,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(NULL) { QWidget* central_value_widget = new QWidget(); value_layout = new QHBoxLayout(); value_layout->setMargin(0); + value_layout->addWidget(new QLabel("")); // a spacer so the layout doesn't jump central_value_widget->setLayout(value_layout); values->addWidget(central_value_widget); @@ -193,11 +194,11 @@ void GraphEditor::set_row(EffectRow *r) { current_row_desc->setText(0); } view->set_row(row); - update_panel(); + update_panel(); } bool GraphEditor::view_is_focused() { - return view->hasFocus() || header->hasFocus(); + return view->hasFocus() || header->hasFocus(); } void GraphEditor::set_key_button_enabled(bool e, int type) { diff --git a/project/effectrow.cpp b/project/effectrow.cpp index a9b7fd0f1..da203fd74 100644 --- a/project/effectrow.cpp +++ b/project/effectrow.cpp @@ -112,13 +112,11 @@ void EffectRow::toggle_key() { set_keyframe_now(ca); } else { for (int i=0;iappend(new KeyframeDelete(key_fields.at(i), key_field_index.at(i))); } } undo_stack.push(ca); - panel_effect_controls->update_keyframes(); - panel_sequence_viewer->viewer_widget->update(); + update_ui(false); } void EffectRow::goto_next_key() { diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 7bbd9b85e..0118a95fd 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "panels/panels.h" #include "panels/timeline.h" @@ -42,6 +44,60 @@ GraphView::GraphView(QWidget* parent) : { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); +} + +void GraphView::show_context_menu(const QPoint& pos) { + QMenu menu(this); + + QAction* reset_action = menu.addAction("Reset View"); + connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); + + menu.exec(mapToGlobal(pos)); +} + +void GraphView::reset_view() { + zoom = 1.0; + set_scroll_x(0); + set_scroll_y(0); + emit zoom_changed(zoom); + update(); +} + +void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { + // draws last line's text + QString str = QString::number(line_no*GRAPH_SIZE); + int text_sz = vert ? fontMetrics().height() : fontMetrics().width(str); + if (text_sz < (next_line_pos - line_pos)) { + QRect text_rect = vert ? QRect(0, line_pos-50, 50, 50) : QRect(line_pos, height()-50, 50, 50); + p.drawText(text_rect, Qt::AlignBottom | Qt::AlignLeft, str); + } +} + +void GraphView::draw_lines(QPainter& p, bool vert) { + int last_line = INT_MIN; + int last_line_x = INT_MIN; + int lim = vert ? height() : width(); + int scroll = vert ? y_scroll : x_scroll; + for (int i=0;iseq != NULL) { // draw grid lines - //int graph_size = GRAPH_SIZE*zoom; - bool draw_text = true;//(fontMetrics().height() < graph_size && fontMetrics().width("0000") < graph_size); - p.setPen(Qt::gray); - int i = 0; - while (true) { - int line_x = (i*GRAPH_SIZE*zoom) - x_scroll; - if (line_x >= width()) { - break; - } - if (line_x >= 0) { - if (line_x > 0) p.drawLine(line_x, 0, line_x, height()); - if (draw_text) p.drawText(QRect(line_x, height()-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); - } - i++; - } - i = 0; - while (true) { - int line_y = height() - (i*GRAPH_SIZE*zoom) + y_scroll; - if (line_y <= 0) { - break; - } - if (line_y <= height()) { - if (line_y < height()) p.drawLine(0, line_y, width(), line_y); - if (draw_text) p.drawText(QRect(0, line_y-50, 50, 50), Qt::AlignBottom | Qt::AlignLeft, QString::number(i*GRAPH_SIZE)); - } - i++; - } + + draw_lines(p, true); + draw_lines(p, false); // draw keyframes if (row != NULL) { QPen line_pen; line_pen.setWidth(2); - for (int i=0;ifieldCount();i++) { + for (int i=row->fieldCount()-1;i>=0;i--) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { @@ -129,14 +161,14 @@ void GraphView::paintEvent(QPaintEvent *event) { if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { // cubic bezier bezier_path.cubicTo( - QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), QPointF(key_x, key_y) ); } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier // last keyframe is the bezier one bezier_path.quadTo( - QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y+last_key.post_handle_y*zoom), + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), QPointF(key_x, key_y) ); } else { @@ -205,10 +237,6 @@ void GraphView::mousePressEvent(QMouseEvent *event) { // selecting int sel_key = -1; int sel_key_field = -1; - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); - } current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { for (int i=0;ifieldCount();i++) { @@ -227,7 +255,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { break; } else { // selecting a handle - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y + key.pre_handle_y*zoom); + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE @@ -239,7 +267,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { current_handle = BEZIER_HANDLE_PRE; break; } else { - QPointF post_point(key_x + key.post_handle_x*zoom, key_y + key.post_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE @@ -255,11 +283,29 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } } } + if (sel_key > -1) break; } } - if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); + bool already_selected = false; + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; + } + } + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } } selected_keys_old_vals.clear(); @@ -293,24 +339,37 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_y = event->pos().y(); update(); } else { + bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { case BEZIER_HANDLE_NONE: for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); - row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); + if (shift) { + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(i); + } else { + row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); + } } moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_PRE: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + if (shift) { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y; + } else { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + } moved_keys = true; update_ui(false); break; case BEZIER_HANDLE_POST: row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + if (shift) { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y; + } else { + row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + } moved_keys = true; update_ui(false); break; @@ -366,9 +425,13 @@ void GraphView::wheelEvent(QWheelEvent *event) { // set zoom if (event->angleDelta().y() != 0) { double zoom_diff = (GRAPH_ZOOM_SPEED*zoom); - if (event->angleDelta().y() < 0) zoom_diff = -zoom_diff; - zoom += zoom_diff; - emit zoom_changed(zoom); + double new_zoom = (event->angleDelta().y() < 0) ? zoom - zoom_diff : zoom + zoom_diff; + + // center zoom on screen + set_scroll_x(qRound(x_scroll + double(event->pos().x())*new_zoom - double(event->pos().x())*zoom)); + set_scroll_y(qRound(y_scroll + double(height()-event->pos().y())*new_zoom - double(height()-event->pos().y())*zoom)); + + set_zoom(new_zoom); redraw = true; } } @@ -379,17 +442,19 @@ void GraphView::wheelEvent(QWheelEvent *event) { } void GraphView::set_row(EffectRow *r) { - selected_keys.clear(); - selected_keys_fields.clear(); - selected_keys_old_vals.clear(); - selected_keys_old_doubles.clear(); - emit selection_changed(false, -1); - row = r; - if (row != NULL) { - field_visibility.resize(row->fieldCount()); - field_visibility.fill(true); + if (row != r) { + selected_keys.clear(); + selected_keys_fields.clear(); + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + emit selection_changed(false, -1); + row = r; + if (row != NULL) { + field_visibility.resize(row->fieldCount()); + field_visibility.fill(true); + } + update(); } - update(); } void GraphView::set_selected_keyframe_type(int type) { @@ -419,6 +484,11 @@ void GraphView::set_scroll_y(int s) { emit y_scroll_changed(y_scroll); } +void GraphView::set_zoom(double z) { + zoom = z; + emit zoom_changed(zoom); +} + int GraphView::get_screen_x(double d) { return (d*zoom) - x_scroll; } diff --git a/ui/graphview.h b/ui/graphview.h index d7511184b..474ffee91 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -38,6 +38,7 @@ private: void set_scroll_x(int s); void set_scroll_y(int s); + void set_zoom(double z); int get_screen_x(double); int get_screen_y(double); @@ -56,7 +57,13 @@ private: int current_handle; + void draw_lines(QPainter &p, bool vert); + void draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos); + EffectRow* row; +private slots: + void show_context_menu(const QPoint& pos); + void reset_view(); }; #endif // GRAPHVIEW_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 4137f68e2..6db4164cf 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -252,6 +252,20 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { if (keyframe_index > -1) { selected_fields.append(rows.at(row_index)->field(field_index)); selected_keyframes.append(keyframe_index); + + // find other field with keyframes at the same time + long comp_time = rows.at(row_index)->field(field_index)->keyframes.at(keyframe_index).time; + for (int i=0;ifieldCount();i++) { + if (i != field_index) { + EffectField* f = rows.at(row_index)->field(i); + for (int j=0;jkeyframes.size();j++) { + if (f->keyframes.at(j).time == comp_time) { + selected_fields.append(f); + selected_keyframes.append(j); + } + } + } + } } } From 28fbab2d78782dc2cd02472bb3423d83f19a3018 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 16:33:07 +1100 Subject: [PATCH 23/32] rect select for graph editor --- olive.pro | 6 +- panels/timeline.cpp | 34 ++++----- panels/timeline.h | 1 - ui/graphview.cpp | 165 ++++++++++++++++++++++++++++------------- ui/graphview.h | 9 +++ ui/keyframeview.cpp | 1 + ui/rectangleselect.cpp | 7 ++ ui/rectangleselect.h | 8 ++ ui/timelinewidget.cpp | 5 +- 9 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 ui/rectangleselect.cpp create mode 100644 ui/rectangleselect.h diff --git a/olive.pro b/olive.pro index 66dc5ed66..234d5c5f1 100644 --- a/olive.pro +++ b/olive.pro @@ -110,7 +110,8 @@ SOURCES += \ ui/graphview.cpp \ ui/keyframedrawing.cpp \ ui/clickablelabel.cpp \ - project/keyframe.cpp + project/keyframe.cpp \ + ui/rectangleselect.cpp HEADERS += \ mainwindow.h \ @@ -195,7 +196,8 @@ HEADERS += \ ui/graphview.h \ ui/keyframedrawing.h \ ui/clickablelabel.h \ - project/keyframe.h + project/keyframe.h \ + ui/rectangleselect.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 37b9dc98e..5482df7d8 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -43,12 +43,6 @@ long refactor_frame_number(long framenumber, double source_frame_rate, double ta return qRound(((double)framenumber/source_frame_rate)*target_frame_rate); } -void draw_selection_rectangle(QPainter& painter, const QRect& rect) { - painter.setPen(QColor(204, 204, 204)); - painter.setBrush(QColor(0, 0, 0, 32)); - painter.drawRect(rect); -} - Timeline::Timeline(QWidget *parent) : QDockWidget(parent), cursor_frame(0), @@ -184,7 +178,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector can_import = m->ready; if (m->using_inout) { double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate; default_clip_in = refactor_frame_number(m->in, source_fr, seq->frame_rate); default_clip_out = refactor_frame_number(m->out, source_fr, seq->frame_rate); } @@ -216,7 +210,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector switch (medium->get_type()) { case MEDIA_TYPE_FOOTAGE: // is video source a still image? - if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { + if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { g.out = g.in + 100; } else { long length = m->get_length_in_frames(seq->frame_rate); @@ -227,20 +221,20 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector } for (int j=0;jaudio_tracks.size();j++) { - if (m->audio_tracks.at(j).enabled) { - g.track = j; - g.media_stream = m->audio_tracks.at(j).file_index; - ghosts.append(g); - audio_ghosts = true; - } + if (m->audio_tracks.at(j).enabled) { + g.track = j; + g.media_stream = m->audio_tracks.at(j).file_index; + ghosts.append(g); + audio_ghosts = true; + } } for (int j=0;jvideo_tracks.size();j++) { - if (m->video_tracks.at(j).enabled) { - g.track = -1-j; - g.media_stream = m->video_tracks.at(j).file_index; - ghosts.append(g); - video_ghosts = true; - } + if (m->video_tracks.at(j).enabled) { + g.track = -1-j; + g.media_stream = m->video_tracks.at(j).file_index; + ghosts.append(g); + video_ghosts = true; + } } break; case MEDIA_TYPE_SEQUENCE: diff --git a/panels/timeline.h b/panels/timeline.h index a76adc3a9..278aacbb3 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -38,7 +38,6 @@ struct FootageStream; long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); int getScreenPointFromFrame(double zoom, long frame); long getFrameFromScreenPoint(double zoom, int x); -void draw_selection_rectangle(QPainter& painter, const QRect& rect); bool selection_contains_transition(const Selection& s, Clip* c, int type); void move_clip(ComboAction *ca, Clip *c, long iin, long iout, long iclip_in, int itrack, bool verify_transitions = true, bool relative = false); void ripple_clips(ComboAction *ca, Sequence* s, long point, long length, const QVector& ignore = QVector()); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 0118a95fd..14ef7ddae 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -14,12 +14,14 @@ #include "ui/keyframedrawing.h" #include "project/undo.h" #include "project/effect.h" +#include "ui/rectangleselect.h" #include "debug.h" #define GRAPH_ZOOM_SPEED 0.05 #define GRAPH_SIZE 100 #define BEZIER_HANDLE_SIZE 3 +#define BEZIER_LINE_SIZE 2 #define BEZIER_HANDLE_NONE 1 #define BEZIER_HANDLE_PRE 2 @@ -40,7 +42,8 @@ GraphView::GraphView(QWidget* parent) : zoom(1.0), row(NULL), moved_keys(false), - current_handle(BEZIER_HANDLE_NONE) + current_handle(BEZIER_HANDLE_NONE), + rect_select(false) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -114,7 +117,7 @@ void GraphView::paintEvent(QPaintEvent *event) { // draw keyframes if (row != NULL) { QPen line_pen; - line_pen.setWidth(2); + line_pen.setWidth(BEZIER_LINE_SIZE); for (int i=row->fieldCount()-1;i>=0;i--) { EffectField* field = row->field(i); @@ -209,7 +212,15 @@ void GraphView::paintEvent(QPaintEvent *event) { p.drawEllipse(post_point, BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE); } - draw_keyframe(p, key.type, key_x, key_y, (selected_keys.contains(sorted_keys.at(j)) && selected_keys_fields.contains(i))); + bool selected = false; + for (int k=0;kseq->playhead); p.drawLine(playhead_x, 0, playhead_x, height()); + + if (rect_select) { + draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); + p.setBrush(Qt::NoBrush); + } } p.setPen(Qt::white); @@ -230,15 +246,16 @@ void GraphView::paintEvent(QPaintEvent *event) { } void GraphView::mousePressEvent(QMouseEvent *event) { - mousedown = true; - start_x = event->pos().x(); - start_y = event->pos().y(); - - // selecting - int sel_key = -1; - int sel_key_field = -1; - current_handle = BEZIER_HANDLE_NONE; if (row != NULL) { + mousedown = true; + start_x = event->pos().x(); + start_y = event->pos().y(); + + // selecting + int sel_key = -1; + int sel_key_field = -1; + current_handle = BEZIER_HANDLE_NONE; + for (int i=0;ifieldCount();i++) { EffectField* field = row->field(i); if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { @@ -285,49 +302,40 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } if (sel_key > -1) break; } - } - bool already_selected = false; - for (int i=0;imodifiers() & Qt::ShiftModifier)) { - selected_keys.removeAt(i); - selected_keys_fields.removeAt(i); - } - already_selected = true; - break; - } - } - if (!already_selected) { - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); - } + + bool already_selected = false; if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; + } + } } - } - - selected_keys_old_vals.clear(); - selected_keys_old_doubles.clear(); - - int selected_key_type = -1; - - for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); - selected_keys_old_vals.append(key.time); - selected_keys_old_doubles.append(key.data.toDouble()); - - if (selected_key_type == -1) { - selected_key_type = key.type; - } else if (selected_key_type != key.type) { - selected_key_type = -2; + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } else { + rect_select = true; + rect_select_x = event->pos().x(); + rect_select_y = event->pos().y(); + rect_select_w = 0; + rect_select_h = 0; + rect_select_offset = selected_keys.size(); + } } + + selection_update(); } - - update(); - - emit selection_changed(selected_keys.size() > 0, selected_key_type); } void GraphView::mouseMoveEvent(QMouseEvent *event) { @@ -338,6 +346,35 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { start_x = event->pos().x(); start_y = event->pos().y(); update(); + } else if (rect_select) { + rect_select_w = event->pos().x() - rect_select_x; + rect_select_h = event->pos().y() - rect_select_y; + + selected_keys.resize(rect_select_offset); + selected_keys_fields.resize(rect_select_offset); + + for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); + for (int j=0;jkeyframes.size();j++) { + bool already_selected = false; + for (int k=0;kkeyframes.at(j).time), get_screen_y(f->keyframes.at(j).data.toDouble())); + QRect select_rect(rect_select_x, rect_select_y, rect_select_w, rect_select_h); + if (select_rect.contains(key_screen_point)) { + selected_keys.append(j); + selected_keys_fields.append(i); + } + } + } + } + update(); } else { bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { @@ -409,6 +446,11 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } moved_keys = false; mousedown = false; + if (rect_select) { + rect_select = false; + selection_update(); + update(); + } } void GraphView::wheelEvent(QWheelEvent *event) { @@ -496,3 +538,26 @@ int GraphView::get_screen_x(double d) { int GraphView::get_screen_y(double d) { return height() + y_scroll - d*zoom; } + +void GraphView::selection_update() { + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); + + int selected_key_type = -1; + + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + selected_keys_old_vals.append(key.time); + selected_keys_old_doubles.append(key.data.toDouble()); + + if (selected_key_type == -1) { + selected_key_type = key.type; + } else if (selected_key_type != key.type) { + selected_key_type = -2; + } + } + + update(); + + emit selection_changed(selected_keys.size() > 0, selected_key_type); +} diff --git a/ui/graphview.h b/ui/graphview.h index 474ffee91..6a694a999 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -43,6 +43,8 @@ private: int get_screen_x(double); int get_screen_y(double); + void selection_update(); + QVector field_visibility; QVector selected_keys; @@ -61,6 +63,13 @@ private: void draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos); EffectRow* row; + + bool rect_select; + int rect_select_x; + int rect_select_y; + int rect_select_w; + int rect_select_h; + int rect_select_offset; private slots: void show_context_menu(const QPoint& pos); void reset_view(); diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 6db4164cf..bbe8d7dac 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -15,6 +15,7 @@ #include "ui/keyframedrawing.h" #include "ui/clickablelabel.h" #include "ui/resizablescrollbar.h" +#include "ui/rectangleselect.h" #include #include diff --git a/ui/rectangleselect.cpp b/ui/rectangleselect.cpp new file mode 100644 index 000000000..b4a9348ce --- /dev/null +++ b/ui/rectangleselect.cpp @@ -0,0 +1,7 @@ +#include "rectangleselect.h" + +void draw_selection_rectangle(QPainter& painter, const QRect& rect) { + painter.setPen(QColor(204, 204, 204)); + painter.setBrush(QColor(0, 0, 0, 32)); + painter.drawRect(rect); +} diff --git a/ui/rectangleselect.h b/ui/rectangleselect.h new file mode 100644 index 000000000..40eee2aeb --- /dev/null +++ b/ui/rectangleselect.h @@ -0,0 +1,8 @@ +#ifndef RECTANGLESELECT_H +#define RECTANGLESELECT_H + +#include + +void draw_selection_rectangle(QPainter& painter, const QRect& rect); + +#endif // RECTANGLESELECT_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 2e11b423e..71cdca314 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -21,6 +21,7 @@ #include "ui/resizablescrollbar.h" #include "dialogs/newsequencedialog.h" #include "mainwindow.h" +#include "ui/rectangleselect.h" #include "debug.h" #include "project/effect.h" @@ -1272,9 +1273,9 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Clip* c = NULL; if (g.clip != -1) c = sequence->clips.at(g.clip); - const FootageStream* ms = NULL; + const FootageStream* ms = NULL; if (g.clip != -1 && c->media != NULL && c->media->get_type() == MEDIA_TYPE_FOOTAGE) { - ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); + ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream); } // validate ghosts for trimming From f444cdb8bbe21876f1dcde87aa2626f99db0ff31 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 18:36:47 +1100 Subject: [PATCH 24/32] correct frame offsets in graph view --- panels/grapheditor.cpp | 1 + ui/graphview.cpp | 8 ++++++-- ui/graphview.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index b73bf2438..c6e8500f6 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -185,6 +185,7 @@ void GraphEditor::set_row(EffectRow *r) { if (found_vals) { row = r; current_row_desc->setText(row->parent_effect->parent_clip->name + " :: " + row->parent_effect->meta->name + " :: " + row->get_name()); + header->set_visible_in(r->parent_effect->parent_clip->timeline_in); connect(keyframe_nav, SIGNAL(goto_previous_key()), row, SLOT(goto_previous_key())); connect(keyframe_nav, SIGNAL(toggle_key()), row, SLOT(toggle_key())); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 14ef7ddae..ec535fc10 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -14,6 +14,7 @@ #include "ui/keyframedrawing.h" #include "project/undo.h" #include "project/effect.h" +#include "project/clip.h" #include "ui/rectangleselect.h" #include "debug.h" @@ -43,7 +44,8 @@ GraphView::GraphView(QWidget* parent) : row(NULL), moved_keys(false), current_handle(BEZIER_HANDLE_NONE), - rect_select(false) + rect_select(false), + visible_in(0) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -228,7 +230,7 @@ void GraphView::paintEvent(QPaintEvent *event) { // draw playhead p.setPen(Qt::red); - int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead); + int playhead_x = get_screen_x(panel_sequence_viewer->seq->playhead - visible_in); p.drawLine(playhead_x, 0, playhead_x, height()); if (rect_select) { @@ -339,6 +341,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } void GraphView::mouseMoveEvent(QMouseEvent *event) { + unsetCursor(); if (mousedown) { if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); @@ -494,6 +497,7 @@ void GraphView::set_row(EffectRow *r) { if (row != NULL) { field_visibility.resize(row->fieldCount()); field_visibility.fill(true); + visible_in = row->parent_effect->parent_clip->timeline_in; } update(); } diff --git a/ui/graphview.h b/ui/graphview.h index 6a694a999..5d1246269 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -70,6 +70,8 @@ private: int rect_select_w; int rect_select_h; int rect_select_offset; + + long visible_in; private slots: void show_context_menu(const QPoint& pos); void reset_view(); From a2eef2e8292fba4bf6c326389444ca294d55bec7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 21:01:36 +1100 Subject: [PATCH 25/32] numerous keyframe improvements --- mainwindow.cpp | 5 +++ panels/grapheditor.cpp | 8 ++++ panels/grapheditor.h | 4 +- project/effect.h | 1 - project/effectrow.h | 5 +-- project/keyframe.cpp | 37 ++++++++++++++++ project/keyframe.h | 4 ++ ui/graphview.cpp | 94 ++++++++++++++++++++++++++++++++++++++++ ui/graphview.h | 6 +++ ui/keyframedrawing.cpp | 12 ++++-- ui/keyframedrawing.h | 3 +- ui/keyframeview.cpp | 97 ++++++++++++++++++++---------------------- ui/keyframeview.h | 5 ++- 13 files changed, 217 insertions(+), 64 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index bcca7a9fa..6bdb414ed 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -206,6 +206,7 @@ MainWindow::MainWindow(QWidget *parent) : autorecovery_filename = data_dir + "/autorecovery.ove"; if (QFile::exists(autorecovery_filename)) { if (QMessageBox::question(NULL, "Auto-recovery", "Olive didn't close properly and an autorecovery file was detected. Would you like to open it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + enable_launch_with_project = false; open_project_worker(autorecovery_filename, true); } } @@ -258,12 +259,16 @@ void MainWindow::delete_slot() { panel_project->delete_selected_media(); } else if (panel_effect_controls->keyframe_focus()) { panel_effect_controls->delete_selected_keyframes(); + } else if (panel_graph_editor->view_is_focused()) { + panel_graph_editor->delete_selected_keys(); } } void MainWindow::select_all() { if (panel_timeline->focused()) { panel_timeline->select_all(); + } else if (panel_graph_editor->view_is_focused()) { + panel_graph_editor->select_all(); } } diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index c6e8500f6..2cb44f673 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -202,6 +202,14 @@ bool GraphEditor::view_is_focused() { return view->hasFocus() || header->hasFocus(); } +void GraphEditor::delete_selected_keys() { + view->delete_selected_keys(); +} + +void GraphEditor::select_all() { + view->select_all(); +} + void GraphEditor::set_key_button_enabled(bool e, int type) { linear_button->setEnabled(e); linear_button->setChecked(type == KEYFRAME_TYPE_LINEAR); diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 16c71d23a..1a5277b58 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -18,7 +18,9 @@ public: GraphEditor(QWidget* parent = 0); void update_panel(); void set_row(EffectRow* r); - bool view_is_focused(); + bool view_is_focused(); + void delete_selected_keys(); + void select_all(); private: GraphView* view; TimelineHeader* header; diff --git a/project/effect.h b/project/effect.h index a7210a3de..290fa6cc7 100644 --- a/project/effect.h +++ b/project/effect.h @@ -23,7 +23,6 @@ class QXmlStreamWriter; class Effect; class EffectRow; class CheckboxEx; -class KeyframeDelete; struct EffectMeta { QString name; diff --git a/project/effectrow.h b/project/effectrow.h index a91171545..8326cb1f2 100644 --- a/project/effectrow.h +++ b/project/effectrow.h @@ -8,7 +8,6 @@ class Effect; class QGridLayout; class EffectField; class QLabel; -class KeyframeDelete; class QPushButton; class ComboAction; class QHBoxLayout; @@ -23,8 +22,8 @@ public: EffectField* add_field(int type, const QString &id, int colspan = 1); EffectField* field(int i); int fieldCount(); - void set_keyframe_now(ComboAction *ca); - void delete_keyframe_at_time(ComboAction *ca, long time); + void set_keyframe_now(ComboAction *ca); + void delete_keyframe_at_time(ComboAction *ca, long time); ClickableLabel* label; Effect* parent_effect; bool savable; diff --git a/project/keyframe.cpp b/project/keyframe.cpp index 3d12220df..09b73be75 100644 --- a/project/keyframe.cpp +++ b/project/keyframe.cpp @@ -1,5 +1,10 @@ #include "keyframe.h" +#include + +#include "effectfield.h" +#include "undo.h" +#include "panels/panels.h" EffectKeyframe::EffectKeyframe() { pre_handle_x = -40; @@ -7,3 +12,35 @@ EffectKeyframe::EffectKeyframe() { post_handle_x = 40; post_handle_y = 0; } + +void delete_keyframes(QVector& selected_key_fields, QVector &selected_keys) { + QVector fields; + QVector key_indices; + + for (int i=0;i 0) { + ComboAction* ca = new ComboAction(); + for (int i=0;iappend(new KeyframeDelete(fields.at(i), key_indices.at(i))); + } + undo_stack.push(ca); + selected_keys.clear(); + selected_key_fields.clear(); + update_ui(false); + } +} diff --git a/project/keyframe.h b/project/keyframe.h index 7974a8a1b..fd7e4ac0f 100644 --- a/project/keyframe.h +++ b/project/keyframe.h @@ -3,6 +3,8 @@ #include +class EffectField; + class EffectKeyframe { public: EffectKeyframe(); @@ -18,4 +20,6 @@ public: double post_handle_y; }; +void delete_keyframes(QVector &selected_key_fields, QVector &selected_keys); + #endif // KEYFRAME_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index ec535fc10..3f226ad8c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -56,6 +56,18 @@ GraphView::GraphView(QWidget* parent) : void GraphView::show_context_menu(const QPoint& pos) { QMenu menu(this); + QAction* zoom_to_selection = menu.addAction("Zoom to Selection"); + if (selected_keys.size() == 0) { + zoom_to_selection->setEnabled(false); + } else { + connect(zoom_to_selection, SIGNAL(triggered(bool)), this, SLOT(set_view_to_selection())); + } + + QAction* zoom_to_all = menu.addAction("Zoom to Show All"); + connect(zoom_to_all, SIGNAL(triggered(bool)), this, SLOT(set_view_to_all())); + + menu.addSeparator(); + QAction* reset_action = menu.addAction("Reset View"); connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); @@ -70,6 +82,61 @@ void GraphView::reset_view() { update(); } +void GraphView::set_view_to_selection() { + if (selected_keys.size() > 0) { + long min_time = LONG_MAX; + long max_time = LONG_MIN; + double min_dbl = DBL_MAX; + double max_dbl = DBL_MIN; + for (int i=0;ifield(selected_keys_fields.at(i))->keyframes.at(selected_keys.at(i)); + min_time = qMin(key.time, min_time); + max_time = qMax(key.time, max_time); + min_dbl = qMin(key.data.toDouble(), min_dbl); + max_dbl = qMax(key.data.toDouble(), max_dbl); + } + set_view_to_rect(min_time, min_dbl, max_time, max_dbl); + } +} + +void GraphView::set_view_to_all() { + bool can_set = false; + + long min_time = LONG_MAX; + long max_time = LONG_MIN; + double min_dbl = DBL_MAX; + double max_dbl = DBL_MIN; + for (int i=0;ifieldCount();i++) { + for (int j=0;jfield(i)->keyframes.size();j++) { + const EffectKeyframe& key = row->field(i)->keyframes.at(j); + min_time = qMin(key.time, min_time); + max_time = qMax(key.time, max_time); + min_dbl = qMin(key.data.toDouble(), min_dbl); + max_dbl = qMax(key.data.toDouble(), max_dbl); + can_set = true; + } + } + if (can_set) { + set_view_to_rect(min_time, min_dbl, max_time, max_dbl); + } +} + +void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { + double padding = 1.5; + double inverse_padding = 1.0/padding; + double x_diff = double(x2 - x1); + double y_diff = (y2 - y1); + double x_diff_padded = x_diff*padding; + double y_diff_padded = y_diff*padding; + set_zoom(qMin(double(width()) / x_diff_padded, double(height()) / y_diff_padded)); + + set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom)); + set_scroll_y(qRound((double(y1) - ((y_diff_padded-y_diff)/2))*zoom)); + + //set_scroll_y(height() - y1); + +} + void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { // draws last line's text QString str = QString::number(line_no*GRAPH_SIZE); @@ -520,12 +587,39 @@ void GraphView::set_field_visibility(int field, bool b) { update(); } +void GraphView::delete_selected_keys() { + if (row != NULL) { + QVector fields; + for (int i=0;ifield(selected_keys_fields.at(i))); + } + delete_keyframes(fields, selected_keys); + } +} + +void GraphView::select_all() { + if (row != NULL) { + selected_keys.clear(); + selected_keys_fields.clear(); + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + for (int j=0;jkeyframes.size();j++) { + selected_keys.append(j); + selected_keys_fields.append(i); + } + } + selection_update(); + } +} + void GraphView::set_scroll_x(int s) { x_scroll = s; emit x_scroll_changed(x_scroll); } void GraphView::set_scroll_y(int s) { + dout << s << height() << zoom; + y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index 5d1246269..e4646cb66 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -23,6 +23,9 @@ public: void set_selected_keyframe_type(int type); void set_field_visibility(int field, bool b); + + void delete_selected_keys(); + void select_all(); signals: void x_scroll_changed(int); void y_scroll_changed(int); @@ -75,6 +78,9 @@ private: private slots: void show_context_menu(const QPoint& pos); void reset_view(); + void set_view_to_selection(); + void set_view_to_all(); + void set_view_to_rect(int x1, double y1, int x2, double y2); }; #endif // GRAPHVIEW_H diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 07d515957..53229e46c 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -4,10 +4,14 @@ #define KEYFRAME_POINT_COUNT 4 -void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { - int color = (darker) ? 100 : 160; +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r, int g, int b) { + if (darker) { + r *= 0.625; + g *= 0.625; + b *= 0.625; + } p.setPen(QColor(0, 0, 0)); - p.setBrush(QColor(color, color, color)); + p.setBrush(QColor(r, g, b)); switch (type) { case KEYFRAME_TYPE_LINEAR: @@ -24,5 +28,5 @@ void draw_keyframe(QPainter &p, int type, int x, int y, bool darker) { break; } - p.setBrush(Qt::NoBrush); + p.setBrush(Qt::NoBrush); } diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h index c09ae2626..c8c60e9e3 100644 --- a/ui/keyframedrawing.h +++ b/ui/keyframedrawing.h @@ -4,7 +4,8 @@ #include #define KEYFRAME_SIZE 6 +#define KEYFRAME_COLOR 160 -void draw_keyframe(QPainter &p, int type, int x, int y, bool darker); +void draw_keyframe(QPainter &p, int type, int x, int y, bool darker, int r = KEYFRAME_COLOR, int g = KEYFRAME_COLOR, int b = KEYFRAME_COLOR); #endif // KEYFRAMEDRAWING_H diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index bbe8d7dac..ddebcf1c4 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -16,6 +16,7 @@ #include "ui/clickablelabel.h" #include "ui/resizablescrollbar.h" #include "ui/rectangleselect.h" +#include "project/keyframe.h" #include #include @@ -165,23 +166,7 @@ void KeyframeView::update_keys() { } void KeyframeView::delete_selected_keyframes() { - ComboAction* ca = new ComboAction(); - bool del = false; - for (int i=0;iappend(new KeyframeDelete(selected_fields.at(i), selected_keyframes.at(i))); - del = true; - } - if (del) { - undo_stack.push(ca); - - selected_keyframes.clear(); - selected_fields.clear(); - update_keys(); - panel_sequence_viewer->viewer_widget->update(); - } else { - delete ca; - } + delete_keyframes(selected_fields, selected_keyframes); } void KeyframeView::set_x_scroll(int s) { @@ -201,6 +186,8 @@ void KeyframeView::resize_move(double d) { void KeyframeView::mousePressEvent(QMouseEvent *event) { rect_select_x = event->x(); rect_select_y = event->y(); + rect_select_w = 0; + rect_select_h = 0; if (panel_timeline->tool == TIMELINE_TOOL_HAND || event->buttons() & Qt::MiddleButton) { scroll_drag = true; @@ -244,7 +231,11 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } bool already_selected = false; keys_selected = false; - if (keyframe_index > -1) already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + if (keyframe_index > -1) { + already_selected = keyframeIsSelected(rows.at(row_index)->field(field_index), keyframe_index); + } else { + select_rect = true; + } if (!already_selected) { if (!(event->modifiers() & Qt::ShiftModifier)) { selected_fields.clear(); @@ -274,10 +265,11 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { for (int i=0;ikeyframes.at(selected_keyframes.at(i)).time); } - keys_selected = true; } + rect_select_offset = selected_fields.size(); + update_keys(); if (event->button() == Qt::LeftButton) { @@ -298,7 +290,40 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { rect_select_y = event->pos().y(); } else if (mousedown) { int mouse_x = event->x() + x_scroll; - if (keys_selected) { + if (select_rect) { + // do a rect select + selected_fields.resize(rect_select_offset); + selected_keyframes.resize(rect_select_offset); + + rect_select_w = event->x() - rect_select_x; + rect_select_h = event->y() - rect_select_y; + + int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; + int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; + + long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll); + long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); + long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; + long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; + + for (int i=0;i= min_row && rowY.at(i) <= max_row) { + EffectRow* row = rows.at(i); + for (int k=0;kfieldCount();k++) { + EffectField* field = row->field(k); + for (int j=0;jkeyframes.size();j++) { + long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); + if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { + selected_fields.append(field); + selected_keyframes.append(j); + } + } + } + } + } + + update_keys(); + } else if (keys_selected) { // move keyframes long frame_diff = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x) - drag_frame_start; @@ -345,38 +370,6 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { dragging = true; update_ui(false); - } else { - // do a rect select - rect_select_w = event->x() - rect_select_x; - rect_select_h = event->y() - rect_select_y; - - int min_row = qMin(rect_select_y, event->y())-KEYFRAME_SIZE; - int max_row = qMax(rect_select_y, event->y())+KEYFRAME_SIZE; - - long frame_start = getFrameFromScreenPoint(panel_effect_controls->zoom, rect_select_x+x_scroll); - long frame_end = getFrameFromScreenPoint(panel_effect_controls->zoom, mouse_x); - long min_frame = qMin(frame_start, frame_end)-KEYFRAME_SIZE; - long max_frame = qMax(frame_start, frame_end)+KEYFRAME_SIZE; - - for (int i=0;i= min_row && rowY.at(i) <= max_row) { - EffectRow* row = rows.at(i); - for (int k=0;kfieldCount();k++) { - EffectField* field = row->field(k); - for (int j=0;jkeyframes.size();j++) { - long keyframe_frame = adjust_row_keyframe(row, field->keyframes.at(j).time); - if (!keyframeIsSelected(field, j) && keyframe_frame >= min_frame && keyframe_frame <= max_frame) { - selected_fields.append(field); - selected_keyframes.append(j); - } - } - } - } - } - - select_rect = true; - - update_keys(); } } } diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 59a328ed9..51bbd361d 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -27,7 +27,7 @@ public slots: void resize_move(double d); private: long adjust_row_keyframe(EffectRow* row, long time); - QVector selected_fields; + QVector selected_fields; QVector selected_keyframes; QVector rowY; QVector rows; @@ -42,7 +42,7 @@ private: bool select_rect; bool scroll_drag; - bool keyframeIsSelected(EffectField *field, int keyframe); + bool keyframeIsSelected(EffectField *field, int keyframe); long drag_frame_start; long last_frame_diff; @@ -50,6 +50,7 @@ private: int rect_select_y; int rect_select_w; int rect_select_h; + int rect_select_offset; int x_scroll; int y_scroll; From 3f641ff815eeb85f51d66f133b768d8777a68fe2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 21:26:34 +1100 Subject: [PATCH 26/32] more graph tweaks --- ui/graphview.cpp | 96 ++++++++++++++++++++++++--------------------- ui/graphview.h | 6 ++- ui/keyframeview.cpp | 23 ++++++++++- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 3f226ad8c..f1e7cab72 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -123,11 +123,10 @@ void GraphView::set_view_to_all() { void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { double padding = 1.5; - double inverse_padding = 1.0/padding; double x_diff = double(x2 - x1); double y_diff = (y2 - y1); - double x_diff_padded = x_diff*padding; - double y_diff_padded = y_diff*padding; + double x_diff_padded = (x_diff+10)*padding; + double y_diff_padded = (y_diff+10)*padding; set_zoom(qMin(double(width()) / x_diff_padded, double(height()) / y_diff_padded)); set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*zoom)); @@ -342,29 +341,27 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } else { // selecting a handle QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_PRE; + } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_POST; + } + + if (current_handle != BEZIER_HANDLE_NONE) { sel_key = j; sel_key_field = i; - old_handle_x = key.pre_handle_x; - old_handle_y = key.pre_handle_y; - current_handle = BEZIER_HANDLE_PRE; + old_pre_handle_x = key.pre_handle_x; + old_pre_handle_y = key.pre_handle_y; + old_post_handle_x = key.post_handle_x; + old_post_handle_y = key.post_handle_y; break; - } else { - QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); - if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { - sel_key = j; - sel_key_field = i; - old_handle_x = key.post_handle_x; - old_handle_y = key.post_handle_y; - current_handle = BEZIER_HANDLE_POST; - break; - } } } } @@ -446,12 +443,11 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } update(); } else { - bool shift = (event->modifiers() & Qt::ShiftModifier); switch (current_handle) { case BEZIER_HANDLE_NONE: for (int i=0;ifield(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].time = qRound(selected_keys_old_vals.at(i) + (double(event->pos().x() - start_x)/zoom)); - if (shift) { + if (event->modifiers() & Qt::ShiftModifier) { row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = selected_keys_old_doubles.at(i); } else { row->field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)].data = qRound(selected_keys_old_doubles.at(i) + (double(start_y - event->pos().y())/zoom)); @@ -461,27 +457,43 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { update_ui(false); break; case BEZIER_HANDLE_PRE: - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - if (shift) { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y; - } else { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].pre_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; - } - moved_keys = true; - update_ui(false); - break; case BEZIER_HANDLE_POST: - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_x = old_handle_x + double(event->pos().x() - start_x)/zoom; - if (shift) { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y; + { + double new_pre_handle_x = old_pre_handle_x; + double new_pre_handle_y = old_pre_handle_y; + double new_post_handle_x = old_post_handle_x; + double new_post_handle_y = old_post_handle_y; + + double x_diff = double(event->pos().x() - start_x)/zoom; + double y_diff = double(start_y - event->pos().y())/zoom; + + if (current_handle == BEZIER_HANDLE_PRE) { + new_pre_handle_x += x_diff; + if (!(event->modifiers() & Qt::ShiftModifier)) new_pre_handle_y += y_diff; + if (!(event->modifiers() & Qt::ControlModifier)) { + new_post_handle_x = -new_pre_handle_x; + new_post_handle_y = -new_pre_handle_y; + } } else { - row->field(selected_keys_fields.last())->keyframes[selected_keys.last()].post_handle_y = old_handle_y + double(start_y - event->pos().y())/zoom; + new_post_handle_x += x_diff; + if (!(event->modifiers() & Qt::ShiftModifier)) new_post_handle_y += y_diff; + if (!(event->modifiers() & Qt::ControlModifier)) { + new_pre_handle_x = -new_post_handle_x; + new_pre_handle_y = -new_post_handle_y; + } } + + EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + key.pre_handle_x = new_pre_handle_x; + key.pre_handle_y = new_pre_handle_y; + key.post_handle_x = new_post_handle_x; + key.post_handle_y = new_post_handle_y; + moved_keys = true; update_ui(false); + } break; } - } } } @@ -498,17 +510,13 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } break; case BEZIER_HANDLE_PRE: - { - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; - ca->append(new SetDouble(&key.pre_handle_x, old_handle_x, key.pre_handle_x)); - ca->append(new SetDouble(&key.pre_handle_y, old_handle_y, key.pre_handle_y)); - } - break; case BEZIER_HANDLE_POST: { EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; - ca->append(new SetDouble(&key.post_handle_x, old_handle_x, key.post_handle_x)); - ca->append(new SetDouble(&key.post_handle_y, old_handle_y, key.post_handle_y)); + ca->append(new SetDouble(&key.pre_handle_x, old_pre_handle_x, key.pre_handle_x)); + ca->append(new SetDouble(&key.pre_handle_y, old_pre_handle_y, key.pre_handle_y)); + ca->append(new SetDouble(&key.post_handle_x, old_post_handle_x, key.post_handle_x)); + ca->append(new SetDouble(&key.post_handle_y, old_post_handle_y, key.post_handle_y)); } break; } @@ -618,8 +626,6 @@ void GraphView::set_scroll_x(int s) { } void GraphView::set_scroll_y(int s) { - dout << s << height() << zoom; - y_scroll = s; emit y_scroll_changed(y_scroll); } diff --git a/ui/graphview.h b/ui/graphview.h index e4646cb66..2070b5420 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -55,8 +55,10 @@ private: QVector selected_keys_old_vals; QVector selected_keys_old_doubles; - double old_handle_x; - double old_handle_y; + double old_pre_handle_x; + double old_pre_handle_y; + double old_post_handle_x; + double old_post_handle_y; bool moved_keys; diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index ddebcf1c4..ba8557694 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -17,6 +17,7 @@ #include "ui/resizablescrollbar.h" #include "ui/rectangleselect.h" #include "project/keyframe.h" +#include "ui/graphview.h" #include #include @@ -113,7 +114,27 @@ void KeyframeView::paintEvent(QPaintEvent*) { if (!key_times.contains(f->keyframes.at(k).time)) { bool keyframe_selected = keyframeIsSelected(f, k); long keyframe_frame = adjust_row_keyframe(row, f->keyframes.at(k).time); - draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + + // see if any other keyframes have this time + bool solo = true; + for (int m=0;mfieldCount();m++) { + EffectField* compf = row->field(m); + for (int n=0;nkeyframes.size();n++) { + if (f->keyframes.at(k).time == compf->keyframes.at(n).time + && !(m == l && k == n)) { + solo = false; + break; + } + } + } + + if (solo) { + QColor cc = get_curve_color(l, row->fieldCount()); + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected, cc.red(), cc.green(), cc.blue()); + } else { + draw_keyframe(p, f->keyframes.at(k).type, getScreenPointFromFrame(panel_effect_controls->zoom, keyframe_frame) - x_scroll, keyframe_y, keyframe_selected); + } + key_times.append(f->keyframes.at(k).time); } } From fc7c17dee90d6191ad8830b74561b17147e6a107 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:35:26 +1100 Subject: [PATCH 27/32] implemented adding keyframes by click to graph editor --- project/undo.cpp | 22 ++++ project/undo.h | 15 +++ ui/graphview.cpp | 327 +++++++++++++++++++++++++++++++++++------------ ui/graphview.h | 8 ++ 4 files changed, 292 insertions(+), 80 deletions(-) diff --git a/project/undo.cpp b/project/undo.cpp index fae0bfc10..a8f7eacda 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1269,3 +1269,25 @@ void SetLong::redo() { *p = newval; mainWindow->setWindowModified(true); } + +KeyframeFieldSet::KeyframeFieldSet(EffectField *ifield, int ii) : + field(ifield), + index(ii), + key(ifield->keyframes.at(ii)), + done(true), + old_project_changed(mainWindow->isWindowModified()) +{} + +void KeyframeFieldSet::undo() { + field->keyframes.removeAt(index); + mainWindow->setWindowModified(old_project_changed); + done = false; +} + +void KeyframeFieldSet::redo() { + if (!done) { + field->keyframes.insert(index, key); + mainWindow->setWindowModified(true); + } + done = true; +} diff --git a/project/undo.h b/project/undo.h index fcb44f500..c2e9f62a9 100644 --- a/project/undo.h +++ b/project/undo.h @@ -360,6 +360,21 @@ private: bool done; }; +// a more modern version of the above, could probably replace it +// assumes the keyframe already exists +class KeyframeFieldSet : public QUndoCommand { +public: + KeyframeFieldSet(EffectField* ifield, int ii); + void undo(); + void redo(); +private: + EffectField* field; + int index; + EffectKeyframe key; + bool done; + bool old_project_changed; +}; + class EffectFieldUndo : public QUndoCommand { public: EffectFieldUndo(EffectField* field); diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f1e7cab72..fa15c25ce 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -16,6 +16,7 @@ #include "project/effect.h" #include "project/clip.h" #include "ui/rectangleselect.h" +#include "Ui/labelslider.h" #include "debug.h" @@ -171,6 +172,24 @@ void GraphView::draw_lines(QPainter& p, bool vert) { draw_line_text(p, vert, last_line, last_line_x, width()); } +QVector sort_keys_from_field(EffectField* field) { + QVector sorted_keys; + for (int k=0;kkeyframes.size();k++) { + bool inserted = false; + for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { + sorted_keys.insert(j, k); + inserted = true; + break; + } + } + if (!inserted) { + sorted_keys.append(k); + } + } + return sorted_keys; +} + void GraphView::paintEvent(QPaintEvent *event) { QPainter p(this); @@ -192,20 +211,7 @@ void GraphView::paintEvent(QPaintEvent *event) { if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { // sort keyframes by time - QVector sorted_keys; - for (int k=0;kkeyframes.size();k++) { - bool inserted = false; - for (int j=0;jkeyframes.at(sorted_keys.at(j)).time > field->keyframes.at(k).time) { - sorted_keys.insert(j, k); - inserted = true; - break; - } - } - if (!inserted) { - sorted_keys.append(k); - } - } + QVector sorted_keys = sort_keys_from_field(field); int last_key_x, last_key_y; @@ -258,6 +264,7 @@ void GraphView::paintEvent(QPaintEvent *event) { last_key_x = key_x; last_key_y = key_y; } + if (last_key_x < width()) p.drawLine(last_key_x, last_key_y, width(), last_key_y); // draw keys for (int j=0;jfieldCount();i++) { - EffectField* field = row->field(i); - if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { - for (int j=0;jkeyframes.size();j++) { - const EffectKeyframe& key = field->keyframes.at(j); - int key_x = get_screen_x(key.time); - int key_y = get_screen_y(key.data.toDouble()); - if (event->pos().x() > key_x-KEYFRAME_SIZE - && event->pos().x() < key_x+KEYFRAME_SIZE - && event->pos().y() > key_y-KEYFRAME_SIZE - && event->pos().y() < key_y+KEYFRAME_SIZE) { - sel_key = j; - sel_key_field = i; - break; - } else { - // selecting a handle - QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); - QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); - if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { - current_handle = BEZIER_HANDLE_PRE; - } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE - && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE - && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE - && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { - current_handle = BEZIER_HANDLE_POST; - } + if (click_add) { + selected_keys.clear(); + selected_keys_fields.clear(); - if (current_handle != BEZIER_HANDLE_NONE) { + EffectKeyframe key; + key.time = get_value_x(event->pos().x()); + key.data = get_value_y(event->pos().y()); + key.type = click_add_type; + click_add_key = click_add_field->keyframes.size(); + click_add_field->keyframes.append(key); + update_ui(false); + } else { + for (int i=0;ifieldCount();i++) { + EffectField* field = row->field(i); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + for (int j=0;jkeyframes.size();j++) { + const EffectKeyframe& key = field->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); + if (event->pos().x() > key_x-KEYFRAME_SIZE + && event->pos().x() < key_x+KEYFRAME_SIZE + && event->pos().y() > key_y-KEYFRAME_SIZE + && event->pos().y() < key_y+KEYFRAME_SIZE) { sel_key = j; sel_key_field = i; - old_pre_handle_x = key.pre_handle_x; - old_pre_handle_y = key.pre_handle_y; - old_post_handle_x = key.post_handle_x; - old_post_handle_y = key.post_handle_y; break; + } else { + // selecting a handle + QPointF pre_point(key_x + key.pre_handle_x*zoom, key_y - key.pre_handle_y*zoom); + QPointF post_point(key_x + key.post_handle_x*zoom, key_y - key.post_handle_y*zoom); + if (event->pos().x() > pre_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < pre_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > pre_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < pre_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_PRE; + } else if (event->pos().x() > post_point.x()-BEZIER_HANDLE_SIZE + && event->pos().x() < post_point.x()+BEZIER_HANDLE_SIZE + && event->pos().y() > post_point.y()-BEZIER_HANDLE_SIZE + && event->pos().y() < post_point.y()+BEZIER_HANDLE_SIZE) { + current_handle = BEZIER_HANDLE_POST; + } + + if (current_handle != BEZIER_HANDLE_NONE) { + sel_key = j; + sel_key_field = i; + old_pre_handle_x = key.pre_handle_x; + old_pre_handle_y = key.pre_handle_y; + old_post_handle_x = key.post_handle_x; + old_post_handle_y = key.post_handle_y; + break; + } } } } + if (sel_key > -1) break; } - if (sel_key > -1) break; - } - bool already_selected = false; - if (sel_key > -1) { - for (int i=0;imodifiers() & Qt::ShiftModifier)) { - selected_keys.removeAt(i); - selected_keys_fields.removeAt(i); + bool already_selected = false; + if (sel_key > -1) { + for (int i=0;imodifiers() & Qt::ShiftModifier)) { + selected_keys.removeAt(i); + selected_keys_fields.removeAt(i); + } + already_selected = true; + break; } - already_selected = true; - break; } } - } - if (!already_selected) { - if (!(event->modifiers() & Qt::ShiftModifier)) { - selected_keys.clear(); - selected_keys_fields.clear(); + if (!already_selected) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + selected_keys.clear(); + selected_keys_fields.clear(); + } + if (sel_key > -1) { + selected_keys.append(sel_key); + selected_keys_fields.append(sel_key_field); + } else { + rect_select = true; + rect_select_x = event->pos().x(); + rect_select_y = event->pos().y(); + rect_select_w = 0; + rect_select_h = 0; + rect_select_offset = selected_keys.size(); + } } - if (sel_key > -1) { - selected_keys.append(sel_key); - selected_keys_fields.append(sel_key_field); - } else { - rect_select = true; - rect_select_x = event->pos().x(); - rect_select_y = event->pos().y(); - rect_select_w = 0; - rect_select_h = 0; - rect_select_offset = selected_keys.size(); - } - } - selection_update(); + selection_update(); + } } } void GraphView::mouseMoveEvent(QMouseEvent *event) { - unsetCursor(); + if (!mousedown || !click_add) unsetCursor(); if (mousedown) { - if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (click_add) { + click_add_field->keyframes[click_add_key].time = get_value_x(event->pos().x()); + click_add_field->keyframes[click_add_key].data = get_value_y(event->pos().y()); + update_ui(false); + } else if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); set_scroll_y(y_scroll + event->pos().y() - start_y); start_x = event->pos().x(); @@ -495,11 +519,145 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { break; } } + } else if (row != NULL) { + // clicking on the curve + click_add = false; + + bool hovering_key = false; + + for (int i=0;ifieldCount();i++) { + for (int j=0;jfield(i)->keyframes.size();j++) { + const EffectKeyframe& key = row->field(i)->keyframes.at(j); + int key_x = get_screen_x(key.time); + int key_y = get_screen_y(key.data.toDouble()); + QRect test_rect( + key_x - KEYFRAME_SIZE, + key_y - KEYFRAME_SIZE, + KEYFRAME_SIZE+KEYFRAME_SIZE, + KEYFRAME_SIZE+KEYFRAME_SIZE + ); + QRect pre_rect( + key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE, + key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE + ); + QRect post_rect( + key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE, + key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, + BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE + ); + + if (test_rect.contains(event->pos()) + || pre_rect.contains(event->pos()) + || post_rect.contains(event->pos())) { + hovering_key = true; + break; + } + } + } + + if (!hovering_key) { + for (int i=0;ifieldCount();i++) { + EffectField* f = row->field(i); + if (field_visibility.at(i)) { + QVector sorted_keys = sort_keys_from_field(f); + + if (event->pos().x() <= get_screen_x(f->keyframes.at(sorted_keys.first()).time)) { + int y_comp = get_screen_y(f->keyframes.at(sorted_keys.first()).data.toDouble()); + if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE + && event->pos().y() <= y_comp+BEZIER_LINE_SIZE) { + // dout << "make an EARLY key on field" << i; + click_add = true; + click_add_type = f->keyframes.at(sorted_keys.first()).type; + } + } else if (event->pos().x() >= get_screen_x(f->keyframes.at(sorted_keys.last()).time)) { + int y_comp = get_screen_y(f->keyframes.at(sorted_keys.last()).data.toDouble()); + if (event->pos().y() >= y_comp-BEZIER_LINE_SIZE + && event->pos().y() <= y_comp+BEZIER_LINE_SIZE) { + // dout << "make an LATE key on field" << i; + click_add = true; + click_add_type = f->keyframes.at(sorted_keys.last()).type; + } + } else { + for (int j=1;jkeyframes.at(sorted_keys.at(j-1)); + const EffectKeyframe& key = f->keyframes.at(sorted_keys.at(j)); + + int last_key_x = get_screen_x(last_key.time); + int key_x = get_screen_x(key.time); + int last_key_y = get_screen_y(last_key.data.toDouble()); + int key_y = get_screen_y(key.data.toDouble()); + + click_add_type = last_key.type; + + if (event->pos().x() >= last_key_x + && event->pos().x() <= key_x) { + QRect mouse_rect(event->pos().x()-BEZIER_LINE_SIZE, event->pos().y()-BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE, BEZIER_LINE_SIZE+BEZIER_LINE_SIZE); + // NOTE: FILTHY copy/paste from paintEvent + if (last_key.type == KEYFRAME_TYPE_HOLD) { + // hold + if (event->pos().y() >= last_key_y-BEZIER_LINE_SIZE + && event->pos().y() <= last_key_y+BEZIER_LINE_SIZE) { + // dout << "make an HOLD key on field" << i << "after key" << j; + click_add = true; + } + } else if (last_key.type == KEYFRAME_TYPE_BEZIER || key.type == KEYFRAME_TYPE_BEZIER) { + QPainterPath bezier_path; + bezier_path.moveTo(last_key_x, last_key_y); + if (last_key.type == KEYFRAME_TYPE_BEZIER && key.type == KEYFRAME_TYPE_BEZIER) { + // cubic bezier + bezier_path.cubicTo( + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), + QPointF(key_x, key_y) + ); + } else if (key.type == KEYFRAME_TYPE_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + bezier_path.quadTo( + QPointF(last_key_x+last_key.post_handle_x*zoom, last_key_y-last_key.post_handle_y*zoom), + QPointF(key_x, key_y) + ); + } else { + // this keyframe is the bezier one + bezier_path.quadTo( + QPointF(key_x+key.pre_handle_x*zoom, key_y-key.pre_handle_y*zoom), + QPointF(key_x, key_y) + ); + } + if (bezier_path.intersects(mouse_rect)) { + // dout << "make an BEZIER key on field" << i << "after key" << j; + click_add = true; + } + } else { + // linear + QPainterPath linear_path; + linear_path.moveTo(last_key_x, last_key_y); + linear_path.lineTo(key_x, key_y); + if (linear_path.intersects(mouse_rect)) { + // dout << "make an LINEAR key on field" << i << "after key" << j; + click_add = true; + } + } + } + } + } + } + if (click_add) { + click_add_field = f; + setCursor(Qt::CrossCursor); + break; + } + } + } } } void GraphView::mouseReleaseEvent(QMouseEvent *event) { - if (moved_keys && selected_keys.size() > 0) { + if (click_add) { + undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key)); + } else if (moved_keys && selected_keys.size() > 0) { ComboAction* ca = new ComboAction(); switch (current_handle) { case BEZIER_HANDLE_NONE: @@ -524,6 +682,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { } moved_keys = false; mousedown = false; + click_add = false; if (rect_select) { rect_select = false; selection_update(); @@ -643,6 +802,14 @@ int GraphView::get_screen_y(double d) { return height() + y_scroll - d*zoom; } +long GraphView::get_value_x(int i) { + return qRound((i + x_scroll)/zoom); +} + +double GraphView::get_value_y(int i) { + return double(height() + y_scroll - i)/zoom; +} + void GraphView::selection_update() { selected_keys_old_vals.clear(); selected_keys_old_doubles.clear(); diff --git a/ui/graphview.h b/ui/graphview.h index 2070b5420..f7aa805bc 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -5,6 +5,7 @@ #include class EffectRow; +class EffectField; QColor get_curve_color(int index, int length); @@ -45,6 +46,8 @@ private: int get_screen_x(double); int get_screen_y(double); + long get_value_x(int); + double get_value_y(int); void selection_update(); @@ -77,6 +80,11 @@ private: int rect_select_offset; long visible_in; + + bool click_add; + EffectField* click_add_field; + int click_add_key; + int click_add_type; private slots: void show_context_menu(const QPoint& pos); void reset_view(); From 4aa44f8fa605ff1e76b0c9e6eb42d26e94ef240f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:45:08 +1100 Subject: [PATCH 28/32] implemented saving bezier handles and fixed selection crash --- project/effect.cpp | 12 ++++++++++++ ui/graphview.cpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/project/effect.cpp b/project/effect.cpp index d91019d79..0315e1c4f 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -701,6 +701,14 @@ void Effect::load(QXmlStreamReader& stream) { key.time = attr.value().toLong(); } else if (attr.name() == "type") { key.type = attr.value().toInt(); + } else if (attr.name() == "prehx") { + key.pre_handle_x = attr.value().toDouble(); + } else if (attr.name() == "prehy") { + key.pre_handle_y = attr.value().toDouble(); + } else if (attr.name() == "posthx") { + key.post_handle_x = attr.value().toDouble(); + } else if (attr.name() == "posthy") { + key.post_handle_y = attr.value().toDouble(); } } field->keyframes.append(key); @@ -740,6 +748,10 @@ void Effect::save(QXmlStreamWriter& stream) { stream.writeAttribute("value", save_data_to_string(field->type, key.data)); stream.writeAttribute("frame", QString::number(key.time)); stream.writeAttribute("type", QString::number(key.type)); + stream.writeAttribute("prehx", QString::number(key.pre_handle_x)); + stream.writeAttribute("prehy", QString::number(key.pre_handle_y)); + stream.writeAttribute("posthx", QString::number(key.post_handle_x)); + stream.writeAttribute("posthy", QString::number(key.post_handle_y)); stream.writeEndElement(); // key } stream.writeEndElement(); // field diff --git a/ui/graphview.cpp b/ui/graphview.cpp index fa15c25ce..a939b155d 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -392,7 +392,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { if (sel_key > -1) { for (int i=0;imodifiers() & Qt::ShiftModifier)) { + if ((event->modifiers() & Qt::ShiftModifier) && current_handle == BEZIER_HANDLE_NONE) { selected_keys.removeAt(i); selected_keys_fields.removeAt(i); } From d9c5dfa489c6ba4cd80a3cc1545da55133fe2023 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:49:44 +1100 Subject: [PATCH 29/32] tiny code cleanup --- ui/graphview.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index a939b155d..d97bf58c8 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -16,7 +16,6 @@ #include "project/effect.h" #include "project/clip.h" #include "ui/rectangleselect.h" -#include "Ui/labelslider.h" #include "debug.h" @@ -190,7 +189,7 @@ QVector sort_keys_from_field(EffectField* field) { return sorted_keys; } -void GraphView::paintEvent(QPaintEvent *event) { +void GraphView::paintEvent(QPaintEvent *) { QPainter p(this); if (panel_sequence_viewer->seq != NULL) { @@ -537,14 +536,14 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { KEYFRAME_SIZE+KEYFRAME_SIZE ); QRect pre_rect( - key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE, - key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE, + qRound(key_x + key.pre_handle_x*zoom - BEZIER_HANDLE_SIZE), + qRound(key_y + key.pre_handle_y*zoom - BEZIER_HANDLE_SIZE), BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE ); QRect post_rect( - key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE, - key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE, + qRound(key_x + key.post_handle_x*zoom - BEZIER_HANDLE_SIZE), + qRound(key_y + key.post_handle_y*zoom - BEZIER_HANDLE_SIZE), BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE, BEZIER_HANDLE_SIZE+BEZIER_HANDLE_SIZE ); @@ -654,7 +653,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } -void GraphView::mouseReleaseEvent(QMouseEvent *event) { +void GraphView::mouseReleaseEvent(QMouseEvent *) { if (click_add) { undo_stack.push(new KeyframeFieldSet(click_add_field, click_add_key)); } else if (moved_keys && selected_keys.size() > 0) { @@ -693,7 +692,6 @@ void GraphView::mouseReleaseEvent(QMouseEvent *event) { void GraphView::wheelEvent(QWheelEvent *event) { bool redraw = false; bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom - bool alt = (event->modifiers() & Qt::AltModifier); // horiz scroll instead of vert scroll if (shift) { // scroll @@ -795,11 +793,11 @@ void GraphView::set_zoom(double z) { } int GraphView::get_screen_x(double d) { - return (d*zoom) - x_scroll; + return qRound((d*zoom) - x_scroll); } int GraphView::get_screen_y(double d) { - return height() + y_scroll - d*zoom; + return qRound(height() + y_scroll - d*zoom); } long GraphView::get_value_x(int i) { From 8619aceb91aa0b77cc8b1ad030f093778c4bdb0c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:55:30 +1100 Subject: [PATCH 30/32] fixed graph editor using wrong key for handles --- ui/graphview.cpp | 6 ++++-- ui/graphview.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index d97bf58c8..f71fd98ee 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -375,6 +375,8 @@ void GraphView::mousePressEvent(QMouseEvent *event) { if (current_handle != BEZIER_HANDLE_NONE) { sel_key = j; sel_key_field = i; + handle_index = j; + handle_field = i; old_pre_handle_x = key.pre_handle_x; old_pre_handle_y = key.pre_handle_y; old_post_handle_x = key.post_handle_x; @@ -506,7 +508,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index]; key.pre_handle_x = new_pre_handle_x; key.pre_handle_y = new_pre_handle_y; key.post_handle_x = new_post_handle_x; @@ -669,7 +671,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) { case BEZIER_HANDLE_PRE: case BEZIER_HANDLE_POST: { - EffectKeyframe& key = row->field(selected_keys_fields.last())->keyframes[selected_keys.last()]; + EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index]; ca->append(new SetDouble(&key.pre_handle_x, old_pre_handle_x, key.pre_handle_x)); ca->append(new SetDouble(&key.pre_handle_y, old_pre_handle_y, key.pre_handle_y)); ca->append(new SetDouble(&key.post_handle_x, old_post_handle_x, key.post_handle_x)); diff --git a/ui/graphview.h b/ui/graphview.h index f7aa805bc..1a678083a 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -63,6 +63,9 @@ private: double old_post_handle_x; double old_post_handle_y; + int handle_field; + int handle_index; + bool moved_keys; int current_handle; From 212f7b84ca0d3cd4f12ad80b5865d2d7a0adec5c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 22:59:42 +1100 Subject: [PATCH 31/32] added include for some compilers --- ui/graphview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/graphview.cpp b/ui/graphview.cpp index f71fd98ee..39c05da7c 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "panels/panels.h" #include "panels/timeline.h" From 9b5e5a9ed679e2ba8bd14cf866d92537f5d1f8ca Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 4 Jan 2019 23:11:17 +1100 Subject: [PATCH 32/32] changed project version for keyframe overhaul --- io/config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io/config.h b/io/config.h index ea91f7f99..394710270 100644 --- a/io/config.h +++ b/io/config.h @@ -3,8 +3,8 @@ #include -#define SAVE_VERSION 181124 // YYMMDD -#define MIN_SAVE_VERSION 181114 // lowest compatible project version +#define SAVE_VERSION 190104 // YYMMDD +#define MIN_SAVE_VERSION 190104 // lowest compatible project version #define TIMECODE_DROP 0 #define TIMECODE_NONDROP 1