New background tasks window

This commit is contained in:
Nav
2023-03-19 12:32:39 +00:00
parent dba7d83c4f
commit bb5270849d
9 changed files with 391 additions and 1 deletions

View File

@@ -39,10 +39,14 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/CaptureMemorySnapshot.cpp
${CMAKE_CURRENT_SOURCE_DIR}/InsightWorker/Tasks/RetrieveMemorySnapshots.cpp
# Task indicator
# Task indicators
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TaskIndicator/TaskIndicator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TaskProgressIndicator/TaskProgressIndicator.cpp
# Task window
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TaskWindow/TaskWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TaskWindow/Task.cpp
# Error dialogue window
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.cpp
@@ -155,6 +159,10 @@ qt_add_resources(
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/ascii-view-disabled.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg"
# Task window
"./UserInterfaces/InsightWindow/Widgets/TaskWindow/UiFiles/TaskWindow.ui"
"./UserInterfaces/InsightWindow/Widgets/TaskWindow/Stylesheets/TaskWindow.qss"
# Target package widgets
"./UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/DualInlinePackage.qss"
"./UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss"

View File

@@ -13,6 +13,8 @@ namespace Bloom::Widgets
this->setObjectName("task-indicator");
this->setFixedSize(50, 26);
this->taskWindow = new TaskWindow(this);
auto* insightSignals = InsightSignals::instance();
QObject::connect(
@@ -42,6 +44,11 @@ namespace Bloom::Widgets
this->update();
}
void TaskIndicator::mousePressEvent(QMouseEvent* event) {
this->taskWindow->show();
this->taskWindow->activateWindow();
}
void TaskIndicator::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
painter.setPen(Qt::PenStyle::NoPen);

View File

@@ -4,8 +4,10 @@
#include <QSharedPointer>
#include <unordered_map>
#include <QEvent>
#include <QMouseEvent>
#include "src/Insight/InsightWorker/Tasks/InsightWorkerTask.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TaskWindow/TaskWindow.hpp"
namespace Bloom::Widgets
{
@@ -19,12 +21,15 @@ namespace Bloom::Widgets
protected:
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
private:
std::unordered_map<InsightWorkerTask::IdType, QSharedPointer<InsightWorkerTask>> activeTasksById;
bool hovered = false;
TaskWindow* taskWindow = nullptr;
void onTaskQueued(QSharedPointer<InsightWorkerTask> task);
void onTaskFinished(QSharedPointer<InsightWorkerTask> task);

View File

@@ -0,0 +1,10 @@
#task-window,
#task-window #container {
background-color: #373835;
border: none;
}
#loading-placeholder-label {
color: #737375;
font-size: 14px;
}

View File

@@ -0,0 +1,148 @@
#include "Task.hpp"
#include <QPainter>
#include <QColor>
#include <QTimer>
namespace Bloom::Widgets
{
Task::Task(
const QSharedPointer<InsightWorkerTask>& task,
QWidget* parent
)
: QWidget(parent)
, task(task)
{
this->setObjectName("task");
this->setFixedHeight(80);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
this->setContentsMargins(15, 0, 15, 0);
QObject::connect(
this->task.get(),
&InsightWorkerTask::started,
this,
&Task::onTaskStateChanged
);
QObject::connect(
this->task.get(),
&InsightWorkerTask::finished,
this,
&Task::onTaskStateChanged
);
QObject::connect(
this->task.get(),
&InsightWorkerTask::failed,
this,
&Task::onTaskStateChanged
);
QObject::connect(
this->task.get(),
&InsightWorkerTask::finished,
this,
&Task::onTaskFinished
);
QObject::connect(
this->task.get(),
&InsightWorkerTask::progressUpdate,
this,
&Task::onTaskProgressUpdate
);
}
void Task::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
const auto margins = this->contentsMargins();
const auto size = this->size();
static constexpr auto backgroundBarColor = QColor(0x8E, 0x8B, 0x83, 40);
static constexpr auto barColor = QColor(0x8E, 0x8B, 0x83, 90);
static constexpr auto fontColor = QColor(0x99, 0x9a, 0x9d);
static constexpr auto statusFontColor = QColor(0x99, 0x9a, 0x9d, 200);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(14);
static auto statusFont = QFont("'Ubuntu', sans-serif");
statusFont.setPixelSize(12);
painter.setFont(font);
painter.setPen(fontColor);
static constexpr auto barHeight = 5;
const auto barYPosition = 35;
painter.drawText(
margins.left(),
barYPosition - 12,
this->task->brief()
);
const auto status = QString(
this->task->state == InsightWorkerTaskState::FAILED
? "Failed"
: this->task->state == InsightWorkerTaskState::COMPLETED
? "Completed"
: this->task->state == InsightWorkerTaskState::STARTED
? "Running"
: "Queued"
);
painter.setFont(statusFont);
painter.setPen(statusFontColor);
painter.drawText(
margins.left(),
barYPosition + barHeight + 20,
status
);
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(backgroundBarColor);
painter.drawRect(
margins.left(),
barYPosition,
size.width() - margins.left() - margins.right(),
barHeight
);
painter.setBrush(barColor);
painter.drawRect(
margins.left(),
barYPosition,
static_cast<int>(
static_cast<float>(size.width() - margins.left() - margins.right())
* (static_cast<float>(this->task->progressPercentage) / 100)
),
barHeight
);
painter.setPen(backgroundBarColor);
painter.drawLine(
0,
size.height() - 1,
size.width(),
size.height() - 1
);
}
void Task::onTaskProgressUpdate() {
this->update();
}
void Task::onTaskStateChanged() {
this->update();
}
void Task::onTaskFinished() {
emit this->taskComplete(this->task->id);
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <QWidget>
#include <QSharedPointer>
#include <QEvent>
#include "src/Insight/InsightWorker/Tasks/InsightWorkerTask.hpp"
namespace Bloom::Widgets
{
class Task: public QWidget
{
Q_OBJECT
public:
Task(const QSharedPointer<InsightWorkerTask>& task, QWidget* parent);
signals:
void taskComplete(InsightWorkerTask::IdType taskId);
protected:
void paintEvent(QPaintEvent* event) override;
private:
QSharedPointer<InsightWorkerTask> task;
void onTaskProgressUpdate();
void onTaskStateChanged();
void onTaskFinished();
};
}

View File

@@ -0,0 +1,100 @@
#include "TaskWindow.hpp"
#include <QScrollArea>
#include <QTimer>
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
#include "src/Insight/InsightSignals.hpp"
#include "src/Services/PathService.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::Widgets
{
TaskWindow::TaskWindow(QWidget* parent)
: QWidget(parent)
{
this->setObjectName("task-window");
this->setMinimumSize(900, 500);
this->setWindowFlag(Qt::Window);
this->setWindowTitle("Background Tasks");
auto windowUiFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TaskWindow/UiFiles/TaskWindow.ui"
)
);
auto stylesheetFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TaskWindow/Stylesheets/TaskWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exceptions::Exception("Failed to open TaskWindow UI file");
}
if (!stylesheetFile.open(QFile::ReadOnly)) {
throw Exceptions::Exception("Failed to open TaskWindow stylesheet file");
}
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setStyleSheet(stylesheetFile.readAll());
this->container->setContentsMargins(0, 0, 0, 0);
this->taskWidgetLayout = this->container->findChild<QVBoxLayout*>();
this->taskPlaceholderLabel = this->container->findChild<Label*>("loading-placeholder-label");
auto* insightSignals = InsightSignals::instance();
QObject::connect(
insightSignals,
&InsightSignals::taskQueued,
this,
&TaskWindow::onTaskQueued
);
}
void TaskWindow::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(this->size());
}
void TaskWindow::onTaskQueued(const QSharedPointer<InsightWorkerTask>& task) {
auto* taskWidget = new Task(task, this);
this->taskWidgetLayout->insertWidget(0, taskWidget);
QObject::connect(taskWidget, &Task::taskComplete, this, [this] (InsightWorkerTask::IdType taskId) {
auto* finishedSignalTimer = new QTimer();
finishedSignalTimer->setSingleShot(true);
finishedSignalTimer->setInterval(10000);
QObject::connect(finishedSignalTimer, &QTimer::timeout, this, [this, taskId] {
const auto& taskWidgetIt = this->taskWidgetsByTaskId.find(taskId);
if (taskWidgetIt == this->taskWidgetsByTaskId.end()) {
return;
}
auto* taskWidget = taskWidgetIt->second;
this->taskWidgetLayout->removeWidget(taskWidget);
taskWidget->deleteLater();
this->taskWidgetsByTaskId.erase(taskWidgetIt);
if (this->taskWidgetsByTaskId.empty()) {
this->taskPlaceholderLabel->show();
}
});
finishedSignalTimer->start();
});
this->taskWidgetsByTaskId.emplace(task->id, taskWidget);
this->taskPlaceholderLabel->hide();
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <QWidget>
#include <QSharedPointer>
#include <QVBoxLayout>
#include <unordered_map>
#include <QEvent>
#include "Task.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
#include "src/Insight/InsightWorker/Tasks/InsightWorkerTask.hpp"
namespace Bloom::Widgets
{
class TaskWindow: public QWidget
{
Q_OBJECT
public:
TaskWindow(QWidget* parent);
protected:
void resizeEvent(QResizeEvent* event) override;
private:
QWidget* container = nullptr;
QVBoxLayout* taskWidgetLayout = nullptr;
Label* taskPlaceholderLabel = nullptr;
std::unordered_map<InsightWorkerTask::IdType, Task*> taskWidgetsByTaskId;
void onTaskQueued(const QSharedPointer<InsightWorkerTask>& task);
};
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<widget class="QScrollArea" name="container">
<property name="widgetResizable"><bool>true</bool></property>
<property name="verticalScrollBarPolicy"><enum>Qt::ScrollBarAsNeeded</enum></property>
<property name="sizeAdjustPolicy"><enum>QAbstractScrollArea::AdjustToContents</enum></property>
<property name="horizontalScrollBarPolicy"><enum>Qt::ScrollBarAlwaysOff</enum></property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<widget class="QWidget" name="item-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<layout class="QVBoxLayout" name="item-container-layout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item alignment="Qt::AlignHCenter">
<widget class="Label" name="loading-placeholder-label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<property name="visible">
<bool>true</bool>
</property>
<property name="alignment">
<enum>Qt::AlignCenter</enum>
</property>
<property name="text">
<string>No background tasks</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</ui>