SnapshotViewer window

This commit is contained in:
Nav
2023-04-12 22:52:28 +01:00
parent 694ba6385b
commit 761fef0cae
9 changed files with 822 additions and 0 deletions

View File

@@ -106,6 +106,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/MemorySnapshotItem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/CreateSnapshotWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/SnapshotViewer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/MemoryRegionItem.cpp
# Memory region manager window
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
@@ -188,6 +189,8 @@ qt_add_resources(
# Memory snapshots
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss"
# Memory region manager window
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"

View File

@@ -100,6 +100,18 @@ namespace Bloom::Widgets
}
);
QObject::connect(
this->snapshotListScene,
&ListScene::itemDoubleClicked,
this,
[this] (ListItem* item) {
auto* snapshotItem = dynamic_cast<MemorySnapshotItem*>(item);
if (snapshotItem != nullptr) {
this->onSnapshotItemDoubleClick(snapshotItem);
}
}
);
QObject::connect(
this->snapshotListScene,
@@ -108,6 +120,17 @@ namespace Bloom::Widgets
&SnapshotManager::onSnapshotItemContextMenu
);
QObject::connect(
this->openSnapshotViewerAction,
&QAction::triggered,
this,
[this] {
if (this->contextMenuSnapshotItem != nullptr) {
this->openSnapshotViewer(this->contextMenuSnapshotItem->memorySnapshot.id);
}
}
);
QObject::connect(
this->restoreSnapshotAction,
&QAction::triggered,
@@ -223,6 +246,25 @@ namespace Bloom::Widgets
this->selectedItem = item;
}
void SnapshotManager::openSnapshotViewer(const QString& snapshotId) {
auto snapshotViewerIt = this->snapshotViewersById.find(snapshotId);
if (snapshotViewerIt == this->snapshotViewersById.end()) {
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
assert(snapshotIt != this->snapshotsById.end());
snapshotViewerIt = this->snapshotViewersById.insert(
snapshotId,
new SnapshotViewer(snapshotIt.value(), this->memoryDescriptor, this)
);
}
auto* snapshotViewer = snapshotViewerIt.value();
snapshotViewer->show();
snapshotViewer->activateWindow();
}
void SnapshotManager::restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) {
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
assert(snapshotIt != this->snapshotsById.end());
@@ -316,6 +358,10 @@ namespace Bloom::Widgets
InsightWorker::queueTask(writeMemoryTask);
}
void SnapshotManager::onSnapshotItemDoubleClick(MemorySnapshotItem* item) {
this->openSnapshotViewer(item->memorySnapshot.id);
}
void SnapshotManager::onSnapshotItemContextMenu(ListItem *item, QPoint sourcePosition) {
auto* snapshotItem = dynamic_cast<MemorySnapshotItem*>(item);
@@ -326,6 +372,7 @@ namespace Bloom::Widgets
this->contextMenuSnapshotItem = snapshotItem;
auto* menu = new QMenu(this);
menu->addAction(this->openSnapshotViewerAction);
menu->addAction(this->deleteSnapshotAction);
menu->addSeparator();

View File

@@ -22,6 +22,7 @@
#include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp"
#include "MemorySnapshotItem.hpp"
#include "SnapshotViewer/SnapshotViewer.hpp"
namespace Bloom::Widgets
{
@@ -63,6 +64,7 @@ namespace Bloom::Widgets
QMap<QString, MemorySnapshot> snapshotsById;
QMap<QString, MemorySnapshotItem*> snapshotItemsById;
QMap<QString, SnapshotViewer*> snapshotViewersById;
QWidget* container = nullptr;
QWidget* toolBar = nullptr;
@@ -75,6 +77,7 @@ namespace Bloom::Widgets
MemorySnapshotItem* contextMenuSnapshotItem = nullptr;
QAction* openSnapshotViewerAction = new QAction("Open", this);
QAction* deleteSnapshotAction = new QAction("Delete", this);
QAction* restoreSnapshotAction = new QAction("Restore", this);
@@ -84,9 +87,12 @@ namespace Bloom::Widgets
bool captureFocusedRegions,
bool captureDirectlyFromTarget
);
void addSnapshot(MemorySnapshot&& snapshotTmp);
void onSnapshotItemSelected(MemorySnapshotItem* item);
void openSnapshotViewer(const QString& snapshotId);
void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled);
void onSnapshotItemDoubleClick(MemorySnapshotItem* item);
void onSnapshotItemContextMenu(ListItem* item, QPoint sourcePosition);
void onTargetStateChanged(Targets::TargetState newState);
};

View File

@@ -0,0 +1,100 @@
#include "MemoryRegionItem.hpp"
#include "src/Services/DateTimeService.hpp"
namespace Bloom::Widgets
{
MemoryRegionItem::MemoryRegionItem(const MemoryRegion& memoryRegion)
: memoryRegion(memoryRegion)
{
this->size = QSize(0, MemoryRegionItem::HEIGHT);
this->nameText = memoryRegion.name;
this->addressRangeText = "0x" + QString::number(this->memoryRegion.addressRange.startAddress, 16).toUpper()
+ QString(" -> ") + "0x" + QString::number(this->memoryRegion.addressRange.endAddress, 16).toUpper();
this->regionTypeText = this->memoryRegion.type == MemoryRegionType::EXCLUDED ? "Excluded" : "Focused";
this->createdDateText = memoryRegion.createdDate.toString(
memoryRegion.createdDate.date() == Services::DateTimeService::currentDate()
? "hh:mm"
: "dd/MM/yyyy hh:mm"
);
this->setToolTip(this->nameText);
}
void MemoryRegionItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
static constexpr auto margins = QMargins(10, 5, 10, 0);
painter->setOpacity(0.7);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(14);
static auto secondaryFont = QFont("'Ubuntu', sans-serif");
secondaryFont.setPixelSize(13);
static constexpr auto fontColor = QColor(0xAF, 0xB1, 0xB3);
static constexpr auto secondaryFontColor = QColor(0x8A, 0x8A, 0x8D);
painter->setFont(font);
painter->setPen(fontColor);
const auto fontMetrics = painter->fontMetrics();
const auto addressRangeTextSize = fontMetrics.size(Qt::TextSingleLine, this->addressRangeText);
const auto regionTypeTextSize = fontMetrics.size(Qt::TextSingleLine, this->regionTypeText);
const auto createdDateTextSize = fontMetrics.size(Qt::TextSingleLine, this->createdDateText);
constexpr auto nameTextRightMargin = 10;
const auto availableNameTextWidth = this->size.width() - margins.left() - margins.right()
- regionTypeTextSize.width() - nameTextRightMargin;
const auto nameText = fontMetrics.elidedText(
this->nameText,
Qt::TextElideMode::ElideRight,
availableNameTextWidth
);
const auto nameTextSize = fontMetrics.size(Qt::TextSingleLine, nameText);
const auto nameTextRect = QRect(
margins.left(),
margins.top(),
nameTextSize.width(),
nameTextSize.height()
);
painter->drawText(nameTextRect, Qt::AlignLeft, nameText);
painter->setFont(secondaryFont);
painter->setPen(secondaryFontColor);
const auto addressRangeTextRect = QRect(
margins.left(),
nameTextRect.bottom() + 5,
addressRangeTextSize.width(),
addressRangeTextSize.height()
);
painter->drawText(addressRangeTextRect, Qt::AlignLeft, this->addressRangeText);
const auto regionTypeTextRect = QRect(
this->size.width() - margins.right() - regionTypeTextSize.width(),
margins.top(),
regionTypeTextSize.width(),
regionTypeTextSize.height()
);
painter->drawText(regionTypeTextRect, Qt::AlignRight, this->regionTypeText);
const auto createdDateTextRect = QRect(
this->size.width() - margins.right() - createdDateTextSize.width(),
nameTextRect.bottom() + 5,
createdDateTextSize.width(),
createdDateTextSize.height()
);
painter->drawText(createdDateTextRect, Qt::AlignRight, this->createdDateText);
static constexpr auto borderColor = QColor(0x41, 0x42, 0x3F);
painter->setPen(borderColor);
painter->drawLine(0, this->size.height() - 1, this->size.width(), this->size.height() - 1);
}
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <QString>
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListItem.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::Widgets
{
class MemoryRegionItem: public ListItem
{
public:
const MemoryRegion& memoryRegion;
MemoryRegionItem(const MemoryRegion& memoryRegion);
bool operator < (const ListItem& rhs) const override {
const auto& rhsRegionItem = dynamic_cast<const MemoryRegionItem&>(rhs);
return this->memoryRegion.createdDate < rhsRegionItem.memoryRegion.createdDate;
}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
private:
static constexpr int HEIGHT = 50;
QString nameText;
QString addressRangeText;
QString regionTypeText;
QString createdDateText;
};
}

View File

@@ -0,0 +1,284 @@
#include "SnapshotViewer.hpp"
#include <QFile>
#include <QVBoxLayout>
#include <algorithm>
#include <QSize>
#include <QDesktopServices>
#include <algorithm>
#include <map>
#include "src/Insight/InsightWorker/Tasks/WriteTargetMemory.hpp"
#include "src/Insight/InsightWorker/InsightWorker.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ConfirmationDialog.hpp"
#include "src/Services/PathService.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/InsightSignals.hpp"
#include "src/Logger/Logger.hpp"
namespace Bloom::Widgets
{
using Bloom::Exceptions::Exception;
SnapshotViewer::SnapshotViewer(
MemorySnapshot& snapshot,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
)
: QWidget(parent)
, snapshot(snapshot)
, memoryDescriptor(memoryDescriptor)
, hexViewerData(snapshot.data)
{
this->setWindowFlag(Qt::Window);
this->setObjectName("snapshot-viewer");
this->setWindowTitle(this->snapshot.name + " (" + this->snapshot.id + ")");
auto windowUiFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui"
)
);
auto stylesheetFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open SnapshotViewer UI file");
}
if (!stylesheetFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open SnapshotViewer stylesheet file");
}
// Set ideal window size
this->setFixedSize(1200, 850);
this->setMinimumSize(700, 600);
this->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
auto uiLoader = UiLoader(this);
this->setStyleSheet(stylesheetFile.readAll());
this->container = uiLoader.load(&windowUiFile, this);
auto* containerLayout = this->container->findChild<QVBoxLayout*>();
this->detailsContainer = this->container->findChild<QWidget*>("details-container");
this->nameInput = this->detailsContainer->findChild<TextInput*>("name-input");
this->descriptionInput = this->detailsContainer->findChild<QPlainTextEdit*>("description-input");
auto* detailsContainerLayout = this->detailsContainer->findChild<QHBoxLayout*>();
detailsContainerLayout->setContentsMargins(0, 0, 0, 0);
auto* attributesLayout = this->detailsContainer->findChild<QVBoxLayout*>("attributes-layout");
attributesLayout->setContentsMargins(10, 10, 10, 0);
auto* rightPanelLayout = this->detailsContainer->findChild<QVBoxLayout*>("right-panel-layout");
rightPanelLayout->setContentsMargins(0, 0, 0, 0);
if (!this->snapshot.excludedRegions.empty() || !this->snapshot.focusedRegions.empty()) {
auto* memoryRegionsContainer = this->detailsContainer->findChild<QWidget*>("memory-regions-container");
auto* memoryRegionsLayout = memoryRegionsContainer->findChild<QVBoxLayout*>();
auto* noMemoryRegionsLabel = memoryRegionsContainer->findChild<Label*>("no-regions-label");
std::transform(
this->snapshot.focusedRegions.begin(),
this->snapshot.focusedRegions.end(),
std::back_inserter(this->memoryRegionItems),
[] (const MemoryRegion& focusedRegion) {
return new MemoryRegionItem(focusedRegion);
}
);
std::transform(
this->snapshot.excludedRegions.begin(),
this->snapshot.excludedRegions.end(),
std::back_inserter(this->memoryRegionItems),
[] (const MemoryRegion& excludedRegion) {
return new MemoryRegionItem(excludedRegion);
}
);
this->memoryRegionListView = new ListView(
std::vector<ListItem*>(this->memoryRegionItems.begin(), this->memoryRegionItems.end()),
this
);
this->memoryRegionListView->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
this->memoryRegionListScene = this->memoryRegionListView->listScene();
this->memoryRegionListScene->margins = QMargins(0, 5, 0, 5);
this->memoryRegionListScene->setSelectionLimit(2);
noMemoryRegionsLabel->hide();
memoryRegionsLayout->insertWidget(0, this->memoryRegionListView);
}
this->restoreBytesAction = new ContextMenuAction("Restore Selected", std::nullopt, this);
this->hexViewerWidget = new HexViewerWidget(
this->memoryDescriptor,
this->hexViewerData,
this->hexViewerWidgetSettings,
this->snapshot.focusedRegions,
this->snapshot.excludedRegions,
this
);
containerLayout->insertWidget(1, this->hexViewerWidget);
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
this->bottomBarLayout = this->bottomBar->findChild<QHBoxLayout*>();
auto* memoryCapacityLabel = this->bottomBar->findChild<Label*>("memory-capacity-label");
auto* snapshotIdLabel = this->bottomBar->findChild<Label*>("id-label");
auto* programCounterLabel = this->bottomBar->findChild<Label*>("program-counter-label");
auto* dateLabel = this->bottomBar->findChild<Label*>("date-label");
memoryCapacityLabel->setText(QLocale(QLocale::English).toString(this->memoryDescriptor.size()) + " Bytes");
snapshotIdLabel->setText(this->snapshot.id);
programCounterLabel->setText(
"0x" + QString::number(this->snapshot.programCounter, 16).rightJustified(8, '0').toUpper()
);
dateLabel->setText(this->snapshot.createdDate.toString("dd/MM/yyyy hh:mm"));
this->nameInput->setText(this->snapshot.name);
this->descriptionInput->setPlainText(this->snapshot.description);
this->taskProgressIndicator = new TaskProgressIndicator(this);
this->bottomBarLayout->insertWidget(2, this->taskProgressIndicator);
auto* insightSignals = InsightSignals::instance();
QObject::connect(
this->restoreBytesAction,
&ContextMenuAction::invoked,
this,
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
}
);
QObject::connect(this->hexViewerWidget, &HexViewerWidget::ready, this, &SnapshotViewer::onHexViewerReady);
this->hexViewerWidget->init();
this->move(this->parentWidget()->window()->geometry().center() - this->rect().center());
}
void SnapshotViewer::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
}
void SnapshotViewer::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(this->size());
QWidget::resizeEvent(event);
}
void SnapshotViewer::onHexViewerReady() {
this->hexViewerWidget->addExternalContextMenuAction(this->restoreBytesAction);
}
void SnapshotViewer::restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
bool confirmationPromptEnabled
) {
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
// Ideally, we'd use std::transform here, but that would require an additional pass to remove excluded bytes
for (const auto& pair : selectedByteItemsByAddress) {
if (pair.second->excluded) {
continue;
}
sortedByteItemsByAddress.insert(pair);
}
if (sortedByteItemsByAddress.empty()) {
// The user has only selected bytes that are within an excluded region - nothing to do here
return;
}
if (confirmationPromptEnabled) {
auto* confirmationDialog = new ConfirmationDialog(
"Restore selected bytes",
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
+ " byte(s) to the target's "
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
+ ".<br/><br/>Do you wish to proceed?",
"Proceed",
std::nullopt,
this
);
QObject::connect(
confirmationDialog,
&ConfirmationDialog::confirmed,
this,
[this, selectedByteItemsByAddress] {
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
}
);
confirmationDialog->show();
return;
}
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
if (address > (blockEndAddress + 1)) {
// Commit the block
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
const auto dataEndOffset = blockEndAddress - this->memoryDescriptor.addressRange.startAddress + 1;
writeBlocks.emplace_back(
blockStartAddress,
Targets::TargetMemoryBuffer(
this->snapshot.data.begin() + dataBeginOffset,
this->snapshot.data.begin() + dataEndOffset
)
);
blockStartAddress = address;
blockEndAddress = address;
continue;
}
blockEndAddress = address;
}
{
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
const auto dataEndOffset = blockEndAddress - this->memoryDescriptor.addressRange.startAddress + 1;
writeBlocks.emplace_back(
blockStartAddress,
Targets::TargetMemoryBuffer(
this->snapshot.data.begin() + dataBeginOffset,
this->snapshot.data.begin() + dataEndOffset
)
);
}
const auto writeMemoryTask = QSharedPointer<WriteTargetMemory>(
new WriteTargetMemory(this->memoryDescriptor, std::move(writeBlocks)),
&QObject::deleteLater
);
this->taskProgressIndicator->addTask(writeMemoryTask);
InsightWorker::queueTask(writeMemoryTask);
}
}

View File

@@ -0,0 +1,72 @@
#pragma once
#include <QWidget>
#include <QShowEvent>
#include <QResizeEvent>
#include <QHBoxLayout>
#include <QPlainTextEdit>
#include <optional>
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PushButton.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListView.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TaskProgressIndicator/TaskProgressIndicator.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.hpp"
#include "MemoryRegionItem.hpp"
namespace Bloom::Widgets
{
class SnapshotViewer: public QWidget
{
Q_OBJECT
public:
SnapshotViewer(
MemorySnapshot& snapshot,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent = nullptr
);
protected:
void showEvent(QShowEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
private:
MemorySnapshot& snapshot;
const Targets::TargetMemoryDescriptor& memoryDescriptor;
QWidget* container = nullptr;
QWidget* detailsContainer = nullptr;
TextInput* nameInput = nullptr;
QPlainTextEdit* descriptionInput = nullptr;
ListView* memoryRegionListView = nullptr;
ListScene* memoryRegionListScene = nullptr;
std::vector<MemoryRegionItem*> memoryRegionItems;
std::optional<Targets::TargetMemoryBuffer> hexViewerData;
HexViewerWidget* hexViewerWidget = nullptr;
HexViewerWidgetSettings hexViewerWidgetSettings = HexViewerWidgetSettings();
ContextMenuAction* restoreBytesAction = nullptr;
QWidget* bottomBar = nullptr;
QHBoxLayout* bottomBarLayout = nullptr;
TaskProgressIndicator* taskProgressIndicator = nullptr;
PushButton* closeButton = nullptr;
void onHexViewerReady();
void restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
bool confirmationPromptEnabled
);
};
}

View File

@@ -0,0 +1,49 @@
#snapshot-viewer,
#snapshot-viewer #container {
background-color: #373835;
}
#snapshot-viewer #top-bar {
border-bottom: 1px solid #41423f;
}
#snapshot-viewer #bottom-bar {
border-top: 1px solid #41423f;
}
#snapshot-viewer #details-container {
border-bottom: 1px solid #41423f;
}
#snapshot-viewer #right-panel-container {
border-left: 1px solid #41423f;
}
#snapshot-viewer #memory-regions-container #no-regions-label {
color: #838386;
}
#snapshot-viewer #bottom-bar #separator {
background-color: transparent;
border-right: 1px solid #41423f;
padding: 0;
min-width: 1px;
max-width: 1px;
}
#snapshot-viewer #memory-capacity-label,
#snapshot-viewer #id-label,
#snapshot-viewer #program-counter-label,
#snapshot-viewer #date-label {
color: #8a8a8d;
}
#snapshot-viewer #program-counter-label,
#snapshot-viewer #date-label {
padding: 1px 3px;
}
#snapshot-viewer #memory-capacity-label,
#snapshot-viewer #id-label {
padding: 1px 5px;
}

View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<widget class="QWidget" name="container">
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="details-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="attributes-layout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="LabeledSeparator" name="details-separator">
<property name="title">
<string>Details</string>
</property>
</widget>
</item>
<item>
<spacer name="vertical-spacer">
<property name="sizeHint">
<size>
<height>10</height>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item alignment="Qt::AlignVCenter">
<widget class="TextInput" name="name-input">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<property name="maximumWidth">
<number>800</number>
</property>
</widget>
</item>
<item>
<spacer name="vertical-spacer">
<property name="sizeHint">
<size>
<height>10</height>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPlainTextEdit" name="description-input">
<property name="minimumHeight">
<number>100</number>
</property>
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="vertical-spacer">
<property name="sizeHint">
<size>
<height>10</height>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="right-panel-container">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding"/>
</property>
<property name="minimumWidth">
<number>400</number>
</property>
<layout class="QVBoxLayout" name="right-panel-layout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="memory-regions-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item alignment="Qt::AlignHCenter">
<widget class="Label" name="no-regions-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 memory regions were captured in this snapshot</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bottom-bar">
<property name="minimumHeight">
<number>28</number>
</property>
<property name="maximumHeight">
<number>28</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="Label" name="memory-capacity-label">
<property name="toolTip">
<string>Memory capacity</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="separator"/>
</item>
<item>
<widget class="QFrame" name="separator"/>
</item>
<item>
<widget class="Label" name="program-counter-label">
<property name="toolTip">
<string>Program counter at point of capture</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="separator"/>
</item>
<item>
<widget class="Label" name="date-label">
<property name="toolTip">
<string>Date of capture</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="separator"/>
</item>
<item>
<widget class="Label" name="id-label">
<property name="toolTip">
<string>Snapshot ID</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</ui>