From e101739214607fbb5e3ffcd5f751f34c4b792e9c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 28 Dec 2018 22:06:38 +1100 Subject: [PATCH] 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);