This commit is contained in:
itsmattkc
2020-06-26 00:22:03 +10:00
71 changed files with 955 additions and 533 deletions
+1
View File
@@ -107,6 +107,7 @@ else()
-Wall
-Wextra
-Wno-unused-parameter
-Wshadow
)
endif()
+6 -3
View File
@@ -231,10 +231,13 @@ void AudioVisualWaveform::DrawSample(QPainter *painter, const QVector<SamplePerC
int channel_half_height = channel_height / 2;
for (int i=0;i<sample.size();i++) {
qfloat16 max = qMin(sample.at(i).max, static_cast<qfloat16>(1.0f));
qfloat16 min = qMax(sample.at(i).min, static_cast<qfloat16>(-1.0));
if (Config::Current()[QStringLiteral("RectifiedWaveforms")].toBool()) {
int channel_bottom = y + channel_height * (i + 1);
int diff = qRound((sample.at(i).max - sample.at(i).min) * channel_half_height);
int diff = qRound((max - min) * channel_half_height);
painter->drawLine(x,
channel_bottom - diff,
@@ -244,9 +247,9 @@ void AudioVisualWaveform::DrawSample(QPainter *painter, const QVector<SamplePerC
int channel_mid = y + channel_height * i + channel_half_height;
painter->drawLine(x,
channel_mid + qRound(sample.at(i).min * static_cast<float>(channel_half_height)),
channel_mid + qRound(min * static_cast<float>(channel_half_height)),
x,
channel_mid + qRound(sample.at(i).max * static_cast<float>(channel_half_height)));
channel_mid + qRound(max * static_cast<float>(channel_half_height)));
}
}
}
+38 -41
View File
@@ -151,12 +151,39 @@ int Core::execute(QCoreApplication* a)
return exit_code;
}
void Core::DeclareTypesForQt()
{
qRegisterMetaType<rational>();
qRegisterMetaType<OpenGLTexturePtr>();
qRegisterMetaType<OpenGLTextureCache::ReferencePtr>();
qRegisterMetaType<NodeValue>();
qRegisterMetaType<NodeValueTable>();
qRegisterMetaType<NodeValueDatabase>();
qRegisterMetaType<FramePtr>();
qRegisterMetaType<SampleBufferPtr>();
qRegisterMetaType<AudioParams>();
qRegisterMetaType<NodeKeyframe::Type>();
qRegisterMetaType<Decoder::RetrieveState>();
qRegisterMetaType<OLIVE_NAMESPACE::TimeRange>();
qRegisterMetaType<Color>();
qRegisterMetaType<OLIVE_NAMESPACE::ProjectPtr>();
qRegisterMetaType<OLIVE_NAMESPACE::AudioVisualWaveform>();
qRegisterMetaType<OLIVE_NAMESPACE::SampleJob>();
qRegisterMetaType<OLIVE_NAMESPACE::ShaderJob>();
qRegisterMetaType<OLIVE_NAMESPACE::GenerateJob>();
qRegisterMetaType<OLIVE_NAMESPACE::VideoParams>();
qRegisterMetaType<OLIVE_NAMESPACE::MainWindowLayoutInfo>();
}
void Core::Start()
{
// Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes
// the fact that some of the config paths set by default rely on the app name having been set (in main())
Config::Current().SetDefaults();
// Load application config
Config::Load();
// Declare custom types for Qt signal/slot system
DeclareTypesForQt();
@@ -169,9 +196,8 @@ void Core::Start()
// Initialize task manager
TaskManager::CreateInstance();
// Load application config
Config::Load();
// Initialize OpenGL service
OpenGLProxy::CreateInstance();
//
// Start application
@@ -199,6 +225,8 @@ void Core::Stop()
}
}
OpenGLProxy::DestroyInstance();
MenuShared::DestroyInstance();
TaskManager::DestroyInstance();
@@ -480,9 +508,11 @@ void Core::AddOpenProject(ProjectPtr p)
void Core::AddOpenProjectFromTask(Task *task)
{
QList<ProjectPtr> projects = static_cast<ProjectLoadTask*>(task)->GetLoadedProjects();
QList<MainWindowLayoutInfo> layouts = static_cast<ProjectLoadTask*>(task)->GetLoadedLayouts();
foreach (ProjectPtr p, projects) {
AddOpenProject(p);
for (int i=0; i<projects.size(); i++) {
AddOpenProject(projects.at(i));
main_window_->LoadLayout(layouts.at(i));
}
}
@@ -629,29 +659,6 @@ void Core::OpenStartupProject()
}
}
void Core::DeclareTypesForQt()
{
qRegisterMetaType<rational>();
qRegisterMetaType<OpenGLTexturePtr>();
qRegisterMetaType<OpenGLTextureCache::ReferencePtr>();
qRegisterMetaType<NodeValue>();
qRegisterMetaType<NodeValueTable>();
qRegisterMetaType<NodeValueDatabase>();
qRegisterMetaType<FramePtr>();
qRegisterMetaType<SampleBufferPtr>();
qRegisterMetaType<AudioParams>();
qRegisterMetaType<NodeKeyframe::Type>();
qRegisterMetaType<Decoder::RetrieveState>();
qRegisterMetaType<OLIVE_NAMESPACE::TimeRange>();
qRegisterMetaType<Color>();
qRegisterMetaType<OLIVE_NAMESPACE::ProjectPtr>();
qRegisterMetaType<OLIVE_NAMESPACE::AudioVisualWaveform>();
qRegisterMetaType<OLIVE_NAMESPACE::SampleJob>();
qRegisterMetaType<OLIVE_NAMESPACE::ShaderJob>();
qRegisterMetaType<OLIVE_NAMESPACE::GenerateJob>();
qRegisterMetaType<OLIVE_NAMESPACE::VideoParams>();
}
void Core::StartGUI(bool full_screen)
{
// Set UI style
@@ -1011,21 +1018,11 @@ void Core::OpenProjectInternal(const QString &filename)
ProjectLoadTask* plm = new ProjectLoadTask(filename);
if (gui_active_) {
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTask);
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTask);
task_dialog->open();
} else {
//connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
}
task_dialog->open();
}
int Core::CountFilesInFileList(const QFileInfoList &filenames)
-1
View File
@@ -50,7 +50,6 @@ RichTextDialog::RichTextDialog(const QString &start, QWidget* parent) :
toolbar_layout->addWidget(font_combo_);
size_slider_ = new FloatSlider();
size_slider_->SetMinimum(0.1);
size_slider_->SetLadderEnabled(true);
size_slider_->SetLadderElementCount(1);
size_slider_->setToolTip(tr("Font Size"));
toolbar_layout->addWidget(size_slider_);
+8 -6
View File
@@ -81,13 +81,15 @@ QString NodeInput::name()
void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
{
XMLAttributeLoop(reader, attr) {
if (cancelled && *cancelled) {
return;
}
{
XMLAttributeLoop(reader, attr) {
if (cancelled && *cancelled) {
return;
}
if (attr.name() == QStringLiteral("keyframing")) {
set_is_keyframing(attr.value() == QStringLiteral("1"));
if (attr.name() == QStringLiteral("keyframing")) {
set_is_keyframing(attr.value() == QStringLiteral("1"));
}
}
}
+7 -2
View File
@@ -285,8 +285,13 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
}
} else if (pairing == kPairTextureMatrix) {
// Only allow matrix multiplication
if (operation != kOpMultiply
|| number_val.data().value<QMatrix4x4>().isIdentity()) {
bool matrix_is_identity = false;
// FIXME: The matrix in the shader is transformed around footage+sequence resolution so we
// need to do that here to determine if the matrix is truly identity. But to do that,
// we need access to the texture parameters which is currently not possible.
if (operation != kOpMultiply || matrix_is_identity) {
operation_is_noop = true;
} else {
// It's likely an alpha channel will result from this operation
+3 -3
View File
@@ -151,9 +151,9 @@ void TrackList::RemoveTrack()
void TrackList::TrackConnected(NodeEdgePtr edge)
{
int track_index = track_input_->IndexOfSubParameter(edge->input());
int input_index = track_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(track_index >= 0);
Q_ASSERT(input_index >= 0);
Node* connected_node = edge->output()->parentNode();
@@ -163,7 +163,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge)
{
// Find "real" index
TrackOutput* next = nullptr;
for (int i=track_index+1; i<track_input_->GetSize(); i++) {
for (int i=input_index+1; i<track_input_->GetSize(); i++) {
Node* that_track = track_input_->At(i)->get_connected_node();
if (that_track && that_track->IsTrack()) {
+11 -3
View File
@@ -129,12 +129,20 @@ void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, Node
void ViewerOutput::set_video_params(const VideoParams &video)
{
bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height();
bool timebase_changed = video_params_.time_base() != video.time_base();
video_params_ = video;
video_frame_cache_.SetTimebase(video_params_.time_base());
if (size_changed) {
emit SizeChanged(video_params_.width(), video_params_.height());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(video_params_.time_base());
emit TimebaseChanged(video_params_.time_base());
}
emit SizeChanged(video_params_.width(), video_params_.height());
emit TimebaseChanged(video_params_.time_base());
emit ParamsChanged();
}
+6 -2
View File
@@ -44,7 +44,12 @@ void NodeValueDatabase::Insert(const NodeInput *key, const NodeValueTable &value
NodeValueTable NodeValueDatabase::Merge() const
{
return NodeValueTable::Merge(tables_.values());
QHash<QString, NodeValueTable> copy = tables_;
// Kinda hacky, but we don't need this table to slipstream
copy.remove(QStringLiteral("global"));
return NodeValueTable::Merge(copy.values());
}
NodeValue::NodeValue() :
@@ -176,7 +181,6 @@ NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
NodeValueTable merged_table;
// Slipstreams all tables together
// FIXME: I don't actually know if this is the right approach...
foreach (const NodeValueTable& t, tables) {
if (row >= t.Count()) {
continue;
+9 -2
View File
@@ -108,14 +108,21 @@ public:
using const_iterator = QHash<QString, NodeValueTable>::const_iterator;
inline QHash<QString, NodeValueTable>::const_iterator begin() const {
inline QHash<QString, NodeValueTable>::const_iterator begin() const
{
return tables_.cbegin();
}
inline QHash<QString, NodeValueTable>::const_iterator end() const {
inline QHash<QString, NodeValueTable>::const_iterator end() const
{
return tables_.cend();
}
inline bool contains(const QString& s) const
{
return tables_.contains(s);
}
private:
QHash<QString, NodeValueTable> tables_;
-5
View File
@@ -49,11 +49,6 @@ void CurvePanel::SetInput(NodeInput *input)
Retranslate();
}
void CurvePanel::SetTimeTarget(Node *target)
{
static_cast<CurveWidget*>(GetTimeBasedWidget())->SetTimeTarget(target);
}
void CurvePanel::IncreaseTrackHeight()
{
CurveWidget* c = static_cast<CurveWidget*>(GetTimeBasedWidget());
-2
View File
@@ -39,8 +39,6 @@ public:
public slots:
void SetInput(NodeInput* input);
void SetTimeTarget(Node* target);
virtual void IncreaseTrackHeight() override;
virtual void DecreaseTrackHeight() override;
+36 -45
View File
@@ -29,10 +29,7 @@ ParamPanel::ParamPanel(QWidget* parent) :
{
NodeParamView* view = new NodeParamView();
connect(view, &NodeParamView::InputDoubleClicked, this, &ParamPanel::CreateCurvePanel);
connect(view, &NodeParamView::TimeTargetChanged, this, &ParamPanel::TimeTargetChanged);
connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode);
connect(view, &NodeParamView::OpenedNode, this, &ParamPanel::OpeningNode);
connect(view, &NodeParamView::ClosedNode, this, &ParamPanel::ClosingNode);
connect(view, &NodeParamView::FoundGizmos, this, &ParamPanel::FoundGizmos);
SetTimeBasedWidget(view);
@@ -51,13 +48,7 @@ void ParamPanel::SetTimestamp(const int64_t &timestamp)
TimeBasedPanel::SetTimestamp(timestamp);
// Ensure all CurvePanels are updated with this time too
QHash<NodeInput*, CurvePanel*>::const_iterator i;
for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) {
if (i.value() && i.value() != sender()) {
i.value()->SetTimestamp(timestamp);
}
}
ParamViewTimeChanged(timestamp);
}
void ParamPanel::DeleteSelected()
@@ -97,52 +88,52 @@ void ParamPanel::CreateCurvePanel(NodeInput *input)
panel = Core::instance()->main_window()->AppendCurvePanel();
panel->SetInput(input);
panel->SetTimebase(view->timebase());
panel->ConnectViewerNode(view->GetConnectedNode());
panel->SetTimestamp(view->GetTimestamp());
panel->SetTimeTarget(view->GetTimeTarget());
panel->SetInput(input);
connect(view, &NodeParamView::TimebaseChanged, panel, &CurvePanel::SetTimebase);
connect(view, &NodeParamView::TimeChanged, panel, &CurvePanel::SetTimestamp);
connect(view, &NodeParamView::TimeTargetChanged, panel, &CurvePanel::SetTimeTarget);
connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::SetTimestamp);
connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::TimeChanged);
connect(view, &NodeParamView::TimeChanged, this, &ParamPanel::ParamViewTimeChanged);
connect(panel, &CurvePanel::TimeChanged, this, &ParamPanel::CurvePanelTimeChanged);
connect(panel, &CurvePanel::CloseRequested, this, &ParamPanel::ClosingCurvePanel);
open_curve_panels_.insert(input, panel);
}
void ParamPanel::OpeningNode(Node *n)
{
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
if (open_curve_panels_.contains(i)) {
// We had a CurvePanel open for this input that was closed in ClosingNode(), re-open it
CreateCurvePanel(i);
}
}
}
void ParamPanel::ClosingNode(Node *n)
{
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* i, inputs) {
CurvePanel* panel = open_curve_panels_.value(i);
// Close the panel (this also destroys it), but keep a reference in the hash
if (panel) {
panel->close();
open_curve_panels_.insert(i, nullptr);
}
}
}
void ParamPanel::ClosingCurvePanel()
{
CurvePanel* panel = static_cast<CurvePanel*>(sender());
open_curve_panels_.remove(panel->GetInput());
}
void ParamPanel::ParamViewTimeChanged(const int64_t &time)
{
// Ensure all CurvePanels are updated with this time too
QHash<NodeInput*, CurvePanel*>::const_iterator i;
for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) {
// If connected viewers are the same, set the timestamp
if (i.value()->GetConnectedViewer() == GetConnectedViewer()) {
i.value()->SetTimestamp(time);
}
}
}
void ParamPanel::CurvePanelTimeChanged(const int64_t &time)
{
GetTimeBasedWidget()->SetTimestamp(time);
emit GetTimeBasedWidget()->TimeChanged(time);
CurvePanel* src = static_cast<CurvePanel*>(sender());
// Ensure all CurvePanels are updated with this time too
QHash<NodeInput*, CurvePanel*>::const_iterator i;
for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) {
// If connected viewers are the same and the panel isn't the source, set the timestamp
if (i.value() != src && i.value()->GetConnectedViewer() == src->GetConnectedViewer()) {
i.value()->SetTimestamp(time);
}
}
}
OLIVE_NAMESPACE_EXIT
+5 -6
View File
@@ -41,8 +41,6 @@ public slots:
virtual void DeleteSelected() override;
signals:
void TimeTargetChanged(Node* node);
void RequestSelectNode(const QList<Node*>& target);
void FoundGizmos(Node* node);
@@ -53,15 +51,16 @@ protected:
private slots:
void CreateCurvePanel(NodeInput* input);
void OpeningNode(Node* n);
void ClosingNode(Node* n);
void ClosingCurvePanel();
private:
QHash<NodeInput*, CurvePanel*> open_curve_panels_;
private slots:
void ParamViewTimeChanged(const int64_t& time);
void CurvePanelTimeChanged(const int64_t& time);
};
OLIVE_NAMESPACE_EXIT
+1
View File
@@ -28,6 +28,7 @@ OLIVE_NAMESPACE_ENTER
class NodeTablePanel : public TimeBasedPanel
{
Q_OBJECT
public:
NodeTablePanel(QWidget* parent);
+4
View File
@@ -125,6 +125,10 @@ TimeRuler *TimeBasedPanel::ruler() const
void TimeBasedPanel::ConnectViewerNode(ViewerOutput *node)
{
if (widget_->GetConnectedNode() == node) {
return;
}
if (widget_->GetConnectedNode()) {
disconnect(widget_->GetConnectedNode(), &ViewerOutput::MediaNameChanged, this, &TimeBasedPanel::SetSubtitle);
}
+1 -1
View File
@@ -149,7 +149,7 @@ void Sequence::Save(QXmlStreamWriter *writer) const
writer->writeAttribute(QStringLiteral("name"), name());
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(viewer_output_)));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeStartElement(QStringLiteral("video"));
+9 -3
View File
@@ -37,7 +37,7 @@ Project::Project() :
root_.set_project(this);
}
void Project::Load(QXmlStreamReader *reader, const QAtomicInt* cancelled)
void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, const QAtomicInt* cancelled)
{
XMLNodeData xml_node_data;
@@ -62,7 +62,12 @@ void Project::Load(QXmlStreamReader *reader, const QAtomicInt* cancelled)
} else if (reader->name() == QStringLiteral("layout")) {
Core::instance()->main_window()->LoadLayout(reader, xml_node_data);
// Since the main window's functions have to occur in the GUI thread (and we're likely
// loading in a secondary thread), we load all necessary data into a separate struct so we
// can continue loading and queue it with the main window so it can handle the data
// appropriately in its own thread.
*layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data);
} else {
reader->skipCurrentElement();
@@ -93,7 +98,8 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // colormanagement
// Save main window project layout
Core::instance()->main_window()->SaveLayout(writer);
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
main_window_info.toXml(writer);
writer->writeEndElement(); // project
}
+2 -1
View File
@@ -26,6 +26,7 @@
#include "render/colormanager.h"
#include "project/item/folder/folder.h"
#include "window/mainwindow/mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -46,7 +47,7 @@ class Project : public QObject
public:
Project();
void Load(QXmlStreamReader* reader, const QAtomicInt* cancelled);
void Load(QXmlStreamReader* reader, MainWindowLayoutInfo *layout, const QAtomicInt* cancelled);
void Save(QXmlStreamWriter* writer) const;
+1 -22
View File
@@ -27,38 +27,17 @@ OLIVE_NAMESPACE_ENTER
OpenGLBackend::OpenGLBackend(QObject* parent) :
RenderBackend(parent)
{
proxy_ = new OpenGLProxy();
QThread* proxy_thread = new QThread();
proxy_thread->start(QThread::IdlePriority);
proxy_->moveToThread(proxy_thread);
if (!proxy_->Init()) {
ClearProxy();
}
}
OpenGLBackend::~OpenGLBackend()
{
Close();
ClearProxy();
}
RenderWorker *OpenGLBackend::CreateNewWorker()
{
return new OpenGLWorker(this, proxy_);
}
void OpenGLBackend::ClearProxy()
{
if (proxy_) {
proxy_->thread()->quit();
proxy_->thread()->wait();
proxy_->thread()->deleteLater();
proxy_->deleteLater();
proxy_ = nullptr;
}
return new OpenGLWorker(this);
}
OLIVE_NAMESPACE_EXIT
@@ -36,11 +36,6 @@ public:
protected:
virtual RenderWorker* CreateNewWorker() override;
private:
void ClearProxy();
OpenGLProxy* proxy_;
};
OLIVE_NAMESPACE_EXIT
+38 -12
View File
@@ -33,6 +33,8 @@
OLIVE_NAMESPACE_ENTER
OpenGLProxy* OpenGLProxy::instance_ = nullptr;
OpenGLProxy::OpenGLProxy(QObject *parent) :
QObject(parent),
ctx_(nullptr),
@@ -48,6 +50,30 @@ OpenGLProxy::~OpenGLProxy()
surface_.destroy();
}
void OpenGLProxy::CreateInstance()
{
instance_ = new OpenGLProxy();
QThread* proxy_thread = new QThread();
proxy_thread->start(QThread::IdlePriority);
instance_->moveToThread(proxy_thread);
if (!instance_->Init()) {
DestroyInstance();
}
}
void OpenGLProxy::DestroyInstance()
{
if (instance_) {
instance_->thread()->quit();
instance_->thread()->wait();
instance_->thread()->deleteLater();
instance_->deleteLater();
instance_ = nullptr;
}
}
bool OpenGLProxy::Init()
{
// Create context object
@@ -250,21 +276,21 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->bind();
NodeValueMap::const_iterator i;
for (i=job.GetValues().constBegin(); i!=job.GetValues().constEnd(); i++) {
NodeValueMap::const_iterator it;
for (it=job.GetValues().constBegin(); it!=job.GetValues().constEnd(); it++) {
// See if the shader has takes this parameter as an input
int variable_location = shader->uniformLocation(i.key()->id());
int variable_location = shader->uniformLocation(it.key()->id());
if (variable_location == -1) {
continue;
}
// This variable is used in the shader, let's set it
const QVariant& value = i.value().data();
const QVariant& value = it.value().data();
const NodeParam::DataType& data_type = (i.value().type() != NodeParam::kNone)
? i.value().type()
: i.key()->data_type();
const NodeParam::DataType& data_type = (it.value().type() != NodeParam::kNone)
? it.value().type()
: it.key()->data_type();
switch (data_type) {
case NodeInput::kInt:
@@ -274,7 +300,7 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->setUniformValue(variable_location, value.toFloat());
break;
case NodeInput::kVec2:
if (i.key()->IsArray()) {
if (it.key()->IsArray()) {
QVector<NodeValue> nv = value.value< QVector<NodeValue> >();
QVector<QVector2D> a(nv.size());
@@ -284,7 +310,7 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->setUniformValueArray(variable_location, a.constData(), a.size());
int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(i.key()->id()));
int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(it.key()->id()));
if (count_location > -1) {
shader->setUniformValue(count_location, a.size());
}
@@ -328,7 +354,7 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->setUniformValue(variable_location, textures_to_bind.size());
// If this texture binding is the iterative input, set it here
if (i.key() == job.GetIterativeInput()) {
if (it.key() == job.GetIterativeInput()) {
iterative_input = textures_to_bind.size();
}
@@ -336,7 +362,7 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
textures_to_bind.append(tex_id);
// Set enable flag if shader wants it
int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(i.key()->id()));
int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(it.key()->id()));
if (enable_param_location > -1) {
shader->setUniformValue(enable_param_location,
tex_id > 0);
@@ -344,7 +370,7 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
if (tex_id > 0) {
// Set texture resolution if shader wants it
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(i.key()->id()));
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(it.key()->id()));
if (res_param_location > -1) {
shader->setUniformValue(res_param_location,
static_cast<GLfloat>(texture->texture()->width() * texture->texture()->divider()),
+11
View File
@@ -41,6 +41,15 @@ public:
virtual ~OpenGLProxy() override;
static void CreateInstance();
static void DestroyInstance();
static OpenGLProxy* instance()
{
return instance_;
}
/**
* @brief Initialize OpenGL instance in whatever thread this object is a part of
*
@@ -101,6 +110,8 @@ private:
OpenGLTextureCache texture_cache_;
static OpenGLProxy* instance_;
private slots:
void FinishInit();
+6 -7
View File
@@ -22,15 +22,14 @@
OLIVE_NAMESPACE_ENTER
OpenGLWorker::OpenGLWorker(RenderBackend *parent, OpenGLProxy* proxy) :
RenderWorker(parent),
proxy_(proxy)
OpenGLWorker::OpenGLWorker(RenderBackend *parent) :
RenderWorker(parent)
{
}
void OpenGLWorker::TextureToFrame(const QVariant &texture, FramePtr frame, const QMatrix4x4& mat) const
{
QMetaObject::invokeMethod(proxy_,
QMetaObject::invokeMethod(OpenGLProxy::instance(),
"TextureToBuffer",
Qt::BlockingQueuedConnection,
Q_ARG(const QVariant&, texture),
@@ -42,7 +41,7 @@ QVariant OpenGLWorker::FootageFrameToTexture(StreamPtr stream, FramePtr frame) c
{
QVariant value;
QMetaObject::invokeMethod(proxy_,
QMetaObject::invokeMethod(OpenGLProxy::instance(),
"FrameToValue",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, value),
@@ -58,7 +57,7 @@ QVariant OpenGLWorker::CachedFrameToTexture(FramePtr frame) const
{
QVariant value;
QMetaObject::invokeMethod(proxy_,
QMetaObject::invokeMethod(OpenGLProxy::instance(),
"PreCachedFrameToValue",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, value),
@@ -71,7 +70,7 @@ QVariant OpenGLWorker::ProcessShader(const Node *node, const TimeRange &range, c
{
QVariant value;
QMetaObject::invokeMethod(proxy_,
QMetaObject::invokeMethod(OpenGLProxy::instance(),
"RunNodeAccelerated",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, value),
+1 -4
View File
@@ -29,7 +29,7 @@ OLIVE_NAMESPACE_ENTER
class OpenGLWorker : public RenderWorker
{
public:
OpenGLWorker(RenderBackend* parent, OpenGLProxy* proxy);
OpenGLWorker(RenderBackend* parent);
protected:
virtual void TextureToFrame(const QVariant& texture, FramePtr frame, const QMatrix4x4 &mat) const override;
@@ -42,9 +42,6 @@ protected:
virtual bool TextureHasAlpha(const QVariant& v) const override;
private:
OpenGLProxy* proxy_;
};
OLIVE_NAMESPACE_EXIT
+3 -3
View File
@@ -115,13 +115,13 @@ void RenderBackend::ClearVideoQueue()
QFuture<QVector<QByteArray> > RenderBackend::Hash(const QVector<rational> &times)
{
return QtConcurrent::run(&pool_, [this](const QVector<rational> &times){
QVector<QByteArray> hashes(times.size());
return QtConcurrent::run(&pool_, [this](const QVector<rational> &t){
QVector<QByteArray> hashes(t.size());
for (int i=0;i<hashes.size();i++) {
hashes[i] = HashNode(copied_viewer_node_->texture_input()->get_connected_node(),
video_params_,
times.at(i));
t.at(i));
}
return hashes;
+1
View File
@@ -71,6 +71,7 @@ void PlaybackCache::SetLength(const rational &r)
} else if (r > length_) {
// If new length is greater, simply extend the invalidated range for now
invalidated_.InsertTimeRange(range_diff);
jobs_.append({range_diff, QDateTime::currentMSecsSinceEpoch()});
} else {
// If new length is smaller, removed hashes
invalidated_.RemoveTimeRange(range_diff);
+3 -11
View File
@@ -96,21 +96,13 @@ bool ExportTask::Run()
if (params_.audio_enabled()) {
audio_range.append(range);
audio_data_.SetLength(range.length());
}
Render(video_range, audio_range, mat, false);
bool success = true;
foreach (QFuture<bool> f, write_frame_futures_) {
f.waitForFinished();
if (!f.result()) {
SetError(tr("Failed to write AVFrame"));
success = false;
}
}
if (params_.audio_enabled()) {
// Write audio data now
encoder_->WriteAudio(audio_params(), audio_data_.GetCacheFilename());
@@ -165,7 +157,7 @@ void ExportTask::FrameDownloaded(const QByteArray &hash, const std::list<rationa
// Unfortunately this can't be done in another thread since the frames need to be sent
// one after the other chronologically.
encoder_->WriteFrame(time_map_.value(real_time), real_time);
encoder_->WriteFrame(time_map_.take(real_time), real_time);
frame_time_++;
@@ -180,7 +172,7 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples
adjusted_range -= params_.custom_range().in();
}
audio_data_.WritePCM(adjusted_range, samples, job_time());
audio_data_.WritePCM(adjusted_range, samples, QDateTime::currentMSecsSinceEpoch());
}
OLIVE_NAMESPACE_EXIT
-2
View File
@@ -49,8 +49,6 @@ private:
QHash<rational, FramePtr> time_map_;
QList< QFuture<bool> > write_frame_futures_;
ColorManager* color_manager_;
ExportParams params_;
+4 -1
View File
@@ -51,13 +51,16 @@ bool ProjectLoadTask::Run()
project->set_filename(filename_);
project->Load(&reader, &IsCancelled());
MainWindowLayoutInfo layout;
project->Load(&reader, &layout, &IsCancelled());
// Ensure project is in main thread
moveToThread(qApp->thread());
if (!IsCancelled()) {
projects_.append(project);
layout_info_.append(layout);
}
} else {
reader.skipCurrentElement();
+9 -1
View File
@@ -23,6 +23,7 @@
#include "project/project.h"
#include "task/task.h"
#include "window/mainwindow/mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -32,17 +33,24 @@ class ProjectLoadTask : public Task
public:
ProjectLoadTask(const QString& filename);
const QList<ProjectPtr>& GetLoadedProjects()
const QList<ProjectPtr>& GetLoadedProjects() const
{
return projects_;
}
const QList<MainWindowLayoutInfo>& GetLoadedLayouts() const
{
return layout_info_;
}
protected:
virtual bool Run() override;
private:
QList<ProjectPtr> projects_;
QList<MainWindowLayoutInfo> layout_info_;
QString filename_;
};
+11 -10
View File
@@ -93,12 +93,10 @@ void RenderTask::Render(const TimeRangeList& video_range,
if (!video_range.isEmpty()) {
QList<QByteArray> existing_hashes;
foreach (const TimeRange& r, video_range) {
total_length += r.length().toDouble();
}
times = viewer_->video_frame_cache()->GetFrameListFromTimeRange(video_range);
total_length += video_frame_sz * times.size();
QFuture<QVector<QByteArray> > hash_future = backend_.Hash(times);
hashes = hash_future.result();
@@ -125,7 +123,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
|| !download_futures.empty()
|| !audio_lookup_table.empty())) {
if (!frame_queue.empty()) {
if (!IsCancelled() && !frame_queue.empty()) {
// Pop another frame off the frame queue
const HashTimePair& p = frame_queue.front();
@@ -138,11 +136,14 @@ void RenderTask::Render(const TimeRangeList& video_range,
bool hash_exists = false;
if (use_disk_cache) {
bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), p.hash) != existing_hashes.end());
// Check if this hash is in our "existing hashes" list
hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), p.hash) != existing_hashes.end());
// If not, check if it's in the filesystem
if (!hash_exists) {
hash_exists = QFileInfo::exists(viewer_->video_frame_cache()->CachePathName(p.hash));
// If so, add it to the list so we don't have to check the filesystem again later
if (hash_exists) {
existing_hashes.push_back(p.hash);
}
@@ -167,7 +168,7 @@ void RenderTask::Render(const TimeRangeList& video_range,
frame_queue.pop_front();
}
if (!audio_queue.empty()) {
if (!IsCancelled() && !audio_queue.empty()) {
audio_lookup_table.push_back({audio_queue.front(), backend_.RenderAudio(audio_queue.front())});
audio_queue.pop_front();
}
@@ -194,9 +195,9 @@ void RenderTask::Render(const TimeRangeList& video_range,
// Place it in the cache
std::list<rational> times_with_hash;
for (int k=0;k<hashes.size();k++) {
if (hashes.at(k) == j->hash) {
times_with_hash.push_back(times.at(k));
for (int hash_index=0;hash_index<hashes.size();hash_index++) {
if (hashes.at(hash_index) == j->hash) {
times_with_hash.push_back(times.at(hash_index));
}
}
+2 -2
View File
@@ -90,11 +90,11 @@ void AudioMonitor::Stop()
}
}
void AudioMonitor::OutputPushed(const QByteArray &data)
void AudioMonitor::OutputPushed(const QByteArray &d)
{
QVector<double> v(params_.channel_count(), 0);
BytesToSampleSummary(data, v);
BytesToSampleSummary(d, v);
PushValue(v);
+1 -1
View File
@@ -45,7 +45,7 @@ public slots:
void Stop();
void OutputPushed(const QByteArray& data);
void OutputPushed(const QByteArray& d);
protected:
//virtual void paintEvent(QPaintEvent* event) override;
@@ -203,7 +203,6 @@ FloatSlider *ColorValuesTab::CreateColorSlider()
FloatSlider* fs = new FloatSlider();
fs->SetDragMultiplier(0.01);
fs->SetDecimalPlaces(5);
fs->SetLadderEnabled(true);
fs->SetLadderElementCount(1);
connect(fs, &FloatSlider::ValueChanged, this, &ColorValuesTab::SliderChanged);
return fs;
+72 -12
View File
@@ -20,6 +20,7 @@
#include "curveview.h"
#include <QScrollBar>
#include <QMouseEvent>
#include <QtMath>
@@ -61,6 +62,16 @@ void CurveView::Clear()
void CurveView::SetTrackCount(int count)
{
track_count_ = count;
track_visible_.resize(track_count_);
track_visible_.fill(true);
}
void CurveView::SetTrackVisible(int track, bool visible)
{
track_visible_[track] = visible;
SetKeyframeTrackVisible(track, visible);
}
void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
@@ -116,6 +127,10 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
// Draw keyframe lines
for (int j=0;j<track_count_;j++) {
if (!track_visible_.at(j)) {
continue;
}
painter->setPen(QPen(GetKeyframeColor(j), qMax(1, fontMetrics().height() / 4)));
QList<NodeKeyframe*> keys = GetKeyframesSortedByTime(j);
@@ -237,21 +252,22 @@ void CurveView::VerticalScaleChangedEvent(double scale)
void CurveView::wheelEvent(QWheelEvent *event)
{
if (WheelEventIsAZoomEvent(event)) {
if (!event->angleDelta().isNull()) {
if (event->angleDelta().x() + event->angleDelta().y() > 0) {
emit ScaleChanged(GetScale() * 1.1);
SetYScale(GetYScale() * 1.1);
} else {
emit ScaleChanged(GetScale() * 0.9);
SetYScale(GetYScale() * 0.9);
}
}
} else {
if (!HandleZoomFromScroll(event)) {
KeyframeViewBase::wheelEvent(event);
}
}
void CurveView::ContextMenuEvent(Menu &m)
{
m.addSeparator();
// View settings
QAction* zoom_fit_action = m.addAction(tr("Zoom to Fit"));
connect(zoom_fit_action, &QAction::triggered, this, &CurveView::ZoomToFit);
//QAction* reset_zoom_action = m.addAction(tr("Reset Zoom"));
}
QList<NodeKeyframe *> CurveView::GetKeyframesSortedByTime(int track)
{
QList<NodeKeyframe *> sorted;
@@ -285,7 +301,12 @@ QList<NodeKeyframe *> CurveView::GetKeyframesSortedByTime(int track)
qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key)
{
return -key->value().toDouble() * GetYScale();
return GetItemYFromKeyframeValue(key->value().toDouble());
}
qreal CurveView::GetItemYFromKeyframeValue(double value)
{
return -value * GetYScale();
}
void CurveView::SetItemYFromKeyframeValue(NodeKeyframe *key, KeyframeViewItem *item)
@@ -367,6 +388,45 @@ void CurveView::BezierControlPointDestroyed()
bezier_control_points_.removeOne(item);
}
void CurveView::ZoomToFit()
{
if (item_map().isEmpty()) {
// Prevent scaling to DBL_MIN/DBL_MAX
return;
}
QMap<NodeKeyframe *, KeyframeViewItem *>::const_iterator i;
rational min_time = RATIONAL_MAX;
rational max_time = RATIONAL_MIN;
double min_val = DBL_MAX;
double max_val = DBL_MIN;
for (i=item_map().constBegin(); i!=item_map().constEnd(); i++) {
rational transformed_time = GetAdjustedTime(i.key()->parent()->parentNode(),
GetTimeTarget(),
i.key()->time(),
NodeParam::kOutput);
min_time = qMin(transformed_time, min_time);
max_time = qMax(transformed_time, max_time);
min_val = qMin(i.key()->value().toDouble(), min_val);
max_val = qMax(i.key()->value().toDouble(), max_val);
}
double time_range = max_time.toDouble() - min_time.toDouble();
double new_x_scale = CalculateScaleFromDimensions(this->width(), time_range);
double new_y_scale = CalculateScaleFromDimensions(this->height(), max_val - min_val);
emit ScaleChanged(new_x_scale);
SetYScale(new_y_scale);
horizontalScrollBar()->setValue(TimeToScene(min_time) - CalculatePaddingFromDimensionScale(this->width()));
verticalScrollBar()->setValue(GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height()));
}
void CurveView::AddKeyframe(NodeKeyframePtr key)
{
KeyframeViewItem* item = AddKeyframeInternal(key);
+10
View File
@@ -30,6 +30,7 @@ OLIVE_NAMESPACE_ENTER
class CurveView : public KeyframeViewBase
{
Q_OBJECT
public:
CurveView(QWidget* parent = nullptr);
@@ -39,9 +40,13 @@ public:
void SetTrackCount(int count);
void SetTrackVisible(int track, bool visible);
public slots:
void AddKeyframe(NodeKeyframePtr key);
void ZoomToFit();
protected:
virtual void drawBackground(QPainter* painter, const QRectF& rect) override;
@@ -53,10 +58,13 @@ protected:
virtual void wheelEvent(QWheelEvent* event) override;
virtual void ContextMenuEvent(Menu &m) override;
private:
QList<NodeKeyframe*> GetKeyframesSortedByTime(int track);
qreal GetItemYFromKeyframeValue(NodeKeyframe* key);
qreal GetItemYFromKeyframeValue(double value);
void SetItemYFromKeyframeValue(NodeKeyframe* key, KeyframeViewItem* item);
@@ -76,6 +84,8 @@ private:
QList<BezierControlPointItem*> bezier_control_points_;
QVector<bool> track_visible_;
int track_count_;
private slots:
+22 -10
View File
@@ -117,12 +117,17 @@ void CurveWidget::SetInput(NodeInput *input)
{
if (bridge_) {
foreach (QWidget* bridge_widget, bridge_->widgets()) {
delete bridge_widget;
bridge_widget->deleteLater();
}
delete bridge_;
bridge_->deleteLater();
bridge_ = nullptr;
}
foreach (QCheckBox* box, checkboxes_) {
box->deleteLater();
}
checkboxes_.clear();
if (input_) {
disconnect(input_, &NodeInput::KeyframeAdded, view_, &CurveView::AddKeyframe);
disconnect(input_, &NodeInput::KeyframeRemoved, view_, &CurveView::RemoveKeyframe);
@@ -142,7 +147,15 @@ void CurveWidget::SetInput(NodeInput *input)
for (int i=0;i<bridge_->widgets().size();i++) {
// Insert between two stretches to center the widget
widget_bridge_layout_->insertWidget(2 + i, bridge_->widgets().at(i));
QCheckBox* checkbox = new QCheckBox();
checkbox->setChecked(true);
widget_bridge_layout_->insertWidget(2 + i*2, checkbox);
checkboxes_.append(checkbox);
connect(checkbox, &QCheckBox::clicked, this, [this](bool e){
view_->SetTrackVisible(checkboxes_.indexOf(static_cast<QCheckBox*>(sender())), e);
});
widget_bridge_layout_->insertWidget(2 + i*2 + 1, bridge_->widgets().at(i));
}
connect(input_, &NodeInput::KeyframeAdded, view_, &CurveView::AddKeyframe);
@@ -156,6 +169,8 @@ void CurveWidget::SetInput(NodeInput *input)
}
UpdateInputLabel();
QMetaObject::invokeMethod(view_, "ZoomToFit", Qt::QueuedConnection);
}
const double &CurveWidget::GetVerticalScale()
@@ -205,8 +220,6 @@ void CurveWidget::ScaleChangedEvent(const double &scale)
void CurveWidget::TimeTargetChangedEvent(Node *target)
{
ConnectViewerNode(nullptr);
key_control_->SetTimeTarget(target);
view_->SetTimeTarget(target);
@@ -214,12 +227,11 @@ void CurveWidget::TimeTargetChangedEvent(Node *target)
if (bridge_) {
bridge_->SetTimeTarget(target);
}
}
// FIXME: If a non-viewer node is ever set here, it will fail to update the length
ViewerOutput* viewer = dynamic_cast<ViewerOutput*>(target);
if (viewer) {
ConnectViewerNode(viewer);
}
void CurveWidget::ConnectedNodeChanged(ViewerOutput *n)
{
SetTimeTarget(n);
}
void CurveWidget::UpdateInputLabel()
+5
View File
@@ -21,6 +21,7 @@
#ifndef CURVEWIDGET_H
#define CURVEWIDGET_H
#include <QCheckBox>
#include <QHBoxLayout>
#include <QPushButton>
#include <QWidget>
@@ -58,6 +59,8 @@ protected:
virtual void TimeTargetChangedEvent(Node* target) override;
virtual void ConnectedNodeChanged(ViewerOutput* n) override;
private:
void UpdateInputLabel();
@@ -87,6 +90,8 @@ private:
NodeParamViewKeyframeControl* key_control_;
QList<QCheckBox*> checkboxes_;
private slots:
void SelectionChanged();
+8 -1
View File
@@ -23,7 +23,8 @@
OLIVE_NAMESPACE_ENTER
KeyframeView::KeyframeView(QWidget *parent) :
KeyframeViewBase(parent)
KeyframeViewBase(parent),
max_scroll_(0)
{
setAlignment(Qt::AlignLeft | Qt::AlignTop);
}
@@ -35,6 +36,12 @@ void KeyframeView::wheelEvent(QWheelEvent *event)
}
}
void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
{
rect.setY(0);
rect.setHeight(max_scroll_);
}
void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
{
QPoint global_pt(0, y);
+10
View File
@@ -31,12 +31,22 @@ class KeyframeView : public KeyframeViewBase
public:
KeyframeView(QWidget* parent = nullptr);
void SetMaxScroll(int i)
{
max_scroll_ = i;
}
protected:
virtual void wheelEvent(QWheelEvent* event) override;
virtual void SceneRectUpdateEvent(QRectF& rect) override;
public slots:
void AddKeyframe(NodeKeyframePtr key, int y);
private:
int max_scroll_;
};
OLIVE_NAMESPACE_EXIT
+45 -33
View File
@@ -35,8 +35,6 @@ OLIVE_NAMESPACE_ENTER
KeyframeViewBase::KeyframeViewBase(QWidget *parent) :
TimelineViewBase(parent),
dragging_bezier_point_(nullptr),
y_axis_enabled_(false),
y_scale_(1.0),
currently_autoselecting_(false)
{
SetDefaultDragMode(RubberBandDrag);
@@ -57,22 +55,6 @@ void KeyframeViewBase::Clear()
item_map_.clear();
}
const double &KeyframeViewBase::GetYScale() const
{
return y_scale_;
}
void KeyframeViewBase::SetYScale(const double &y_scale)
{
y_scale_ = y_scale;
if (y_axis_enabled_) {
VerticalScaleChangedEvent(y_scale_);
viewport()->update();
}
}
void KeyframeViewBase::DeleteSelected()
{
QUndoCommand* command = new QUndoCommand();
@@ -101,11 +83,20 @@ void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key)
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframePtr key)
{
KeyframeViewItem* item = new KeyframeViewItem(key);
item->SetTimeTarget(GetTimeTarget());
item->SetScale(GetScale());
item_map_.insert(key.get(), item);
scene()->addItem(item);
KeyframeViewItem* item = item_map_.value(key.get());
if (!item) {
item = new KeyframeViewItem(key);
item->SetTimeTarget(GetTimeTarget());
item->SetScale(GetScale());
item_map_.insert(key.get(), item);
scene()->addItem(item);
if (hidden_tracks_.contains(key->track())) {
item->setVisible(false);
}
}
return item;
}
@@ -185,7 +176,7 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
keypair.key->key()->set_time(node_time);
if (y_axis_enabled_) {
if (IsYAxisEnabled()) {
keypair.key->key()->set_value(keypair.value - mouse_diff_scaled.y());
}
@@ -248,7 +239,7 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event)
command);
// Commit value if we're setting a value
if (y_axis_enabled_) {
if (IsYAxisEnabled()) {
item->key()->set_value(keypair.value);
new NodeParamSetKeyframeValueCommand(item->key(),
keypair.value - mouse_diff_scaled.y(),
@@ -279,10 +270,6 @@ void KeyframeViewBase::ScaleChangedEvent(const double &scale)
}
}
void KeyframeViewBase::VerticalScaleChangedEvent(double)
{
}
const QMap<NodeKeyframe *, KeyframeViewItem *> &KeyframeViewBase::item_map() const
{
return item_map_;
@@ -301,9 +288,30 @@ void KeyframeViewBase::TimeTargetChangedEvent(Node *target)
}
}
void KeyframeViewBase::SetYAxisEnabled(bool e)
void KeyframeViewBase::SetKeyframeTrackVisible(int track, bool visible)
{
y_axis_enabled_ = e;
if (!visible == hidden_tracks_.contains(track)) {
return;
}
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator i;
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
if (i.key()->track() == track) {
i.value()->setVisible(visible);
}
}
if (visible) {
hidden_tracks_.removeOne(track);
} else {
hidden_tracks_.append(track);
}
}
void KeyframeViewBase::ContextMenuEvent(Menu& m)
{
Q_UNUSED(m)
}
rational KeyframeViewBase::CalculateNewTimeFromScreen(const rational &old_time, double cursor_diff)
@@ -403,7 +411,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
QPointF KeyframeViewBase::GetScaledCursorPos(const QPoint &cursor_pos)
{
return QPointF(static_cast<double>(cursor_pos.x()) / GetScale(),
static_cast<double>(cursor_pos.y()) / y_scale_);
static_cast<double>(cursor_pos.y()) / GetYScale());
}
void KeyframeViewBase::ShowContextMenu()
@@ -450,7 +458,11 @@ void KeyframeViewBase::ShowContextMenu()
break;
}
}
}
ContextMenuEvent(m);
if (!items.isEmpty()) {
m.addSeparator();
QAction* properties_action = m.addAction(tr("P&roperties"));
@@ -502,7 +514,7 @@ void KeyframeViewBase::ShowKeyframePropertiesDialog()
void KeyframeViewBase::AutoSelectKeyTimeNeighbors()
{
if (currently_autoselecting_ || y_axis_enabled_) {
if (currently_autoselecting_ || IsYAxisEnabled()) {
return;
}
+7 -11
View File
@@ -23,9 +23,10 @@
#include "keyframeviewitem.h"
#include "node/keyframe.h"
#include "widget/timetarget/timetarget.h"
#include "widget/curvewidget/beziercontrolpointitem.h"
#include "widget/menu/menu.h"
#include "widget/timelinewidget/view/timelineviewbase.h"
#include "widget/timetarget/timetarget.h"
OLIVE_NAMESPACE_ENTER
@@ -37,9 +38,6 @@ public:
virtual void Clear();
const double& GetYScale() const;
void SetYScale(const double& y_scale);
void DeleteSelected();
public slots:
@@ -54,15 +52,15 @@ protected:
virtual void ScaleChangedEvent(const double& scale) override;
virtual void VerticalScaleChangedEvent(double scale);
const QMap<NodeKeyframe*, KeyframeViewItem*>& item_map() const;
virtual void KeyframeAboutToBeRemoved(NodeKeyframe* key);
virtual void TimeTargetChangedEvent(Node*) override;
void SetYAxisEnabled(bool e);
void SetKeyframeTrackVisible(int track, bool visible);
virtual void ContextMenuEvent(Menu &m);
private:
rational CalculateNewTimeFromScreen(const rational& old_time, double cursor_diff);
@@ -94,12 +92,10 @@ private:
QVector<KeyframeItemAndTime> selected_keys_;
bool y_axis_enabled_;
double y_scale_;
bool currently_autoselecting_;
QList<int> hidden_tracks_;
private slots:
void ShowContextMenu();
+3 -3
View File
@@ -50,13 +50,13 @@ Menu::Menu(const QString &s, QWidget *parent) :
Init();
}
QAction *Menu::AddActionWithData(const QString &text, const QVariant &data, const QVariant &compare)
QAction *Menu::AddActionWithData(const QString &text, const QVariant &d, const QVariant &compare)
{
QAction* a = addAction(text);
a->setData(data);
a->setData(d);
a->setCheckable(true);
a->setChecked(data == compare);
a->setChecked(d == compare);
return a;
}
+1 -1
View File
@@ -132,7 +132,7 @@ public:
}
QAction* AddActionWithData(const QString& text,
const QVariant& data,
const QVariant& d,
const QVariant& compare);
QAction *InsertAlphabetically(const QString& s);
+31 -36
View File
@@ -43,18 +43,21 @@ NodeParamView::NodeParamView(QWidget *parent) :
// Set up scroll area for params
QScrollArea* scroll_area = new QScrollArea();
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll_area->setWidgetResizable(true);
splitter->addWidget(scroll_area);
// Param widget
QWidget* param_widget_area = new QWidget();
scroll_area->setWidget(param_widget_area);
param_widget_area_ = new QWidget();
scroll_area->setWidget(param_widget_area_);
// Set up scroll area layout
param_layout_ = new QVBoxLayout(param_widget_area);
param_layout_ = new QVBoxLayout(param_widget_area_);
param_layout_->setSpacing(0);
param_layout_->setMargin(0);
// KeyframeView is offset by a ruler, so to stay synchronized with it, we should be too
param_layout_->setContentsMargins(0, ruler()->height(), 0, 0);
// Add a stretch to allow empty space at the bottom of the layout
param_layout_->addStretch();
@@ -73,7 +76,6 @@ NodeParamView::NodeParamView(QWidget *parent) :
keyframe_view_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ConnectTimelineView(keyframe_view_);
connect(keyframe_view_, &KeyframeView::RequestCenterScrollOnPlayhead, this, &NodeParamView::CenterScrollOnPlayhead);
bottom_item_ = keyframe_view_->scene()->addRect(0, 0, 1, 1);
keyframe_area_layout->addWidget(keyframe_view_);
// Connect ruler and keyframe view together
@@ -122,19 +124,15 @@ NodeParamView::NodeParamView(QWidget *parent) :
void NodeParamView::SetNodes(QList<Node *> nodes)
{
ConnectViewerNode(nullptr);
// If we already have item widgets, delete them all now
foreach (NodeParamViewItem* item, items_) {
emit ClosedNode(item->GetNode());
emit FoundGizmos(nullptr);
delete item;
item->deleteLater();
}
items_.clear();
emit TimeTargetChanged(nullptr);
// Reset keyframe view
SetTimebase(rational());
keyframe_view_->Clear();
// Set the internal list to the one we've received
@@ -158,10 +156,6 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
items_.append(item);
QMetaObject::invokeMethod(item,
"SignalAllKeyframes",
Qt::QueuedConnection);
emit OpenedNode(node);
if (!found_gizmos && node->HasGizmos()) {
@@ -170,25 +164,9 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
}
}
ViewerOutput* viewer = nodes_.first()->FindOutputNode<ViewerOutput>();
UpdateItemTime(GetTimestamp());
ConnectViewerNode(viewer);
if (viewer) {
SetTimebase(viewer->video_params().time_base());
// Set viewer as a time target
keyframe_view_->SetTimeTarget(viewer);
foreach (NodeParamViewItem* item, items_) {
item->SetTimeTarget(viewer);
}
emit TimeTargetChanged(viewer);
}
// Forces the scroll to update to this time
keyframe_view_->SetTime(ruler()->GetTime());
QMetaObject::invokeMethod(this, "PlaceKeyframesOnView", Qt::QueuedConnection);
}
}
@@ -211,6 +189,8 @@ void NodeParamView::TimebaseChangedEvent(const rational &timebase)
TimeBasedWidget::TimebaseChangedEvent(timebase);
keyframe_view_->SetTimebase(timebase);
UpdateItemTime(GetTimestamp());
}
void NodeParamView::TimeChangedEvent(const int64_t &timestamp)
@@ -222,6 +202,16 @@ void NodeParamView::TimeChangedEvent(const int64_t &timestamp)
UpdateItemTime(timestamp);
}
void NodeParamView::ConnectedNodeChanged(ViewerOutput *n)
{
// Set viewer as a time target
keyframe_view_->SetTimeTarget(n);
foreach (NodeParamViewItem* item, items_) {
item->SetTimeTarget(n);
}
}
const QList<Node *> &NodeParamView::nodes()
{
return nodes_;
@@ -239,7 +229,7 @@ void NodeParamView::DeleteSelected()
void NodeParamView::UpdateItemTime(const int64_t &timestamp)
{
rational time = Timecode::timestamp_to_time(timestamp, keyframe_view_->timebase());
rational time = Timecode::timestamp_to_time(timestamp, timebase());
foreach (NodeParamViewItem* item, items_) {
item->SetTime(time);
@@ -251,11 +241,16 @@ void NodeParamView::ItemRequestedTimeChanged(const rational &time)
SetTimeAndSignal(Timecode::time_to_timestamp(time, keyframe_view_->timebase()));
}
void NodeParamView::ForceKeyframeViewToScroll(int min, int max)
void NodeParamView::ForceKeyframeViewToScroll()
{
Q_UNUSED(min)
keyframe_view_->SetMaxScroll(param_widget_area_->height() - ruler()->height());
}
bottom_item_->setY(keyframe_view_->viewport()->height() + max);
void NodeParamView::PlaceKeyframesOnView()
{
foreach (NodeParamViewItem* item, items_) {
QMetaObject::invokeMethod(item, "SignalAllKeyframes", Qt::QueuedConnection);
}
}
OLIVE_NAMESPACE_EXIT
+7 -5
View File
@@ -47,8 +47,6 @@ public:
signals:
void InputDoubleClicked(NodeInput* input);
void TimeTargetChanged(Node* target);
void RequestSelectNode(const QList<Node*>& target);
void OpenedNode(Node* n);
@@ -64,6 +62,8 @@ protected:
virtual void TimebaseChangedEvent(const rational&) override;
virtual void TimeChangedEvent(const int64_t &) override;
virtual void ConnectedNodeChanged(ViewerOutput* n) override;
private:
void UpdateItemTime(const int64_t &timestamp);
@@ -77,14 +77,16 @@ private:
QScrollBar* vertical_scrollbar_;
QGraphicsRectItem* bottom_item_;
int last_scroll_val_;
QWidget* param_widget_area_;
private slots:
void ItemRequestedTimeChanged(const rational& time);
void ForceKeyframeViewToScroll(int min, int max);
void ForceKeyframeViewToScroll();
void PlaceKeyframesOnView();
};
@@ -96,7 +96,7 @@ void NodeParamViewWidgetBridge::CreateWidgets()
{
IntegerSlider* slider = new IntegerSlider();
slider->SetDefaultValue(input_->GetDefaultValue());
slider->SetLadderEnabled(true);
slider->SetLadderElementCount(2);
widgets_.append(slider);
connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
@@ -373,7 +373,7 @@ void NodeParamViewWidgetBridge::CreateSliders(int count)
for (int i=0;i<count;i++) {
FloatSlider* fs = new FloatSlider();
fs->SetDefaultValue(input_->GetDefaultValueForTrack(i));
fs->SetLadderEnabled(true);
fs->SetLadderElementCount(2);
widgets_.append(fs);
connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
}
+46 -10
View File
@@ -29,7 +29,8 @@
OLIVE_NAMESPACE_ENTER
NodeTableView::NodeTableView(QWidget* parent) :
QTreeWidget(parent)
QTreeWidget(parent),
last_set_node_(nullptr)
{
setColumnCount(3);
setHeaderLabels({tr("Type"),
@@ -42,11 +43,23 @@ NodeTableView::NodeTableView(QWidget* parent) :
void NodeTableView::SetNode(Node *n, const rational &time)
{
clear();
if (last_set_node_ != n) {
// Clear everything if the node has changed
clear();
}
last_set_node_ = n;
NodeTableTraverser traverser;
NodeValueDatabase db = traverser.GenerateDatabase(n, TimeRange(time, time));
// Remove top items if necessary
for (int i=0;i<this->topLevelItemCount();i++) {
if (!db.contains(this->topLevelItem(i)->data(0, Qt::UserRole).toString())) {
delete this->takeTopLevelItem(i);
i--;
}
}
NodeValueDatabase::const_iterator i;
for (i=db.begin(); i!=db.end(); i++) {
@@ -58,17 +71,40 @@ void NodeTableView::SetNode(Node *n, const rational &time)
continue;
}
QTreeWidgetItem* top_item = new QTreeWidgetItem();
top_item->setText(0, input->name());
top_item->setFirstColumnSpanned(true);
this->addTopLevelItem(top_item);
QTreeWidgetItem* top_item = nullptr;
for (int j=table.Count()-1; j>=0; j--) {
const NodeValue& value = table.at(j);
for (int j=0;j<this->topLevelItemCount();j++) {
QTreeWidgetItem* compare = this->topLevelItem(j);
if (compare->data(0, Qt::UserRole).toString() == input->id()) {
top_item = compare;
break;
}
}
if (!top_item) {
top_item = new QTreeWidgetItem();
top_item->setText(0, input->name());
top_item->setData(0, Qt::UserRole, input->id());
top_item->setFirstColumnSpanned(true);
this->addTopLevelItem(top_item);
}
// Create children if necessary
while (top_item->childCount() < table.Count()) {
top_item->addChild(new QTreeWidgetItem());
}
// Remove children if necessary
while (top_item->childCount() > table.Count()) {
delete top_item->takeChild(top_item->childCount() - 1);
}
for (int j=0;j<table.Count();j++) {
const NodeValue& value = table.at(table.Count() - 1 - j);
// Create item
QTreeWidgetItem* sub_item = new QTreeWidgetItem();
top_item->addChild(sub_item);
QTreeWidgetItem* sub_item = top_item->child(j);
// Set data type name
sub_item->setText(0, NodeParam::GetPrettyDataTypeName(value.type()));
+3
View File
@@ -36,6 +36,9 @@ public:
void SetMultipleNodeMessage();
private:
Node* last_set_node_;
};
OLIVE_NAMESPACE_EXIT
+25 -2
View File
@@ -25,7 +25,8 @@
OLIVE_NAMESPACE_ENTER
NodeTableWidget::NodeTableWidget(QWidget* parent) :
TimeBasedWidget(parent)
TimeBasedWidget(parent),
node_(nullptr)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setSpacing(0);
@@ -37,13 +38,35 @@ NodeTableWidget::NodeTableWidget(QWidget* parent) :
void NodeTableWidget::SetNodes(const QList<Node *> &nodes)
{
node_ = nullptr;
if (nodes.isEmpty()) {
view_->clear();
} else if (nodes.size() == 1) {
view_->SetNode(nodes.first(), rational());
node_ = nodes.first();
ViewerOutput* viewer = node_->FindOutputNode<ViewerOutput>();
if (viewer) {
qDebug() << "Found timebase";
SetTimebase(viewer->video_params().time_base());
}
UpdateView();
} else {
view_->SetMultipleNodeMessage();
}
}
void NodeTableWidget::TimeChangedEvent(const int64_t &)
{
UpdateView();
}
void NodeTableWidget::UpdateView()
{
if (node_) {
view_->SetNode(node_, GetTime());
}
}
OLIVE_NAMESPACE_EXIT
@@ -33,9 +33,16 @@ public:
void SetNodes(const QList<Node*>& nodes);
protected:
virtual void TimeChangedEvent(const int64_t& ts) override;
private:
void UpdateView();
NodeTableView* view_;
Node* node_;
};
OLIVE_NAMESPACE_EXIT
+59 -35
View File
@@ -40,8 +40,8 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) :
require_valid_input_(true),
tristate_(false),
drag_ladder_(nullptr),
enable_ladder_(false),
ladder_element_count_(2)
ladder_element_count_(0),
dragged_(false)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
@@ -52,9 +52,8 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) :
editor_ = new FocusableLineEdit(this);
addWidget(editor_);
connect(label_, &SliderLabel::LabelMoved, this, &SliderBase::LabelDragged);
connect(label_, &SliderLabel::LabelReleased, this, &SliderBase::LabelClicked);
connect(label_, &SliderLabel::focused, this, &SliderBase::LabelClicked);
connect(label_, &SliderLabel::LabelPressed, this, &SliderBase::LabelPressed);
connect(label_, &SliderLabel::focused, this, &SliderBase::ShowEditor);
connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue);
connect(editor_, &FocusableLineEdit::Confirmed, this, &SliderBase::LineEditConfirmed);
connect(editor_, &FocusableLineEdit::Cancelled, this, &SliderBase::LineEditCancelled);
@@ -201,6 +200,22 @@ QString SliderBase::GetFormat() const
}
}
void SliderBase::RepositionLadder()
{
QPoint label_global_pos = label_->mapToGlobal(label_->pos());
int text_width = QFontMetricsWidth(label_->fontMetrics(), label_->text());
QPoint ladder_pos(label_global_pos.x(),
label_global_pos.y() + label_->height() / 2 - drag_ladder_->height() / 2);
if (ladder_element_count_ > 0) {
ladder_pos.setX(ladder_pos.x() + text_width + QFontMetricsWidth(label_->fontMetrics(), QStringLiteral("H")));
} else {
ladder_pos.setX(ladder_pos.x() + text_width / 2 - drag_ladder_->width() / 2);
}
drag_ladder_->move(ladder_pos);
}
void SliderBase::UpdateLabel(const QVariant &v)
{
if (tristate_) {
@@ -226,23 +241,21 @@ QVariant SliderBase::StringToValue(const QString &s, bool *ok)
return s;
}
void SliderBase::LabelClicked()
void SliderBase::ShowEditor()
{
if (!drag_ladder_) {
// This was a simple click
// Load label's text into editor
editor_->setText(ValueToString(value_));
// This was a simple click
// Load label's text into editor
editor_->setText(ValueToString(value_));
// Show editor
setCurrentWidget(editor_);
// Show editor
setCurrentWidget(editor_);
// Select all text in the editor
editor_->setFocus();
editor_->selectAll();
}
// Select all text in the editor
editor_->setFocus();
editor_->selectAll();
}
void SliderBase::LabelDragged()
void SliderBase::LabelPressed()
{
switch (mode_) {
case kString:
@@ -250,22 +263,24 @@ void SliderBase::LabelDragged()
break;
case kInteger:
case kFloat:
drag_ladder_ = new SliderLadder(drag_multiplier_, enable_ladder_ ? ladder_element_count_ : 0);
{
drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_);
drag_ladder_->SetValue(ValueToString(value_));
drag_ladder_->show();
QPoint label_global_pos = label_->mapToGlobal(label_->pos());
drag_ladder_->move(label_global_pos.x() + QFontMetricsWidth(label_->fontMetrics(), label_->text()) / 2 - drag_ladder_->width() / 2,
label_global_pos.y() + label_->height() / 2 - drag_ladder_->height() / 2);
RepositionLadder();
connect(drag_ladder_, &SliderLadder::DraggedByValue, this, &SliderBase::LadderDragged);
connect(drag_ladder_, &SliderLadder::Released, this, &SliderBase::LadderReleased);
break;
}
}
}
void SliderBase::LadderDragged(double value, double multiplier)
void SliderBase::LadderDragged(int value, double multiplier)
{
dragged_ = true;
switch (mode_) {
case kString:
// No dragging supported for strings
@@ -292,7 +307,10 @@ void SliderBase::LadderDragged(double value, double multiplier)
}
UpdateLabel(temp_dragged_value_);
drag_ladder_->SetValue(ValueToString(temp_dragged_value_));
RepositionLadder();
emit ValueChanged(temp_dragged_value_);
break;
}
@@ -305,20 +323,26 @@ void SliderBase::LadderReleased()
drag_ladder_ = nullptr;
dragged_diff_ = 0;
// This was a drag
switch (mode_) {
case kString:
// No-op
break;
case kInteger:
SetValue(temp_dragged_value_.toInt());
break;
case kFloat:
SetValue(temp_dragged_value_.toDouble());
break;
}
if (dragged_) {
// This was a drag
switch (mode_) {
case kString:
// No-op
break;
case kInteger:
SetValue(temp_dragged_value_.toInt());
break;
case kFloat:
SetValue(temp_dragged_value_.toDouble());
break;
}
emit ValueChanged(value_);
emit ValueChanged(value_);
dragged_ = false;
} else {
ShowEditor();
}
}
void SliderBase::LineEditConfirmed()
+7 -10
View File
@@ -56,11 +56,6 @@ public:
void SetFormat(const QString& s);
void ClearFormat();
void SetLadderEnabled(bool e)
{
enable_ladder_ = e;
}
void SetLadderElementCount(int b)
{
ladder_element_count_ = b;
@@ -97,6 +92,8 @@ private:
QString GetFormat() const;
void RepositionLadder();
SliderLabel* label_;
FocusableLineEdit* editor_;
@@ -124,16 +121,16 @@ private:
SliderLadder* drag_ladder_;
bool enable_ladder_;
int ladder_element_count_;
bool dragged_;
private slots:
void LabelClicked();
void ShowEditor();
void LabelDragged();
void LabelPressed();
void LadderDragged(double value, double multiplier);
void LadderDragged(int value, double multiplier);
void LadderReleased();
+1 -18
View File
@@ -27,8 +27,7 @@
OLIVE_NAMESPACE_ENTER
SliderLabel::SliderLabel(QWidget *parent) :
QLabel(parent),
dragging_(false)
QLabel(parent)
{
QPalette p = palette();
@@ -55,26 +54,10 @@ void SliderLabel::mousePressEvent(QMouseEvent *e)
if (e->modifiers() & Qt::AltModifier) {
emit RequestReset();
} else {
dragging_ = true;
emit LabelPressed();
}
}
void SliderLabel::mouseMoveEvent(QMouseEvent *)
{
if (dragging_) {
emit LabelMoved();
}
}
void SliderLabel::mouseReleaseEvent(QMouseEvent *)
{
if (dragging_) {
emit LabelReleased();
dragging_ = false;
}
}
void SliderLabel::focusInEvent(QFocusEvent *event)
{
QWidget::focusInEvent(event);
-11
View File
@@ -36,26 +36,15 @@ public:
protected:
virtual void mousePressEvent(QMouseEvent *ev) override;
virtual void mouseMoveEvent(QMouseEvent *ev) override;
virtual void mouseReleaseEvent(QMouseEvent *ev) override;
virtual void focusInEvent(QFocusEvent *event) override;
signals:
void LabelPressed();
void LabelMoved();
void LabelReleased();
void focused();
void RequestReset();
private:
bool dragging_;
};
OLIVE_NAMESPACE_EXIT
+7 -12
View File
@@ -131,14 +131,15 @@ void SliderLadder::TimerUpdate()
QCursor::setPos(drag_start_);
#endif
int y_threshold = fontMetrics().height() / 2;
if (!x_mvmt && !y_mvmt) {
return;
}
if (qAbs(y_mvmt) > qAbs(x_mvmt)
|| qApp->keyboardModifiers() & Qt::ControlModifier) {
if (qApp->keyboardModifiers() & Qt::ControlModifier) {
// Movement is vertical
y_mobility_ += y_mvmt;
if (qAbs(y_mobility_) > y_threshold) {
if (qAbs(y_mobility_) > fontMetrics().height()) {
int new_active_element;
if (y_mvmt < 0) {
@@ -161,15 +162,9 @@ void SliderLadder::TimerUpdate()
y_mobility_ = 0;
}
} else {
// Movement is horizontal
emit DraggedByValue(x_mvmt , elements_.at(active_element_)->GetMultiplier());
y_mobility_ = 0;
// Reduce Y mobility
if (y_mobility_ > 0) {
y_mobility_--;
} else if (y_mobility_ < 0) {
y_mobility_++;
}
emit DraggedByValue(x_mvmt + y_mvmt, elements_.at(active_element_)->GetMultiplier());
}
}
+26 -3
View File
@@ -36,7 +36,8 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_statu
viewer_node_(nullptr),
auto_max_scrollbar_(false),
points_(nullptr),
toggle_show_all_(false)
toggle_show_all_(false),
auto_set_timebase_(true)
{
ruler_ = new TimeRuler(ruler_text_visible, ruler_cache_status_visible, this);
connect(ruler_, &TimeRuler::TimeChanged, this, &TimeBasedWidget::SetTimeAndSignal);
@@ -79,6 +80,11 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
DisconnectNodeInternal(viewer_node_);
disconnect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll);
disconnect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
if (auto_set_timebase_) {
SetTimebase(rational());
}
points_ = nullptr;
ruler()->ConnectTimelinePoints(nullptr);
@@ -95,6 +101,18 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node)
ruler()->ConnectTimelinePoints(points_);
}
if (auto_set_timebase_) {
if (!viewer_node_->video_params().time_base().isNull()) {
SetTimebase(viewer_node_->video_params().time_base());
} else if (viewer_node_->audio_params().sample_rate() > 0) {
SetTimebase(viewer_node_->audio_params().time_base());
} else {
SetTimebase(rational());
}
connect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
}
ConnectNodeInternal(viewer_node_);
}
@@ -332,6 +350,11 @@ void TimeBasedWidget::CenterScrollOnPlayhead()
scrollbar_->setValue(qRound(TimeToScene(Timecode::timestamp_to_time(ruler_->GetTime(), timebase()))) - scrollbar_->width()/2);
}
void TimeBasedWidget::SetAutoSetTimebase(bool e)
{
auto_set_timebase_ = e;
}
void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time)
{
if (!points_) {
@@ -459,12 +482,12 @@ void TimeBasedWidget::ToggleShowAll()
w = timeline_views_.first()->width();
}
w = w / 10 * 9;
toggle_show_all_old_scale_ = GetScale();
toggle_show_all_old_scroll_ = scrollbar_->value();
SetScale(w / GetConnectedNode()->GetLength().toDouble());
SetScaleFromDimensions(w, GetConnectedNode()->GetLength().toDouble());
scrollbar_->setValue(0);
// Must explicitly do this because SetScale() will automatically set this to false
+10 -2
View File
@@ -52,6 +52,8 @@ public:
void SetScaleAndCenterOnPlayhead(const double& scale);
TimeRuler* ruler() const;
public slots:
void SetTimestamp(int64_t timestamp);
@@ -89,8 +91,6 @@ public slots:
void GoToOut();
TimeRuler* ruler() const;
protected slots:
void SetTimeAndSignal(const int64_t& t);
@@ -127,6 +127,12 @@ protected slots:
*/
void CenterScrollOnPlayhead();
/**
* @brief By default, TimeBasedWidget will set the timebase to the viewer node's video timebase.
* Set this to false if you want to set your own timebase.
*/
void SetAutoSetTimebase(bool e);
signals:
void TimeChanged(const int64_t&);
@@ -170,6 +176,8 @@ private:
double toggle_show_all_old_scale_;
int toggle_show_all_old_scroll_;
bool auto_set_timebase_;
private slots:
void UpdateMaximumScroll();
@@ -27,6 +27,8 @@
OLIVE_NAMESPACE_ENTER
const int TimelineScaledObject::kCalculateDimensionsPadding = 10;
TimelineScaledObject::TimelineScaledObject() :
scale_(1.0),
min_scale_(0),
@@ -112,6 +114,21 @@ void TimelineScaledObject::SetScale(const double& scale)
ScaleChangedEvent(scale_);
}
void TimelineScaledObject::SetScaleFromDimensions(double viewport_width, double content_width)
{
SetScale(CalculateScaleFromDimensions(viewport_width, content_width));
}
double TimelineScaledObject::CalculateScaleFromDimensions(double viewport_sz, double content_sz)
{
return static_cast<double>(viewport_sz / kCalculateDimensionsPadding * (kCalculateDimensionsPadding-1)) / static_cast<double>(content_sz);
}
double TimelineScaledObject::CalculatePaddingFromDimensionScale(double viewport_sz)
{
return (viewport_sz / (kCalculateDimensionsPadding * 2));
}
TimelineScaledWidget::TimelineScaledWidget(QWidget *parent) :
QWidget(parent)
{
@@ -44,6 +44,10 @@ public:
void SetScale(const double& scale);
void SetScaleFromDimensions(double viewport_width, double content_width);
static double CalculateScaleFromDimensions(double viewport_sz, double content_sz);
static double CalculatePaddingFromDimensionScale(double viewport_sz);
protected:
double TimeToScene(const rational& time);
rational SceneToTime(const double &x, bool round = false);
@@ -67,6 +71,8 @@ private:
double max_scale_;
static const int kCalculateDimensionsPadding;
};
class TimelineScaledWidget : public QWidget, public TimelineScaledObject
+6 -4
View File
@@ -147,8 +147,10 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
view_splitter->setSizes({INT_MAX, INT_MAX});
// FIXME: Magic number
SetMaximumScale(TimelineViewBase::kMaximumScale);
SetScale(90.0);
SetMaximumScale(TimelineViewBase::kMaximumScale);
SetAutoSetTimebase(false);
}
TimelineWidget::~TimelineWidget()
@@ -1584,9 +1586,9 @@ bool TimelineWidget::SnapPoint(QList<rational> start_times, rational* movement,
// Find all points at this movement
QList<rational> snap_times;
foreach (const SnapData& data, potential_snaps) {
if (data.movement == *movement) {
snap_times.append(data.time);
foreach (const SnapData& d, potential_snaps) {
if (d.movement == *movement) {
snap_times.append(d.time);
}
}
@@ -45,7 +45,6 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setBackgroundRole(QPalette::Window);
setContextMenuPolicy(Qt::CustomContextMenu);
SetLimitYAxis(true);
viewport()->setMouseTracking(true);
connect(scene(), &QGraphicsScene::selectionChanged, this, &TimelineView::SelectionChanged);
@@ -39,9 +39,10 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) :
playhead_scene_left_(-1),
playhead_scene_right_(-1),
dragging_playhead_(false),
limit_y_axis_(false),
snapped_(false),
snap_service_(nullptr)
snap_service_(nullptr),
y_axis_enabled_(false),
y_scale_(1.0)
{
setScene(&scene_);
@@ -81,6 +82,26 @@ void TimelineViewBase::SetSnapService(SnapService *service)
snap_service_ = service;
}
const double &TimelineViewBase::GetYScale() const
{
return y_scale_;
}
void TimelineViewBase::VerticalScaleChangedEvent(double)
{
}
void TimelineViewBase::SetYScale(const double &y_scale)
{
y_scale_ = y_scale;
if (y_axis_enabled_) {
VerticalScaleChangedEvent(y_scale_);
viewport()->update();
}
}
void TimelineViewBase::SetTime(const int64_t time)
{
playhead_ = time;
@@ -253,10 +274,56 @@ bool TimelineViewBase::HandleZoomFromScroll(QWheelEvent *event)
if (WheelEventIsAZoomEvent(event)) {
// If CTRL is held (or a preference is set to swap CTRL behavior), we zoom instead of scrolling
if (!event->angleDelta().isNull()) {
if (event->angleDelta().x() + event->angleDelta().y() > 0) {
emit ScaleChanged(GetScale() * 1.1);
bool only_vertical = false;
bool only_horizontal = false;
// Ctrl+Shift limits to only one axis
// Alt switches between horizontal only (alt held) or vertical only (alt not held)
if (y_axis_enabled_) {
if (event->modifiers() & Qt::ShiftModifier) {
if (event->modifiers() & Qt::AltModifier) {
only_horizontal = true;
} else {
only_vertical = true;
}
}
} else {
emit ScaleChanged(GetScale() * 0.9);
only_horizontal = true;
}
double scale_multiplier;
if (event->angleDelta().x() + event->angleDelta().y() > 0) {
scale_multiplier = 1.1;
} else {
scale_multiplier = 0.9;
}
QPointF cursor_pos;
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
cursor_pos = event->position();
#else
cursor_pos = event->posF();
#endif
if (!only_vertical) {
double new_x_scale = GetScale() * scale_multiplier;
int new_x_scroll = qRound(horizontalScrollBar()->value() / GetScale() * new_x_scale + (cursor_pos.x() - cursor_pos.x() / new_x_scale * GetScale()));
emit ScaleChanged(new_x_scale);
horizontalScrollBar()->setValue(new_x_scroll);
}
if (!only_horizontal) {
double new_y_scale = GetYScale() * scale_multiplier;
int new_y_scroll = qRound(verticalScrollBar()->value() / GetYScale() * new_y_scale + (cursor_pos.y() - cursor_pos.y() / new_y_scale * GetYScale()));
SetYScale(new_y_scale);
verticalScrollBar()->setValue(new_y_scroll);
}
}
@@ -271,10 +338,4 @@ bool TimelineViewBase::WheelEventIsAZoomEvent(QWheelEvent *event)
return (static_cast<bool>(event->modifiers() & Qt::ControlModifier) == !Config::Current()["ScrollZooms"].toBool());
}
void TimelineViewBase::SetLimitYAxis(bool)
{
limit_y_axis_ = true;
UpdateSceneRect();
}
OLIVE_NAMESPACE_EXIT
@@ -48,6 +48,9 @@ public:
void SetSnapService(SnapService* service);
const double& GetYScale() const;
void SetYScale(const double& y_scale);
public slots:
void SetTime(const int64_t time);
@@ -69,12 +72,12 @@ protected:
virtual void SceneRectUpdateEvent(QRectF&){}
virtual void VerticalScaleChangedEvent(double scale);
bool HandleZoomFromScroll(QWheelEvent* event);
bool WheelEventIsAZoomEvent(QWheelEvent* event);
void SetLimitYAxis(bool e);
rational GetPlayheadTime() const;
bool PlayheadPress(QMouseEvent* event);
@@ -83,6 +86,16 @@ protected:
virtual void TimebaseChangedEvent(const rational &) override;
bool IsYAxisEnabled() const
{
return y_axis_enabled_;
}
void SetYAxisEnabled(bool e)
{
y_axis_enabled_ = e;
}
private:
qreal GetPlayheadX();
@@ -97,8 +110,6 @@ private:
QGraphicsScene scene_;
bool limit_y_axis_;
bool snapped_;
QList<rational> snap_time_;
@@ -106,6 +117,10 @@ private:
SnapService* snap_service_;
bool y_axis_enabled_;
double y_scale_;
private slots:
/**
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
-12
View File
@@ -168,15 +168,6 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i)
void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
{
if (!n->video_params().time_base().isNull()) {
SetTimebase(n->video_params().time_base());
} else if (n->audio_params().sample_rate() > 0) {
SetTimebase(n->audio_params().time_base());
} else {
SetTimebase(rational());
}
connect(n, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase);
connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot);
connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
connect(n, &ViewerOutput::ParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
@@ -231,9 +222,6 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
}
cache_wait_timer_.stop();
SetTimebase(rational());
disconnect(n, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase);
disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot);
disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
disconnect(n, &ViewerOutput::ParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
+2
View File
@@ -22,5 +22,7 @@ set(OLIVE_SOURCES
window/mainwindow/mainstatusbar.cpp
window/mainwindow/mainwindow.h
window/mainwindow/mainwindow.cpp
window/mainwindow/mainwindowlayoutinfo.h
window/mainwindow/mainwindowlayoutinfo.cpp
PARENT_SCOPE
)
+29 -71
View File
@@ -86,7 +86,9 @@ MainWindow::MainWindow(QWidget *parent) :
connect(node_panel_, &NodePanel::SelectionChanged, table_panel_, &NodeTablePanel::SetNodes);
connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select);
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp);
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp);
connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp);
connect(param_panel_, &ParamPanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp);
connect(param_panel_, &ParamPanel::FoundGizmos, sequence_viewer_panel_, &SequenceViewerPanel::SetGizmos);
connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged);
@@ -109,43 +111,41 @@ MainWindow::~MainWindow()
#endif
}
void MainWindow::LoadLayout(QXmlStreamReader *reader, XMLNodeData &xml_data)
void MainWindow::LoadLayout(const MainWindowLayoutInfo &info)
{
QMetaObject::invokeMethod(this,
"LoadLayoutInternal",
Qt::BlockingQueuedConnection,
Q_ARG(QXmlStreamReader*, reader),
Q_ARG(XMLNodeData*, &xml_data));
foreach (Folder* folder, info.open_folders()) {
FolderOpen(folder->project(), folder, true);
}
foreach (Sequence* sequence, info.open_sequences()) {
OpenSequence(sequence, info.open_sequences().size() == 1);
}
restoreState(info.state());
}
void MainWindow::SaveLayout(QXmlStreamWriter *writer) const
MainWindowLayoutInfo MainWindow::SaveLayout() const
{
writer->writeStartElement(QStringLiteral("layout"));
writer->writeStartElement(QStringLiteral("folders"));
MainWindowLayoutInfo info;
foreach (ProjectPanel* panel, folder_panels_) {
writer->writeTextElement(QStringLiteral("folder"),
QString::number(reinterpret_cast<quintptr>(panel->get_root_index().internalPointer())));
if (panel->project()) {
info.add_folder(static_cast<Folder*>(panel->get_root_index().internalPointer()));
}
}
writer->writeEndElement(); // folders
writer->writeStartElement(QStringLiteral("timeline"));
foreach (TimelinePanel* panel, timeline_panels_) {
writer->writeTextElement(QStringLiteral("sequence"),
QString::number(reinterpret_cast<quintptr>(panel->GetConnectedViewer())));
if (panel->GetConnectedViewer()) {
info.add_sequence(static_cast<Sequence*>(panel->GetConnectedViewer()->parent()));
}
}
writer->writeEndElement(); // timeline
info.set_state(saveState());
writer->writeTextElement(QStringLiteral("state"), QString(saveState().toBase64()));
writer->writeEndElement(); // layout
return info;
}
void MainWindow::OpenSequence(Sequence *sequence)
void MainWindow::OpenSequence(Sequence *sequence, bool enable_focus)
{
// See if this sequence is already open, and switch to it if so
foreach (TimelinePanel* tl, timeline_panels_) {
@@ -162,11 +162,14 @@ void MainWindow::OpenSequence(Sequence *sequence)
panel = timeline_panels_.first();
} else {
panel = AppendTimelinePanel();
enable_focus = false;
}
panel->ConnectViewerNode(sequence->viewer_output());
TimelineFocused(sequence->viewer_output());
if (enable_focus) {
TimelineFocused(sequence->viewer_output());
}
}
void MainWindow::CloseSequence(Sequence *sequence)
@@ -465,59 +468,13 @@ void MainWindow::FloatingPanelCloseRequested()
panel->deleteLater();
}
void MainWindow::LoadLayoutInternal(QXmlStreamReader *reader, XMLNodeData *xml_data)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folders")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folder")) {
quintptr item_id = reader->readElementText().toULongLong();
Item* open_item = xml_data->item_ptrs.value(item_id);
if (open_item) {
FolderOpen(open_item->project(), open_item, true);
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("timeline")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sequence")) {
quintptr item_id = reader->readElementText().toULongLong();
Sequence* open_seq = dynamic_cast<Sequence*>(xml_data->item_ptrs.value(item_id));
if (open_seq) {
OpenSequence(open_seq);
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("state")) {
QByteArray state = QByteArray::fromBase64(reader->readElementText().toLatin1());
restoreState(state);
} else {
reader->skipCurrentElement();
}
}
}
TimelinePanel* MainWindow::AppendTimelinePanel()
{
TimelinePanel* panel = AppendPanelInternal<TimelinePanel>(timeline_panels_);
connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::TimelineCloseRequested);
connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp);
connect(panel, &TimelinePanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp);
connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp);
connect(panel, &TimelinePanel::SelectionChanged, node_panel_, &NodePanel::SelectBlocks);
connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp);
@@ -565,6 +522,7 @@ void MainWindow::RemoveProjectPanel(ProjectPanel *panel)
void MainWindow::TimelineFocused(ViewerOutput* viewer)
{
sequence_viewer_panel_->ConnectViewerNode(viewer);
param_panel_->ConnectViewerNode(viewer);
Sequence* seq = nullptr;
+4 -5
View File
@@ -23,6 +23,7 @@
#include <QMainWindow>
#include "mainwindowlayoutinfo.h"
#include "panel/panelmanager.h"
#include "panel/audiomonitor/audiomonitor.h"
#include "panel/curve/curve.h"
@@ -55,11 +56,11 @@ public:
virtual ~MainWindow() override;
void LoadLayout(QXmlStreamReader* reader, XMLNodeData& xml_data);
void LoadLayout(const MainWindowLayoutInfo &info);
void SaveLayout(QXmlStreamWriter* writer) const;
MainWindowLayoutInfo SaveLayout() const;
void OpenSequence(Sequence* sequence);
void OpenSequence(Sequence* sequence, bool enable_focus = true);
void CloseSequence(Sequence* sequence);
@@ -167,8 +168,6 @@ private slots:
void FloatingPanelCloseRequested();
void LoadLayoutInternal(QXmlStreamReader* reader, XMLNodeData *xml_data);
void StatusBarDoubleClicked();
#ifdef Q_OS_LINUX
@@ -0,0 +1,96 @@
#include "mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
void MainWindowLayoutInfo::toXml(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("layout"));
writer->writeStartElement(QStringLiteral("folders"));
foreach (Folder* folder, open_folders_) {
writer->writeTextElement(QStringLiteral("folder"),
QString::number(reinterpret_cast<quintptr>(folder)));
}
writer->writeEndElement(); // folders
writer->writeStartElement(QStringLiteral("timeline"));
foreach (Sequence* sequence, open_sequences_) {
writer->writeTextElement(QStringLiteral("sequence"),
QString::number(reinterpret_cast<quintptr>(sequence)));
}
writer->writeEndElement(); // timeline
writer->writeTextElement(QStringLiteral("state"), QString(state_.toBase64()));
writer->writeEndElement(); // layout
}
MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, XMLNodeData &xml_data)
{
MainWindowLayoutInfo info;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folders")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folder")) {
quintptr item_id = reader->readElementText().toULongLong();
Item* open_item = xml_data.item_ptrs.value(item_id);
if (open_item) {
info.open_folders_.append(static_cast<Folder*>(open_item));
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("timeline")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sequence")) {
quintptr item_id = reader->readElementText().toULongLong();
Sequence* open_seq = dynamic_cast<Sequence*>(xml_data.item_ptrs.value(item_id));
if (open_seq) {
info.open_sequences_.append(open_seq);
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("state")) {
info.state_ = QByteArray::fromBase64(reader->readElementText().toLatin1());
} else {
reader->skipCurrentElement();
}
}
return info;
}
void MainWindowLayoutInfo::add_folder(olive::Folder *f)
{
open_folders_.append(f);
}
void MainWindowLayoutInfo::add_sequence(Sequence *s)
{
open_sequences_.append(s);
}
void MainWindowLayoutInfo::set_state(const QByteArray &layout)
{
state_ = layout;
}
OLIVE_NAMESPACE_EXIT
@@ -0,0 +1,52 @@
#ifndef MAINWINDOWLAYOUTINFO_H
#define MAINWINDOWLAYOUTINFO_H
#include "project/item/folder/folder.h"
#include "project/item/sequence/sequence.h"
OLIVE_NAMESPACE_ENTER
class MainWindowLayoutInfo
{
public:
MainWindowLayoutInfo() = default;
void toXml(QXmlStreamWriter* writer) const;
static MainWindowLayoutInfo fromXml(QXmlStreamReader* reader, XMLNodeData &xml_data);
void add_folder(Folder* f);
void add_sequence(Sequence* s);
void set_state(const QByteArray& layout);
const QList<Folder*>& open_folders() const
{
return open_folders_;
}
const QList<Sequence*>& open_sequences() const
{
return open_sequences_;
}
const QByteArray& state() const
{
return state_;
}
private:
QByteArray state_;
QList<Folder*> open_folders_;
QList<Sequence*> open_sequences_;
};
OLIVE_NAMESPACE_EXIT
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::MainWindowLayoutInfo)
#endif // MAINWINDOWLAYOUTINFO_H