Merge branch 'master' into hashremoval
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -87,7 +87,7 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl
|
||||
if (!streams.isEmpty()) {
|
||||
data_stream << streams << reinterpret_cast<quintptr>(GetConnectedNode());
|
||||
|
||||
mimedata->setData(QStringLiteral("application/x-oliveprojectitemdata"), encoded_data);
|
||||
mimedata->setData(Project::kItemMimeType, encoded_data);
|
||||
drag->setMimeData(mimedata);
|
||||
|
||||
drag->exec();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -37,12 +37,15 @@
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/generator/shape/shapenodebase.h"
|
||||
#include "node/project/project.h"
|
||||
#include "render/rendermanager.h"
|
||||
#include "task/taskmanager.h"
|
||||
#include "viewerpreventsleep.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
#include "widget/timelinewidget/tool/add.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -92,7 +95,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
connect(display_widget_, &ViewerDisplayWidget::DragEntered, this, &ViewerWidget::DragEntered);
|
||||
connect(display_widget_, &ViewerDisplayWidget::Dropped, this, &ViewerWidget::Dropped);
|
||||
connect(display_widget_, &ViewerDisplayWidget::TextureChanged, this, &ViewerWidget::TextureChanged);
|
||||
connect(display_widget_, &ViewerDisplayWidget::QueueStarved, this, &ViewerWidget::ForceRequeueFromCurrentTime);
|
||||
connect(display_widget_, &ViewerDisplayWidget::QueueStarved, this, &ViewerWidget::QueueStarved);
|
||||
connect(display_widget_, &ViewerDisplayWidget::QueueNoLongerStarved, this, &ViewerWidget::QueueNoLongerStarved);
|
||||
connect(display_widget_, &ViewerDisplayWidget::CreateAddableAt, this, &ViewerWidget::CreateAddableAt);
|
||||
connect(sizer_, &ViewerSizer::RequestScale, display_widget_, &ViewerDisplayWidget::SetMatrixZoom);
|
||||
connect(sizer_, &ViewerSizer::RequestTranslate, display_widget_, &ViewerDisplayWidget::SetMatrixTranslate);
|
||||
connect(display_widget_, &ViewerDisplayWidget::HandDragMoved, sizer_, &ViewerSizer::HandDragMove);
|
||||
@@ -476,6 +481,48 @@ void ViewerWidget::UpdateAudioProcessor()
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::CreateAddableAt(const QRectF &f)
|
||||
{
|
||||
if (Sequence *s = dynamic_cast<Sequence*>(GetConnectedNode())) {
|
||||
Track::Type type = Track::kVideo;
|
||||
int track_index = -1;
|
||||
TrackList *list = s->track_list(type);
|
||||
const rational &in = GetTime();
|
||||
rational length = OLIVE_CONFIG("DefaultStillLength").value<rational>();
|
||||
rational out = in + length;
|
||||
|
||||
// Find a free track where we won't overwrite anything
|
||||
while (true) {
|
||||
track_index++;
|
||||
|
||||
if (track_index >= list->GetTrackCount()) {
|
||||
// Just create a new track
|
||||
break;
|
||||
}
|
||||
|
||||
Track *track = list->GetTrackAt(track_index);
|
||||
if (track->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Block *b = track->NearestBlockBeforeOrAt(in);
|
||||
if (!b || (dynamic_cast<GapBlock*>(b) && b->out() >= out)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
Node *clip = AddTool::CreateAddableClip(command, s, Track::Reference(type, track_index), in, length);
|
||||
|
||||
if (ShapeNodeBase *shape = dynamic_cast<ShapeNodeBase*>(clip)) {
|
||||
shape->SetRect(f, s->GetVideoParams(), command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
SetGizmos(clip);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::CloseAudioProcessor()
|
||||
{
|
||||
audio_processor_.Close();
|
||||
@@ -499,7 +546,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), true));
|
||||
watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(audio_playback_queue_time_, queue_end), RenderTicketPriority::kHigh));
|
||||
|
||||
audio_playback_queue_time_ = queue_end;
|
||||
}
|
||||
@@ -590,6 +637,24 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
||||
delete watcher;
|
||||
}
|
||||
|
||||
void ViewerWidget::QueueStarved()
|
||||
{
|
||||
static const int kMaximumWaitTime = 250;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
if (!queue_starved_start_) {
|
||||
queue_starved_start_ = now;
|
||||
} else if (now > queue_starved_start_ + kMaximumWaitTime) {
|
||||
ForceRequeueFromCurrentTime();
|
||||
queue_starved_start_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::QueueNoLongerStarved()
|
||||
{
|
||||
queue_starved_start_ = 0;
|
||||
}
|
||||
|
||||
void ViewerWidget::ForceRequeueFromCurrentTime()
|
||||
{
|
||||
ClearVideoAutoCacherQueue();
|
||||
@@ -629,7 +694,7 @@ void ViewerWidget::UpdateTextureFromNode()
|
||||
ClearVideoAutoCacherQueue();
|
||||
}
|
||||
|
||||
watcher->SetTicket(GetFrame(time, true));
|
||||
watcher->SetTicket(GetFrame(time, RenderTicketPriority::kHigh));
|
||||
} else {
|
||||
// There is definitely no frame here, we can immediately flip to showing nothing
|
||||
nonqueue_watchers_.clear();
|
||||
@@ -686,6 +751,8 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
|
||||
controls_->ShowPauseButton();
|
||||
|
||||
queue_starved_start_ = 0;
|
||||
|
||||
// Attempt to fill playback queue
|
||||
if (display_widget_->isVisible() || !windows_.isEmpty()) {
|
||||
prequeue_length_ = DeterminePlaybackQueueSize();
|
||||
@@ -702,7 +769,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
|
||||
for (int i=0; i<prequeue_length_; i++) {
|
||||
playback_queue_next_frame_ -= playback_speed_;
|
||||
RequestNextFrameForQueue(true, false);
|
||||
RequestNextFrameForQueue(RenderTicketPriority::kHigh, false);
|
||||
}
|
||||
|
||||
playback_queue_next_frame_ = temp;
|
||||
@@ -786,7 +853,7 @@ void ViewerWidget::PushScrubbedAudio()
|
||||
|
||||
RenderTicketWatcher *watcher = new RenderTicketWatcher();
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing);
|
||||
watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval), true));
|
||||
watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval), RenderTicketPriority::kHigh));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -836,7 +903,7 @@ void ViewerWidget::SetDisplayImage(QVariant frame)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment)
|
||||
void ViewerWidget::RequestNextFrameForQueue(RenderTicketPriority priority, bool increment)
|
||||
{
|
||||
rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
@@ -850,17 +917,17 @@ void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment)
|
||||
watcher->setProperty("time", QVariant::fromValue(next_time));
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue);
|
||||
queue_watchers_.append(watcher);
|
||||
watcher->SetTicket(GetFrame(next_time, prioritize));
|
||||
watcher->SetTicket(GetFrame(next_time, priority));
|
||||
}
|
||||
}
|
||||
|
||||
RenderTicketPtr ViewerWidget::GetFrame(const rational &t, bool prioritize)
|
||||
RenderTicketPtr ViewerWidget::GetFrame(const rational &t, RenderTicketPriority priority)
|
||||
{
|
||||
QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(t);
|
||||
|
||||
if (!QFileInfo::exists(cache_fn)) {
|
||||
// Frame hasn't been cached, start render job
|
||||
return auto_cacher_.GetSingleFrame(t, prioritize);
|
||||
return auto_cacher_.GetSingleFrame(t, priority);
|
||||
} else {
|
||||
// Frame has been cached, grab the frame
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
@@ -1508,14 +1575,14 @@ void ViewerWidget::ViewerShiftedRange(const rational &from, const rational &to)
|
||||
|
||||
void ViewerWidget::DragEntered(QDragEnterEvent* event)
|
||||
{
|
||||
if (event->mimeData()->formats().contains(QStringLiteral("application/x-oliveprojectitemdata"))) {
|
||||
if (event->mimeData()->formats().contains(Project::kItemMimeType)) {
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::Dropped(QDropEvent *event)
|
||||
{
|
||||
QByteArray mimedata = event->mimeData()->data(QStringLiteral("application/x-oliveprojectitemdata"));
|
||||
QByteArray mimedata = event->mimeData()->data(Project::kItemMimeType);
|
||||
QDataStream stream(&mimedata, QIODevice::ReadOnly);
|
||||
|
||||
// Variables to deserialize into
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -119,6 +119,11 @@ public slots:
|
||||
|
||||
void UpdateTextureFromNode();
|
||||
|
||||
void RequestStartEditingText()
|
||||
{
|
||||
display_widget_->RequestStartEditingText();
|
||||
}
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::CursorColor()
|
||||
@@ -185,9 +190,9 @@ private:
|
||||
|
||||
void SetDisplayImage(QVariant frame);
|
||||
|
||||
void RequestNextFrameForQueue(bool prioritize = false, bool increment = true);
|
||||
void RequestNextFrameForQueue(RenderTicketPriority priority = RenderTicketPriority::kNormal, bool increment = true);
|
||||
|
||||
RenderTicketPtr GetFrame(const rational& t, bool prioritize);
|
||||
RenderTicketPtr GetFrame(const rational& t, RenderTicketPriority priority);
|
||||
|
||||
void FinishPlayPreprocess();
|
||||
|
||||
@@ -269,6 +274,8 @@ private:
|
||||
Track::Reference recording_track_;
|
||||
QString recording_filename_;
|
||||
|
||||
qint64 queue_starved_start_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
@@ -316,10 +323,15 @@ private slots:
|
||||
|
||||
void ReceivedAudioBufferForScrubbing();
|
||||
|
||||
void QueueStarved();
|
||||
void QueueNoLongerStarved();
|
||||
|
||||
void ForceRequeueFromCurrentTime();
|
||||
|
||||
void UpdateAudioProcessor();
|
||||
|
||||
void CreateAddableAt(const QRectF &f);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -45,8 +45,6 @@
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
#include "node/gizmo/screen.h"
|
||||
#include "node/gizmo/text.h"
|
||||
#include "node/traverser.h"
|
||||
#include "viewertexteditor.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
@@ -68,9 +66,11 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
show_fps_(false),
|
||||
frames_skipped_(0),
|
||||
show_widget_background_(false),
|
||||
push_mode_(kPushNull)
|
||||
push_mode_(kPushNull),
|
||||
add_band_(nullptr),
|
||||
queue_starved_(false)
|
||||
{
|
||||
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::UpdateCursor);
|
||||
connect(Core::instance(), &Core::ToolChanged, this, &ViewerDisplayWidget::ToolChanged);
|
||||
|
||||
connect(this, &ViewerDisplayWidget::InnerWidgetMouseMove, this, &ViewerDisplayWidget::EmitColorAtCursor);
|
||||
|
||||
@@ -106,6 +106,8 @@ void ViewerDisplayWidget::UpdateCursor()
|
||||
{
|
||||
if (Core::instance()->tool() == Tool::kHand) {
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
} else if (Core::instance()->tool() == Tool::kAdd) {
|
||||
setCursor(Qt::CrossCursor);
|
||||
} else {
|
||||
unsetCursor();
|
||||
}
|
||||
@@ -137,6 +139,11 @@ void ViewerDisplayWidget::SetBlank()
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::ToolChanged()
|
||||
{
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetDeinterlacing(bool e)
|
||||
{
|
||||
deinterlace_ = e;
|
||||
@@ -214,7 +221,7 @@ QPointF ViewerDisplayWidget::TransformViewerSpaceToBufferSpace(const QPointF &po
|
||||
* Inversion will only fail if the viewer has been scaled by 0 in any direction
|
||||
* which I think should never happen.
|
||||
*/
|
||||
return pos * GenerateGizmoTransform().inverted();
|
||||
return pos * GenerateDisplayTransform().inverted();
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::ResetFPSTimer()
|
||||
@@ -236,8 +243,18 @@ void ViewerDisplayWidget::IncrementSkippedFrames()
|
||||
|
||||
void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton && gizmos_
|
||||
&& (current_gizmo_ = TryGizmoPress(gizmo_db_, TransformViewerSpaceToBufferSpace(event->pos())))) {
|
||||
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();
|
||||
@@ -275,12 +292,16 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
|
||||
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 = TransformViewerSpaceToBufferSpace(gizmo_start_drag_);
|
||||
QPointF start = gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
|
||||
rational gizmo_time = GetGizmoTime();
|
||||
NodeTraverser t;
|
||||
@@ -291,17 +312,17 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
gizmo_drag_started_ = true;
|
||||
}
|
||||
|
||||
QPointF v = TransformViewerSpaceToBufferSpace(event->pos());
|
||||
QPointF v = event->pos() * gizmo_last_draw_transform_inverted_;
|
||||
switch (draggable->GetDragValueBehavior()) {
|
||||
case DraggableGizmo::kAbsolute:
|
||||
// Above value is correct
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromPrevious:
|
||||
v -= TransformViewerSpaceToBufferSpace(gizmo_last_drag_);
|
||||
v -= gizmo_last_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
gizmo_last_drag_ = event->pos();
|
||||
break;
|
||||
case DraggableGizmo::kDeltaFromStart:
|
||||
v -= TransformViewerSpaceToBufferSpace(gizmo_start_drag_);
|
||||
v -= gizmo_start_drag_ * gizmo_last_draw_transform_inverted_;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -325,6 +346,17 @@ void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
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
|
||||
@@ -353,53 +385,7 @@ void ViewerDisplayWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
foreach (NodeGizmo *g, gizmos_->GetGizmos()) {
|
||||
if (TextGizmo *text = dynamic_cast<TextGizmo*>(g)) {
|
||||
if (text->GetRect().contains(ptr)) {
|
||||
QTransform gizmo_transform = GenerateGizmoTransform();
|
||||
|
||||
ViewerTextEditor *text_edit = new ViewerTextEditor(gizmo_transform.m11(), this);
|
||||
Html::HtmlToDoc(text_edit->document(), text->GetHtml());
|
||||
text_edit->setProperty("gizmo", reinterpret_cast<quintptr>(text));
|
||||
|
||||
QRectF transformed_geom = gizmo_transform.map(text->GetRect()).boundingRect();
|
||||
text_edit->setGeometry(transformed_geom.toRect());
|
||||
|
||||
ViewerTextEditorToolBar *toolbar = new ViewerTextEditorToolBar(this);
|
||||
|
||||
QPoint pos = mapToGlobal(QPoint(transformed_geom.x(), transformed_geom.y() - toolbar->height()));
|
||||
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());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
toolbar->move(pos);
|
||||
toolbar->show();
|
||||
|
||||
text_edit->show();
|
||||
|
||||
connect(text_edit, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
|
||||
text_edit->ConnectToolBar(toolbar);
|
||||
|
||||
QPoint text_edit_pos = text_edit->mapFrom(this, event->pos());
|
||||
|
||||
// Ensure text edit is actually focused rather than the toolbar
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FirstPaint, this, [this, text_edit, text_edit_pos]{
|
||||
// Grab focus back from the toolbar
|
||||
this->raise();
|
||||
this->activateWindow();
|
||||
text_edit->setFocus();
|
||||
|
||||
// Start text cursor where the user clicked
|
||||
text_edit->setTextCursor(text_edit->cursorForPosition(text_edit_pos));
|
||||
|
||||
// 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);
|
||||
});
|
||||
OpenTextGizmo(text, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -525,7 +511,8 @@ void ViewerDisplayWidget::OnPaint()
|
||||
gizmo_db_ = gt.GenerateRow(gizmos_, range);
|
||||
|
||||
QPainter p(inner_widget());
|
||||
p.setWorldTransform(GenerateGizmoTransform());
|
||||
gizmo_last_draw_transform_ = GenerateGizmoTransform(gt, range);
|
||||
p.setWorldTransform(gizmo_last_draw_transform_);
|
||||
|
||||
gizmos_->UpdateGizmoPositions(gizmo_db_, NodeTraverser::GenerateGlobals(gizmo_params_, range));
|
||||
foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) {
|
||||
@@ -737,7 +724,7 @@ QTransform ViewerDisplayWidget::GenerateWorldTransform()
|
||||
return world;
|
||||
}
|
||||
|
||||
QTransform ViewerDisplayWidget::GenerateGizmoTransform()
|
||||
QTransform ViewerDisplayWidget::GenerateDisplayTransform()
|
||||
{
|
||||
QVector2D viewer_scale(GetTexturePosition(size()));
|
||||
QTransform gizmo_transform = GenerateWorldTransform();
|
||||
@@ -746,13 +733,39 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform()
|
||||
return gizmo_transform;
|
||||
}
|
||||
|
||||
QTransform ViewerDisplayWidget::GenerateGizmoTransform(NodeTraverser >, const TimeRange &range)
|
||||
{
|
||||
QTransform t = GenerateDisplayTransform();
|
||||
if (GetTimeTarget()) {
|
||||
Node *target = GetTimeTarget();
|
||||
if (ViewerOutput *v = dynamic_cast<ViewerOutput *>(target)) {
|
||||
if (Node *n = v->GetConnectedTextureOutput()) {
|
||||
target = n;
|
||||
}
|
||||
}
|
||||
|
||||
QTransform nt;
|
||||
gt.Transform(&nt, gizmos_, target, range);
|
||||
|
||||
t.translate(gizmo_params_.width()*0.5, gizmo_params_.height()*0.5);
|
||||
t.scale(gizmo_params_.width(), gizmo_params_.height());
|
||||
|
||||
t = nt * t;
|
||||
|
||||
t.scale(1.0 / gizmo_params_.width(), 1.0 / gizmo_params_.height());
|
||||
t.translate(-gizmo_params_.width()*0.5, -gizmo_params_.height()*0.5);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPointF &p)
|
||||
{
|
||||
for (auto it=gizmos_->GetGizmos().crbegin(); it!=gizmos_->GetGizmos().crend(); it++) {
|
||||
NodeGizmo *gizmo = *it;
|
||||
if (gizmo->IsVisible()) {
|
||||
if (PointGizmo *point = dynamic_cast<PointGizmo*>(gizmo)) {
|
||||
if (point->GetClickingRect(GenerateGizmoTransform()).contains(p)) {
|
||||
if (point->GetClickingRect(gizmo_last_draw_transform_).contains(p)) {
|
||||
return point;
|
||||
}
|
||||
} else if (PolygonGizmo *poly = dynamic_cast<PolygonGizmo*>(gizmo)) {
|
||||
@@ -773,6 +786,62 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPo
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event)
|
||||
{
|
||||
QTransform gizmo_transform = GenerateDisplayTransform();
|
||||
|
||||
ViewerTextEditor *text_edit = new ViewerTextEditor(gizmo_transform.m11(), this);
|
||||
Html::HtmlToDoc(text_edit->document(), text->GetHtml());
|
||||
text_edit->setProperty("gizmo", reinterpret_cast<quintptr>(text));
|
||||
|
||||
QRectF transformed_geom = gizmo_transform.map(text->GetRect()).boundingRect();
|
||||
text_edit->setGeometry(transformed_geom.toRect());
|
||||
|
||||
ViewerTextEditorToolBar *toolbar = new ViewerTextEditorToolBar(this);
|
||||
|
||||
QPoint pos = mapToGlobal(QPoint(transformed_geom.x(), transformed_geom.y() - toolbar->height()));
|
||||
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());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
toolbar->move(pos);
|
||||
toolbar->show();
|
||||
|
||||
text_edit->show();
|
||||
|
||||
connect(text_edit, &ViewerTextEditor::textChanged, this, &ViewerDisplayWidget::TextEditChanged);
|
||||
|
||||
text_edit->ConnectToolBar(toolbar);
|
||||
|
||||
QPoint text_edit_pos;
|
||||
if (event) {
|
||||
text_edit_pos = text_edit->mapFrom(this, event->pos());
|
||||
}
|
||||
|
||||
// Ensure text edit is actually focused rather than the toolbar
|
||||
connect(toolbar, &ViewerTextEditorToolBar::FirstPaint, this, [this, text_edit, text_edit_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));
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
|
||||
{
|
||||
// Do this no matter what, emits signal to any pixel samplers
|
||||
@@ -780,7 +849,7 @@ void ViewerDisplayWidget::EmitColorAtCursor(QMouseEvent *e)
|
||||
Color reference, display;
|
||||
|
||||
if (texture_) {
|
||||
QPointF pixel_pos = GenerateGizmoTransform().inverted().map(e->pos());
|
||||
QPointF pixel_pos = GenerateDisplayTransform().inverted().map(e->pos());
|
||||
pixel_pos /= texture_->params().divider();
|
||||
|
||||
reference = renderer()->GetPixelFromTexture(texture_.get(), pixel_pos);
|
||||
@@ -798,6 +867,18 @@ void ViewerDisplayWidget::SetShowFPS(bool e)
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::RequestStartEditingText()
|
||||
{
|
||||
if (gizmos_) {
|
||||
foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) {
|
||||
if (TextGizmo *text = dynamic_cast<TextGizmo*>(gizmo)) {
|
||||
OpenTextGizmo(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::Play(const int64_t &start_timestamp, const int &playback_speed, const rational &timebase)
|
||||
{
|
||||
playback_timebase_ = timebase;
|
||||
@@ -814,6 +895,7 @@ void ViewerDisplayWidget::Pause()
|
||||
disconnect(this, &ViewerDisplayWidget::frameSwapped, this, &ViewerDisplayWidget::UpdateFromQueue);
|
||||
|
||||
queue_.clear();
|
||||
queue_starved_ = false;
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::UpdateFromQueue()
|
||||
@@ -825,6 +907,7 @@ void ViewerDisplayWidget::UpdateFromQueue()
|
||||
bool popped = false;
|
||||
|
||||
if (queue_.empty()) {
|
||||
queue_starved_ = true;
|
||||
emit QueueStarved();
|
||||
} else {
|
||||
while (!queue_.empty()) {
|
||||
@@ -834,6 +917,11 @@ void ViewerDisplayWidget::UpdateFromQueue()
|
||||
|
||||
// Frame was in queue, no need to decode anything
|
||||
SetImage(pf.frame);
|
||||
|
||||
if (queue_starved_) {
|
||||
queue_starved_ = false;
|
||||
emit QueueNoLongerStarved();
|
||||
}
|
||||
return;
|
||||
|
||||
} else if (pf.timestamp > time) {
|
||||
@@ -856,6 +944,7 @@ void ViewerDisplayWidget::UpdateFromQueue()
|
||||
}
|
||||
|
||||
if (queue_.empty()) {
|
||||
queue_starved_ = true;
|
||||
emit QueueStarved();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,12 +21,15 @@
|
||||
#ifndef VIEWERGLWIDGET_H
|
||||
#define VIEWERGLWIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QMatrix4x4>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QRubberBand>
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/gizmo/text.h"
|
||||
#include "node/node.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "node/traverser.h"
|
||||
#include "render/color.h"
|
||||
#include "tool/tool.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
@@ -162,6 +165,8 @@ public slots:
|
||||
*/
|
||||
void UpdateCursor();
|
||||
|
||||
void ToolChanged();
|
||||
|
||||
/**
|
||||
* @brief Enables/disables a basic deinterlace on the viewer
|
||||
*/
|
||||
@@ -169,6 +174,8 @@ public slots:
|
||||
|
||||
void SetShowFPS(bool e);
|
||||
|
||||
void RequestStartEditingText();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when the user starts dragging from the viewer
|
||||
@@ -205,6 +212,10 @@ signals:
|
||||
|
||||
void QueueStarved();
|
||||
|
||||
void QueueNoLongerStarved();
|
||||
|
||||
void CreateAddableAt(const QRectF &rect);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override the mouse press event for the DragStarted() signal and gizmos
|
||||
@@ -254,7 +265,9 @@ private:
|
||||
|
||||
QTransform GenerateWorldTransform();
|
||||
|
||||
QTransform GenerateGizmoTransform();
|
||||
QTransform GenerateDisplayTransform();
|
||||
|
||||
QTransform GenerateGizmoTransform(NodeTraverser >, const TimeRange &range);
|
||||
|
||||
TimeRange GenerateGizmoTime()
|
||||
{
|
||||
@@ -264,6 +277,8 @@ private:
|
||||
|
||||
NodeGizmo *TryGizmoPress(const NodeValueRow &row, const QPointF &p);
|
||||
|
||||
void OpenTextGizmo(TextGizmo *text, QMouseEvent *event = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL().
|
||||
*/
|
||||
@@ -316,6 +331,8 @@ private:
|
||||
QPoint gizmo_last_drag_;
|
||||
NodeGizmo *current_gizmo_;
|
||||
bool gizmo_drag_started_;
|
||||
QTransform gizmo_last_draw_transform_;
|
||||
QTransform gizmo_last_draw_transform_inverted_;
|
||||
|
||||
bool show_subtitles_;
|
||||
Sequence *subtitle_tracks_;
|
||||
@@ -366,6 +383,11 @@ private:
|
||||
|
||||
rational playback_timebase_;
|
||||
|
||||
QRubberBand *add_band_;
|
||||
QPoint add_band_start_;
|
||||
|
||||
bool queue_starved_;
|
||||
|
||||
private slots:
|
||||
void EmitColorAtCursor(QMouseEvent* e);
|
||||
|
||||
@@ -375,6 +397,7 @@ private slots:
|
||||
|
||||
void SubtitlesChanged(const TimeRange &r);
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -64,6 +64,8 @@ ViewerTextEditor::ViewerTextEditor(double scale, QWidget *parent) :
|
||||
connect(qApp, &QApplication::focusChanged, this, &ViewerTextEditor::FocusChanged);
|
||||
connect(this, &QTextEdit::currentCharFormatChanged, this, &ViewerTextEditor::FormatChanged);
|
||||
connect(document(), &QTextDocument::contentsChanged, this, &ViewerTextEditor::DocumentChanged, Qt::QueuedConnection);
|
||||
|
||||
setAcceptRichText(false);
|
||||
}
|
||||
|
||||
void ViewerTextEditor::ConnectToolBar(ViewerTextEditorToolBar *toolbar)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
Copyright (C) 2022 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
Reference in New Issue
Block a user