diff --git a/app/dialog/task/CMakeLists.txt b/app/dialog/task/CMakeLists.txt
new file mode 100644
index 000000000..df35aad3a
--- /dev/null
+++ b/app/dialog/task/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Olive - Non-Linear Video Editor
+# Copyright (C) 2019 Olive Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+set(OLIVE_SOURCES
+ ${OLIVE_SOURCES}
+ dialog/task/task.h
+ dialog/task/task.cpp
+ PARENT_SCOPE
+)
diff --git a/app/dialog/task/task.cpp b/app/dialog/task/task.cpp
new file mode 100644
index 000000000..88ded60bc
--- /dev/null
+++ b/app/dialog/task/task.cpp
@@ -0,0 +1,67 @@
+#include "task.h"
+
+#include
+#include
+
+TaskDialog::TaskDialog(Task* task, const QString& title, QWidget *parent) :
+ ProgressDialog(task->GetTitle(), title, parent),
+ task_(task),
+ task_failed_(false)
+{
+ thread_ = new QThread();
+
+ // Connect the save manager progress signal to the progress bar update on the dialog
+ connect(task_, &Task::ProgressChanged, this, &TaskDialog::SetProgress, Qt::QueuedConnection);
+
+ // Connect cancel signal (must be a direct connection or it'll be queued after the save is already finished)
+ connect(this, &TaskDialog::Cancelled, this, &TaskDialog::reject, Qt::DirectConnection);
+
+ // Connect cleanup functions (ensure everything new'd in this function is deleteLater'd)
+ connect(task_, &Task::Finished, this, &TaskDialog::accept, Qt::QueuedConnection);
+ connect(task_, &Task::Finished, this, &TaskDialog::deleteLater, Qt::QueuedConnection);
+ connect(task_, &Task::Finished, task_, &Task::deleteLater, Qt::QueuedConnection);
+ connect(task_, &Task::Finished, thread_, &QThread::quit, Qt::QueuedConnection);
+ connect(task_, &Task::Finished, thread_, &QThread::deleteLater, Qt::QueuedConnection);
+}
+
+void TaskDialog::open()
+{
+ ProgressDialog::open();
+
+ if (thread_->isRunning()) {
+ // Task has already started, no need to do anymore
+ return;
+ }
+
+ // Create a separate thread to run this task in
+ thread_->start(QThread::LowPriority);
+
+ // Move the task to this thread
+ task_->moveToThread(thread_);
+
+ // Start the task
+ QMetaObject::invokeMethod(task_, "Start", Qt::QueuedConnection);
+}
+
+void TaskDialog::accept()
+{
+ if (task_failed_) {
+ QMessageBox::critical(this,
+ tr("Task Error"),
+ task_error_,
+ QMessageBox::Ok);
+ }
+
+ ProgressDialog::accept();
+}
+
+void TaskDialog::reject()
+{
+ task_->Cancel();
+}
+
+void TaskDialog::TaskFailed(const QString &s)
+{
+ task_failed_ = true;
+ task_error_ = s;
+}
diff --git a/app/dialog/task/task.h b/app/dialog/task/task.h
new file mode 100644
index 000000000..a2d339c2c
--- /dev/null
+++ b/app/dialog/task/task.h
@@ -0,0 +1,31 @@
+#ifndef TASKDIALOG_H
+#define TASKDIALOG_H
+
+#include "dialog/progress/progress.h"
+#include "task/task.h"
+
+class TaskDialog : public ProgressDialog
+{
+public:
+ TaskDialog(Task *task, const QString &title, QWidget* parent = nullptr);
+
+public slots:
+ virtual void open() override;
+
+ virtual void accept() override;
+ virtual void reject() override;
+
+private:
+ Task* task_;
+
+ QThread* thread_;
+
+ bool task_failed_;
+ QString task_error_;
+
+private slots:
+ void TaskFailed(const QString& s);
+
+};
+
+#endif // TASKDIALOG_H