timeruler: add ability to resize markers and workarea
This commit is contained in:
@@ -204,9 +204,7 @@ void TimelineMarkerList::HandleMarkerTimeChange()
|
||||
|
||||
auto it = std::find(markers_.begin(), markers_.end(), m);
|
||||
|
||||
if ((it+1 != markers_.end() && (*(it+1))->time() < m->time())
|
||||
|| (it != markers_.begin() && (*(it-1))->time() > m->time())) {
|
||||
// Re-sort into list
|
||||
if (it != markers_.end()) {
|
||||
markers_.erase(it);
|
||||
InsertIntoList(m);
|
||||
}
|
||||
@@ -304,8 +302,9 @@ void MarkerChangeNameCommand::undo()
|
||||
marker_->set_name(old_name_);
|
||||
}
|
||||
|
||||
MarkerChangeTimeCommand::MarkerChangeTimeCommand(TimelineMarker* marker, TimeRange time) :
|
||||
MarkerChangeTimeCommand::MarkerChangeTimeCommand(TimelineMarker* marker, const TimeRange &time, const TimeRange &old_time) :
|
||||
marker_(marker),
|
||||
old_time_(old_time),
|
||||
new_time_(time)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -227,7 +227,10 @@ private:
|
||||
|
||||
class MarkerChangeTimeCommand : public UndoCommand {
|
||||
public:
|
||||
MarkerChangeTimeCommand(TimelineMarker* marker, TimeRange time);
|
||||
MarkerChangeTimeCommand(TimelineMarker* marker, const TimeRange &time, const TimeRange &old_time);
|
||||
MarkerChangeTimeCommand(TimelineMarker* marker, const TimeRange &time) :
|
||||
MarkerChangeTimeCommand(marker, time, marker->time_range())
|
||||
{}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
|
||||
@@ -195,8 +195,26 @@ public:
|
||||
rational time_diff = view_->SceneToTimeNoGrid(view_->mapToScene(event->pos()).x() - drag_mouse_start_.x());
|
||||
|
||||
// Snap points
|
||||
rational presnap_time_diff = time_diff;
|
||||
SnapPoints(&time_diff);
|
||||
|
||||
// Validate snapping
|
||||
if (Core::instance()->snapping() && view_->GetSnapService()) {
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
rational proposed_time = dragging_.at(i) + time_diff;
|
||||
T *sel = selected_.at(i);
|
||||
|
||||
if (sel->has_sibling_at_time(proposed_time)) {
|
||||
// Unsnap
|
||||
time_diff = presnap_time_diff;
|
||||
if (view_->GetSnapService()) {
|
||||
view_->GetSnapService()->HideSnaps();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate movement
|
||||
for (size_t i=0; i<selected_.size(); i++) {
|
||||
rational proposed_time = dragging_.at(i) + time_diff;
|
||||
|
||||
@@ -486,7 +486,7 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time)
|
||||
}
|
||||
|
||||
// Set workarea
|
||||
command->add_child(new WorkareaSetRangeCommand(viewer_node_->project(), points, TimeRange(in_point, out_point)));
|
||||
command->add_child(new WorkareaSetRangeCommand(points->workarea(), TimeRange(in_point, out_point)));
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
@@ -511,7 +511,7 @@ void TimeBasedWidget::ResetPoint(Timeline::MovementMode m)
|
||||
r.set_out(TimelineWorkArea::kResetOut);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(viewer_node_->project(), points, r));
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(points->workarea(), r));
|
||||
}
|
||||
|
||||
void TimeBasedWidget::PageScrollInternal(QScrollBar *bar, int maximum, int screen_position, bool whole_page_scroll)
|
||||
@@ -772,6 +772,11 @@ bool TimeBasedWidget::SnapPoint(const std::vector<rational> &start_times, ration
|
||||
for (auto it=ruler()->GetTimelinePoints()->markers()->cbegin(); it!=ruler()->GetTimelinePoints()->markers()->cend(); it++) {
|
||||
TimelineMarker* m = *it;
|
||||
|
||||
// Ignore selected markers
|
||||
if (std::find(ruler()->GetSelectedMarkers().cbegin(), ruler()->GetSelectedMarkers().cend(), m) != ruler()->GetSelectedMarkers().cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qreal marker_pos = TimeToScene(m->time_range().in());
|
||||
AttemptSnap(potential_snaps, screen_pt, marker_pos, start_times, m->time_range().in());
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ protected:
|
||||
virtual const QVector<Block*> *GetSnapBlocks() const { return nullptr; }
|
||||
virtual const QVector<KeyframeViewInputConnection*> *GetSnapKeyframes() const { return nullptr; }
|
||||
virtual const std::vector<NodeKeyframe*> *GetSnapIgnoreKeyframes() const { return nullptr; }
|
||||
virtual const std::vector<TimelineMarker*> *GetSnapIgnoreMarkers() const { return nullptr; }
|
||||
|
||||
protected slots:
|
||||
/**
|
||||
@@ -164,8 +165,6 @@ signals:
|
||||
void ConnectedNodeChanged(ViewerOutput* old, ViewerOutput* now);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set either in or out point to the current playhead
|
||||
*
|
||||
|
||||
@@ -65,34 +65,36 @@ private:
|
||||
|
||||
class WorkareaSetRangeCommand : public UndoCommand {
|
||||
public:
|
||||
WorkareaSetRangeCommand(Project *project, TimelinePoints* points, const TimeRange& range) :
|
||||
project_(project),
|
||||
points_(points),
|
||||
old_range_(points_->workarea()->range()),
|
||||
WorkareaSetRangeCommand(TimelineWorkArea *workarea, const TimeRange& range, const TimeRange &old_range) :
|
||||
workarea_(workarea),
|
||||
old_range_(old_range),
|
||||
new_range_(range)
|
||||
{
|
||||
}
|
||||
|
||||
WorkareaSetRangeCommand(TimelineWorkArea *workarea, const TimeRange& range) :
|
||||
WorkareaSetRangeCommand(workarea, range, workarea->range())
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return project_;
|
||||
return Project::GetProjectFromObject(workarea_);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
points_->workarea()->set_range(new_range_);
|
||||
workarea_->set_range(new_range_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
points_->workarea()->set_range(old_range_);
|
||||
workarea_->set_range(old_range_);
|
||||
}
|
||||
|
||||
private:
|
||||
Project* project_;
|
||||
|
||||
TimelinePoints* points_;
|
||||
TimelineWorkArea *workarea_;
|
||||
|
||||
TimeRange old_range_;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ public:
|
||||
|
||||
void SeekToScenePoint(qreal scene);
|
||||
|
||||
const std::vector<TimelineMarker*> &GetSelectedMarkers() const
|
||||
{
|
||||
return selection_manager_.GetSelectedObjects();
|
||||
}
|
||||
|
||||
virtual void SelectionManagerSelectEvent(void *obj) override;
|
||||
virtual void SelectionManagerDeselectEvent(void *obj) override;
|
||||
|
||||
@@ -95,6 +100,18 @@ protected slots:
|
||||
virtual bool ShowContextMenu(const QPoint &p);
|
||||
|
||||
private:
|
||||
enum ResizeMode {
|
||||
kResizeNone,
|
||||
kResizeIn,
|
||||
kResizeOut
|
||||
};
|
||||
|
||||
bool FindResizeHandle(QMouseEvent *event);
|
||||
|
||||
void DragResizeHandle(const QPointF &scene_pos);
|
||||
|
||||
void CommitResizeHandle();
|
||||
|
||||
TimelinePoints* timeline_points_;
|
||||
|
||||
int text_height_;
|
||||
@@ -107,6 +124,15 @@ private:
|
||||
|
||||
TimeBasedViewSelectionManager<TimelineMarker> selection_manager_;
|
||||
|
||||
QObject *resize_item_;
|
||||
ResizeMode resize_mode_;
|
||||
TimeRange resize_item_range_;
|
||||
QPointF resize_start_;
|
||||
uint32_t resize_snap_mask_;
|
||||
|
||||
int marker_top_;
|
||||
int marker_bottom_;
|
||||
|
||||
private slots:
|
||||
void SetMarkerColor(int c);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user