Merge branch 'master' of https://github.com/olive-editor/olive.git
This commit is contained in:
@@ -107,6 +107,7 @@ else()
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Wshadow
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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_;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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 ×tamp)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,6 +28,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class NodeTablePanel : public TimeBasedPanel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeTablePanel(QWidget* parent);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -115,13 +115,13 @@ void RenderBackend::ClearVideoQueue()
|
||||
|
||||
QFuture<QVector<QByteArray> > RenderBackend::Hash(const QVector<rational> ×)
|
||||
{
|
||||
return QtConcurrent::run(&pool_, [this](const QVector<rational> ×){
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,8 +49,6 @@ private:
|
||||
|
||||
QHash<rational, FramePtr> time_map_;
|
||||
|
||||
QList< QFuture<bool> > write_frame_futures_;
|
||||
|
||||
ColorManager* color_manager_;
|
||||
|
||||
ExportParams params_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
}
|
||||
|
||||
QAction* AddActionWithData(const QString& text,
|
||||
const QVariant& data,
|
||||
const QVariant& d,
|
||||
const QVariant& compare);
|
||||
|
||||
QAction *InsertAlphabetically(const QString& s);
|
||||
|
||||
@@ -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 ×tamp)
|
||||
@@ -222,6 +202,16 @@ void NodeParamView::TimeChangedEvent(const int64_t ×tamp)
|
||||
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 ×tamp)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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 ×tamp);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
|
||||
void SetMultipleNodeMessage();
|
||||
|
||||
private:
|
||||
Node* last_set_node_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user