project: improved main window layout loading

There were a lot of issues that arose from trying to make GUI changes
from another thread (even though we ran those functions in the right
thread). Now we store layout information until the end of the load and
make the changes then. This works much better from both a business logic
and user experience perspective.

Also prevents multiple sequences from taking focus during load and
starting a render job.
This commit is contained in:
itsmattkc
2020-06-19 02:23:06 +10:00
parent 115e62f140
commit 0280ade20d
11 changed files with 231 additions and 121 deletions
+31 -38
View File
@@ -151,6 +151,30 @@ 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
@@ -480,9 +504,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 +655,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 +1014,11 @@ void Core::OpenProjectInternal(const QString &filename)
ProjectLoadTask* plm = new ProjectLoadTask(filename);
if (gui_active_) {
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTask);
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTask);
task_dialog->open();
} else {
//connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
}
task_dialog->open();
}
int Core::CountFilesInFileList(const QFileInfoList &filenames)
+1 -1
View File
@@ -149,7 +149,7 @@ void Sequence::Save(QXmlStreamWriter *writer) const
writer->writeAttribute(QStringLiteral("name"), name());
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(viewer_output_)));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeStartElement(QStringLiteral("video"));
+9 -3
View File
@@ -37,7 +37,7 @@ Project::Project() :
root_.set_project(this);
}
void Project::Load(QXmlStreamReader *reader, const QAtomicInt* cancelled)
void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, const QAtomicInt* cancelled)
{
XMLNodeData xml_node_data;
@@ -62,7 +62,12 @@ void Project::Load(QXmlStreamReader *reader, const QAtomicInt* cancelled)
} else if (reader->name() == QStringLiteral("layout")) {
Core::instance()->main_window()->LoadLayout(reader, xml_node_data);
// Since the main window's functions have to occur in the GUI thread (and we're likely
// loading in a secondary thread), we load all necessary data into a separate struct so we
// can continue loading and queue it with the main window so it can handle the data
// appropriately in its own thread.
*layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data);
} else {
reader->skipCurrentElement();
@@ -93,7 +98,8 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // colormanagement
// Save main window project layout
Core::instance()->main_window()->SaveLayout(writer);
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
main_window_info.toXml(writer);
writer->writeEndElement(); // project
}
+2 -1
View File
@@ -26,6 +26,7 @@
#include "render/colormanager.h"
#include "project/item/folder/folder.h"
#include "window/mainwindow/mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -46,7 +47,7 @@ class Project : public QObject
public:
Project();
void Load(QXmlStreamReader* reader, const QAtomicInt* cancelled);
void Load(QXmlStreamReader* reader, MainWindowLayoutInfo *layout, const QAtomicInt* cancelled);
void Save(QXmlStreamWriter* writer) const;
+4 -1
View File
@@ -51,13 +51,16 @@ bool ProjectLoadTask::Run()
project->set_filename(filename_);
project->Load(&reader, &IsCancelled());
MainWindowLayoutInfo layout;
project->Load(&reader, &layout, &IsCancelled());
// Ensure project is in main thread
moveToThread(qApp->thread());
if (!IsCancelled()) {
projects_.append(project);
layout_info_.append(layout);
}
} else {
reader.skipCurrentElement();
+9 -1
View File
@@ -23,6 +23,7 @@
#include "project/project.h"
#include "task/task.h"
#include "window/mainwindow/mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -32,17 +33,24 @@ class ProjectLoadTask : public Task
public:
ProjectLoadTask(const QString& filename);
const QList<ProjectPtr>& GetLoadedProjects()
const QList<ProjectPtr>& GetLoadedProjects() const
{
return projects_;
}
const QList<MainWindowLayoutInfo>& GetLoadedLayouts() const
{
return layout_info_;
}
protected:
virtual bool Run() override;
private:
QList<ProjectPtr> projects_;
QList<MainWindowLayoutInfo> layout_info_;
QString filename_;
};
+2
View File
@@ -22,5 +22,7 @@ set(OLIVE_SOURCES
window/mainwindow/mainstatusbar.cpp
window/mainwindow/mainwindow.h
window/mainwindow/mainwindow.cpp
window/mainwindow/mainwindowlayoutinfo.h
window/mainwindow/mainwindowlayoutinfo.cpp
PARENT_SCOPE
)
+21 -71
View File
@@ -111,43 +111,37 @@ 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, false);
}
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())));
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())));
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_) {
@@ -164,11 +158,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)
@@ -467,53 +464,6 @@ 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_);
+4 -5
View File
@@ -23,6 +23,7 @@
#include <QMainWindow>
#include "mainwindowlayoutinfo.h"
#include "panel/panelmanager.h"
#include "panel/audiomonitor/audiomonitor.h"
#include "panel/curve/curve.h"
@@ -55,11 +56,11 @@ public:
virtual ~MainWindow() override;
void LoadLayout(QXmlStreamReader* reader, XMLNodeData& xml_data);
void LoadLayout(const MainWindowLayoutInfo &info);
void SaveLayout(QXmlStreamWriter* writer) const;
MainWindowLayoutInfo SaveLayout() const;
void OpenSequence(Sequence* sequence);
void OpenSequence(Sequence* sequence, bool enable_focus = true);
void CloseSequence(Sequence* sequence);
@@ -167,8 +168,6 @@ private slots:
void FloatingPanelCloseRequested();
void LoadLayoutInternal(QXmlStreamReader* reader, XMLNodeData *xml_data);
void StatusBarDoubleClicked();
#ifdef Q_OS_LINUX
@@ -0,0 +1,96 @@
#include "mainwindowlayoutinfo.h"
OLIVE_NAMESPACE_ENTER
void MainWindowLayoutInfo::toXml(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("layout"));
writer->writeStartElement(QStringLiteral("folders"));
foreach (Folder* folder, open_folders_) {
writer->writeTextElement(QStringLiteral("folder"),
QString::number(reinterpret_cast<quintptr>(folder)));
}
writer->writeEndElement(); // folders
writer->writeStartElement(QStringLiteral("timeline"));
foreach (Sequence* sequence, open_sequences_) {
writer->writeTextElement(QStringLiteral("sequence"),
QString::number(reinterpret_cast<quintptr>(sequence)));
}
writer->writeEndElement(); // timeline
writer->writeTextElement(QStringLiteral("state"), QString(state_.toBase64()));
writer->writeEndElement(); // layout
}
MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, XMLNodeData &xml_data)
{
MainWindowLayoutInfo info;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folders")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folder")) {
quintptr item_id = reader->readElementText().toULongLong();
Item* open_item = xml_data.item_ptrs.value(item_id);
if (open_item) {
info.open_folders_.append(static_cast<Folder*>(open_item));
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("timeline")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sequence")) {
quintptr item_id = reader->readElementText().toULongLong();
Sequence* open_seq = dynamic_cast<Sequence*>(xml_data.item_ptrs.value(item_id));
if (open_seq) {
info.open_sequences_.append(open_seq);
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("state")) {
info.state_ = QByteArray::fromBase64(reader->readElementText().toLatin1());
} else {
reader->skipCurrentElement();
}
}
return info;
}
void MainWindowLayoutInfo::add_folder(olive::Folder *f)
{
open_folders_.append(f);
}
void MainWindowLayoutInfo::add_sequence(Sequence *s)
{
open_sequences_.append(s);
}
void MainWindowLayoutInfo::set_state(const QByteArray &layout)
{
state_ = layout;
}
OLIVE_NAMESPACE_EXIT
@@ -0,0 +1,52 @@
#ifndef MAINWINDOWLAYOUTINFO_H
#define MAINWINDOWLAYOUTINFO_H
#include "project/item/folder/folder.h"
#include "project/item/sequence/sequence.h"
OLIVE_NAMESPACE_ENTER
class MainWindowLayoutInfo
{
public:
MainWindowLayoutInfo() = default;
void toXml(QXmlStreamWriter* writer) const;
static MainWindowLayoutInfo fromXml(QXmlStreamReader* reader, XMLNodeData &xml_data);
void add_folder(Folder* f);
void add_sequence(Sequence* s);
void set_state(const QByteArray& layout);
const QList<Folder*>& open_folders() const
{
return open_folders_;
}
const QList<Sequence*>& open_sequences() const
{
return open_sequences_;
}
const QByteArray& state() const
{
return state_;
}
private:
QByteArray state_;
QList<Folder*> open_folders_;
QList<Sequence*> open_sequences_;
};
OLIVE_NAMESPACE_EXIT
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::MainWindowLayoutInfo)
#endif // MAINWINDOWLAYOUTINFO_H