Merge branch 'master' into cache-update
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "audiomonitor.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
@@ -35,8 +36,7 @@ const int kMaximumSmoothness = 8;
|
||||
|
||||
QVector<AudioMonitor*> AudioMonitor::instances_;
|
||||
|
||||
AudioMonitor::AudioMonitor(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
AudioMonitor::AudioMonitor() :
|
||||
waveform_(nullptr),
|
||||
cached_channels_(0)
|
||||
{
|
||||
@@ -126,7 +126,10 @@ void AudioMonitor::SetUpdateLoop(bool e)
|
||||
void AudioMonitor::paintGL()
|
||||
{
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), palette().window().color());
|
||||
QPalette palette = qApp->palette();
|
||||
QRect geometry(0, 0, width(), height());
|
||||
|
||||
p.fillRect(geometry, palette.window().color());
|
||||
|
||||
if (!params_.channel_count()) {
|
||||
return;
|
||||
@@ -138,12 +141,12 @@ void AudioMonitor::paintGL()
|
||||
int font_height = fm.height();
|
||||
|
||||
// Create rect where decibel markings will go on the side
|
||||
QRect db_labels_rect = rect();
|
||||
QRect db_labels_rect = geometry;
|
||||
db_labels_rect.setWidth(QtUtils::QFontMetricsWidth(p.fontMetrics(), "-00"));
|
||||
db_labels_rect.adjust(0, font_height, 0, 0);
|
||||
|
||||
// Determine rect where the main meter will go
|
||||
QRect full_meter_rect = rect();
|
||||
QRect full_meter_rect = geometry;
|
||||
full_meter_rect.adjust(db_labels_rect.width(), font_height, 0, 0);
|
||||
|
||||
// Width of each channel in the meter
|
||||
@@ -164,7 +167,7 @@ void AudioMonitor::paintGL()
|
||||
// Draw decibel markings
|
||||
QRect last_db_marking_rect;
|
||||
|
||||
cached_painter.setPen(palette().text().color());
|
||||
cached_painter.setPen(palette.text().color());
|
||||
|
||||
for (int i=0;i>=kDecibelMinimum;i-=kDecibelStep) {
|
||||
QString db_label;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define AUDIOMONITORWIDGET_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QTimer>
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
@@ -32,11 +32,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AudioMonitor : public QOpenGLWidget
|
||||
class AudioMonitor : public QOpenGLWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioMonitor(QWidget* parent = nullptr);
|
||||
AudioMonitor();
|
||||
|
||||
virtual ~AudioMonitor() override;
|
||||
|
||||
|
||||
@@ -23,18 +23,19 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "panel/panelmanager.h"
|
||||
#include "render/opengl/openglrenderer.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super QWidget
|
||||
|
||||
ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
color_manager_(nullptr),
|
||||
color_service_(nullptr)
|
||||
{
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(0);
|
||||
layout->setMargin(0);
|
||||
@@ -55,17 +56,18 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) :
|
||||
&ManagedDisplayWidgetOpenGL::frameSwapped,
|
||||
this, &ManagedDisplayWidget::frameSwapped, Qt::DirectConnection);
|
||||
|
||||
connect(static_cast<ManagedDisplayWidgetOpenGL*>(inner_widget_),
|
||||
&ManagedDisplayWidgetOpenGL::OnMouseMove,
|
||||
this, &ManagedDisplayWidget::InnerWidgetMouseMove);
|
||||
inner_widget_->installEventFilter(this);
|
||||
|
||||
// Create OpenGL renderer
|
||||
attached_renderer_ = new OpenGLRenderer(this);
|
||||
|
||||
// Create widget wrapper for OpenGL window
|
||||
wrapper_ = QWidget::createWindowContainer(static_cast<ManagedDisplayWidgetOpenGL*>(inner_widget_));
|
||||
layout->addWidget(wrapper_);
|
||||
} else {
|
||||
inner_widget_ = nullptr;
|
||||
wrapper_ = nullptr;
|
||||
}
|
||||
|
||||
layout->addWidget(inner_widget_);
|
||||
}
|
||||
|
||||
ManagedDisplayWidget::~ManagedDisplayWidget()
|
||||
@@ -253,6 +255,22 @@ void ManagedDisplayWidget::doneCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
QPaintDevice *ManagedDisplayWidget::paint_device() const
|
||||
{
|
||||
if (RenderManager::instance()->backend() == RenderManager::kOpenGL) {
|
||||
return static_cast<ManagedDisplayWidgetOpenGL*>(inner_widget_);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedDisplayWidget::SetInnerMouseTracking(bool e)
|
||||
{
|
||||
if (wrapper_) {
|
||||
wrapper_->setMouseTracking(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedDisplayWidget::update()
|
||||
{
|
||||
if (RenderManager::instance()->backend() == RenderManager::kOpenGL) {
|
||||
@@ -260,6 +278,42 @@ void ManagedDisplayWidget::update()
|
||||
}
|
||||
}
|
||||
|
||||
bool ManagedDisplayWidget::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (o != inner_widget_) {
|
||||
return super::eventFilter(o, e);
|
||||
}
|
||||
|
||||
switch (e->type()) {
|
||||
case QEvent::FocusIn:
|
||||
// HACK: QWindow focus isn't accounted for in QApplication::focusChanged, so we handle it
|
||||
// manually here.
|
||||
PanelManager::instance()->FocusChanged(nullptr, this);
|
||||
break;
|
||||
case QEvent::ContextMenu:
|
||||
{
|
||||
QContextMenuEvent *ctx = static_cast<QContextMenuEvent*>(e);
|
||||
emit customContextMenuRequested(ctx->pos());
|
||||
return true;
|
||||
}
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
// HACK: QWindows don't seem to receive ContextMenu events on right click (only when pressing
|
||||
// the menu button on the keyboard) so we handle it manually here
|
||||
QMouseEvent *ev = static_cast<QMouseEvent*>(e);
|
||||
if (ev->button() == Qt::RightButton) {
|
||||
emit customContextMenuRequested(ev->pos());
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super::eventFilter(o, e);
|
||||
}
|
||||
|
||||
Menu* ManagedDisplayWidget::GetDisplayMenu(QMenu* parent, bool auto_connect)
|
||||
{
|
||||
QStringList displays = color_manager()->ListAvailableDisplays();
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLWindow>
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "render/renderer.h"
|
||||
@@ -31,24 +31,18 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ManagedDisplayWidgetOpenGL : public QOpenGLWidget
|
||||
class ManagedDisplayWidgetOpenGL : public QOpenGLWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ManagedDisplayWidgetOpenGL(QWidget* parent = nullptr) :
|
||||
QOpenGLWidget(parent)
|
||||
{
|
||||
}
|
||||
ManagedDisplayWidgetOpenGL() = default;
|
||||
|
||||
signals:
|
||||
// Render signals
|
||||
void OnInit();
|
||||
|
||||
void OnPaint();
|
||||
|
||||
void OnDestroy();
|
||||
|
||||
void OnMouseMove(QMouseEvent* e);
|
||||
|
||||
protected:
|
||||
virtual void initializeGL() override
|
||||
{
|
||||
@@ -63,13 +57,6 @@ protected:
|
||||
emit OnPaint();
|
||||
}
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* e) override
|
||||
{
|
||||
emit OnMouseMove(e);
|
||||
|
||||
QOpenGLWidget::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DestroyListener()
|
||||
{
|
||||
@@ -135,6 +122,8 @@ public:
|
||||
*/
|
||||
void update();
|
||||
|
||||
virtual bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Replaces the color transform with a new one
|
||||
@@ -159,8 +148,6 @@ signals:
|
||||
|
||||
void frameSwapped();
|
||||
|
||||
void InnerWidgetMouseMove(QMouseEvent* event);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Provides access to the color processor (nullptr if none is set)
|
||||
@@ -188,11 +175,26 @@ protected:
|
||||
|
||||
void doneCurrent();
|
||||
|
||||
QWidget* inner_widget() const
|
||||
QWindow* inner_widget() const
|
||||
{
|
||||
return inner_widget_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get inner widget as paint device for QPainter
|
||||
*
|
||||
* NOTE: This will be incompatible with QVulkanWindow so functions using it
|
||||
* will need to be replaced soon.
|
||||
*/
|
||||
QPaintDevice *paint_device() const;
|
||||
|
||||
void SetInnerMouseTracking(bool e);
|
||||
|
||||
QRect GetInnerRect() const
|
||||
{
|
||||
return wrapper_ ? wrapper_->rect() : QRect();
|
||||
}
|
||||
|
||||
protected slots:
|
||||
/**
|
||||
* @brief Called whenever the internal rendering context has been created
|
||||
@@ -223,7 +225,8 @@ private:
|
||||
/**
|
||||
* @brief Main drawing surface abstraction
|
||||
*/
|
||||
QWidget* inner_widget_;
|
||||
QWindow* inner_widget_;
|
||||
QWidget *wrapper_;
|
||||
|
||||
/**
|
||||
* @brief Renderer abstraction
|
||||
|
||||
@@ -291,7 +291,7 @@ void MenuShared::NestTriggered()
|
||||
|
||||
void MenuShared::DefaultTransitionTriggered()
|
||||
{
|
||||
qDebug() << "FIXME: Stub";
|
||||
PanelManager::instance()->MostRecentlyFocused<TimelinePanel>()->AddDefaultTransitionsToSelected();
|
||||
}
|
||||
|
||||
void MenuShared::TimecodeDisplayTriggered()
|
||||
|
||||
@@ -84,6 +84,7 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) :
|
||||
NodeParamViewItemTitleBar *title_bar = static_cast<NodeParamViewItemTitleBar*>(c->titleBarWidget());
|
||||
|
||||
if (i == Track::kVideo || i == Track::kAudio) {
|
||||
c->SetEffectType(static_cast<Track::Type>(i));
|
||||
title_bar->SetAddEffectButtonVisible(true);
|
||||
title_bar->SetText(tr("%1 Nodes").arg(Footage::GetStreamTypeName(static_cast<Track::Type>(i))));
|
||||
} else {
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace olive {
|
||||
#define super NodeParamViewItemBase
|
||||
|
||||
NodeParamViewContext::NodeParamViewContext(QWidget *parent) :
|
||||
super(parent)
|
||||
super(parent),
|
||||
type_(Track::kNone)
|
||||
{
|
||||
QWidget *body = new QWidget();
|
||||
QHBoxLayout *body_layout = new QHBoxLayout(body);
|
||||
@@ -126,13 +127,30 @@ void NodeParamViewContext::SetTime(const rational &time)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewContext::SetEffectType(Track::Type type)
|
||||
{
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
void NodeParamViewContext::Retranslate()
|
||||
{
|
||||
}
|
||||
|
||||
void NodeParamViewContext::AddEffectButtonClicked()
|
||||
{
|
||||
Menu *m = NodeFactory::CreateMenu(this, false, Node::kCategoryUnknown, Node::kVideoEffect);
|
||||
Node::Flag flag = Node::kNone;
|
||||
|
||||
if (type_ == Track::kVideo) {
|
||||
flag = Node::kVideoEffect;
|
||||
} else {
|
||||
flag = Node::kAudioEffect;
|
||||
}
|
||||
|
||||
if (flag == Node::kNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
Menu *m = NodeFactory::CreateMenu(this, false, Node::kCategoryUnknown, flag);
|
||||
connect(m, &Menu::triggered, this, &NodeParamViewContext::AddEffectMenuItemTriggered);
|
||||
m->exec(QCursor::pos());
|
||||
delete m;
|
||||
|
||||
@@ -64,6 +64,8 @@ public:
|
||||
|
||||
void SetTime(const rational &time);
|
||||
|
||||
void SetEffectType(Track::Type type);
|
||||
|
||||
signals:
|
||||
void AboutToDeleteItem(NodeParamViewItem *item);
|
||||
|
||||
@@ -88,6 +90,8 @@ private:
|
||||
|
||||
QVector<NodeParamViewItem*> items_;
|
||||
|
||||
Track::Type type_;
|
||||
|
||||
private slots:
|
||||
void AddEffectButtonClicked();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QUrl>
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "task/taskmanager.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
#include "window/mainwindow/mainwindowundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
@@ -392,6 +394,9 @@ void ProjectExplorer::ShowContextMenu()
|
||||
QAction* reveal_action = menu.addAction(reveal_text);
|
||||
connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage);
|
||||
|
||||
QAction *replace_action = menu.addAction(tr("Replace Footage"));
|
||||
connect(replace_action, &QAction::triggered, this, &ProjectExplorer::ReplaceSelectedFootage);
|
||||
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
@@ -497,6 +502,17 @@ void ProjectExplorer::RevealSelectedFootage()
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProjectExplorer::ReplaceSelectedFootage()
|
||||
{
|
||||
Footage* footage = static_cast<Footage*>(context_menu_items_.first());
|
||||
|
||||
QString file = QFileDialog::getOpenFileName(this, tr("Replace Footage"));
|
||||
if (!file.isEmpty()) {
|
||||
auto c = new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(footage, Footage::kFilenameInput)), file);
|
||||
Core::instance()->undo_stack()->push(c);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectExplorer::OpenContextMenuItemInNewTab()
|
||||
{
|
||||
Core::instance()->main_window()->FolderOpen(project(), static_cast<Folder*>(context_menu_items_.first()), false);
|
||||
|
||||
@@ -185,6 +185,8 @@ private slots:
|
||||
|
||||
void RevealSelectedFootage();
|
||||
|
||||
void ReplaceSelectedFootage();
|
||||
|
||||
void OpenContextMenuItemInNewTab();
|
||||
|
||||
void OpenContextMenuItemInNewWindow();
|
||||
|
||||
@@ -31,36 +31,51 @@ namespace olive {
|
||||
|
||||
ResizableTimelineScrollBar::ResizableTimelineScrollBar(QWidget* parent) :
|
||||
ResizableScrollBar(parent),
|
||||
points_(nullptr),
|
||||
markers_(nullptr),
|
||||
workarea_(nullptr),
|
||||
scale_(1.0)
|
||||
{
|
||||
}
|
||||
|
||||
ResizableTimelineScrollBar::ResizableTimelineScrollBar(Qt::Orientation orientation, QWidget* parent) :
|
||||
ResizableScrollBar(orientation, parent),
|
||||
points_(nullptr),
|
||||
markers_(nullptr),
|
||||
workarea_(nullptr),
|
||||
scale_(1.0)
|
||||
{
|
||||
}
|
||||
|
||||
void ResizableTimelineScrollBar::ConnectTimelinePoints(TimelinePoints *points)
|
||||
void ResizableTimelineScrollBar::ConnectMarkers(TimelineMarkerList *markers)
|
||||
{
|
||||
if (points_) {
|
||||
disconnect(points_->workarea(), &TimelineWorkArea::RangeChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(points_->workarea(), &TimelineWorkArea::EnabledChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(points_->markers(), &TimelineMarkerList::MarkerAdded, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(points_->markers(), &TimelineMarkerList::MarkerRemoved, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(points_->markers(), &TimelineMarkerList::MarkerModified, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
if (markers_) {
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerAdded, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerRemoved, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerModified, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
}
|
||||
|
||||
points_ = points;
|
||||
markers_ = markers;
|
||||
|
||||
if (points_) {
|
||||
connect(points_->workarea(), &TimelineWorkArea::RangeChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(points_->workarea(), &TimelineWorkArea::EnabledChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(points_->markers(), &TimelineMarkerList::MarkerAdded, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(points_->markers(), &TimelineMarkerList::MarkerRemoved, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(points_->markers(), &TimelineMarkerList::MarkerModified, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
if (markers_) {
|
||||
connect(markers_, &TimelineMarkerList::MarkerAdded, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(markers_, &TimelineMarkerList::MarkerRemoved, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(markers_, &TimelineMarkerList::MarkerModified, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ResizableTimelineScrollBar::ConnectWorkArea(TimelineWorkArea *workarea)
|
||||
{
|
||||
if (workarea_) {
|
||||
disconnect(workarea_, &TimelineWorkArea::RangeChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
disconnect(workarea_, &TimelineWorkArea::EnabledChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
}
|
||||
|
||||
workarea_ = workarea;
|
||||
|
||||
if (workarea_) {
|
||||
connect(workarea_, &TimelineWorkArea::RangeChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
connect(workarea_, &TimelineWorkArea::EnabledChanged, this, static_cast<void (ResizableTimelineScrollBar::*)()>(&ResizableTimelineScrollBar::update));
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -77,9 +92,8 @@ void ResizableTimelineScrollBar::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
ResizableScrollBar::paintEvent(event);
|
||||
|
||||
if (points_
|
||||
&& !timebase().isNull()
|
||||
&& (points_->workarea()->enabled() || !points_->markers()->empty())) {
|
||||
if (!timebase().isNull() && ((workarea_ && workarea_->enabled()) || (markers_ && !markers_->empty()))) {
|
||||
// Draw workarea
|
||||
QStyleOptionSlider opt;
|
||||
initStyleOption(&opt);
|
||||
|
||||
@@ -87,20 +101,20 @@ void ResizableTimelineScrollBar::paintEvent(QPaintEvent *event)
|
||||
QStyle::SC_ScrollBarGroove, this);
|
||||
|
||||
double ratio = scale_ * double(gr.width()) / double(this->maximum() + gr.width());
|
||||
|
||||
QPainter p(this);
|
||||
|
||||
if (points_->workarea()->enabled()) {
|
||||
if (workarea_ && workarea_->enabled()) {
|
||||
|
||||
QColor workarea_color(this->palette().highlight().color());
|
||||
workarea_color.setAlpha(128);
|
||||
|
||||
qint64 in = qMax(qint64(0), qRound64(ratio * TimeToScene(points_->workarea()->in())));
|
||||
qint64 in = qMax(qint64(0), qRound64(ratio * TimeToScene(workarea_->in())));
|
||||
|
||||
qint64 out;
|
||||
if (points_->workarea()->out() == RATIONAL_MAX) {
|
||||
if (workarea_->out() == RATIONAL_MAX) {
|
||||
out = gr.width();
|
||||
} else {
|
||||
out = qMin(qint64(gr.width()), qRound64(ratio * TimeToScene(points_->workarea()->out())));
|
||||
out = qMin(qint64(gr.width()), qRound64(ratio * TimeToScene(workarea_->out())));
|
||||
}
|
||||
|
||||
qint64 length = qMax(qint64(1), out-in);
|
||||
@@ -112,8 +126,9 @@ void ResizableTimelineScrollBar::paintEvent(QPaintEvent *event)
|
||||
workarea_color);
|
||||
}
|
||||
|
||||
if (!points_->markers()->empty()) {
|
||||
for (auto it=points_->markers()->cbegin(); it!=points_->markers()->cend(); it++) {
|
||||
// Draw markers
|
||||
if (markers_ && !markers_->empty()) {
|
||||
for (auto it=markers_->cbegin(); it!=markers_->cend(); it++) {
|
||||
TimelineMarker* marker = *it;
|
||||
|
||||
QColor marker_color = ColorCoding::GetColor(marker->color()).toQColor();
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
#define RESIZABLETIMELINESCROLLBAR_H
|
||||
|
||||
#include "resizablescrollbar.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
#include "timeline/timelinemarker.h"
|
||||
#include "timeline/timelineworkarea.h"
|
||||
#include "widget/timebased/timescaledobject.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -34,7 +35,8 @@ public:
|
||||
ResizableTimelineScrollBar(QWidget* parent = nullptr);
|
||||
ResizableTimelineScrollBar(Qt::Orientation orientation, QWidget* parent = nullptr);
|
||||
|
||||
void ConnectTimelinePoints(TimelinePoints* points);
|
||||
void ConnectMarkers(TimelineMarkerList *markers);
|
||||
void ConnectWorkArea(TimelineWorkArea *workarea);
|
||||
|
||||
void SetScale(double d);
|
||||
|
||||
@@ -42,7 +44,9 @@ protected:
|
||||
virtual void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
TimelinePoints* points_;
|
||||
TimelineMarkerList* markers_;
|
||||
|
||||
TimelineWorkArea* workarea_;
|
||||
|
||||
double scale_;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
renderer()->Blit(pipeline_secondary_, shader_job, texture_row_sums_->params());
|
||||
|
||||
// Draw line overlays
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
QFont font = p.font();
|
||||
font.setPixelSize(10);
|
||||
QFontMetrics font_metrics = QFontMetrics(font);
|
||||
|
||||
@@ -85,7 +85,7 @@ void WaveformScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
float waveform_end_dim_x = (width() - 1.0) - waveform_start_dim_x;
|
||||
|
||||
// Draw line overlays
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
QFont font;
|
||||
font.setPixelSize(10);
|
||||
QFontMetrics font_metrics = QFontMetrics(font);
|
||||
|
||||
@@ -120,7 +120,7 @@ void SliderLadder::SetValue(const QString &s)
|
||||
|
||||
void SliderLadder::StartListeningToMouseInput()
|
||||
{
|
||||
drag_timer_.start();
|
||||
QMetaObject::invokeMethod(&drag_timer_, "start", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void SliderLadder::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
@@ -35,15 +35,7 @@ public:
|
||||
QComboBox(parent)
|
||||
{
|
||||
foreach (int d, VideoParams::kSupportedDividers) {
|
||||
QString name;
|
||||
|
||||
if (d == 1) {
|
||||
name = tr("Full");
|
||||
} else {
|
||||
name = tr("1/%1").arg(d);
|
||||
}
|
||||
|
||||
this->addItem(name, d);
|
||||
this->addItem(VideoParams::GetNameForDivider(d), d);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu
|
||||
viewer_node_(nullptr),
|
||||
auto_max_scrollbar_(false),
|
||||
toggle_show_all_(false),
|
||||
auto_set_timebase_(true)
|
||||
auto_set_timebase_(true),
|
||||
workarea_(nullptr),
|
||||
markers_(nullptr)
|
||||
{
|
||||
ruler_ = new TimeRuler(ruler_text_visible, ruler_cache_status_visible, this);
|
||||
ConnectTimelineView(ruler_, true);
|
||||
@@ -98,8 +100,8 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
|
||||
SetTimebase(rational());
|
||||
|
||||
// Disconnect ruler and scrollbar from timeline points
|
||||
ruler()->ConnectTimelinePoints(nullptr);
|
||||
scrollbar_->ConnectTimelinePoints(nullptr);
|
||||
ConnectWorkArea(nullptr);
|
||||
ConnectMarkers(nullptr);
|
||||
}
|
||||
|
||||
// Call derivatives
|
||||
@@ -111,8 +113,8 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
|
||||
connect(viewer_node_, &ViewerOutput::RemovedFromGraph, this, &TimeBasedWidget::ConnectedNodeRemovedFromGraph);
|
||||
|
||||
// Connect ruler and scrollbar to timeline points
|
||||
ruler()->ConnectTimelinePoints(viewer_node_->GetTimelinePoints());
|
||||
scrollbar_->ConnectTimelinePoints(viewer_node_->GetTimelinePoints());
|
||||
ConnectWorkArea(viewer_node_->GetWorkArea());
|
||||
ConnectMarkers(viewer_node_->GetMarkers());
|
||||
|
||||
// If we're setting the timebase, set it automatically based on the video and audio parameters
|
||||
if (auto_set_timebase_) {
|
||||
@@ -130,6 +132,20 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
|
||||
emit ConnectedNodeChanged(old, node);
|
||||
}
|
||||
|
||||
void TimeBasedWidget::ConnectWorkArea(TimelineWorkArea *workarea)
|
||||
{
|
||||
workarea_ = workarea;
|
||||
ruler()->SetWorkArea(workarea);
|
||||
scrollbar_->ConnectWorkArea(workarea);
|
||||
}
|
||||
|
||||
void TimeBasedWidget::ConnectMarkers(TimelineMarkerList *markers)
|
||||
{
|
||||
markers_ = markers;
|
||||
ruler()->SetMarkers(markers);
|
||||
scrollbar_->ConnectMarkers(markers);
|
||||
}
|
||||
|
||||
void TimeBasedWidget::UpdateMaximumScroll()
|
||||
{
|
||||
rational length = (viewer_node_) ? viewer_node_->GetLength() : 0;
|
||||
@@ -374,14 +390,14 @@ void TimeBasedWidget::GoToNextCut()
|
||||
|
||||
rational closest_cut = RATIONAL_MAX;
|
||||
|
||||
foreach (Track* track, sequence->GetTracks()) {
|
||||
for (Track* track : sequence->GetTracks()) {
|
||||
rational this_track_closest_cut = track->track_length();
|
||||
|
||||
if (this_track_closest_cut <= GetTime()) {
|
||||
this_track_closest_cut = RATIONAL_MAX;
|
||||
}
|
||||
|
||||
foreach (Block* block, track->Blocks()) {
|
||||
for (Block* block : track->Blocks()) {
|
||||
if (block->in() > GetTime()) {
|
||||
this_track_closest_cut = block->in();
|
||||
break;
|
||||
@@ -457,10 +473,10 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time)
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
TimelinePoints* points = viewer_node_->GetTimelinePoints();
|
||||
TimelineWorkArea* points = viewer_node_->GetWorkArea();
|
||||
|
||||
// Enable workarea if it isn't already enabled
|
||||
if (!points->workarea()->enabled()) {
|
||||
if (!points->enabled()) {
|
||||
command->add_child(new WorkareaSetEnabledCommand(viewer_node_->project(), points, true));
|
||||
}
|
||||
|
||||
@@ -470,23 +486,23 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time)
|
||||
if (m == Timeline::kTrimIn) {
|
||||
in_point = time;
|
||||
|
||||
if (!points->workarea()->enabled() || points->workarea()->out() < in_point) {
|
||||
if (!points->enabled() || points->out() < in_point) {
|
||||
out_point = TimelineWorkArea::kResetOut;
|
||||
} else {
|
||||
out_point = points->workarea()->out();
|
||||
out_point = points->out();
|
||||
}
|
||||
} else {
|
||||
out_point = time;
|
||||
|
||||
if (!points->workarea()->enabled() || points->workarea()->in() > out_point) {
|
||||
if (!points->enabled() || points->in() > out_point) {
|
||||
in_point = TimelineWorkArea::kResetIn;
|
||||
} else {
|
||||
in_point = points->workarea()->in();
|
||||
in_point = points->in();
|
||||
}
|
||||
}
|
||||
|
||||
// Set workarea
|
||||
command->add_child(new WorkareaSetRangeCommand(points->workarea(), TimeRange(in_point, out_point)));
|
||||
command->add_child(new WorkareaSetRangeCommand(points, TimeRange(in_point, out_point)));
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
@@ -497,13 +513,13 @@ void TimeBasedWidget::ResetPoint(Timeline::MovementMode m)
|
||||
return;
|
||||
}
|
||||
|
||||
TimelinePoints* points = GetConnectedNode()->GetTimelinePoints();
|
||||
TimelineWorkArea* points = GetConnectedNode()->GetWorkArea();
|
||||
|
||||
if (!GetConnectedNode() || !points->workarea()->enabled()) {
|
||||
if (!points->enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRange r = points->workarea()->range();
|
||||
TimeRange r = points->range();
|
||||
|
||||
if (m == Timeline::kTrimIn) {
|
||||
r.set_in(TimelineWorkArea::kResetIn);
|
||||
@@ -511,7 +527,7 @@ void TimeBasedWidget::ResetPoint(Timeline::MovementMode m)
|
||||
r.set_out(TimelineWorkArea::kResetOut);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(points->workarea(), r));
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(points, r));
|
||||
}
|
||||
|
||||
void TimeBasedWidget::PageScrollInternal(QScrollBar *bar, int maximum, int screen_position, bool whole_page_scroll)
|
||||
@@ -582,8 +598,7 @@ void TimeBasedWidget::ClearInOutPoints()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetEnabledCommand(GetConnectedNode()->project(), GetConnectedNode()->GetTimelinePoints(), false));
|
||||
Core::instance()->undo_stack()->push(new WorkareaSetEnabledCommand(GetConnectedNode()->project(), GetConnectedNode()->GetWorkArea(), false));
|
||||
}
|
||||
|
||||
void TimeBasedWidget::SetMarker()
|
||||
@@ -592,7 +607,7 @@ void TimeBasedWidget::SetMarker()
|
||||
return;
|
||||
}
|
||||
|
||||
TimelineMarkerList *markers = GetConnectedNode()->GetTimelinePoints()->markers();
|
||||
TimelineMarkerList *markers = GetConnectedNode()->GetMarkers();
|
||||
|
||||
if (TimelineMarker *existing = markers->GetMarkerAtTime(GetTime())) {
|
||||
// We already have a marker here, so pop open the edit dialog
|
||||
@@ -661,8 +676,8 @@ void TimeBasedWidget::ToggleShowAll()
|
||||
void TimeBasedWidget::GoToIn()
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
if (GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
SetTimeAndSignal(GetConnectedNode()->GetTimelinePoints()->workarea()->in());
|
||||
if (GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
SetTimeAndSignal(GetConnectedNode()->GetWorkArea()->in());
|
||||
} else {
|
||||
GoToStart();
|
||||
}
|
||||
@@ -672,8 +687,8 @@ void TimeBasedWidget::GoToIn()
|
||||
void TimeBasedWidget::GoToOut()
|
||||
{
|
||||
if (GetConnectedNode()) {
|
||||
if (GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
SetTimeAndSignal(GetConnectedNode()->GetTimelinePoints()->workarea()->out());
|
||||
if (GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
SetTimeAndSignal(GetConnectedNode()->GetWorkArea()->out());
|
||||
} else {
|
||||
GoToEnd();
|
||||
}
|
||||
@@ -750,7 +765,7 @@ bool TimeBasedWidget::SnapPoint(const std::vector<rational> &start_times, ration
|
||||
// Snap to clip markers too
|
||||
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(b)) {
|
||||
if (clip->connected_viewer()) {
|
||||
TimelineMarkerList *markers = clip->connected_viewer()->GetTimelinePoints()->markers();
|
||||
TimelineMarkerList *markers = clip->connected_viewer()->GetMarkers();
|
||||
for (auto jt=markers->cbegin(); jt!=markers->cend(); jt++) {
|
||||
TimelineMarker *marker = *jt;
|
||||
|
||||
@@ -768,8 +783,8 @@ bool TimeBasedWidget::SnapPoint(const std::vector<rational> &start_times, ration
|
||||
}
|
||||
}
|
||||
|
||||
if ((snap_points & kSnapToMarkers) && ruler()->GetTimelinePoints()) {
|
||||
for (auto it=ruler()->GetTimelinePoints()->markers()->cbegin(); it!=ruler()->GetTimelinePoints()->markers()->cend(); it++) {
|
||||
if ((snap_points & kSnapToMarkers) && ruler()->GetMarkers()) {
|
||||
for (auto it=ruler()->GetMarkers()->cbegin(); it!=ruler()->GetMarkers()->cend(); it++) {
|
||||
TimelineMarker* m = *it;
|
||||
|
||||
// Ignore selected markers
|
||||
@@ -787,9 +802,9 @@ bool TimeBasedWidget::SnapPoint(const std::vector<rational> &start_times, ration
|
||||
}
|
||||
}
|
||||
|
||||
if ((snap_points & kSnapToWorkarea) && ruler()->GetTimelinePoints()) {
|
||||
const rational &workarea_in = ruler()->GetTimelinePoints()->workarea()->in();
|
||||
const rational &workarea_out = ruler()->GetTimelinePoints()->workarea()->out();
|
||||
if ((snap_points & kSnapToWorkarea) && ruler()->GetWorkArea()) {
|
||||
const rational &workarea_in = ruler()->GetWorkArea()->in();
|
||||
const rational &workarea_out = ruler()->GetWorkArea()->out();
|
||||
|
||||
AttemptSnap(potential_snaps, screen_pt, TimeToScene(workarea_in), start_times, workarea_in);
|
||||
AttemptSnap(potential_snaps, screen_pt, TimeToScene(workarea_out), start_times, workarea_out);
|
||||
|
||||
@@ -50,6 +50,11 @@ public:
|
||||
|
||||
void ConnectViewerNode(ViewerOutput *node);
|
||||
|
||||
TimelineWorkArea *GetConnectedWorkArea() const { return workarea_; }
|
||||
TimelineMarkerList *GetConnectedMarkers() const { return markers_; }
|
||||
void ConnectWorkArea(TimelineWorkArea *workarea);
|
||||
void ConnectMarkers(TimelineMarkerList *markers);
|
||||
|
||||
void SetScaleAndCenterOnPlayhead(const double& scale);
|
||||
|
||||
TimeRuler* ruler() const;
|
||||
@@ -130,6 +135,9 @@ protected:
|
||||
|
||||
virtual void ConnectedNodeChangeEvent(ViewerOutput*){}
|
||||
|
||||
virtual void ConnectedWorkAreaChangeEvent(TimelineWorkArea *){}
|
||||
virtual void ConnectedMarkersChangeEvent(TimelineMarkerList *){}
|
||||
|
||||
virtual void ConnectNodeEvent(ViewerOutput*){}
|
||||
|
||||
virtual void DisconnectNodeEvent(ViewerOutput*){}
|
||||
@@ -217,6 +225,9 @@ private:
|
||||
double scrollbar_start_scale_;
|
||||
bool scrollbar_top_handle_;
|
||||
|
||||
TimelineWorkArea *workarea_;
|
||||
TimelineMarkerList *markers_;
|
||||
|
||||
private slots:
|
||||
void UpdateMaximumScroll();
|
||||
|
||||
|
||||
@@ -569,6 +569,22 @@ void TimelineWidget::ToggleLinksOnSelected()
|
||||
Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link));
|
||||
}
|
||||
|
||||
void TimelineWidget::AddDefaultTransitionsToSelected()
|
||||
{
|
||||
QVector<ClipBlock*> blocks;
|
||||
|
||||
foreach (Block* item, GetSelectedBlocks()) {
|
||||
// Only clips can be linked
|
||||
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(item)) {
|
||||
blocks.append(clip);
|
||||
}
|
||||
}
|
||||
|
||||
if (!blocks.isEmpty()) {
|
||||
Core::instance()->undo_stack()->push(new TimelineAddDefaultTransitionCommand(blocks, timebase()));
|
||||
}
|
||||
}
|
||||
|
||||
bool TimelineWidget::CopySelected(bool cut)
|
||||
{
|
||||
if (super::CopySelected(cut)) {
|
||||
@@ -639,7 +655,7 @@ void TimelineWidget::PasteInsert()
|
||||
void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
{
|
||||
if (!GetConnectedNode()
|
||||
|| !GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
|| !GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -649,8 +665,8 @@ void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
|
||||
command->add_child(new TimelineRippleRemoveAreaCommand(
|
||||
sequence(),
|
||||
GetConnectedNode()->GetTimelinePoints()->workarea()->in(),
|
||||
GetConnectedNode()->GetTimelinePoints()->workarea()->out()));
|
||||
GetConnectedNode()->GetWorkArea()->in(),
|
||||
GetConnectedNode()->GetWorkArea()->out()));
|
||||
|
||||
} else {
|
||||
QVector<Track*> unlocked_tracks = sequence()->GetUnlockedTracks();
|
||||
@@ -658,7 +674,7 @@ void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
foreach (Track* track, unlocked_tracks) {
|
||||
GapBlock* gap = new GapBlock();
|
||||
|
||||
gap->set_length_and_media_out(GetConnectedNode()->GetTimelinePoints()->workarea()->length());
|
||||
gap->set_length_and_media_out(GetConnectedNode()->GetWorkArea()->length());
|
||||
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(track->parent()),
|
||||
gap));
|
||||
@@ -666,17 +682,17 @@ void TimelineWidget::DeleteInToOut(bool ripple)
|
||||
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track->type()),
|
||||
track->Index(),
|
||||
gap,
|
||||
GetConnectedNode()->GetTimelinePoints()->workarea()->in()));
|
||||
GetConnectedNode()->GetWorkArea()->in()));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear workarea after this
|
||||
command->add_child(new WorkareaSetEnabledCommand(GetConnectedNode()->project(),
|
||||
GetConnectedNode()->GetTimelinePoints(),
|
||||
GetConnectedNode()->GetWorkArea(),
|
||||
false));
|
||||
|
||||
if (ripple) {
|
||||
SetTimeAndSignal(GetConnectedNode()->GetTimelinePoints()->workarea()->in());
|
||||
SetTimeAndSignal(GetConnectedNode()->GetWorkArea()->in());
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
@@ -1079,6 +1095,11 @@ void TimelineWidget::ShowContextMenu()
|
||||
connect(autocache_action, &QAction::triggered, this, &TimelineWidget::SetSelectedClipsAutocaching);
|
||||
|
||||
if (clip->connected_viewer()) {
|
||||
QAction *reveal_in_footage_viewer = menu.addAction(tr("Reveal in Footage Viewer"));
|
||||
reveal_in_footage_viewer->setData(reinterpret_cast<quintptr>(clip->connected_viewer()));
|
||||
reveal_in_footage_viewer->setProperty("range", QVariant::fromValue(clip->media_range()));
|
||||
connect(reveal_in_footage_viewer, &QAction::triggered, this, &TimelineWidget::RevealInFootageViewer);
|
||||
|
||||
QAction *reveal_in_project = menu.addAction(tr("Reveal in Project"));
|
||||
reveal_in_project->setData(reinterpret_cast<quintptr>(clip->connected_viewer()));
|
||||
connect(reveal_in_project, &QAction::triggered, this, &TimelineWidget::RevealInProject);
|
||||
@@ -1221,6 +1242,16 @@ void TimelineWidget::SignalBlockSelectionChange()
|
||||
signal_block_change_timer_->start();
|
||||
}
|
||||
|
||||
void TimelineWidget::RevealInFootageViewer()
|
||||
{
|
||||
QAction *a = static_cast<QAction*>(sender());
|
||||
|
||||
ViewerOutput *item_to_reveal = reinterpret_cast<ViewerOutput*>(a->data().value<quintptr>());
|
||||
TimeRange r = a->property("range").value<TimeRange>();
|
||||
|
||||
emit RevealViewerInFootageViewer(item_to_reveal, r);
|
||||
}
|
||||
|
||||
void TimelineWidget::RevealInProject()
|
||||
{
|
||||
QAction *a = static_cast<QAction*>(sender());
|
||||
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
|
||||
void ToggleLinksOnSelected();
|
||||
|
||||
void AddDefaultTransitionsToSelected();
|
||||
|
||||
virtual bool CopySelected(bool cut) override;
|
||||
|
||||
virtual bool Paste() override;
|
||||
@@ -277,6 +279,7 @@ signals:
|
||||
|
||||
void RequestCaptureStart(const TimeRange &time, const Track::Reference &track);
|
||||
|
||||
void RevealViewerInFootageViewer(ViewerOutput *r, const TimeRange &range);
|
||||
void RevealViewerInProject(ViewerOutput *r);
|
||||
|
||||
protected:
|
||||
@@ -427,6 +430,7 @@ private slots:
|
||||
|
||||
void SignalBlockSelectionChange();
|
||||
|
||||
void RevealInFootageViewer();
|
||||
void RevealInProject();
|
||||
|
||||
void RenameSelectedBlocks();
|
||||
|
||||
@@ -231,7 +231,7 @@ void ImportTool::FootageToGhosts(rational ghost_start, const DraggedFootageData
|
||||
rational footage_duration;
|
||||
rational ghost_in;
|
||||
|
||||
TimelineWorkArea* wk = footage->GetTimelinePoints()->workarea();
|
||||
TimelineWorkArea* wk = footage->GetWorkArea();
|
||||
if (wk->enabled()) {
|
||||
footage_duration = wk->length();
|
||||
ghost_in = wk->in();
|
||||
|
||||
@@ -689,6 +689,8 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
InsertGapsAtGhostDestination(command);
|
||||
}
|
||||
|
||||
QMap<Node*, Node*> relinks;
|
||||
|
||||
// Now we can re-add each clip
|
||||
foreach (const GhostBlockPair& p, blocks_moving) {
|
||||
Block* block = p.block;
|
||||
@@ -696,7 +698,10 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
if (duplicate_clips) {
|
||||
// Duplicate rather than move
|
||||
// Place the copy instead of the original block
|
||||
block = static_cast<Block*>(Node::CopyNodeInGraph(block, command));
|
||||
Block *new_block = static_cast<Block*>(Node::CopyNodeInGraph(block, command));
|
||||
relinks.insert(block, new_block);
|
||||
block = new_block;
|
||||
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(block)) {
|
||||
new_clip->AddCachePassthroughFrom(static_cast<ClipBlock*>(p.block));
|
||||
}
|
||||
@@ -709,6 +714,18 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
p.ghost->GetAdjustedIn()));
|
||||
}
|
||||
|
||||
if (!relinks.empty()) {
|
||||
for (auto it=relinks.cbegin(); it!=relinks.cend(); it++) {
|
||||
for (auto jt=it.key()->links().cbegin(); jt!=it.key()->links().cend(); jt++) {
|
||||
Node *link = *jt;
|
||||
Node *copy_link = relinks.value(link);
|
||||
if (copy_link) {
|
||||
command->add_child(new NodeLinkCommand(it.value(), copy_link, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust selections
|
||||
TimelineWidgetSelections new_sel = parent()->GetSelections();
|
||||
new_sel.ShiftTime(blocks_moving.first().ghost->GetInAdjustment());
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "node/math/merge/merge.h"
|
||||
#include "timelineundocommon.h"
|
||||
#include "widget/timelinewidget/undo/timelineundotrack.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -287,8 +289,8 @@ void TrackListInsertGaps::prepare()
|
||||
QVector<Block*> blocks_to_append_gap_to;
|
||||
QVector<Track*> tracks_to_append_gap_to;
|
||||
|
||||
foreach (Track* track, working_tracks_) {
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
for (Track* track : qAsConst(working_tracks_)) {
|
||||
for (Block* b : track->Blocks()) {
|
||||
if (dynamic_cast<GapBlock*>(b) && b->in() <= point_ && b->out() >= point_) {
|
||||
// Found a gap at the location
|
||||
gaps_to_extend_.append(b);
|
||||
@@ -542,4 +544,121 @@ void TimelineRemoveTrackCommand::undo()
|
||||
remove_command_->undo_now();
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::prepare()
|
||||
{
|
||||
for (auto it=clips_.cbegin(); it!=clips_.cend(); it++) {
|
||||
ClipBlock *c = *it;
|
||||
|
||||
// Handle in transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->previous()))) {
|
||||
// Do nothing, assume this will be handled by a dual transition from that clip
|
||||
} else if (dynamic_cast<GapBlock*>(c->previous()) || !c->previous()) {
|
||||
// Create in transition
|
||||
AddTransition(c, kIn);
|
||||
}
|
||||
|
||||
// Handle out transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->next()))) {
|
||||
AddTransition(c, kOutDual);
|
||||
} else if (dynamic_cast<GapBlock*>(c->next()) || !c->next()) {
|
||||
// Create out transition
|
||||
AddTransition(c, kOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AddTransition(ClipBlock *c, CreateTransitionMode mode)
|
||||
{
|
||||
if (Track *t = c->track()) {
|
||||
Node *p = nullptr;
|
||||
if (t->type() == Track::kVideo) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultVideoTransition").toString());
|
||||
} else if (t->type() == Track::kAudio) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultAudioTransition").toString());
|
||||
}
|
||||
|
||||
rational transition_length = OLIVE_CONFIG("DefaultTransitionLength").value<rational>();
|
||||
|
||||
// Resize original clip
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, false);
|
||||
}
|
||||
break;
|
||||
case kOut:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, true);
|
||||
}
|
||||
break;
|
||||
case kOutDual:
|
||||
{
|
||||
rational half_length = transition_length / 2;
|
||||
|
||||
ValidateTransitionLength(static_cast<ClipBlock*>(c->next()), half_length);
|
||||
ValidateTransitionLength(c, half_length);
|
||||
|
||||
transition_length = half_length * 2;
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(static_cast<ClipBlock*>(c->next()), half_length, false);
|
||||
AdjustClipLength(c, half_length, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transition_length > 0) {
|
||||
if (TransitionBlock *transition = dynamic_cast<TransitionBlock*>(p)) {
|
||||
transition->set_length_and_media_out(transition_length);
|
||||
|
||||
// Add transition
|
||||
commands_.append(new NodeAddCommand(c->parent(), transition));
|
||||
|
||||
// Insert block
|
||||
Block *insert_after = (mode == kIn) ? c->previous() : c;
|
||||
commands_.append(new TrackInsertBlockAfterCommand(c->track(), transition, insert_after));
|
||||
|
||||
// Connect
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
break;
|
||||
case kOutDual:
|
||||
commands_.append(new NodeEdgeAddCommand(c->next(), NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
/* fall through */
|
||||
case kOut:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kOutBlockInput)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational new_len = cur_len - transition_length;
|
||||
if (out) {
|
||||
commands_.append(new BlockResizeCommand(c, new_len));
|
||||
} else {
|
||||
commands_.append(new BlockResizeWithMediaInCommand(c, new_len));
|
||||
}
|
||||
lengths_.insert(c, new_len);
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::ValidateTransitionLength(ClipBlock *c, rational &transition_length)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational half_cur_len = cur_len/2;
|
||||
if (transition_length >= half_cur_len) {
|
||||
transition_length = half_cur_len - timebase_;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -358,6 +358,61 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class TimelineAddDefaultTransitionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TimelineAddDefaultTransitionCommand(const QVector<ClipBlock*> &clips, const rational &timebase) :
|
||||
clips_(clips),
|
||||
timebase_(timebase)
|
||||
{}
|
||||
|
||||
virtual ~TimelineAddDefaultTransitionCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return clips_.empty() ? nullptr : clips_.first()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum CreateTransitionMode {
|
||||
kIn,
|
||||
kOut,
|
||||
kOutDual
|
||||
};
|
||||
|
||||
void AddTransition(ClipBlock *c, CreateTransitionMode mode);
|
||||
void AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out);
|
||||
void ValidateTransitionLength(ClipBlock *c, rational &transition_length);
|
||||
|
||||
|
||||
QVector<ClipBlock*> clips_;
|
||||
rational timebase_;
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
QHash<ClipBlock*, rational> lengths_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOGENERAL_H
|
||||
|
||||
@@ -22,16 +22,15 @@
|
||||
#define TIMELINEUNDOWORKAREA_H
|
||||
|
||||
#include "node/project/project.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class WorkareaSetEnabledCommand : public UndoCommand {
|
||||
public:
|
||||
WorkareaSetEnabledCommand(Project *project, TimelinePoints* points, bool enabled) :
|
||||
WorkareaSetEnabledCommand(Project *project, TimelineWorkArea* points, bool enabled) :
|
||||
project_(project),
|
||||
points_(points),
|
||||
old_enabled_(points_->workarea()->enabled()),
|
||||
old_enabled_(points_->enabled()),
|
||||
new_enabled_(enabled)
|
||||
{
|
||||
}
|
||||
@@ -44,18 +43,18 @@ public:
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
points_->workarea()->set_enabled(new_enabled_);
|
||||
points_->set_enabled(new_enabled_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
points_->workarea()->set_enabled(old_enabled_);
|
||||
points_->set_enabled(old_enabled_);
|
||||
}
|
||||
|
||||
private:
|
||||
Project* project_;
|
||||
|
||||
TimelinePoints* points_;
|
||||
TimelineWorkArea* points_;
|
||||
|
||||
bool old_enabled_;
|
||||
|
||||
|
||||
@@ -585,7 +585,7 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
|
||||
}
|
||||
}
|
||||
|
||||
TimelineMarkerList *marker_list = clip->connected_viewer()->GetTimelinePoints()->markers();
|
||||
TimelineMarkerList *marker_list = clip->connected_viewer()->GetMarkers();
|
||||
if (!marker_list->empty()) {
|
||||
|
||||
clip_marker_rects_.clear();
|
||||
@@ -752,12 +752,14 @@ void TimelineView::ConnectTrackList(TrackList *list)
|
||||
{
|
||||
if (connected_track_list_) {
|
||||
disconnect(connected_track_list_, &TrackList::TrackListChanged, this, &TimelineView::TrackListChanged);
|
||||
disconnect(connected_track_list_, &TrackList::TrackHeightChanged, this, &TimelineView::TrackListChanged);
|
||||
}
|
||||
|
||||
connected_track_list_ = list;
|
||||
|
||||
if (connected_track_list_) {
|
||||
connect(connected_track_list_, &TrackList::TrackListChanged, this, &TimelineView::TrackListChanged);
|
||||
connect(connected_track_list_, &TrackList::TrackHeightChanged, this, &TimelineView::TrackListChanged);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace olive {
|
||||
|
||||
SeekableWidget::SeekableWidget(QWidget* parent) :
|
||||
super(parent),
|
||||
timeline_points_(nullptr),
|
||||
markers_(nullptr),
|
||||
workarea_(nullptr),
|
||||
dragging_(false),
|
||||
ignore_next_focus_out_(false),
|
||||
selection_manager_(this),
|
||||
@@ -63,26 +64,41 @@ SeekableWidget::SeekableWidget(QWidget* parent) :
|
||||
selection_manager_.SetSnapMask(TimeBasedWidget::kSnapAll);
|
||||
}
|
||||
|
||||
void SeekableWidget::ConnectTimelinePoints(TimelinePoints *points)
|
||||
void SeekableWidget::SetMarkers(TimelineMarkerList *markers)
|
||||
{
|
||||
if (timeline_points_) {
|
||||
if (markers_) {
|
||||
selection_manager_.ClearSelection();
|
||||
|
||||
disconnect(timeline_points_->workarea(), &TimelineWorkArea::RangeChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(timeline_points_->workarea(), &TimelineWorkArea::EnabledChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(timeline_points_->markers(), &TimelineMarkerList::MarkerAdded, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(timeline_points_->markers(), &TimelineMarkerList::MarkerRemoved, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(timeline_points_->markers(), &TimelineMarkerList::MarkerModified, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerAdded, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerRemoved, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(markers_, &TimelineMarkerList::MarkerModified, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
timeline_points_ = points;
|
||||
markers_ = markers;
|
||||
|
||||
if (timeline_points_) {
|
||||
connect(timeline_points_->workarea(), &TimelineWorkArea::RangeChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(timeline_points_->workarea(), &TimelineWorkArea::EnabledChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(timeline_points_->markers(), &TimelineMarkerList::MarkerAdded, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(timeline_points_->markers(), &TimelineMarkerList::MarkerRemoved, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(timeline_points_->markers(), &TimelineMarkerList::MarkerModified, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
if (markers_) {
|
||||
connect(markers_, &TimelineMarkerList::MarkerAdded, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(markers_, &TimelineMarkerList::MarkerRemoved, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(markers_, &TimelineMarkerList::MarkerModified, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void SeekableWidget::SetWorkArea(TimelineWorkArea *workarea)
|
||||
{
|
||||
if (workarea_) {
|
||||
selection_manager_.ClearSelection();
|
||||
|
||||
disconnect(workarea_, &TimelineWorkArea::RangeChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(workarea_, &TimelineWorkArea::EnabledChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
workarea_ = workarea;
|
||||
|
||||
if (workarea_) {
|
||||
connect(workarea_, &TimelineWorkArea::RangeChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(workarea_, &TimelineWorkArea::EnabledChanged, viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
viewport()->update();
|
||||
@@ -139,11 +155,11 @@ bool SeekableWidget::PasteMarkers()
|
||||
|
||||
m->set_time(m->time().in() - min);
|
||||
|
||||
if (TimelineMarker *existing = timeline_points_->markers()->GetMarkerAtTime(m->time().in())) {
|
||||
if (TimelineMarker *existing = markers_->GetMarkerAtTime(m->time().in())) {
|
||||
command->add_child(new MarkerRemoveCommand(existing));
|
||||
}
|
||||
|
||||
command->add_child(new MarkerAddCommand(timeline_points_->markers(), m));
|
||||
command->add_child(new MarkerAddCommand(markers_, m));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
@@ -187,7 +203,7 @@ void SeekableWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
} else {
|
||||
SeekToScenePoint(scene.x());
|
||||
}
|
||||
} else if (timeline_points_) {
|
||||
} else {
|
||||
// Look for resize points
|
||||
if (FindResizeHandle(event)) {
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
@@ -238,6 +254,59 @@ void SeekableWidget::focusOutEvent(QFocusEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void SeekableWidget::DrawMarkers(QPainter *p, int marker_bottom)
|
||||
{
|
||||
selection_manager_.ClearDrawnObjects();
|
||||
|
||||
// Draw markers
|
||||
if (markers_ && !markers_->empty() && marker_bottom > 0) {
|
||||
int lim_left = GetLeftLimit();
|
||||
int lim_right = GetRightLimit();
|
||||
|
||||
for (auto it=markers_->cbegin(); it!=markers_->cend(); it++) {
|
||||
TimelineMarker* marker = *it;
|
||||
|
||||
int marker_right = TimeToScene(marker->time().out());
|
||||
if (marker_right < lim_left) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int marker_left = TimeToScene(marker->time().in());
|
||||
if (marker_left >= lim_right) {
|
||||
break;
|
||||
}
|
||||
|
||||
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::DrawWorkArea(QPainter *p)
|
||||
{
|
||||
// Draw in/out workarea
|
||||
if (workarea_ && workarea_->enabled()) {
|
||||
int lim_left = GetLeftLimit();
|
||||
int lim_right = GetRightLimit();
|
||||
|
||||
int workarea_left = qMax(qreal(lim_left), TimeToScene(workarea_->in()));
|
||||
int workarea_right;
|
||||
|
||||
if (workarea_->out() == TimelineWorkArea::kResetOut) {
|
||||
workarea_right = lim_right;
|
||||
} else {
|
||||
workarea_right = qMin(qreal(lim_right), TimeToScene(workarea_->out()));
|
||||
}
|
||||
|
||||
QColor translucent_highlight = palette().highlight().color();
|
||||
translucent_highlight.setAlpha(96);
|
||||
p->fillRect(workarea_left, 0, workarea_right - workarea_left, height(), translucent_highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void SeekableWidget::DeselectAllMarkers()
|
||||
{
|
||||
selection_manager_.ClearSelection();
|
||||
@@ -309,61 +378,15 @@ void SeekableWidget::SelectionManagerDeselectEvent(void *obj)
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
void SeekableWidget::DrawTimelinePoints(QPainter* p, int marker_bottom)
|
||||
{
|
||||
if (!GetTimelinePoints()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int lim_left = GetScroll();
|
||||
int lim_right = lim_left + width();
|
||||
|
||||
selection_manager_.ClearDrawnObjects();
|
||||
|
||||
// Draw in/out workarea
|
||||
if (GetTimelinePoints()->workarea()->enabled()) {
|
||||
int workarea_left = qMax(qreal(lim_left), TimeToScene(GetTimelinePoints()->workarea()->in()));
|
||||
int workarea_right;
|
||||
|
||||
if (GetTimelinePoints()->workarea()->out() == TimelineWorkArea::kResetOut) {
|
||||
workarea_right = lim_right;
|
||||
} else {
|
||||
workarea_right = qMin(qreal(lim_right), TimeToScene(GetTimelinePoints()->workarea()->out()));
|
||||
}
|
||||
|
||||
p->fillRect(workarea_left, 0, workarea_right - workarea_left, height(), palette().highlight());
|
||||
}
|
||||
|
||||
// Draw markers
|
||||
if (marker_bottom > 0 && !GetTimelinePoints()->markers()->empty()) {
|
||||
for (auto it=GetTimelinePoints()->markers()->cbegin(); it!=GetTimelinePoints()->markers()->cend(); it++) {
|
||||
TimelineMarker* marker = *it;
|
||||
|
||||
int marker_right = TimeToScene(marker->time().out());
|
||||
if (marker_right < lim_left) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int marker_left = TimeToScene(marker->time().in());
|
||||
if (marker_left >= lim_right) {
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int half_width = playhead_width_ / 2;
|
||||
|
||||
if (x + half_width < 0 || x - half_width > width()) {
|
||||
return;
|
||||
{
|
||||
int test = x - this->GetScroll();
|
||||
if (test + half_width < 0 || test - half_width > width()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
p->setRenderHint(QPainter::Antialiasing);
|
||||
@@ -384,6 +407,16 @@ void SeekableWidget::DrawPlayhead(QPainter *p, int x, int y)
|
||||
p->setRenderHint(QPainter::Antialiasing, false);
|
||||
}
|
||||
|
||||
int SeekableWidget::GetLeftLimit() const
|
||||
{
|
||||
return GetScroll();
|
||||
}
|
||||
|
||||
int SeekableWidget::GetRightLimit() const
|
||||
{
|
||||
return GetLeftLimit() + width();
|
||||
}
|
||||
|
||||
bool SeekableWidget::ShowContextMenu(const QPoint &p)
|
||||
{
|
||||
if (selection_manager_.GetObjectAtPoint(p) && !selection_manager_.GetSelectedObjects().empty()) {
|
||||
@@ -422,32 +455,38 @@ bool SeekableWidget::FindResizeHandle(QMouseEvent *event)
|
||||
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 (workarea_) {
|
||||
if (workarea_->in() >= min && workarea_->in() < max) {
|
||||
resize_mode_ = kResizeIn;
|
||||
} else if (workarea_->out() >= min && 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;
|
||||
if (workarea_) {
|
||||
resize_item_ = workarea_;
|
||||
resize_item_range_ = 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().in() != m->time().out()) {
|
||||
if (m->time().in() >= min && m->time().in() < max) {
|
||||
resize_mode_ = kResizeIn;
|
||||
} else if (m->time().out() >= min && m->time().out() < max) {
|
||||
resize_mode_ = kResizeOut;
|
||||
}
|
||||
if (markers_) {
|
||||
// Check for markers
|
||||
for (auto it=markers_->cbegin(); it!=markers_->cend(); it++) {
|
||||
TimelineMarker *m = *it;
|
||||
if (m->time().in() != m->time().out()) {
|
||||
if (m->time().in() >= min && m->time().in() < max) {
|
||||
resize_mode_ = kResizeIn;
|
||||
} else if (m->time().out() >= min && m->time().out() < max) {
|
||||
resize_mode_ = kResizeOut;
|
||||
}
|
||||
|
||||
if (resize_mode_ != kResizeNone) {
|
||||
resize_item_ = m;
|
||||
resize_item_range_ = m->time();
|
||||
resize_snap_mask_ = TimeBasedWidget::kSnapAll;
|
||||
break;
|
||||
if (resize_mode_ != kResizeNone) {
|
||||
resize_item_ = m;
|
||||
resize_item_range_ = m->time();
|
||||
resize_snap_mask_ = TimeBasedWidget::kSnapAll;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/timebased/timebasedviewselectionmanager.h"
|
||||
|
||||
@@ -42,8 +41,11 @@ public:
|
||||
return horizontalScrollBar()->value();
|
||||
}
|
||||
|
||||
TimelinePoints* GetTimelinePoints() const { return timeline_points_; }
|
||||
void ConnectTimelinePoints(TimelinePoints* points);
|
||||
TimelineMarkerList *GetMarkers() const { return markers_; }
|
||||
TimelineWorkArea *GetWorkArea() const { return workarea_; }
|
||||
|
||||
void SetMarkers(TimelineMarkerList *markers);
|
||||
void SetWorkArea(TimelineWorkArea *workarea);
|
||||
|
||||
bool IsDraggingPlayhead() const
|
||||
{
|
||||
@@ -84,7 +86,8 @@ protected:
|
||||
|
||||
virtual void focusOutEvent(QFocusEvent *event) override;
|
||||
|
||||
void DrawTimelinePoints(QPainter *p, int marker_bottom = 0);
|
||||
void DrawMarkers(QPainter *p, int marker_bottom = 0);
|
||||
void DrawWorkArea(QPainter *p);
|
||||
|
||||
void DrawPlayhead(QPainter* p, int x, int y);
|
||||
|
||||
@@ -96,6 +99,9 @@ protected:
|
||||
return playhead_width_;
|
||||
}
|
||||
|
||||
int GetLeftLimit() const;
|
||||
int GetRightLimit() const;
|
||||
|
||||
protected slots:
|
||||
virtual bool ShowContextMenu(const QPoint &p);
|
||||
|
||||
@@ -112,7 +118,8 @@ private:
|
||||
|
||||
void CommitResizeHandle();
|
||||
|
||||
TimelinePoints* timeline_points_;
|
||||
TimelineMarkerList* markers_;
|
||||
TimelineWorkArea* workarea_;
|
||||
|
||||
int text_height_;
|
||||
|
||||
|
||||
@@ -102,9 +102,8 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect)
|
||||
|
||||
// Draw timeline points if connected
|
||||
int marker_height = TimelineMarker::GetMarkerHeight(p->fontMetrics());
|
||||
if (GetTimelinePoints()) {
|
||||
DrawTimelinePoints(p, marker_height);
|
||||
}
|
||||
DrawMarkers(p, marker_height);
|
||||
DrawWorkArea(p);
|
||||
|
||||
double width_of_frame = timebase_dbl() * GetScale();
|
||||
double width_of_second = 0;
|
||||
|
||||
@@ -86,7 +86,8 @@ void AudioWaveformView::drawForeground(QPainter *p, const QRectF &rect)
|
||||
}
|
||||
|
||||
// Draw in/out points
|
||||
DrawTimelinePoints(p);
|
||||
DrawWorkArea(p);
|
||||
DrawMarkers(p);
|
||||
|
||||
// Draw waveform
|
||||
p->setPen(QColor(64, 255, 160)); // FIXME: Hardcoded color
|
||||
|
||||
@@ -38,6 +38,22 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
|
||||
controls_->SetAudioVideoDragButtonsVisible(true);
|
||||
connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag);
|
||||
connect(controls_, &PlaybackControls::AudioPressed, this, &FootageViewerWidget::StartAudioDrag);
|
||||
|
||||
override_workarea_ = new TimelineWorkArea(this);
|
||||
}
|
||||
|
||||
void FootageViewerWidget::OverrideWorkArea(const TimeRange &r)
|
||||
{
|
||||
override_workarea_->set_enabled(true);
|
||||
override_workarea_->set_range(r);
|
||||
this->ConnectWorkArea(override_workarea_);
|
||||
}
|
||||
|
||||
void FootageViewerWidget::ResetWorkArea()
|
||||
{
|
||||
if (GetConnectedWorkArea() == override_workarea_) {
|
||||
this->ConnectWorkArea(GetConnectedNode() ? GetConnectedNode()->GetWorkArea() : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void FootageViewerWidget::ConnectNodeEvent(ViewerOutput *n)
|
||||
|
||||
@@ -32,6 +32,9 @@ class FootageViewerWidget : public ViewerWidget
|
||||
public:
|
||||
FootageViewerWidget(QWidget* parent = nullptr);
|
||||
|
||||
void OverrideWorkArea(const TimeRange &r);
|
||||
void ResetWorkArea();
|
||||
|
||||
protected:
|
||||
virtual void ConnectNodeEvent(ViewerOutput *) override;
|
||||
|
||||
@@ -42,6 +45,8 @@ private:
|
||||
|
||||
QHash<ViewerOutput*, rational> cached_timestamps_;
|
||||
|
||||
TimelineWorkArea *override_workarea_;
|
||||
|
||||
private slots:
|
||||
void StartFootageDrag();
|
||||
|
||||
|
||||
+102
-62
@@ -45,6 +45,7 @@
|
||||
#include "viewerpreventsleep.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/timelinewidget/tool/add.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
@@ -72,19 +73,17 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
record_armed_(false),
|
||||
recording_(false),
|
||||
first_requeue_watcher_(nullptr),
|
||||
enable_audio_scrubbing_(true)
|
||||
enable_audio_scrubbing_(true),
|
||||
waveform_mode_(kWFAutomatic)
|
||||
{
|
||||
// Set up main layout
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
// Set up stacked widget to allow switching away from the viewer widget
|
||||
stack_ = new QStackedWidget();
|
||||
layout->addWidget(stack_);
|
||||
|
||||
// Create main OpenGL-based view and sizer
|
||||
sizer_ = new ViewerSizer();
|
||||
stack_->addWidget(sizer_);
|
||||
sizer_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(sizer_);
|
||||
|
||||
display_widget_ = new ViewerDisplayWidget();
|
||||
display_widget_->setAcceptDrops(true);
|
||||
@@ -114,7 +113,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
waveform_view_ = new AudioWaveformView();
|
||||
ConnectTimelineView(waveform_view_, true);
|
||||
PassWheelEventsToScrollBar(waveform_view_);
|
||||
stack_->addWidget(waveform_view_);
|
||||
layout->addWidget(waveform_view_);
|
||||
|
||||
// Create time ruler
|
||||
layout->addWidget(ruler());
|
||||
@@ -210,9 +209,10 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n)
|
||||
connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
connect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot);
|
||||
connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters);
|
||||
connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateTextureFromNode, Qt::QueuedConnection);
|
||||
connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
|
||||
connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
|
||||
connect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack);
|
||||
connect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateWaveformViewFromMode);
|
||||
|
||||
VideoParams vp = n->GetVideoParams();
|
||||
|
||||
@@ -233,10 +233,9 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n)
|
||||
dw->ConnectColorManager(color_manager);
|
||||
}
|
||||
|
||||
UpdateStack();
|
||||
UpdateWaveformViewFromMode();
|
||||
|
||||
waveform_view_->SetViewer(GetConnectedNode());
|
||||
waveform_view_->ConnectTimelinePoints(GetConnectedNode()->GetTimelinePoints());
|
||||
|
||||
UpdateRendererVideoParameters();
|
||||
UpdateRendererAudioParameters();
|
||||
@@ -254,9 +253,10 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
disconnect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot);
|
||||
disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters);
|
||||
disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateTextureFromNode);
|
||||
disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
|
||||
disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
|
||||
disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateWaveformViewFromMode);
|
||||
|
||||
CloseAudioProcessor();
|
||||
|
||||
@@ -272,10 +272,9 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
}
|
||||
|
||||
waveform_view_->SetViewer(nullptr);
|
||||
waveform_view_->ConnectTimelinePoints(nullptr);
|
||||
|
||||
// Queue an UpdateStack so that when it runs, the viewer node will be fully disconnected
|
||||
QMetaObject::invokeMethod(this, &ViewerWidget::UpdateStack, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, &ViewerWidget::UpdateWaveformViewFromMode, Qt::QueuedConnection);
|
||||
|
||||
SetGizmos(nullptr);
|
||||
}
|
||||
@@ -286,6 +285,16 @@ void ViewerWidget::ConnectedNodeChangeEvent(ViewerOutput *n)
|
||||
display_widget_->SetSubtitleTracks(dynamic_cast<Sequence*>(n));
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectedWorkAreaChangeEvent(TimelineWorkArea *workarea)
|
||||
{
|
||||
waveform_view_->SetWorkArea(workarea);
|
||||
}
|
||||
|
||||
void ViewerWidget::ConnectedMarkersChangeEvent(TimelineMarkerList *markers)
|
||||
{
|
||||
waveform_view_->SetMarkers(markers);
|
||||
}
|
||||
|
||||
void ViewerWidget::ScaleChangedEvent(const double &s)
|
||||
{
|
||||
super::ScaleChangedEvent(s);
|
||||
@@ -381,8 +390,8 @@ void ViewerWidget::CacheEntireSequence()
|
||||
|
||||
void ViewerWidget::CacheSequenceInOut()
|
||||
{
|
||||
if (GetConnectedNode() && GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
auto_cacher_->ForceCacheRange(GetConnectedNode()->GetTimelinePoints()->workarea()->range());
|
||||
if (GetConnectedNode() && GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
auto_cacher_->ForceCacheRange(GetConnectedNode()->GetWorkArea()->range());
|
||||
} else {
|
||||
QMessageBox::warning(this,
|
||||
tr("Error"),
|
||||
@@ -528,11 +537,35 @@ void ViewerWidget::CreateAddableAt(const QRectF &f)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::HandleFirstRequeueDestroy()
|
||||
{
|
||||
// Extra protection to ensure we don't reference a destroyed object
|
||||
if (first_requeue_watcher_ == sender()) {
|
||||
first_requeue_watcher_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::CloseAudioProcessor()
|
||||
{
|
||||
audio_processor_.Close();
|
||||
}
|
||||
|
||||
void ViewerWidget::SetWaveformMode(WaveformMode wf)
|
||||
{
|
||||
waveform_mode_ = wf;
|
||||
UpdateWaveformViewFromMode();
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateWaveformViewFromMode()
|
||||
{
|
||||
bool prefer_waveform = ShouldForceWaveform();
|
||||
|
||||
sizer_->setVisible(waveform_mode_ == kWFViewerAndWaveform || waveform_mode_ == kWFViewerOnly || (waveform_mode_ == kWFAutomatic && !prefer_waveform));
|
||||
waveform_view_->setVisible(waveform_mode_ == kWFViewerAndWaveform || waveform_mode_ == kWFWaveformOnly || (waveform_mode_ == kWFAutomatic && prefer_waveform));
|
||||
|
||||
waveform_view_->setSizePolicy(QSizePolicy::Expanding, waveform_mode_ == kWFViewerAndWaveform ? QSizePolicy::Maximum : QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
void ViewerWidget::QueueNextAudioBuffer()
|
||||
{
|
||||
rational queue_end = audio_playback_queue_time_ + (kAudioPlaybackInterval * playback_speed_);
|
||||
@@ -551,7 +584,7 @@ void ViewerWidget::QueueNextAudioBuffer()
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher(this);
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForPlayback);
|
||||
audio_playback_queue_.push_back(watcher);
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(audio_playback_queue_time_, queue_end), RenderTicketPriority::kHigh));
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(audio_playback_queue_time_, queue_end)));
|
||||
|
||||
audio_playback_queue_time_ = queue_end;
|
||||
}
|
||||
@@ -674,6 +707,7 @@ void ViewerWidget::ForceRequeueFromCurrentTime()
|
||||
RenderTicketWatcher *watcher = RequestNextFrameForQueue();
|
||||
if (!first_requeue_watcher_) {
|
||||
first_requeue_watcher_ = watcher;
|
||||
connect(first_requeue_watcher_, &RenderTicketWatcher::destroyed, this, &ViewerWidget::HandleFirstRequeueDestroy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,7 +739,7 @@ void ViewerWidget::UpdateTextureFromNode()
|
||||
// Clear queue because we want this frame more than any others
|
||||
auto_cacher_->ClearSingleFrameRenders();
|
||||
|
||||
watcher->SetTicket(GetFrame(time, RenderTicketPriority::kNormal));
|
||||
watcher->SetTicket(GetFrame(time));
|
||||
} else {
|
||||
// There is definitely no frame here, we can immediately flip to showing nothing
|
||||
nonqueue_watchers_.clear();
|
||||
@@ -736,6 +770,8 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
viewer->auto_cacher_->SetRendersPaused(true);
|
||||
}
|
||||
|
||||
RenderManager::instance()->SetAggressiveGarbageCollection(true);
|
||||
|
||||
// Disarm recording if armed
|
||||
if (record_armed_) {
|
||||
DisarmRecording();
|
||||
@@ -833,6 +869,8 @@ void ViewerWidget::PauseInternal()
|
||||
}
|
||||
|
||||
UpdateTextureFromNode();
|
||||
|
||||
RenderManager::instance()->SetAggressiveGarbageCollection(false);
|
||||
}
|
||||
|
||||
prequeuing_video_ = false;
|
||||
@@ -854,7 +892,7 @@ void ViewerWidget::PushScrubbedAudio()
|
||||
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing);
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval), RenderTicketPriority::kHigh));
|
||||
watcher->SetTicket(auto_cacher_->GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -904,7 +942,7 @@ void ViewerWidget::SetDisplayImage(QVariant frame)
|
||||
}
|
||||
}
|
||||
|
||||
RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(RenderTicketPriority priority, bool increment)
|
||||
RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(bool increment)
|
||||
{
|
||||
RenderTicketWatcher *watcher = nullptr;
|
||||
|
||||
@@ -920,19 +958,19 @@ RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(RenderTicketPriority
|
||||
watcher->setProperty("time", QVariant::fromValue(next_time));
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue);
|
||||
queue_watchers_.append(watcher);
|
||||
watcher->SetTicket(GetFrame(next_time, priority));
|
||||
watcher->SetTicket(GetFrame(next_time));
|
||||
}
|
||||
|
||||
return watcher;
|
||||
}
|
||||
|
||||
RenderTicketPtr ViewerWidget::GetFrame(const rational &t, RenderTicketPriority priority)
|
||||
RenderTicketPtr ViewerWidget::GetFrame(const rational &t)
|
||||
{
|
||||
QString cache_fn = GetConnectedNode()->video_frame_cache()->GetValidCacheFilename(t);
|
||||
|
||||
if (!QFileInfo::exists(cache_fn)) {
|
||||
// Frame hasn't been cached, start render job
|
||||
return auto_cacher_->GetSingleFrame(t, priority);
|
||||
return auto_cacher_->GetSingleFrame(t);
|
||||
} else {
|
||||
// Frame has been cached, grab the frame
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
@@ -1000,33 +1038,22 @@ int ViewerWidget::DeterminePlaybackQueueSize()
|
||||
return qMin(max_frames, remaining_frames);
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
{
|
||||
rational new_tb;
|
||||
|
||||
if (ShouldForceWaveform()) {
|
||||
// If we have a node AND video is disconnected AND audio is connected, show waveform view
|
||||
stack_->setCurrentWidget(waveform_view_);
|
||||
//new_tb = GetConnectedNode()->audio_params().time_base();
|
||||
} else {
|
||||
// Otherwise show regular display
|
||||
stack_->setCurrentWidget(sizer_);
|
||||
|
||||
/*if (GetConnectedNode()) {
|
||||
new_tb = GetConnectedNode()->video_params().time_base();
|
||||
}*/
|
||||
}
|
||||
|
||||
/*if (new_tb != timebase()) {
|
||||
SetTimebase(new_tb);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ViewerWidget::ContextMenuSetFullScreen(QAction *action)
|
||||
{
|
||||
SetFullScreen(QGuiApplication::screens().at(action->data().toInt()));
|
||||
}
|
||||
|
||||
void ViewerWidget::ContextMenuSetPlaybackRes(QAction *action)
|
||||
{
|
||||
int div = action->data().toInt();
|
||||
|
||||
auto vp = GetConnectedNode()->GetVideoParams();
|
||||
vp.set_divider(div);
|
||||
|
||||
auto c = new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(GetConnectedNode(), ViewerOutput::kVideoParamsInput, 0)), QVariant::fromValue(vp));
|
||||
Core::instance()->undo_stack()->push(c);
|
||||
}
|
||||
|
||||
void ViewerWidget::ContextMenuDisableSafeMargins()
|
||||
{
|
||||
context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(false));
|
||||
@@ -1102,7 +1129,9 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
|
||||
prequeuing_video_ = false;
|
||||
FinishPlayPreprocess();
|
||||
} else {
|
||||
RequestNextFrameForQueue();
|
||||
// This call was mostly necessary to keep the threads busy between prequeue and playback.
|
||||
// If we only have a single render thread, it's no longer necessary.
|
||||
//RequestNextFrameForQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1182,6 +1211,18 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
connect(full_screen_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuSetFullScreen);
|
||||
}
|
||||
|
||||
{
|
||||
// Playback Resolution Menu
|
||||
Menu *playback_res_menu = new Menu(tr("Playback Resolution"), &menu);
|
||||
menu.addMenu(playback_res_menu);
|
||||
|
||||
for (int d : VideoParams::kSupportedDividers) {
|
||||
playback_res_menu->AddActionWithData(VideoParams::GetNameForDivider(d), d, GetConnectedNode()->GetVideoParams().divider());
|
||||
}
|
||||
|
||||
connect(playback_res_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuSetPlaybackRes);
|
||||
}
|
||||
|
||||
{
|
||||
// Deinterlace Option
|
||||
if (GetConnectedNode()->GetVideoParams().interlacing() != VideoParams::kInterlaceNone) {
|
||||
@@ -1245,11 +1286,14 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
}
|
||||
|
||||
{
|
||||
QAction* show_waveform_action = menu.addAction(tr("Show Audio Waveform"));
|
||||
show_waveform_action->setCheckable(true);
|
||||
show_waveform_action->setChecked(stack_->currentWidget() == waveform_view_);
|
||||
show_waveform_action->setEnabled(!ShouldForceWaveform());
|
||||
connect(show_waveform_action, &QAction::triggered, this, &ViewerWidget::ManualSwitchToWaveform);
|
||||
auto waveform_menu = new Menu(tr("Audio Waveform"), &menu);
|
||||
menu.addMenu(waveform_menu);
|
||||
|
||||
waveform_menu->AddActionWithData(tr("Automatically Show/Hide"), kWFAutomatic, waveform_mode_);
|
||||
waveform_menu->AddActionWithData(tr("Show Waveform Only"), kWFWaveformOnly, waveform_mode_);
|
||||
waveform_menu->AddActionWithData(tr("Show Both Viewer And Waveform"), kWFViewerAndWaveform, waveform_mode_);
|
||||
|
||||
connect(waveform_menu, &Menu::triggered, this, &ViewerWidget::UpdateWaveformModeFromMenu);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1273,9 +1317,9 @@ void ViewerWidget::Play(bool in_to_out_only)
|
||||
{
|
||||
if (in_to_out_only) {
|
||||
if (GetConnectedNode()
|
||||
&& GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
&& GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
// Jump to in point
|
||||
SetTimeAndSignal(GetConnectedNode()->GetTimelinePoints()->workarea()->in());
|
||||
SetTimeAndSignal(GetConnectedNode()->GetWorkArea()->in());
|
||||
} else {
|
||||
in_to_out_only = false;
|
||||
}
|
||||
@@ -1403,11 +1447,11 @@ void ViewerWidget::PlaybackTimerUpdate()
|
||||
min_time = recording_range_.in();
|
||||
max_time = recording_range_.out();
|
||||
|
||||
} else if (play_in_to_out_only_ && GetConnectedNode()->GetTimelinePoints()->workarea()->enabled()) {
|
||||
} else if (play_in_to_out_only_ && GetConnectedNode()->GetWorkArea()->enabled()) {
|
||||
|
||||
// If "play in to out" is enabled or we're looping AND we have a workarea, only play the workarea
|
||||
min_time = GetConnectedNode()->GetTimelinePoints()->workarea()->in();
|
||||
max_time = GetConnectedNode()->GetTimelinePoints()->workarea()->out();
|
||||
min_time = GetConnectedNode()->GetWorkArea()->in();
|
||||
max_time = GetConnectedNode()->GetWorkArea()->out();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -1483,7 +1527,7 @@ void ViewerWidget::PlaybackTimerUpdate()
|
||||
}
|
||||
|
||||
if (IsPlaying()) {
|
||||
while (queue_watchers_.size() < DeterminePlaybackQueueSize()) {
|
||||
while ((int(display_widget_->queue()->size()) + queue_watchers_.size()) < DeterminePlaybackQueueSize()) {
|
||||
if (!RequestNextFrameForQueue()) {
|
||||
// Prevent infinite loop
|
||||
break;
|
||||
@@ -1565,13 +1609,9 @@ void ViewerWidget::ViewerInvalidatedVideoRange(const TimeRange &range)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::ManualSwitchToWaveform(bool e)
|
||||
void ViewerWidget::UpdateWaveformModeFromMenu(QAction *a)
|
||||
{
|
||||
if (e) {
|
||||
stack_->setCurrentWidget(waveform_view_);
|
||||
} else {
|
||||
stack_->setCurrentWidget(sizer_);
|
||||
}
|
||||
SetWaveformMode(static_cast<WaveformMode>(a->data().toInt()));
|
||||
}
|
||||
|
||||
void ViewerWidget::DragEntered(QDragEnterEvent* event)
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "render/previewaudiodevice.h"
|
||||
#include "render/previewautocacher.h"
|
||||
#include "threading/threadticketwatcher.h"
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewersizer.h"
|
||||
#include "viewerwindow.h"
|
||||
@@ -51,6 +50,13 @@ class ViewerWidget : public TimeBasedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum WaveformMode {
|
||||
kWFAutomatic,
|
||||
kWFViewerOnly,
|
||||
kWFWaveformOnly,
|
||||
kWFViewerAndWaveform
|
||||
};
|
||||
|
||||
ViewerWidget(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~ViewerWidget() override;
|
||||
@@ -157,6 +163,8 @@ protected:
|
||||
virtual void ConnectNodeEvent(ViewerOutput *) override;
|
||||
virtual void DisconnectNodeEvent(ViewerOutput *) override;
|
||||
virtual void ConnectedNodeChangeEvent(ViewerOutput *) override;
|
||||
virtual void ConnectedWorkAreaChangeEvent(TimelineWorkArea *) override;
|
||||
virtual void ConnectedMarkersChangeEvent(TimelineMarkerList *) override;
|
||||
|
||||
virtual void ScaleChangedEvent(const double& s) override;
|
||||
|
||||
@@ -195,9 +203,9 @@ private:
|
||||
|
||||
void SetDisplayImage(QVariant frame);
|
||||
|
||||
RenderTicketWatcher *RequestNextFrameForQueue(RenderTicketPriority priority = RenderTicketPriority::kNormal, bool increment = true);
|
||||
RenderTicketWatcher *RequestNextFrameForQueue(bool increment = true);
|
||||
|
||||
RenderTicketPtr GetFrame(const rational& t, RenderTicketPriority priority);
|
||||
RenderTicketPtr GetFrame(const rational& t);
|
||||
|
||||
void FinishPlayPreprocess();
|
||||
|
||||
@@ -221,7 +229,7 @@ private:
|
||||
|
||||
void CloseAudioProcessor();
|
||||
|
||||
QStackedWidget* stack_;
|
||||
void SetWaveformMode(WaveformMode wf);
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
|
||||
@@ -282,6 +290,8 @@ private:
|
||||
|
||||
bool enable_audio_scrubbing_;
|
||||
|
||||
WaveformMode waveform_mode_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
@@ -297,10 +307,12 @@ private slots:
|
||||
|
||||
void SetZoomFromMenu(QAction* action);
|
||||
|
||||
void UpdateStack();
|
||||
void UpdateWaveformViewFromMode();
|
||||
|
||||
void ContextMenuSetFullScreen(QAction* action);
|
||||
|
||||
void ContextMenuSetPlaybackRes(QAction* action);
|
||||
|
||||
void ContextMenuDisableSafeMargins();
|
||||
|
||||
void ContextMenuSetSafeMargins();
|
||||
@@ -315,7 +327,7 @@ private slots:
|
||||
|
||||
void ViewerInvalidatedVideoRange(const olive::TimeRange &range);
|
||||
|
||||
void ManualSwitchToWaveform(bool e);
|
||||
void UpdateWaveformModeFromMenu(QAction *a);
|
||||
|
||||
void DragEntered(QDragEnterEvent* event);
|
||||
|
||||
@@ -336,6 +348,8 @@ private slots:
|
||||
|
||||
void CreateAddableAt(const QRectF &f);
|
||||
|
||||
void HandleFirstRequeueDestroy();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+286
-211
@@ -72,8 +72,6 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
{
|
||||
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::ToolChanged);
|
||||
|
||||
connect(this, &ViewerDisplayWidget::InnerWidgetMouseMove, this, &ViewerDisplayWidget::EmitColorAtCursor);
|
||||
|
||||
// Initializes cursor based on tool
|
||||
UpdateCursor();
|
||||
|
||||
@@ -116,7 +114,7 @@ void ViewerDisplayWidget::UpdateCursor()
|
||||
void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e)
|
||||
{
|
||||
signal_cursor_color_ = e;
|
||||
inner_widget()->setMouseTracking(e);
|
||||
SetInnerMouseTracking(e);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetImage(const QVariant &buffer)
|
||||
@@ -241,185 +239,53 @@ void ViewerDisplayWidget::IncrementSkippedFrames()
|
||||
Core::instance()->ShowStatusBarMessage(tr("%n skipped frame(s) detected during playback", nullptr, frames_skipped_), 10000);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
bool ViewerDisplayWidget::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && Core::instance()->tool() == Tool::kAdd
|
||||
&& (Core::instance()->GetSelectedAddableObject() == Tool::kAddableShape || Core::instance()->GetSelectedAddableObject() == Tool::kAddableTitle)) {
|
||||
|
||||
add_band_start_ = event->pos();
|
||||
|
||||
add_band_ = new QRubberBand(QRubberBand::Rectangle, this);
|
||||
add_band_->setGeometry(QRect(add_band_start_, add_band_start_));
|
||||
add_band_->show();
|
||||
|
||||
} else if (event->button() == Qt::LeftButton && gizmos_
|
||||
&& (gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(),
|
||||
current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) {
|
||||
|
||||
// Handle gizmo click
|
||||
gizmo_start_drag_ = event->pos();
|
||||
gizmo_last_drag_ = gizmo_start_drag_;
|
||||
current_gizmo_->SetGlobals(NodeTraverser::GenerateGlobals(gizmo_params_, GenerateGizmoTime()));
|
||||
|
||||
} else if (IsHandDrag(event)) {
|
||||
|
||||
// Handle hand drag
|
||||
hand_last_drag_pos_ = event->pos();
|
||||
hand_dragging_ = true;
|
||||
emit HandDragStarted();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
|
||||
} else {
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
// Handle standard drag
|
||||
emit DragStarted();
|
||||
}
|
||||
|
||||
super::mousePressEvent(event);
|
||||
|
||||
if (o != this->inner_widget()) {
|
||||
return super::eventFilter(o, e);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
// Handle hand dragging
|
||||
if (hand_dragging_) {
|
||||
|
||||
// Emit movement
|
||||
emit HandDragMoved(event->x() - hand_last_drag_pos_.x(),
|
||||
event->y() - hand_last_drag_pos_.y());
|
||||
|
||||
hand_last_drag_pos_ = event->pos();
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
add_band_->setGeometry(QRect(event->pos(), add_band_start_).normalized());
|
||||
|
||||
} else if (current_gizmo_) {
|
||||
|
||||
// Signal movement
|
||||
if (DraggableGizmo *draggable = dynamic_cast<DraggableGizmo*>(current_gizmo_)) {
|
||||
if (!gizmo_drag_started_) {
|
||||
QPointF start = gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
|
||||
rational gizmo_time = GetGizmoTime();
|
||||
NodeTraverser t;
|
||||
t.SetCacheVideoParams(gizmo_params_);
|
||||
NodeValueRow row = t.GenerateRow(gizmos_, TimeRange(gizmo_time, gizmo_time + gizmo_params_.frame_rate_as_time_base()));
|
||||
|
||||
draggable->DragStart(row, start.x(), start.y(), gizmo_time);
|
||||
gizmo_drag_started_ = true;
|
||||
}
|
||||
|
||||
QPointF v = event->pos() * gizmo_last_draw_transform_inverted_;
|
||||
switch (draggable->GetDragValueBehavior()) {
|
||||
case DraggableGizmo::kAbsolute:
|
||||
// Above value is correct
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromPrevious:
|
||||
v -= gizmo_last_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
gizmo_last_drag_ = event->pos();
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromStart:
|
||||
v -= gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
break;
|
||||
}
|
||||
|
||||
draggable->DragMove(v.x(), v.y(), event->modifiers());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Default behavior
|
||||
super::mouseMoveEvent(event);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (hand_dragging_) {
|
||||
|
||||
// Handle hand drag
|
||||
emit HandDragEnded();
|
||||
hand_dragging_ = false;
|
||||
UpdateCursor();
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
const QRect &band_rect = add_band_->geometry();
|
||||
if (band_rect.width() > 1 && band_rect.height() > 1) {
|
||||
QRectF r = GenerateDisplayTransform().inverted().mapRect(add_band_->geometry());
|
||||
emit CreateAddableAt(r);
|
||||
}
|
||||
|
||||
add_band_->deleteLater();
|
||||
add_band_ = nullptr;
|
||||
|
||||
} else if (current_gizmo_) {
|
||||
|
||||
// Handle gizmo
|
||||
if (gizmo_drag_started_) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
if (DraggableGizmo *draggable = dynamic_cast<DraggableGizmo*>(current_gizmo_)) {
|
||||
draggable->DragEnd(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
gizmo_drag_started_ = false;
|
||||
}
|
||||
current_gizmo_ = nullptr;
|
||||
|
||||
} else {
|
||||
|
||||
// Default behavior
|
||||
super::mouseReleaseEvent(event);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && gizmos_) {
|
||||
QPointF ptr = TransformViewerSpaceToBufferSpace(event->pos());
|
||||
foreach (NodeGizmo *g, gizmos_->GetGizmos()) {
|
||||
if (TextGizmo *text = dynamic_cast<TextGizmo*>(g)) {
|
||||
if (text->GetRect().contains(ptr)) {
|
||||
OpenTextGizmo(text, event);
|
||||
break;
|
||||
}
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
QMouseEvent *mouse = static_cast<QMouseEvent*>(e);
|
||||
if (!(mouse->flags() & Qt::MouseEventCreatedDoubleClick)) {
|
||||
if (OnMousePress(mouse)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
EmitColorAtCursor(static_cast<QMouseEvent*>(e));
|
||||
if (OnMouseMove(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (OnMouseRelease(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
if (OnMouseDoubleClick(static_cast<QMouseEvent*>(e))) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::DragEnter:
|
||||
emit DragEntered(static_cast<QDragEnterEvent*>(e));
|
||||
break;
|
||||
case QEvent::DragLeave:
|
||||
emit DragLeft(static_cast<QDragLeaveEvent*>(e));
|
||||
break;
|
||||
case QEvent::Drop:
|
||||
emit Dropped(static_cast<QDropEvent*>(e));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
super::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
emit DragEntered(event);
|
||||
|
||||
if (!event->isAccepted()) {
|
||||
super::dragEnterEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
emit DragLeft(event);
|
||||
|
||||
if (!event->isAccepted()) {
|
||||
super::dragLeaveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::dropEvent(QDropEvent *event)
|
||||
{
|
||||
emit Dropped(event);
|
||||
|
||||
if (!event->isAccepted()) {
|
||||
super::dropEvent(event);
|
||||
}
|
||||
return super::eventFilter(o, e);
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::OnPaint()
|
||||
@@ -510,7 +376,7 @@ void ViewerDisplayWidget::OnPaint()
|
||||
TimeRange range = GenerateGizmoTime();
|
||||
gizmo_db_ = gt.GenerateRow(gizmos_, range);
|
||||
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
gizmo_last_draw_transform_ = GenerateGizmoTransform(gt, range);
|
||||
p.setWorldTransform(gizmo_last_draw_transform_);
|
||||
|
||||
@@ -524,7 +390,7 @@ void ViewerDisplayWidget::OnPaint()
|
||||
|
||||
// Draw action/title safe areas
|
||||
if (safe_margin_.is_enabled()) {
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
p.setWorldTransform(GenerateWorldTransform());
|
||||
|
||||
p.setPen(QPen(Qt::lightGray, 0));
|
||||
@@ -574,7 +440,7 @@ void ViewerDisplayWidget::OnPaint()
|
||||
}
|
||||
|
||||
if (frame_rate_average_count_ >= frame_rate_averages_.size()) {
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
|
||||
double average = 0.0;
|
||||
for (int i=0; i<frame_rate_averages_.size(); i++) {
|
||||
@@ -582,10 +448,10 @@ void ViewerDisplayWidget::OnPaint()
|
||||
}
|
||||
average /= double(frame_rate_averages_.size());
|
||||
|
||||
DrawTextWithCrudeShadow(&p, inner_widget()->rect(), tr("%1 FPS").arg(QString::number(average, 'f', 1)));
|
||||
DrawTextWithCrudeShadow(&p, GetInnerRect(), tr("%1 FPS").arg(QString::number(average, 'f', 1)));
|
||||
|
||||
if (frames_skipped_ > 0) {
|
||||
DrawTextWithCrudeShadow(&p, inner_widget()->rect().adjusted(0, p.fontMetrics().height(), 0, 0),
|
||||
DrawTextWithCrudeShadow(&p, GetInnerRect().adjusted(0, p.fontMetrics().height(), 0, 0),
|
||||
tr("%1 frames skipped").arg(frames_skipped_));
|
||||
}
|
||||
}
|
||||
@@ -596,7 +462,7 @@ void ViewerDisplayWidget::OnPaint()
|
||||
const QVector<Track*> &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks();
|
||||
|
||||
if (!subtitle_tracklist.empty()) {
|
||||
QPainter p(inner_widget());
|
||||
QPainter p(paint_device());
|
||||
|
||||
QTransform transform = GenerateWorldTransform();
|
||||
QRect bounding_box = transform.mapRect(rect());
|
||||
@@ -641,10 +507,14 @@ void ViewerDisplayWidget::OnPaint()
|
||||
|
||||
void ViewerDisplayWidget::OnDestroy()
|
||||
{
|
||||
renderer()->DestroyNativeShader(deinterlace_shader_);
|
||||
deinterlace_shader_.clear();
|
||||
renderer()->DestroyNativeShader(blank_shader_);
|
||||
blank_shader_.clear();
|
||||
if (!deinterlace_shader_.isNull()) {
|
||||
renderer()->DestroyNativeShader(deinterlace_shader_);
|
||||
deinterlace_shader_.clear();
|
||||
}
|
||||
if (!blank_shader_.isNull()) {
|
||||
renderer()->DestroyNativeShader(blank_shader_);
|
||||
blank_shader_.clear();
|
||||
}
|
||||
|
||||
super::OnDestroy();
|
||||
|
||||
@@ -790,56 +660,261 @@ void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event)
|
||||
{
|
||||
QTransform gizmo_transform = GenerateDisplayTransform();
|
||||
|
||||
ViewerTextEditor *text_edit = new ViewerTextEditor(gizmo_transform.m11(), this);
|
||||
// Create popup container for text and toolbar
|
||||
auto popup = new QWidget(this);
|
||||
popup->setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
|
||||
popup->setAttribute(Qt::WA_DeleteOnClose);
|
||||
popup->setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
// Create text editor
|
||||
ViewerTextEditor *text_edit = new ViewerTextEditor(gizmo_transform.m11(), popup);
|
||||
Html::HtmlToDoc(text_edit->document(), text->GetHtml());
|
||||
text_edit->setProperty("gizmo", reinterpret_cast<quintptr>(text));
|
||||
connect(text_edit, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
|
||||
QRectF transformed_geom = gizmo_transform.map(text->GetRect()).boundingRect();
|
||||
text_edit->setGeometry(transformed_geom.toRect());
|
||||
// Get on screen text rect (this will be the text editor's global geometry)
|
||||
QRect global_text_area = gizmo_transform.map(text->GetRect()).boundingRect().toRect();
|
||||
global_text_area = QRect(mapToGlobal(global_text_area.topLeft()), mapToGlobal(global_text_area.bottomRight()));
|
||||
|
||||
ViewerTextEditorToolBar *toolbar = new ViewerTextEditorToolBar(this);
|
||||
QRect global_popup_area = global_text_area;
|
||||
|
||||
QPoint pos = mapToGlobal(QPoint(transformed_geom.x(), transformed_geom.y() - toolbar->height()));
|
||||
// Create toolbar
|
||||
ViewerTextEditorToolBar *toolbar = new ViewerTextEditorToolBar(popup);
|
||||
text_edit->ConnectToolBar(toolbar);
|
||||
|
||||
// Work out which corner of the text editor to anchor the toolbar to based on screen limitations
|
||||
bool top = true;
|
||||
bool left = true;
|
||||
for (QScreen *screen : qApp->screens()) {
|
||||
if (screen->geometry().contains(pos)) {
|
||||
if (pos.x() + toolbar->width() > screen->geometry().right()) {
|
||||
pos.setX(screen->geometry().right() - toolbar->width());
|
||||
// Look for screen that contains text area
|
||||
if (screen->geometry().contains(global_text_area)) {
|
||||
if (global_text_area.left() + toolbar->width() > screen->geometry().right()) {
|
||||
left = false;
|
||||
}
|
||||
if (global_text_area.top() - toolbar->height() < screen->geometry().top()) {
|
||||
top = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
toolbar->move(pos);
|
||||
toolbar->show();
|
||||
|
||||
text_edit->show();
|
||||
|
||||
connect(text_edit, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
QPoint toolbar_pos;
|
||||
|
||||
text_edit->ConnectToolBar(toolbar);
|
||||
if (top) {
|
||||
global_popup_area.adjust(0, -toolbar->height(), 0, 0);
|
||||
toolbar_pos.setY(0);
|
||||
} else {
|
||||
global_popup_area.adjust(0, 0, 0, toolbar->height());
|
||||
toolbar_pos.setY(global_text_area.height());
|
||||
}
|
||||
|
||||
QPoint text_edit_pos;
|
||||
if (toolbar->width() > global_popup_area.width()) {
|
||||
int diff = toolbar->width() - global_popup_area.width();
|
||||
if (left) {
|
||||
global_popup_area.adjust(0, 0, diff, 0);
|
||||
} else {
|
||||
global_popup_area.adjust(-diff, 0, 0, 0);
|
||||
}
|
||||
toolbar_pos.setX(0);
|
||||
} else {
|
||||
if (left) {
|
||||
toolbar_pos.setX(0);
|
||||
} else {
|
||||
toolbar_pos.setX(global_popup_area.width() - toolbar->width());
|
||||
}
|
||||
}
|
||||
|
||||
toolbar->move(toolbar_pos);
|
||||
|
||||
popup->setGeometry(global_popup_area);
|
||||
|
||||
text_edit->setGeometry(QRect(text_edit->mapFromGlobal(global_text_area.topLeft()), text_edit->mapFromGlobal(global_text_area.bottomRight())));
|
||||
|
||||
popup->show();
|
||||
|
||||
// Store click pos from event so we can use it later to set the initial text cursor position
|
||||
QPoint click_pos;
|
||||
if (event) {
|
||||
text_edit_pos = text_edit->mapFrom(this, event->pos());
|
||||
click_pos = event->globalPos();
|
||||
}
|
||||
|
||||
// Ensure text edit is actually focused rather than the toolbar
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FirstPaint, this, [this, text_edit, text_edit_pos]{
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FirstPaint, this, [text_edit, click_pos]{
|
||||
// Grab focus back from the toolbar
|
||||
this->raise();
|
||||
this->activateWindow();
|
||||
text_edit->setFocus();
|
||||
|
||||
// Start text cursor where the user clicked
|
||||
if (!text_edit_pos.isNull()) {
|
||||
text_edit->setTextCursor(text_edit->cursorForPosition(text_edit_pos));
|
||||
if (!click_pos.isNull()) {
|
||||
text_edit->setTextCursor(text_edit->cursorForPosition(text_edit->mapFromGlobal(click_pos)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event)
|
||||
{
|
||||
if (IsHandDrag(event)) {
|
||||
|
||||
// Handle hand drag
|
||||
hand_last_drag_pos_ = event->pos();
|
||||
hand_dragging_ = true;
|
||||
emit HandDragStarted();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
|
||||
return true;
|
||||
|
||||
} else if (event->button() == Qt::LeftButton) {
|
||||
|
||||
if (Core::instance()->tool() == Tool::kAdd
|
||||
&& (Core::instance()->GetSelectedAddableObject() == Tool::kAddableShape || Core::instance()->GetSelectedAddableObject() == Tool::kAddableTitle)) {
|
||||
|
||||
add_band_start_ = event->pos();
|
||||
|
||||
add_band_ = new QRubberBand(QRubberBand::Rectangle, this);
|
||||
add_band_->setGeometry(QRect(add_band_start_, add_band_start_));
|
||||
add_band_->show();
|
||||
|
||||
} else if (gizmos_
|
||||
&& (gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(),
|
||||
current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) {
|
||||
|
||||
// Handle gizmo click
|
||||
gizmo_start_drag_ = event->pos();
|
||||
gizmo_last_drag_ = gizmo_start_drag_;
|
||||
current_gizmo_->SetGlobals(NodeTraverser::GenerateGlobals(gizmo_params_, GenerateGizmoTime()));
|
||||
|
||||
} else {
|
||||
|
||||
// Handle standard drag
|
||||
emit DragStarted();
|
||||
|
||||
}
|
||||
|
||||
// HACK: On macOS, for some reason the QDockWidget receives focus before the
|
||||
// ViewerTextEditor, causing the editor to close prematurely. However this only
|
||||
// happens the first time the editor receives focus and not subsequent times, so
|
||||
// if we get it to only listen after the first one, this solves the problem.
|
||||
text_edit->SetListenToFocusEvents(true);
|
||||
});
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event)
|
||||
{
|
||||
// Handle hand dragging
|
||||
if (hand_dragging_) {
|
||||
|
||||
// Emit movement
|
||||
emit HandDragMoved(event->x() - hand_last_drag_pos_.x(),
|
||||
event->y() - hand_last_drag_pos_.y());
|
||||
|
||||
hand_last_drag_pos_ = event->pos();
|
||||
|
||||
return true;
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
add_band_->setGeometry(QRect(event->pos(), add_band_start_).normalized());
|
||||
|
||||
return true;
|
||||
|
||||
} else if (current_gizmo_) {
|
||||
|
||||
// Signal movement
|
||||
if (DraggableGizmo *draggable = dynamic_cast<DraggableGizmo*>(current_gizmo_)) {
|
||||
if (!gizmo_drag_started_) {
|
||||
QPointF start = gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
|
||||
rational gizmo_time = GetGizmoTime();
|
||||
NodeTraverser t;
|
||||
t.SetCacheVideoParams(gizmo_params_);
|
||||
NodeValueRow row = t.GenerateRow(gizmos_, TimeRange(gizmo_time, gizmo_time + gizmo_params_.frame_rate_as_time_base()));
|
||||
|
||||
draggable->DragStart(row, start.x(), start.y(), gizmo_time);
|
||||
gizmo_drag_started_ = true;
|
||||
}
|
||||
|
||||
QPointF v = event->pos() * gizmo_last_draw_transform_inverted_;
|
||||
switch (draggable->GetDragValueBehavior()) {
|
||||
case DraggableGizmo::kAbsolute:
|
||||
// Above value is correct
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromPrevious:
|
||||
v -= gizmo_last_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
gizmo_last_drag_ = event->pos();
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromStart:
|
||||
v -= gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
break;
|
||||
}
|
||||
|
||||
draggable->DragMove(v.x(), v.y(), event->modifiers());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnMouseRelease(QMouseEvent *e)
|
||||
{
|
||||
if (hand_dragging_) {
|
||||
|
||||
// Handle hand drag
|
||||
emit HandDragEnded();
|
||||
hand_dragging_ = false;
|
||||
UpdateCursor();
|
||||
|
||||
return true;
|
||||
|
||||
} else if (add_band_) {
|
||||
|
||||
const QRect &band_rect = add_band_->geometry();
|
||||
if (band_rect.width() > 1 && band_rect.height() > 1) {
|
||||
QRectF r = GenerateDisplayTransform().inverted().mapRect(add_band_->geometry());
|
||||
emit CreateAddableAt(r);
|
||||
}
|
||||
|
||||
add_band_->deleteLater();
|
||||
add_band_ = nullptr;
|
||||
|
||||
return true;
|
||||
|
||||
} else if (current_gizmo_) {
|
||||
|
||||
// Handle gizmo
|
||||
if (gizmo_drag_started_) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
if (DraggableGizmo *draggable = dynamic_cast<DraggableGizmo*>(current_gizmo_)) {
|
||||
draggable->DragEnd(command);
|
||||
}
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
gizmo_drag_started_ = false;
|
||||
}
|
||||
current_gizmo_ = nullptr;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::OnMouseDoubleClick(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && gizmos_) {
|
||||
QPointF ptr = TransformViewerSpaceToBufferSpace(event->pos());
|
||||
foreach (NodeGizmo *g, gizmos_->GetGizmos()) {
|
||||
if (TextGizmo *text = dynamic_cast<TextGizmo*>(g)) {
|
||||
if (text->GetRect().contains(ptr)) {
|
||||
OpenTextGizmo(text, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#define VIEWERGLWIDGET_H
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QRubberBand>
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
@@ -131,6 +130,8 @@ public:
|
||||
return &timer_;
|
||||
}
|
||||
|
||||
virtual bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Set the transformation matrix to draw with
|
||||
@@ -216,30 +217,6 @@ signals:
|
||||
|
||||
void CreateAddableAt(const QRectF &rect);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override the mouse press event for the DragStarted() signal and gizmos
|
||||
*/
|
||||
virtual void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
/**
|
||||
* @brief Override mouse move to signal for the pixel sampler and gizmos
|
||||
*/
|
||||
virtual void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
/**
|
||||
* @brief Override mouse release event for gizmos
|
||||
*/
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent* event) override;
|
||||
|
||||
virtual void dropEvent(QDropEvent* event) override;
|
||||
|
||||
protected slots:
|
||||
/**
|
||||
* @brief Paint function to display the texture (received in SetTexture()) on screen.
|
||||
@@ -279,6 +256,13 @@ private:
|
||||
|
||||
void OpenTextGizmo(TextGizmo *text, QMouseEvent *event = nullptr);
|
||||
|
||||
bool OnMousePress(QMouseEvent *e);
|
||||
bool OnMouseMove(QMouseEvent *e);
|
||||
bool OnMouseRelease(QMouseEvent *e);
|
||||
bool OnMouseDoubleClick(QMouseEvent *e);
|
||||
|
||||
void EmitColorAtCursor(QMouseEvent* e);
|
||||
|
||||
/**
|
||||
* @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL().
|
||||
*/
|
||||
@@ -391,8 +375,6 @@ private:
|
||||
bool queue_starved_;
|
||||
|
||||
private slots:
|
||||
void EmitColorAtCursor(QMouseEvent* e);
|
||||
|
||||
void UpdateFromQueue();
|
||||
|
||||
void TextEditChanged();
|
||||
|
||||
@@ -61,7 +61,6 @@ ViewerTextEditor::ViewerTextEditor(double scale, QWidget *parent) :
|
||||
dpi_force_.setDotsPerMeterY(dpm);
|
||||
document()->documentLayout()->setPaintDevice(&dpi_force_);
|
||||
|
||||
connect(qApp, &QApplication::focusChanged, this, &ViewerTextEditor::FocusChanged);
|
||||
connect(this, &QTextEdit::currentCharFormatChanged, this, &ViewerTextEditor::FormatChanged);
|
||||
connect(document(), &QTextDocument::contentsChanged, this, &ViewerTextEditor::DocumentChanged, Qt::QueuedConnection);
|
||||
|
||||
@@ -70,8 +69,6 @@ ViewerTextEditor::ViewerTextEditor(double scale, QWidget *parent) :
|
||||
|
||||
void ViewerTextEditor::ConnectToolBar(ViewerTextEditorToolBar *toolbar)
|
||||
{
|
||||
connect(this, &ViewerTextEditor::destroyed, toolbar, &ViewerTextEditorToolBar::deleteLater);
|
||||
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FamilyChanged, this, &ViewerTextEditor::SetFamily);
|
||||
connect(toolbar, &ViewerTextEditorToolBar::SizeChanged, this, &ViewerTextEditor::setFontPointSize);
|
||||
connect(toolbar, &ViewerTextEditorToolBar::StyleChanged, this, &ViewerTextEditor::SetStyle);
|
||||
@@ -94,15 +91,6 @@ void ViewerTextEditor::ConnectToolBar(ViewerTextEditorToolBar *toolbar)
|
||||
toolbars_.append(toolbar);
|
||||
}
|
||||
|
||||
void ViewerTextEditor::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
super::keyPressEvent(event);
|
||||
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerTextEditor::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
QPainter p(this->viewport());
|
||||
@@ -134,7 +122,9 @@ void ViewerTextEditor::paintEvent(QPaintEvent *e)
|
||||
ctx.selections.append(selection);
|
||||
}
|
||||
|
||||
transparent_clone_->documentLayout()->draw(&p, ctx);
|
||||
if (transparent_clone_) {
|
||||
transparent_clone_->documentLayout()->draw(&p, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerTextEditor::UpdateToolBar(ViewerTextEditorToolBar *toolbar, const QTextCharFormat &f, const QTextBlockFormat &b, Qt::Alignment alignment)
|
||||
@@ -176,34 +166,6 @@ void ViewerTextEditor::UpdateToolBar(ViewerTextEditorToolBar *toolbar, const QTe
|
||||
toolbar->SetLineHeight(b.lineHeight() == 0.0 ? 100 : b.lineHeight());
|
||||
}
|
||||
|
||||
void ViewerTextEditor::FocusChanged(QWidget *old, QWidget *now)
|
||||
{
|
||||
if (!listen_to_focus_events_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget *test = now;
|
||||
|
||||
if (!test) {
|
||||
// Ignore null focuses because that could be one of the toolbar widgets simply losing focus
|
||||
// and that would be undesirable to close the text editor from
|
||||
return;
|
||||
}
|
||||
|
||||
while (test) {
|
||||
if (test == this
|
||||
|| dynamic_cast<ViewerTextEditorToolBar*>(test)
|
||||
|| dynamic_cast<SliderLadder*>(test)) {
|
||||
return;
|
||||
}
|
||||
|
||||
test = test->parentWidget();
|
||||
}
|
||||
|
||||
// If we didn't return in the loop, the user must have focused on something else
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void ViewerTextEditor::FormatChanged(const QTextCharFormat &f)
|
||||
{
|
||||
if (!block_update_toolbar_signal_) {
|
||||
@@ -323,8 +285,9 @@ void ViewerTextEditor::DocumentChanged()
|
||||
}
|
||||
|
||||
ViewerTextEditorToolBar::ViewerTextEditorToolBar(QWidget *parent) :
|
||||
QWidget(parent, Qt::Tool | Qt::FramelessWindowHint),
|
||||
painted_(false)
|
||||
QWidget(parent),
|
||||
painted_(false),
|
||||
drag_enabled_(false)
|
||||
{
|
||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
||||
outer_layout->setSpacing(0);
|
||||
@@ -509,7 +472,7 @@ void ViewerTextEditorToolBar::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QWidget::mousePressEvent(event);
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (event->button() == Qt::LeftButton && drag_enabled_) {
|
||||
drag_anchor_ = event->pos();
|
||||
}
|
||||
}
|
||||
@@ -518,7 +481,7 @@ void ViewerTextEditorToolBar::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
QWidget::mouseMoveEvent(event);
|
||||
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
if ((event->buttons() & Qt::LeftButton) && drag_enabled_) {
|
||||
this->move(mapToParent(QPoint(event->pos() - drag_anchor_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ private:
|
||||
|
||||
bool painted_;
|
||||
|
||||
bool drag_enabled_;
|
||||
|
||||
private slots:
|
||||
void UpdateFontStyleList(const QString &family);
|
||||
|
||||
@@ -144,8 +146,6 @@ public:
|
||||
void SetListenToFocusEvents(bool e) { listen_to_focus_events_ = e; }
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
@@ -166,8 +166,6 @@ private:
|
||||
bool listen_to_focus_events_;
|
||||
|
||||
private slots:
|
||||
void FocusChanged(QWidget *old, QWidget *now);
|
||||
|
||||
void FormatChanged(const QTextCharFormat &f);
|
||||
|
||||
void SetFamily(const QString &s);
|
||||
|
||||
Reference in New Issue
Block a user