numerous keyframe improvements
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,7 +23,6 @@ class QXmlStreamWriter;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
class CheckboxEx;
|
||||
class KeyframeDelete;
|
||||
|
||||
struct EffectMeta {
|
||||
QString name;
|
||||
|
||||
+2
-3
@@ -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;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#include "keyframe.h"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#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<EffectField *>& selected_key_fields, QVector<int> &selected_keys) {
|
||||
QVector<EffectField*> fields;
|
||||
QVector<int> key_indices;
|
||||
|
||||
for (int i=0;i<selected_keys.size();i++) {
|
||||
bool added = false;
|
||||
for (int j=0;j<key_indices.size();j++) {
|
||||
if (key_indices.at(j) < selected_keys.at(i)) {
|
||||
key_indices.insert(j, selected_keys.at(i));
|
||||
fields.insert(j, selected_key_fields.at(i));
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
key_indices.append(selected_keys.at(i));
|
||||
fields.append(selected_key_fields.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.size() > 0) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<key_indices.size();i++) {
|
||||
ca->append(new KeyframeDelete(fields.at(i), key_indices.at(i)));
|
||||
}
|
||||
undo_stack.push(ca);
|
||||
selected_keys.clear();
|
||||
selected_key_fields.clear();
|
||||
update_ui(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
class EffectField;
|
||||
|
||||
class EffectKeyframe {
|
||||
public:
|
||||
EffectKeyframe();
|
||||
@@ -18,4 +20,6 @@ public:
|
||||
double post_handle_y;
|
||||
};
|
||||
|
||||
void delete_keyframes(QVector<EffectField *> &selected_key_fields, QVector<int> &selected_keys);
|
||||
|
||||
#endif // KEYFRAME_H
|
||||
|
||||
@@ -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;i<selected_keys.size();i++) {
|
||||
const EffectKeyframe& key = row->field(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;i<row->fieldCount();i++) {
|
||||
for (int j=0;j<row->field(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<EffectField*> fields;
|
||||
for (int i=0;i<selected_keys_fields.size();i++) {
|
||||
fields.append(row->field(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;i<row->fieldCount();i++) {
|
||||
EffectField* field = row->field(i);
|
||||
for (int j=0;j<field->keyframes.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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#include <QPainter>
|
||||
|
||||
#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
|
||||
|
||||
+45
-52
@@ -16,6 +16,7 @@
|
||||
#include "ui/clickablelabel.h"
|
||||
#include "ui/resizablescrollbar.h"
|
||||
#include "ui/rectangleselect.h"
|
||||
#include "project/keyframe.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
@@ -165,23 +166,7 @@ void KeyframeView::update_keys() {
|
||||
}
|
||||
|
||||
void KeyframeView::delete_selected_keyframes() {
|
||||
ComboAction* ca = new ComboAction();
|
||||
bool del = false;
|
||||
for (int i=0;i<selected_keyframes.size();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);
|
||||
|
||||
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;i<selected_fields.size();i++) {
|
||||
old_key_vals.append(selected_fields.at(i)->keyframes.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<rowY.size();i++) {
|
||||
if (rowY.at(i) >= min_row && rowY.at(i) <= max_row) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* field = row->field(k);
|
||||
for (int j=0;j<field->keyframes.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<rowY.size();i++) {
|
||||
if (rowY.at(i) >= min_row && rowY.at(i) <= max_row) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int k=0;k<row->fieldCount();k++) {
|
||||
EffectField* field = row->field(k);
|
||||
for (int j=0;j<field->keyframes.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -27,7 +27,7 @@ public slots:
|
||||
void resize_move(double d);
|
||||
private:
|
||||
long adjust_row_keyframe(EffectRow* row, long time);
|
||||
QVector<EffectField*> selected_fields;
|
||||
QVector<EffectField*> selected_fields;
|
||||
QVector<int> selected_keyframes;
|
||||
QVector<int> rowY;
|
||||
QVector<EffectRow*> 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;
|
||||
|
||||
Reference in New Issue
Block a user