diff --git a/io/config.cpp b/io/config.cpp index b27b267b4..5ec8db596 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -70,7 +70,8 @@ Config::Config() center_timeline_timecodes(true), waveform_resolution(64), thumbnail_resolution(120), - add_default_effects_to_clips(true) + add_default_effects_to_clips(true), + horizontal_timeline_scroll(true) {} void Config::load(QString path) { @@ -87,6 +88,9 @@ void Config::load(QString path) { } else if (stream.name() == "ScrollZooms") { stream.readNext(); scroll_zooms = (stream.text() == "1"); + } else if (stream.name() == "HorizontalTimelineScroll") { + stream.readNext(); + horizontal_timeline_scroll = (stream.text() == "1"); } else if (stream.name() == "EditToolSelectsLinks") { stream.readNext(); edit_tool_selects_links = (stream.text() == "1"); @@ -233,6 +237,7 @@ void Config::save(QString path) { stream.writeTextElement("Version", QString::number(olive::kSaveVersion)); stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines)); stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms)); + stream.writeTextElement("HorizontalTimelineScroll", QString::number(horizontal_timeline_scroll)); stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links)); stream.writeTextElement("EditToolAlsoSeeks", QString::number(edit_tool_also_seeks)); stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks)); diff --git a/io/config.h b/io/config.h index c3362dfed..29973fdb9 100644 --- a/io/config.h +++ b/io/config.h @@ -165,6 +165,7 @@ struct Config { * @brief The scroll wheel zooms rather than scrolls * * **TRUE** if the scroll wheel should zoom in and out rather than scroll up and down. + * The Control key temporarily toggles this setting. */ bool scroll_zooms; @@ -509,6 +510,14 @@ struct Config { */ bool add_default_effects_to_clips; + /** + * @brief Horizontal timeline scroll + * + * **TRUE** Scrolling vertically with a mouse wheel or touchpad scrolls the Timeline horizontally. + * The Shift key temporarily toggles this setting. + */ + bool horizontal_timeline_scroll; + /** * @brief Load config from file * diff --git a/mainwindow.cpp b/mainwindow.cpp index 5421f99a9..7f3d35117 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -717,6 +717,10 @@ void MainWindow::setup_menus() { scroll_wheel_zooms->setCheckable(true); scroll_wheel_zooms->setData(reinterpret_cast(&olive::CurrentConfig.scroll_zooms)); + horizontal_timeline_scroll = MenuHelper::create_menu_action(tools_menu, "horizontaltimelinescroll", &olive::MenuHelper, SLOT(toggle_bool_action())); + horizontal_timeline_scroll->setCheckable(true); + horizontal_timeline_scroll->setData(reinterpret_cast(&olive::CurrentConfig.horizontal_timeline_scroll)); + enable_drag_files_to_timeline = MenuHelper::create_menu_action(tools_menu, "enabledragfilestotimeline", &olive::MenuHelper, SLOT(toggle_bool_action())); enable_drag_files_to_timeline->setCheckable(true); enable_drag_files_to_timeline->setData(reinterpret_cast(&olive::CurrentConfig.enable_drag_files_to_timeline)); @@ -881,7 +885,8 @@ void MainWindow::Retranslate() edit_tool_selects_links->setText(tr("Edit Tool Selects Links")); seek_also_selects->setText(tr("Seek Also Selects")); seek_to_end_of_pastes->setText(tr("Seek to the End of Pastes")); - scroll_wheel_zooms->setText(tr("Scroll Wheel Zooms")); + scroll_wheel_zooms->setText(tr("Scroll Wheel Zooms (Ctrl toggles)")); + horizontal_timeline_scroll->setText(tr("Invert Timeline scroll axes")); enable_drag_files_to_timeline->setText(tr("Enable Drag Files to Timeline")); autoscale_by_default->setText(tr("Auto-Scale By Default")); enable_seek_to_import->setText(tr("Enable Seek to Import")); @@ -1134,6 +1139,7 @@ void MainWindow::toolMenu_About_To_Be_Shown() { olive::MenuHelper.set_bool_action_checked(edit_tool_selects_links); olive::MenuHelper.set_bool_action_checked(seek_to_end_of_pastes); olive::MenuHelper.set_bool_action_checked(scroll_wheel_zooms); + olive::MenuHelper.set_bool_action_checked(horizontal_timeline_scroll); olive::MenuHelper.set_bool_action_checked(rectified_waveforms); olive::MenuHelper.set_bool_action_checked(enable_drag_files_to_timeline); olive::MenuHelper.set_bool_action_checked(autoscale_by_default); diff --git a/mainwindow.h b/mainwindow.h index 15b85d9d3..5cd9eb323 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -325,6 +325,7 @@ private: QAction* edit_tool_selects_links; QAction* seek_to_end_of_pastes; QAction* scroll_wheel_zooms; + QAction* horizontal_timeline_scroll; QAction* rectified_waveforms; QAction* enable_drag_files_to_timeline; QAction* autoscale_by_default; diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 27f625f87..e3ace0128 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -26,6 +26,7 @@ #include #include +#include "io/config.h" #include "panels/panels.h" #include "panels/timeline.h" #include "panels/viewer.h" @@ -49,14 +50,13 @@ const int kBezierHandlePre = 2; const int kBezierHandlePost = 3; QColor get_curve_color(int index, int length) { - QColor c; - int hue = qRound((double(index)/double(length))*255); - c.setHsv(hue, 255, 255); - return c; + QColor c; + int hue = qRound((double(index)/double(length))*255); + c.setHsv(hue, 255, 255); + return c; } -GraphView::GraphView(QWidget* parent) : QWidget(parent) -{ +GraphView::GraphView(QWidget* parent) : QWidget(parent) { x_scroll = 0; y_scroll = 0; mousedown = false; @@ -69,104 +69,104 @@ GraphView::GraphView(QWidget* parent) : QWidget(parent) visible_in = 0; click_add_proc = false; - setMouseTracking(true); - setFocusPolicy(Qt::ClickFocus); - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); + 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); + QMenu menu(this); - QAction* zoom_to_selection = menu.addAction(tr("Zoom to Selection")); - if (selected_keys.size() == 0 || row == nullptr) { - zoom_to_selection->setEnabled(false); - } else { - connect(zoom_to_selection, SIGNAL(triggered(bool)), this, SLOT(set_view_to_selection())); - } + QAction* zoom_to_selection = menu.addAction(tr("Zoom to Selection")); + if (selected_keys.size() == 0 || row == nullptr) { + 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(tr("Zoom to Show All")); - if (row == nullptr) { - zoom_to_all->setEnabled(false); - } else { - connect(zoom_to_all, SIGNAL(triggered(bool)), this, SLOT(set_view_to_all())); - } + QAction* zoom_to_all = menu.addAction(tr("Zoom to Show All")); + if (row == nullptr) { + zoom_to_all->setEnabled(false); + } else { + connect(zoom_to_all, SIGNAL(triggered(bool)), this, SLOT(set_view_to_all())); + } - menu.addSeparator(); + menu.addSeparator(); - QAction* reset_action = menu.addAction(tr("Reset View")); - if (row == nullptr) { - reset_action->setEnabled(false); - } else { - connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); - } + QAction* reset_action = menu.addAction(tr("Reset View")); + if (row == nullptr) { + reset_action->setEnabled(false); + } else { + connect(reset_action, SIGNAL(triggered(bool)), this, SLOT(reset_view())); + } - menu.exec(mapToGlobal(pos)); + menu.exec(mapToGlobal(pos)); } void GraphView::reset_view() { x_zoom = 1.0; y_zoom = 1.0; - set_scroll_x(0); - set_scroll_y(0); + set_scroll_x(0); + set_scroll_y(0); emit zoom_changed(x_zoom, y_zoom); - update(); + update(); } void GraphView::set_view_to_selection() { - if (row != nullptr && 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); - } + if (row != nullptr && 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); + } min_time -= row->parent_effect->parent_clip->clip_in(); max_time -= row->parent_effect->parent_clip->clip_in(); - set_view_to_rect(min_time, min_dbl, max_time, max_dbl); - } + set_view_to_rect(min_time, min_dbl, max_time, max_dbl); + } } void GraphView::set_view_to_all() { - if (row != nullptr) { - bool can_set = false; + if (row != nullptr) { + 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) { + 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) { min_time -= row->parent_effect->parent_clip->clip_in(); max_time -= row->parent_effect->parent_clip->clip_in(); - set_view_to_rect(min_time, min_dbl, max_time, max_dbl); - } - } + 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 x_diff = double(x2 - x1); - double y_diff = (y2 - y1); - double x_diff_padded = (x_diff+10)*padding; - double y_diff_padded = (y_diff+10)*padding; + double padding = 1.5; + double x_diff = double(x2 - x1); + double y_diff = (y2 - y1); + double x_diff_padded = (x_diff+10)*padding; + double y_diff_padded = (y_diff+10)*padding; set_zoom(double(width()) / x_diff_padded, double(height()) / y_diff_padded); set_scroll_x(qRound((double(x1) - ((x_diff_padded-x_diff)/2))*x_zoom)); @@ -174,233 +174,233 @@ void GraphView::set_view_to_rect(int x1, double y1, int x2, double y2) { } void GraphView::draw_line_text(QPainter &p, bool vert, int line_no, int line_pos, int next_line_pos) { - // draws last line's text + // draws last line's text QString str = QString::number(line_no*kGraphSize); - 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); - } + 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;i 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; + 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 *) { - QPainter p(this); + QPainter p(this); - if (panel_sequence_viewer->seq != nullptr) { - // draw grid lines + if (panel_sequence_viewer->seq != nullptr) { + // draw grid lines - p.setPen(Qt::gray); + p.setPen(Qt::gray); - draw_lines(p, true); - draw_lines(p, false); + draw_lines(p, true); + draw_lines(p, false); - // draw keyframes - if (row != nullptr) { - QPen line_pen; + // draw keyframes + if (row != nullptr) { + QPen line_pen; line_pen.setWidth(kBezierLineSize); - for (int i=row->fieldCount()-1;i>=0;i--) { - EffectField* field = row->field(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)) { - // sort keyframes by time - QVector sorted_keys = sort_keys_from_field(field); + if (field->type == EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + // sort keyframes by time + QVector sorted_keys = sort_keys_from_field(field); - int last_key_x = 0; - int last_key_y = 0; + int last_key_x = 0; + int last_key_y = 0; - // draw lines - for (int j=0;jkeyframes.at(sorted_keys.at(j)); + // draw lines + for (int j=0;jkeyframes.at(sorted_keys.at(j)); int key_x = get_screen_x(key.time); - int key_y = get_screen_y(key.data.toDouble()); + int key_y = get_screen_y(key.data.toDouble()); - line_pen.setColor(get_curve_color(i, row->fieldCount())); - p.setPen(line_pen); - if (j == 0) { - p.drawLine(0, key_y, key_x, key_y); - } else { - const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1)); + line_pen.setColor(get_curve_color(i, row->fieldCount())); + p.setPen(line_pen); + if (j == 0) { + p.drawLine(0, key_y, key_x, key_y); + } else { + const EffectKeyframe& last_key = field->keyframes.at(sorted_keys.at(j-1)); - double pre_handle = field->get_validated_keyframe_handle(sorted_keys.at(j), false); - double last_post_handle = field->get_validated_keyframe_handle(sorted_keys.at(j-1), true); + double pre_handle = field->get_validated_keyframe_handle(sorted_keys.at(j), false); + double last_post_handle = field->get_validated_keyframe_handle(sorted_keys.at(j-1), true); - if (last_key.type == EFFECT_KEYFRAME_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 == EFFECT_KEYFRAME_BEZIER || key.type == EFFECT_KEYFRAME_BEZIER) { - QPainterPath bezier_path; - bezier_path.moveTo(last_key_x, last_key_y); - if (last_key.type == EFFECT_KEYFRAME_BEZIER && key.type == EFFECT_KEYFRAME_BEZIER) { - // cubic bezier - bezier_path.cubicTo( + if (last_key.type == EFFECT_KEYFRAME_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 == EFFECT_KEYFRAME_BEZIER || key.type == EFFECT_KEYFRAME_BEZIER) { + QPainterPath bezier_path; + bezier_path.moveTo(last_key_x, last_key_y); + if (last_key.type == EFFECT_KEYFRAME_BEZIER && key.type == EFFECT_KEYFRAME_BEZIER) { + // cubic bezier + bezier_path.cubicTo( QPointF(last_key_x+last_post_handle*x_zoom, last_key_y-last_key.post_handle_y*y_zoom), QPointF(key_x+pre_handle*x_zoom, key_y-key.pre_handle_y*y_zoom), - QPointF(key_x, key_y) - ); - } else if (key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier - // last keyframe is the bezier one - bezier_path.quadTo( + QPointF(key_x, key_y) + ); + } else if (key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier + // last keyframe is the bezier one + bezier_path.quadTo( QPointF(last_key_x+last_post_handle*x_zoom, last_key_y-last_key.post_handle_y*y_zoom), - QPointF(key_x, key_y) - ); - } else { - // this keyframe is the bezier one - bezier_path.quadTo( + QPointF(key_x, key_y) + ); + } else { + // this keyframe is the bezier one + bezier_path.quadTo( QPointF(key_x+pre_handle*x_zoom, key_y-key.pre_handle_y*y_zoom), - 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; - } - if (last_key_x < width()) p.drawLine(last_key_x, last_key_y, width(), last_key_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; + } + if (last_key_x < width()) p.drawLine(last_key_x, last_key_y, width(), last_key_y); - // draw keys - for (int j=0;jkeyframes.at(sorted_keys.at(j)); + // draw keys + for (int j=0;jkeyframes.at(sorted_keys.at(j)); int key_x = get_screen_x(key.time); - int key_y = get_screen_y(key.data.toDouble()); + int key_y = get_screen_y(key.data.toDouble()); - if (key.type == EFFECT_KEYFRAME_BEZIER) { - p.setPen(Qt::gray); + if (key.type == EFFECT_KEYFRAME_BEZIER) { + p.setPen(Qt::gray); - // pre handle line + // pre handle line QPointF pre_point(key_x + key.pre_handle_x*x_zoom, key_y - key.pre_handle_y*y_zoom); - p.drawLine(pre_point, QPointF(key_x, key_y)); + p.drawLine(pre_point, QPointF(key_x, key_y)); p.drawEllipse(pre_point, kBezierHandleSize, kBezierHandleSize); - // post handle line + // post handle line QPointF post_point(key_x + key.post_handle_x*x_zoom, key_y - key.post_handle_y*y_zoom); - p.drawLine(post_point, QPointF(key_x, key_y)); + p.drawLine(post_point, QPointF(key_x, key_y)); p.drawEllipse(post_point, kBezierHandleSize, kBezierHandleSize); - } + } - bool selected = false; - for (int k=0;kseq->playhead - visible_in)*x_zoom) - x_scroll); - p.drawLine(playhead_x, 0, playhead_x, height()); + 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); - } - } + 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); + p.setPen(Qt::white); - QRect border = rect(); - border.setWidth(border.width()-1); - border.setHeight(border.height()-1); - p.drawRect(border); + QRect border = rect(); + border.setWidth(border.width()-1); + border.setHeight(border.height()-1); + p.drawRect(border); } void GraphView::mousePressEvent(QMouseEvent *event) { - if (row != nullptr) { - mousedown = true; - start_x = event->pos().x(); - start_y = event->pos().y(); + if (row != nullptr) { + mousedown = true; + start_x = event->pos().x(); + start_y = event->pos().y(); - // selecting - int sel_key = -1; - int sel_key_field = -1; + // selecting + int sel_key = -1; + int sel_key_field = -1; current_handle = kBezierHandleNone; - if (click_add && (event->buttons() & Qt::LeftButton)) { - selected_keys.clear(); - selected_keys_fields.clear(); + if (click_add && (event->buttons() & Qt::LeftButton)) { + selected_keys.clear(); + selected_keys_fields.clear(); - 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); - click_add_proc = true; - } 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); + 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); + click_add_proc = true; + } 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; - break; - } else { - // selecting a handle + 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*x_zoom, key_y - key.pre_handle_y*y_zoom); QPointF post_point(key_x + key.post_handle_x*x_zoom, key_y - key.post_handle_y*y_zoom); if (event->pos().x() > pre_point.x()-kBezierHandleSize @@ -413,207 +413,207 @@ void GraphView::mousePressEvent(QMouseEvent *event) { && event->pos().y() > post_point.y()-kBezierHandleSize && event->pos().y() < post_point.y()+kBezierHandleSize) { current_handle = kBezierHandlePost; - } + } if (current_handle != kBezierHandleNone) { - 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; - old_post_handle_y = key.post_handle_y; - break; - } - } - } - } - if (sel_key > -1) break; - } + 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; + old_post_handle_y = key.post_handle_y; + break; + } + } + } + } + if (sel_key > -1) break; + } - bool already_selected = false; - if (sel_key > -1) { - for (int i=0;i -1) { + for (int i=0;imodifiers() & Qt::ShiftModifier) && current_handle == kBezierHandleNone) { - 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); - } 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(); - } - } + 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); + } 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) { - if (!mousedown || !click_add) unsetCursor(); - 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(); - } else if (click_add_proc) { - 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 (rect_select) { - rect_select_w = event->pos().x() - rect_select_x; - rect_select_h = event->pos().y() - rect_select_y; + if (!mousedown || !click_add) unsetCursor(); + 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(); + } else if (click_add_proc) { + 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 (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); + 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;kfieldCount();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 { - switch (current_handle) { + if (!already_selected) { + QPoint key_screen_point(get_screen_x(f->keyframes.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 { + switch (current_handle) { case kBezierHandleNone: - 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)/x_zoom)); - 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 { + 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())/y_zoom)); - } - } - moved_keys = true; - update_ui(false); - break; + } + } + moved_keys = true; + update_ui(false); + break; case kBezierHandlePre: case kBezierHandlePost: - { - 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 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)/x_zoom; double y_diff = double(start_y - event->pos().y())/y_zoom; if (current_handle == kBezierHandlePre) { - 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 { - 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; - } - } + 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 { + 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(handle_field)->keyframes[handle_index]; - key.pre_handle_x = qMin(0.0, new_pre_handle_x); - key.pre_handle_y = new_pre_handle_y; - key.post_handle_x = qMax(0.0, new_post_handle_x); - key.post_handle_y = new_post_handle_y; + EffectKeyframe& key = row->field(handle_field)->keyframes[handle_index]; + key.pre_handle_x = qMin(0.0, new_pre_handle_x); + key.pre_handle_y = new_pre_handle_y; + key.post_handle_x = qMax(0.0, new_post_handle_x); + key.post_handle_y = new_post_handle_y; - moved_keys = true; - update_ui(false); - } - break; - } - } - } else if (row != nullptr) { - // clicking on the curve - click_add = false; + moved_keys = true; + update_ui(false); + } + break; + } + } + } else if (row != nullptr) { + // clicking on the curve + click_add = false; - bool hovering_key = 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); + 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( + 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( qRound(key_x + key.pre_handle_x*x_zoom - kBezierHandleSize), qRound(key_y + key.pre_handle_y*y_zoom - kBezierHandleSize), kBezierHandleSize+kBezierHandleSize, kBezierHandleSize+kBezierHandleSize - ); - QRect post_rect( + ); + QRect post_rect( qRound(key_x + key.post_handle_x*x_zoom - kBezierHandleSize), qRound(key_y + key.post_handle_y*y_zoom - kBezierHandleSize), kBezierHandleSize+kBezierHandleSize, kBezierHandleSize+kBezierHandleSize - ); + ); - if (test_rect.contains(event->pos()) - || pre_rect.contains(event->pos()) - || post_rect.contains(event->pos())) { - hovering_key = true; - break; - } - } - } + 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 (!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 (!sorted_keys.isEmpty()) { 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-kBezierLineSize && event->pos().y() <= y_comp+kBezierLineSize) { - // dout << "make an EARLY key on field" << i; + // dout << "make an EARLY key on field" << i; click_add = true; click_add_type = f->keyframes.at(sorted_keys.first()).type; } @@ -621,7 +621,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { int y_comp = get_screen_y(f->keyframes.at(sorted_keys.last()).data.toDouble()); if (event->pos().y() >= y_comp-kBezierLineSize && event->pos().y() <= y_comp+kBezierLineSize) { - // dout << "make an LATE key on field" << i; + // dout << "make an LATE key on field" << i; click_add = true; click_add_type = f->keyframes.at(sorted_keys.last()).type; } @@ -645,7 +645,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { // hold if (event->pos().y() >= last_key_y-kBezierLineSize && event->pos().y() <= last_key_y+kBezierLineSize) { - // dout << "make an HOLD key on field" << i << "after key" << j; + // dout << "make an HOLD key on field" << i << "after key" << j; click_add = true; } } else if (last_key.type == EFFECT_KEYFRAME_BEZIER || key.type == EFFECT_KEYFRAME_BEZIER) { @@ -672,7 +672,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { ); } if (bezier_path.intersects(mouse_rect)) { - // dout << "make an BEZIER key on field" << i << "after key" << j; + // dout << "make an BEZIER key on field" << i << "after key" << j; click_add = true; } } else { @@ -681,7 +681,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { 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; + // dout << "make an LINEAR key on field" << i << "after key" << j; click_add = true; } } @@ -689,173 +689,208 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { } } } - } - if (click_add) { - click_add_field = f; - setCursor(Qt::CrossCursor); - break; - } - } - } - } + } + if (click_add) { + click_add_field = f; + setCursor(Qt::CrossCursor); + break; + } + } + } + } } void GraphView::mouseReleaseEvent(QMouseEvent *) { - if (click_add_proc) { + if (click_add_proc) { olive::UndoStack.push(new KeyframeFieldSet(click_add_field, click_add_key)); - } else if (moved_keys && selected_keys.size() > 0) { - ComboAction* ca = new ComboAction(); - switch (current_handle) { + } else if (moved_keys && selected_keys.size() > 0) { + ComboAction* ca = new ComboAction(); + switch (current_handle) { case kBezierHandleNone: - 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)); - } - break; + 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)); + } + break; case kBezierHandlePre: case kBezierHandlePost: - { - 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)); - ca->append(new SetDouble(&key.post_handle_y, old_post_handle_y, key.post_handle_y)); - } - break; - } + { + 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)); + ca->append(new SetDouble(&key.post_handle_y, old_post_handle_y, key.post_handle_y)); + } + break; + } olive::UndoStack.push(ca); - } - moved_keys = false; - mousedown = false; - click_add = false; - click_add_proc = false; - if (rect_select) { - rect_select = false; - selection_update(); - update(); - } + } + moved_keys = false; + mousedown = false; + click_add = false; + click_add_proc = false; + if (rect_select) { + rect_select = false; + selection_update(); + update(); + } } void GraphView::wheelEvent(QWheelEvent *event) { - bool redraw = false; - bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom - 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 - double new_x_zoom = x_zoom; - double new_y_zoom = y_zoom; + bool redraw = false; + bool zooming = false; - int y_delta = event->angleDelta().y(); + // Respect the "Scroll Wheel Zooms" option here; Ctrl toggles. + // Default zoom: zoom uniformly (both axes equally) + // Alt: zoom vertically + // Shift: zoom horizontally + // Alt + Shift: zoom uniformly - // holding CONTROL will zoom horizontal and vertical axes separately - int x_delta = (event->modifiers() & Qt::ControlModifier) ? event->angleDelta().x() : y_delta; + bool shift = (event->modifiers() & Qt::ShiftModifier); + bool ctrl = (event->modifiers() & Qt::ControlModifier); + bool alt = (event->modifiers() & Qt::AltModifier); - if (y_delta != 0) { - double zoom_diff = (kGraphZoomSpeed*y_zoom); - new_y_zoom = (y_delta < 0) ? y_zoom - zoom_diff : y_zoom + zoom_diff; + int delta_h = event->angleDelta().x(); + int delta_v = event->angleDelta().y(); - // center zoom on screen - set_scroll_y(qRound((double(y_scroll)/y_zoom*new_y_zoom) + double(height()-event->pos().y())*new_y_zoom - double(height()-event->pos().y())*y_zoom)); + double new_x_zoom = x_zoom; + double new_y_zoom = y_zoom; - redraw = true; - } + if (ctrl != olive::CurrentConfig.scroll_zooms) { + zooming = true; + } - if (x_delta != 0) { - double zoom_diff = (kGraphZoomSpeed*x_zoom); - new_x_zoom = (x_delta < 0) ? x_zoom - zoom_diff : x_zoom + zoom_diff; + if (zooming) { + // Combine "source" deltas when zooming. Key modifiers determine axis + delta_h = delta_h + delta_v; + delta_v = delta_h; + } - set_scroll_x(qRound((double(x_scroll)/x_zoom*new_x_zoom) + double(event->pos().x())*new_x_zoom - double(event->pos().x())*x_zoom)); + // If Alt is held but not Shift, make it a vertical zoom + if (zooming && alt && !shift) { + delta_h = 0; + } - redraw = true; - } + // If Shift is held but not Alt, make it a horizontal zoom + if (zooming && shift && !alt) { + delta_v = 0; + } - if (redraw) { - set_zoom(new_x_zoom, new_y_zoom); - } - } + if (!zooming) { + // Minus to correct for scroll vs. zoom behavior on horiz axis + set_scroll_x(x_scroll - (delta_h / 10)); + set_scroll_y(y_scroll + (delta_v / 10)); - if (redraw) { - update(); - } + redraw = true; + } + + if (zooming && (delta_v != 0)) { + double zoom_diff = (kGraphZoomSpeed*y_zoom); + new_y_zoom = y_zoom + (zoom_diff * (delta_v / 120.0)); + + // Center zoom vertically + set_scroll_y(qRound((double(y_scroll)/y_zoom*new_y_zoom) + + double(height()-event->pos().y())*new_y_zoom + - double(height()-event->pos().y())*y_zoom)); + + redraw = true; + } + + if (zooming && (delta_h != 0)) { + double zoom_diff = (kGraphZoomSpeed*x_zoom); + + new_x_zoom = x_zoom + (zoom_diff * (delta_h / 120.0)); + + // Center zoom horizontally + set_scroll_x(qRound((double(x_scroll)/x_zoom*new_x_zoom) + + double(event->pos().x())*new_x_zoom + - double(event->pos().x())*x_zoom)); + + redraw = true; + } + + if (zooming) { + set_zoom(new_x_zoom, new_y_zoom); + } + + if (redraw) { + update(); + } } void GraphView::set_row(EffectRow *r) { - 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 != nullptr) { - field_visibility.resize(row->fieldCount()); - for (int i=0;ifieldCount();i++) { - field_visibility[i] = row->field(i)->is_enabled(); - } + 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 != nullptr) { + field_visibility.resize(row->fieldCount()); + for (int i=0;ifieldCount();i++) { + field_visibility[i] = row->field(i)->is_enabled(); + } visible_in = row->parent_effect->parent_clip->timeline_in(); - set_view_to_all(); - } else { - update(); - } - } + set_view_to_all(); + } else { + 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)); - } + 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)); + } olive::UndoStack.push(ca); - update_ui(false); - } + update_ui(false); + } } void GraphView::set_field_visibility(int field, bool b) { - field_visibility[field] = b; - update(); + field_visibility[field] = b; + update(); } void GraphView::delete_selected_keys() { - if (row != nullptr) { - QVector fields; - for (int i=0;ifield(selected_keys_fields.at(i))); - } - delete_keyframes(fields, selected_keys); - } + if (row != nullptr) { + QVector fields; + for (int i=0;ifield(selected_keys_fields.at(i))); + } + delete_keyframes(fields, selected_keys); + } } void GraphView::select_all() { - if (row != nullptr) { - 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(); - } + if (row != nullptr) { + 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); + x_scroll = s; + emit x_scroll_changed(x_scroll); } void GraphView::set_scroll_y(int s) { - y_scroll = s; - emit y_scroll_changed(y_scroll); + y_scroll = s; + emit y_scroll_changed(y_scroll); } void GraphView::set_zoom(double xz, double yz) { @@ -888,24 +923,24 @@ double GraphView::get_value_y(int i) { } void GraphView::selection_update() { - selected_keys_old_vals.clear(); - selected_keys_old_doubles.clear(); + selected_keys_old_vals.clear(); + selected_keys_old_doubles.clear(); - int selected_key_type = -1; + 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()); + 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 (selected_key_type == -1) { + selected_key_type = key.type; + } else if (selected_key_type != key.type) { + selected_key_type = -2; + } + } - update(); + update(); - emit selection_changed(selected_keys.size() > 0, selected_key_type); + emit selection_changed(selected_keys.size() > 0, selected_key_type); } diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 4b14606cb..25762c9fd 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -337,49 +337,65 @@ void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) { } void TimelineWidget::wheelEvent(QWheelEvent *event) { - // ctrl used to toggle zooming instead of scrolling + + // TODO: implement pixel scrolling + + bool shift = (event->modifiers() & Qt::ShiftModifier); bool ctrl = (event->modifiers() & Qt::ControlModifier); + bool alt = (event->modifiers() & Qt::AltModifier); - // - // NOTE/FIXME: CURRENTLY disabling pixel scrolling because it needs more testing - // + // "Scroll Zooms" false + Control up : not zooming + // "Scroll Zooms" false + Control down: zooming + // "Scroll Zooms" true + Control up : zooming + // "Scroll Zooms" true + Control down: not zooming + bool zooming = (olive::CurrentConfig.scroll_zooms != ctrl); - /*if (!event->pixelDelta().isNull()) { - // if we got pixel scrolling data, prefer it over the angleDelta data + // Allow shift for axis swap, but don't swap on zoom... Unless + // we need to override Qt's axis swap via Alt + bool swap_hv = ((shift != olive::CurrentConfig.horizontal_timeline_scroll) & + !zooming) | (alt & !shift & zooming); - QScrollBar* horiz_bar = panel_timeline->horizontalScrollBar; - QScrollBar* vert_bar = scrollBar; + int delta_h = swap_hv ? event->angleDelta().y() : event->angleDelta().x(); + int delta_v = swap_hv ? event->angleDelta().x() : event->angleDelta().y(); - horiz_bar->setValue(horiz_bar->value() + event->pixelDelta().x()); - vert_bar->setValue(vert_bar->value() + event->pixelDelta().y()); + if (zooming) { - } else*/ if (!event->angleDelta().isNull()) { + // Zoom only uses vertical scrolling, to avoid glitches on touchpads. + // Don't do anything if not scrolling vertically. - // alt is used to swap horizontal and vertical scrolling - bool alt = (event->modifiers() & Qt::AltModifier); + if (delta_v != 0) { - int scroll_amount = alt ? (event->angleDelta().x()) : (event->angleDelta().y()); + // delta_v == 120 for one click of a mousewheel. Less or more for a + // touchpad gesture. Calculate speed to compensate. + // 120 = ratio of 4/3 (1.33), -120 = ratio of 3/4 (.75) - bool in = (scroll_amount > 0); - if (olive::CurrentConfig.scroll_zooms != ctrl) { + double zoom_ratio = 1.0 + (abs(delta_v) * 0.33 / 120); - // if config.scroll_zooms is enabled or ctrl is held, zoom instead of scrolling - if (in) { - panel_timeline->multiply_zoom(1.5); - } else { - panel_timeline->multiply_zoom(0.75); + if (delta_v < 0) { + zoom_ratio = 1.0 / zoom_ratio; } - } else { - // pass the scrolling to the Timeline's main scrollbar for horizontal scrolling, or this widget's - // scrollbar for vertical scrolling - - QScrollBar* bar = alt ? scrollBar : panel_timeline->horizontalScrollBar; - - int step = bar->singleStep(); - if (in) step = -step; - bar->setValue(bar->value() + step); + panel_timeline->multiply_zoom(zoom_ratio); } + + } else { + + // Use the Timeline's main scrollbar for horizontal scrolling, and this + // widget's scrollbar for vertical scrolling. + + QScrollBar* bar_v = scrollBar; + QScrollBar* bar_h = panel_timeline->horizontalScrollBar; + + // Match the wheel events to the size of a step as per + // https://doc.qt.io/qt-5/qwheelevent.html#angleDelta + + int step_h = bar_h->singleStep() * delta_h / -120; + int step_v = bar_v->singleStep() * delta_v / -120; + + // Apply to appropriate scrollbars + + bar_h->setValue(bar_h->value() + step_h); + bar_v->setValue(bar_v->value() + step_v); } }