timeruler: add ability to resize markers and workarea

This commit is contained in:
itsmattkc
2022-05-09 17:53:21 -07:00
parent 7d370320d8
commit 0f99d8a662
8 changed files with 218 additions and 23 deletions
+147 -4
View File
@@ -26,12 +26,14 @@
#include <QtMath>
#include "common/qtutils.h"
#include "common/range.h"
#include "core.h"
#include "dialog/markerproperties/markerpropertiesdialog.h"
#include "node/project/serializer/serializer.h"
#include "widget/colorlabelmenu/colorlabelmenu.h"
#include "widget/menu/menushared.h"
#include "widget/timebased/timebasedwidget.h"
#include "widget/timelinewidget/undo/timelineundoworkarea.h"
namespace olive {
@@ -42,7 +44,10 @@ SeekableWidget::SeekableWidget(QWidget* parent) :
timeline_points_(nullptr),
dragging_(false),
ignore_next_focus_out_(false),
selection_manager_(this)
selection_manager_(this),
resize_item_(nullptr),
marker_top_(0),
marker_bottom_(0)
{
QFontMetrics fm = fontMetrics();
@@ -53,8 +58,9 @@ SeekableWidget::SeekableWidget(QWidget* parent) :
setContextMenuPolicy(Qt::CustomContextMenu);
setFocusPolicy(Qt::ClickFocus);
setMouseTracking(true);
selection_manager_.SetSnapMask(TimeBasedWidget::kSnapAll & ~TimeBasedWidget::kSnapToMarkers);
selection_manager_.SetSnapMask(TimeBasedWidget::kSnapAll);
}
void SeekableWidget::ConnectTimelinePoints(TimelinePoints *points)
@@ -159,7 +165,17 @@ bool SeekableWidget::PasteMarkers(bool insert, rational insert_time)
void SeekableWidget::mousePressEvent(QMouseEvent *event)
{
if (TimelineMarker *initial = selection_manager_.MousePress(event)) {
if (resize_item_) {
// Handle selection, even though we won't be using it for dragging
if (!(event->modifiers() & Qt::ShiftModifier)) {
selection_manager_.ClearSelection();
}
if (TimelineMarker *m = dynamic_cast<TimelineMarker*>(resize_item_)) {
selection_manager_.Select(m);
}
dragging_ = true;
resize_start_ = mapToScene(event->pos());
} else if (TimelineMarker *initial = selection_manager_.MousePress(event)) {
selection_manager_.DragStart(initial, event);
} else if (!selection_manager_.GetObjectAtPoint(event->pos()) && event->button() == Qt::LeftButton) {
SeekToScenePoint(mapToScene(event->pos()).x());
@@ -174,7 +190,15 @@ void SeekableWidget::mouseMoveEvent(QMouseEvent *event)
if (selection_manager_.IsDragging()) {
selection_manager_.DragMove(event);
} else if (dragging_) {
SeekToScenePoint(mapToScene(event->pos()).x());
QPointF scene = mapToScene(event->pos());
if (resize_item_) {
DragResizeHandle(scene);
} else {
SeekToScenePoint(scene.x());
}
} else if (timeline_points_) {
// Look for resize points
setCursor(FindResizeHandle(event) ? Qt::SizeHorCursor : Qt::ArrowCursor);
}
}
@@ -190,6 +214,11 @@ void SeekableWidget::mouseReleaseEvent(QMouseEvent *event)
GetSnapService()->HideSnaps();
}
if (resize_item_) {
CommitResizeHandle();
resize_item_ = nullptr;
}
dragging_ = false;
}
@@ -326,9 +355,12 @@ void SeekableWidget::DrawTimelinePoints(QPainter* p, int marker_bottom)
}
QRect marker_rect = marker->Draw(p, QPoint(marker_left, marker_bottom), GetScale(), selection_manager_.IsSelected(marker));
marker_top_ = marker_rect.top();
selection_manager_.DeclareDrawnObject(marker, marker_rect);
}
}
marker_bottom_ = marker_bottom;
}
void SeekableWidget::DrawPlayhead(QPainter *p, int x, int y)
@@ -384,4 +416,115 @@ bool SeekableWidget::ShowContextMenu(const QPoint &p)
}
}
bool SeekableWidget::FindResizeHandle(QMouseEvent *event)
{
resize_item_ = nullptr;
resize_mode_ = kResizeNone;
QPointF scene = mapToScene(event->pos());
const int border = 10;
rational min = SceneToTimeNoGrid(scene.x() - border);
rational max = SceneToTimeNoGrid(scene.x() + border);
// Test for workarea
if (timeline_points_->workarea()->in() >= min && timeline_points_->workarea()->in() < max) {
resize_mode_ = kResizeIn;
} else if (timeline_points_->workarea()->out() >= min && timeline_points_->workarea()->out() < max) {
resize_mode_ = kResizeOut;
}
if (resize_mode_ != kResizeNone) {
resize_item_ = timeline_points_->workarea();
resize_item_range_ = timeline_points_->workarea()->range();
resize_snap_mask_ = TimeBasedWidget::kSnapAll & ~TimeBasedWidget::kSnapToWorkarea;
} else if (event->pos().y() >= marker_top_ && event->pos().y() < marker_bottom_) {
// Check for markers
for (auto it=timeline_points_->markers()->cbegin(); it!=timeline_points_->markers()->cend(); it++) {
TimelineMarker *m = *it;
if (m->time_range().in() != m->time_range().out()) {
if (m->time_range().in() >= min && m->time_range().in() < max) {
resize_mode_ = kResizeIn;
} else if (m->time_range().out() >= min && m->time_range().out() < max) {
resize_mode_ = kResizeOut;
}
if (resize_mode_ != kResizeNone) {
resize_item_ = m;
resize_item_range_ = m->time_range();
resize_snap_mask_ = TimeBasedWidget::kSnapAll;
break;
}
}
}
}
return resize_item_;
}
void SeekableWidget::DragResizeHandle(const QPointF &scene)
{
qreal diff = scene.x() - resize_start_.x();
rational proposed_time;
if (resize_mode_ == kResizeIn) {
proposed_time = qMax(rational(0), qMin(resize_item_range_.out(), resize_item_range_.in() + SceneToTimeNoGrid(diff)));
} else {
proposed_time = qMax(resize_item_range_.in(), resize_item_range_.out() + SceneToTimeNoGrid(diff));
}
rational presnap_time = proposed_time;
if (Core::instance()->snapping() && GetSnapService()) {
rational movement;
GetSnapService()->SnapPoint({proposed_time}, &movement, resize_snap_mask_);
proposed_time += movement;
}
TimeRange new_range = resize_item_range_;
if (resize_mode_ == kResizeIn) {
// Markers should not have the same time as anything else
// NOTE: This code is largely duplicated from TimeBasedViewSelectionManager::DragMove. Not ideal,
// but I'm not sure if there's a good way to re-use that code
if (TimelineMarker *marker = dynamic_cast<TimelineMarker*>(resize_item_)) {
if (marker->has_sibling_at_time(proposed_time)) {
proposed_time = presnap_time;
if (GetSnapService()) {
GetSnapService()->HideSnaps();
}
}
while (marker->has_sibling_at_time(proposed_time)) {
proposed_time += rational(1, 1000);
}
}
new_range.set_in(proposed_time);
} else {
new_range.set_out(proposed_time);
}
if (TimelineMarker *marker = dynamic_cast<TimelineMarker*>(resize_item_)) {
marker->set_time(new_range);
} else if (TimelineWorkArea *workarea = dynamic_cast<TimelineWorkArea*>(resize_item_)) {
workarea->set_range(new_range);
}
}
void SeekableWidget::CommitResizeHandle()
{
MultiUndoCommand *command = new MultiUndoCommand();
if (TimelineMarker *marker = dynamic_cast<TimelineMarker*>(resize_item_)) {
command->add_child(new MarkerChangeTimeCommand(marker, marker->time_range(), resize_item_range_));
} else if (TimelineWorkArea *workarea = dynamic_cast<TimelineWorkArea*>(resize_item_)) {
command->add_child(new WorkareaSetRangeCommand(workarea, workarea->range(), resize_item_range_));
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
}