From 7c65e182afcac080acf60fb196f3c425ae45e6d6 Mon Sep 17 00:00:00 2001 From: Nav Date: Sun, 17 Oct 2021 20:44:40 +0100 Subject: [PATCH] New target memory inspection pane and hex viewer widget --- CMakeLists.txt | 5 + .../InsightWindow/InsightWindow.cpp | 18 +- .../InsightWindow/InsightWindow.hpp | 2 + .../Stylesheets/InsightWindow.qss | 77 +++++++- .../InsightWindow/UiFiles/InsightWindow.ui | 68 +++++-- .../HexViewerWidget/ByteWidget.cpp | 114 ++++++++++++ .../HexViewerWidget/ByteWidget.hpp | 63 +++++++ .../HexViewerWidget/ByteWidgetContainer.cpp | 152 ++++++++++++++++ .../HexViewerWidget/ByteWidgetContainer.hpp | 63 +++++++ .../HexViewerWidget/HexViewerWidget.cpp | 137 ++++++++++++++ .../HexViewerWidget/HexViewerWidget.hpp | 59 ++++++ .../UiFiles/HexViewerWidget.ui | 125 +++++++++++++ .../memory-inspection-disabled-icon.svg | 115 ++++++++++++ .../Images/memory-inspection-icon.svg | 170 ++++++++++++++++++ .../TargetMemoryInspectionPane.cpp | 155 ++++++++++++++++ .../TargetMemoryInspectionPane.hpp | 63 +++++++ .../UiFiles/TargetMemoryInspectionPane.ui | 62 +++++++ src/resources.qrc | 4 + 18 files changed, 1435 insertions(+), 17 deletions(-) create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-disabled-icon.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fbf09bf..e0695bba 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,11 @@ add_executable(Bloom src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/BitsetWidget/BitBodyWidget.cpp + # Target memory inspection pane + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp ) set_target_properties(Bloom PROPERTIES OUTPUT_NAME bloom) diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp index 28fead77..0ccaadec 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp @@ -27,6 +27,7 @@ using Bloom::Targets::TargetPinState; using Bloom::Targets::TargetVariant; using Bloom::Targets::TargetPackage; using Bloom::Targets::TargetPinDescriptor; +using Bloom::Targets::TargetMemoryType; InsightWindow::InsightWindow(InsightWorker& insightWorker): QMainWindow(nullptr), insightWorker(insightWorker) { this->setObjectName("main-window"); @@ -239,10 +240,12 @@ void InsightWindow::toggleTargetRegistersPane() { void InsightWindow::toggleRamInspectionPane() { if (this->bottomPanel->isVisible()) { + this->ramInspectionPane->deactivate(); this->bottomPanel->hide(); this->ramInspectionButton->setChecked(false); } else { + this->ramInspectionPane->activate(); this->bottomPanel->show(); this->ramInspectionButton->setChecked(true); } @@ -486,13 +489,26 @@ void InsightWindow::activate() { auto leftPanelLayout = this->leftPanel->layout(); this->targetRegistersSidePane = new TargetRegistersPaneWidget( this->targetDescriptor, - insightWorker, + this->insightWorker, this->leftPanel ); leftPanelLayout->addWidget(this->targetRegistersSidePane); this->targetRegistersButton->setChecked(false); this->targetRegistersButton->setDisabled(false); + auto bottomPanelLayout = this->bottomPanel->layout(); + if (this->targetDescriptor.memoryDescriptorsByType.contains(TargetMemoryType::RAM)) { + auto& ramDescriptor = this->targetDescriptor.memoryDescriptorsByType.at(TargetMemoryType::RAM); + this->ramInspectionPane = new TargetMemoryInspectionPane( + ramDescriptor, + this->insightWorker, + this->bottomPanel + ); + bottomPanelLayout->addWidget(this->ramInspectionPane); + this->ramInspectionButton->setChecked(false); + this->ramInspectionButton->setDisabled(false); + } + this->toggleUi(this->targetState != TargetState::STOPPED); this->activated = true; } diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp index ab9a0d65..7ea4747f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp @@ -18,6 +18,7 @@ #include "Widgets/TargetWidgets/TargetPackageWidgetContainer.hpp" #include "Widgets/TargetWidgets/TargetPackageWidget.hpp" #include "Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp" +#include "Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp" #include "AboutWindow.hpp" namespace Bloom @@ -91,6 +92,7 @@ namespace Bloom QWidget* bottomMenuBar = nullptr; Widgets::PanelWidget* bottomPanel = nullptr; + Widgets::TargetMemoryInspectionPane* ramInspectionPane = nullptr; QToolButton* ramInspectionButton = nullptr; QWidget* footer = nullptr; diff --git a/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss b/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss index 7fe730d9..7991aafd 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss +++ b/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss @@ -299,19 +299,30 @@ QScrollBar::sub-line:vertical { border-top: 1px solid #2F2F2D; } -#bottom-menu-bar #ram-inspection-btn { +#bottom-menu-bar #ram-inspection-btn, +#bottom-menu-bar #eeprom-inspection-btn { position: relative; border: none; - min-width: 90px; min-height: 22px; } -#bottom-menu-bar #ram-inspection-btn #ram-inspection-btn-icon { - margin-left: 9px; +#bottom-menu-bar #ram-inspection-btn { + min-width: 71px; } -#bottom-menu-bar #ram-inspection-btn #ram-inspection-btn-label { - margin-left: 2px; +#bottom-menu-bar #eeprom-inspection-btn { + min-width: 97px; +} + +#bottom-menu-bar #ram-inspection-btn #ram-inspection-btn-icon, +#bottom-menu-bar #eeprom-inspection-btn #eeprom-inspection-btn-icon { + margin-left: 7px; + margin-top: 1px; +} + +#bottom-menu-bar #ram-inspection-btn #ram-inspection-btn-label, +#bottom-menu-bar #eeprom-inspection-btn #eeprom-inspection-btn-label { + margin-left: 1px; } #bottom-menu-bar QToolButton:hover, @@ -324,3 +335,57 @@ QScrollBar::sub-line:vertical { border-top: 1px solid #2F2F2D; background-color: transparent; } + +/* Target Memory Inspection Pane */ +#target-memory-inspection-pane #title-bar { + background-color: #383F43; + border-bottom: 1px solid #2F2F2D; +} + +#target-memory-inspection-pane #title { + color: #afb1b3; +} + +#hex-viewer-container #tool-bar, +#hex-viewer-container #search-bar { + border-bottom: 1px solid #2F2F2D; +} + +#hex-viewer-container #tool-bar QToolButton { + background-color: transparent; + border: none; + padding: 0; + qproperty-buttonWidth: 24; + qproperty-buttonHeight: 20; +} + +#hex-viewer-container #tool-bar QToolButton:hover { + background-color: #454541; + border: none; + padding: 0; +} + +#hex-viewer-container QScrollArea, +#hex-viewer-container #byte-widget-scroll-area-container { + background-color: transparent; + border: none; +} + +#hex-viewer-container #byte-widget-scroll-area-container #address-container { + background-color: transparent; + border-right: 1px solid #2F2F2D; +} + +#hex-viewer-container #byte-widget-scroll-area-container #address-container QLabel { + background-color: transparent; + font-size: 12px; + color: rgba(175, 177, 179, 0.72); +} + +#hex-viewer-container #byte-widget-scroll-area-container #address-container QLabel:disabled { + color: rgba(175, 177, 179, 0.3); +} + +#hex-viewer-container #byte { + font-size: 11px; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui b/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui index be94b3ca..14a359de 100644 --- a/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui +++ b/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui @@ -252,11 +252,8 @@ PanelWidgetType::BOTTOM - - 200 - - 200 + 300 10 @@ -275,14 +272,14 @@ - + - 1 + 0 0 @@ -323,20 +320,71 @@ 22 - 15 + 22 - :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg - :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon-disabled.svg - Memory + RAM + + + + + + + Qt::Horizontal + + + + + + + + + + Inspect EEPROM + + + true + + + false + + + + 0 + + + 0 + + + + + 22 + + + 22 + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon-disabled.svg + + + + + + + EEPROM diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp new file mode 100644 index 00000000..5bc94338 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp @@ -0,0 +1,114 @@ +#include "ByteWidget.hpp" + +#include +#include + +using namespace Bloom::Widgets; + +ByteWidget::ByteWidget( + std::size_t byteIndex, + std::uint32_t address, + std::optional& hoveredByteWidget, + QWidget* parent +): ClickableWidget(parent), byteIndex(byteIndex), address(address), hoveredByteWidget(hoveredByteWidget) { + this->setObjectName("byte"); + auto onClick = [this] { + this->setSelected(true); + }; + + this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(8, '0').toUpper(); + this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(8, '0').toUpper(); + + this->connect(this, &ClickableWidget::clicked, this, onClick); + this->connect(this, &ClickableWidget::rightClicked, this, onClick); + + this->setSelected(false); +} + +void ByteWidget::setValue(unsigned char value) { + this->valueChanged = this->valueInitialised && this->value != value; + + this->value = value; + this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper(); + this->asciiValue = (this->value >= 32 && this->value <= 126) + ? std::optional(QString(QChar(this->value))) : std::nullopt; + + this->valueInitialised = true; +} + +void ByteWidget::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); + + if (selected) { + emit this->selected(this); + } + + this->postSetSelected(selected); +} + +bool ByteWidget::event(QEvent* event) { + if (this->isEnabled()) { + switch (event->type()) { + case QEvent::Enter: { + this->hoverActive = true; + emit this->enter(this); + this->update(); + break; + } + case QEvent::Leave: { + this->hoverActive = false; + emit this->leave(this); + this->update(); + break; + } + default: { + break; + } + } + } + + return QWidget::event(event); +} + +void ByteWidget::paintEvent(QPaintEvent* event) { + auto painter = QPainter(this); + this->drawWidget(painter); +} + +void ByteWidget::drawWidget(QPainter& painter) { + painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true); + painter.setPen(Qt::PenStyle::NoPen); + + static const auto widgetRect = QRect(0, 0, ByteWidget::WIDTH, ByteWidget::HEIGHT); + + if (this->hoveredByteWidget.has_value() + && ( + this->hoveredByteWidget.value()->currentColumnIndex == this->currentColumnIndex + || this->hoveredByteWidget.value()->currentRowIndex == this->currentRowIndex + ) + ) { + painter.setBrush(QColor(0x8E, 0x8B, 0x83, this->hoverActive ? 70 : 30)); + painter.drawRect(widgetRect); + } + + auto textColor = QColor(this->valueChanged ? "#547fba" : "#afb1b3"); + + if (this->valueInitialised) { + + if (!this->isEnabled()) { + textColor.setAlpha(100); + } + + painter.setPen(textColor); + painter.drawText(widgetRect, Qt::AlignCenter, this->hexValue); + + } else { + textColor.setAlpha(100); + painter.setPen(textColor); + + static const auto placeholderString = QString("??"); + painter.drawText(widgetRect, Qt::AlignCenter, placeholderString); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.hpp new file mode 100644 index 00000000..93ac63ca --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp" + +namespace Bloom::Widgets +{ + class ByteWidget: public ClickableWidget + { + Q_OBJECT + + public: + static constexpr int WIDTH = 25; + static constexpr int HEIGHT = 20; + + static constexpr int RIGHT_MARGIN = 5; + static constexpr int BOTTOM_MARGIN = 5; + + std::size_t byteIndex; + unsigned char value = 0x00; + std::uint32_t address = 0x00; + QString addressHex; + QString relativeAddressHex; + bool valueInitialised = false; + + std::size_t currentRowIndex = 0; + std::size_t currentColumnIndex = 0; + + ByteWidget( + std::size_t byteNumber, + std::uint32_t address, + std::optional& hoveredByteWidget, + QWidget* parent + ); + void setValue(unsigned char value); + + public slots: + void setSelected(bool selected); + + signals: + void selected(ByteWidget*); + void enter(ByteWidget*); + void leave(ByteWidget*); + + protected: + virtual void postSetSelected(bool selected) {}; + bool event(QEvent* event) override; + void paintEvent(QPaintEvent* event) override; + void drawWidget(QPainter& painter); + + private: + bool hoverActive = false; + bool valueChanged = false; + + QString hexValue; + std::optional asciiValue; + + std::optional& hoveredByteWidget; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp new file mode 100644 index 00000000..92bc5837 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp @@ -0,0 +1,152 @@ +#include "ByteWidgetContainer.hpp" + +#include +#include +#include +#include +#include + +using namespace Bloom::Widgets; +using namespace Bloom::Exceptions; + +using Bloom::Targets::TargetMemoryDescriptor; + +ByteWidgetContainer::ByteWidgetContainer( + const TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + QWidget* parent +): QWidget(parent), targetMemoryDescriptor(targetMemoryDescriptor), insightWorker(insightWorker), parent(parent) { + this->setObjectName("byte-widget-container"); + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored); + + this->setContentsMargins( + 10, + 10, + 10, + 10 + ); + + /* + * Construct ByteWidget objects + * + * No need to position them here - the subsequent call to resizeEvent() will do that. + */ + const auto memorySize = this->targetMemoryDescriptor.size(); + const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress; + for (std::size_t i = 0; i < memorySize; i++) { + const auto address = static_cast(startAddress + i); + + auto byteWidget = new ByteWidget(i, address, this->hoveredByteWidget, this); + this->byteWidgetsByAddress.insert(std::pair( + address, + byteWidget + )); + + this->connect(byteWidget, &ByteWidget::enter, this, &ByteWidgetContainer::onByteWidgetEnter); + this->connect(byteWidget, &ByteWidget::leave, this, &ByteWidgetContainer::onByteWidgetLeave); + } + + this->connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &ByteWidgetContainer::onTargetStateChanged + ); + + this->show(); +} + +void ByteWidgetContainer::updateValues(const Targets::TargetMemoryBuffer& buffer) { + for (auto& [address, byteWidget] : this->byteWidgetsByAddress) { + byteWidget->setValue(buffer.at(byteWidget->byteIndex)); + byteWidget->update(); + } +} + +void ByteWidgetContainer::resizeEvent(QResizeEvent* event) { + this->adjustByteWidgets(); +} + +void ByteWidgetContainer::adjustByteWidgets() { + std::map> byteWidgetsByRowIndex; + std::map> byteWidgetsByColumnIndex; + + const auto margins = this->contentsMargins(); + const auto width = this->width(); + + constexpr auto byteWidgetWidth = ByteWidget::WIDTH + ByteWidget::RIGHT_MARGIN; + constexpr auto byteWidgetHeight = ByteWidget::HEIGHT + ByteWidget::BOTTOM_MARGIN; + const auto rowCapacity = static_cast( + std::floor((width - margins.left() - margins.right()) / byteWidgetWidth) + ); + const auto rowCount = static_cast( + std::ceil(static_cast(this->byteWidgetsByAddress.size()) / static_cast(rowCapacity)) + ); + + for (auto& [address, byteWidget] : this->byteWidgetsByAddress) { + const auto rowIndex = static_cast( + std::ceil(static_cast(byteWidget->byteIndex + 1) / static_cast(rowCapacity)) - 1 + ); + const auto columnIndex = static_cast( + static_cast(byteWidget->byteIndex) + - (std::floor(byteWidget->byteIndex / rowCapacity) * static_cast(rowCapacity)) + ); + + byteWidget->setGeometry(QRect( + static_cast(columnIndex * byteWidgetWidth + static_cast(margins.left())), + static_cast(rowIndex * byteWidgetHeight + static_cast(margins.top())), + ByteWidget::WIDTH, + ByteWidget::HEIGHT + )); + + byteWidget->currentRowIndex = static_cast(rowIndex); + byteWidget->currentColumnIndex = static_cast(columnIndex); + + byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget); + byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget); + + byteWidget->update(); + } + + const auto minHeight = (rowCount * byteWidgetHeight) + margins.top() + margins.bottom(); + this->setMinimumHeight(minHeight); + this->parent->setMinimumHeight(minHeight); + + this->byteWidgetsByRowIndex.swap(byteWidgetsByRowIndex); + this->byteWidgetsByColumnIndex.swap(byteWidgetsByColumnIndex); + + emit this->byteWidgetsAdjusted(); +} + +void ByteWidgetContainer::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; +} + +void ByteWidgetContainer::onByteWidgetEnter(ByteWidget* widget) { + this->hoveredByteWidget = widget; + + if (!this->byteWidgetsByRowIndex.empty()) { + for (auto& byteWidget : this->byteWidgetsByColumnIndex.at(widget->currentColumnIndex)) { + byteWidget->update(); + } + + for (auto& byteWidget : this->byteWidgetsByRowIndex.at(widget->currentRowIndex)) { + byteWidget->update(); + } + } +} + +void ByteWidgetContainer::onByteWidgetLeave(ByteWidget* widget) { + this->hoveredByteWidget = std::nullopt; + + if (!this->byteWidgetsByRowIndex.empty()) { + for (auto& byteWidget : this->byteWidgetsByColumnIndex.at(widget->currentColumnIndex)) { + byteWidget->update(); + } + + for (auto& byteWidget : this->byteWidgetsByRowIndex.at(widget->currentRowIndex)) { + byteWidget->update(); + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.hpp new file mode 100644 index 00000000..86cd354b --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetState.hpp" + +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +#include "ByteWidget.hpp" + +namespace Bloom::Widgets +{ + class ByteWidgetContainer: public QWidget + { + Q_OBJECT + + public: + std::optional hoveredByteWidget; + + std::map byteWidgetsByAddress; + std::map> byteWidgetsByRowIndex; + std::map> byteWidgetsByColumnIndex; + + ByteWidgetContainer( + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + QWidget* parent + ); + + void updateValues(const Targets::TargetMemoryBuffer& buffer); + + signals: + void byteWidgetsAdjusted(); + + protected: + void resizeEvent(QResizeEvent* event) override; + + private: + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; + InsightWorker& insightWorker; + + QWidget* parent = nullptr; + + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + + void adjustByteWidgets(); + + private slots: + void onTargetStateChanged(Targets::TargetState newState); + void onByteWidgetEnter(ByteWidget* widget); + void onByteWidgetLeave(ByteWidget* widget); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp new file mode 100644 index 00000000..747d351c --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp @@ -0,0 +1,137 @@ +#include "HexViewerWidget.hpp" + +#include +#include +#include +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" + +using namespace Bloom::Widgets; +using namespace Bloom::Exceptions; + +using Bloom::Targets::TargetMemoryDescriptor; + +HexViewerWidget::HexViewerWidget( + const TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + QWidget* parent +): QWidget(parent), targetMemoryDescriptor(targetMemoryDescriptor), insightWorker(insightWorker) { + this->setObjectName("hex-viewer-widget"); + this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + auto widgetUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget" + + "/UiFiles/HexViewerWidget.ui" + ) + ); + + if (!widgetUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open HexViewerWidget UI file"); + } + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&widgetUiFile, this); + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(0, 0, 0, 0); + + this->toolBar = this->container->findChild("tool-bar"); + this->refreshButton = this->container->findChild("refresh-memory-btn"); + + this->toolBar->setContentsMargins(0, 0, 0, 0); + this->toolBar->layout()->setContentsMargins(5, 0, 5, 0); + + this->byteWidgetScrollArea = this->container->findChild("byte-widget-scroll-area"); + auto byteWidgetScrollAreaWidgetContainer = this->byteWidgetScrollArea->findChild( + "byte-widget-scroll-area-container" + ); + auto byteWidgetScrollAreaHorizontalLayout = byteWidgetScrollAreaWidgetContainer->findChild( + "byte-widget-scroll-area-horizontal-layout" + ); + + this->byteWidgetContainer = new ByteWidgetContainer( + targetMemoryDescriptor, + insightWorker, + byteWidgetScrollAreaHorizontalLayout->parentWidget() + ); + + byteWidgetScrollAreaHorizontalLayout->addWidget(this->byteWidgetContainer); + + this->byteWidgetAddressContainer = byteWidgetScrollAreaWidgetContainer->findChild( + "address-container" + ); + this->byteWidgetAddressLayout = this->byteWidgetAddressContainer->findChild(); + this->byteWidgetAddressLayout->setContentsMargins(5, 10, 0, 5); + + this->connect( + this->byteWidgetContainer, + &ByteWidgetContainer::byteWidgetsAdjusted, + this, + &HexViewerWidget::onByteWidgetsAdjusted + ); + this->connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &HexViewerWidget::onTargetStateChanged + ); + + this->show(); +} + +void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) { + this->byteWidgetContainer->updateValues(buffer); +} + +void HexViewerWidget::resizeEvent(QResizeEvent* event) { + this->container->setFixedSize( + this->width(), + this->height() + ); +} + +void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; +} + +void HexViewerWidget::onByteWidgetsAdjusted() { + const auto& byteWidgetsByRowIndex = this->byteWidgetContainer->byteWidgetsByRowIndex; + + int layoutItemMaxIndex = this->byteWidgetAddressLayout->count() - 1; + for (const auto& mappingPair : byteWidgetsByRowIndex) { + const auto rowIndex = static_cast(mappingPair.first); + const auto& byteWidgets = mappingPair.second; + + if (byteWidgets.empty()) { + continue; + } + + QLabel* labelWidget; + if (rowIndex > layoutItemMaxIndex) { + labelWidget = new QLabel(this->byteWidgetAddressContainer); + labelWidget->setFixedSize(75, 20); + labelWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + this->byteWidgetAddressLayout->addWidget(labelWidget); + layoutItemMaxIndex++; + + } else { + labelWidget = qobject_cast(this->byteWidgetAddressLayout->itemAt(rowIndex)->widget()); + } + + labelWidget->setText(byteWidgets.front()->relativeAddressHex); + } + + const auto rowCount = static_cast(byteWidgetsByRowIndex.size()); + QLayoutItem* labelItem; + while ((labelItem = this->byteWidgetAddressLayout->takeAt(rowCount)) != nullptr) { + labelItem->widget()->deleteLater(); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp new file mode 100644 index 00000000..0c6c327a --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetState.hpp" + +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +#include "ByteWidgetContainer.hpp" + +namespace Bloom::Widgets +{ + class HexViewerWidget: public QWidget + { + Q_OBJECT + + public: + QToolButton* refreshButton = nullptr; + + HexViewerWidget( + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + QWidget* parent + ); + + void updateValues(const Targets::TargetMemoryBuffer& buffer); + + protected: + void resizeEvent(QResizeEvent* event) override; + + private: + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; + InsightWorker& insightWorker; + + QWidget* container = nullptr; + QWidget* toolBar = nullptr; + + ByteWidgetContainer* byteWidgetContainer = nullptr; + QWidget* byteWidgetScrollArea = nullptr; + QWidget* byteWidgetAddressContainer = nullptr; + QVBoxLayout* byteWidgetAddressLayout = nullptr; + + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + + private slots: + void onTargetStateChanged(Targets::TargetState newState); + void onByteWidgetsAdjusted(); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui new file mode 100644 index 00000000..edb1b5e0 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui @@ -0,0 +1,125 @@ + + + + + + 0 + + + 0 + + + + + + + + 27 + + + 27 + + + + + + + 3 + + + 0 + + + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Images/refresh.svg + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Images/refresh-disabled.svg + + + Refresh Memory + + + + + + + Qt::Horizontal + + + + + + + + + true + Qt::ScrollBarAsNeeded + QAbstractScrollArea::AdjustToContents + + + + + + + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 80 + + + + + + + 5 + + + 0 + + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-disabled-icon.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-disabled-icon.svg new file mode 100644 index 00000000..7d56f1a5 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-disabled-icon.svg @@ -0,0 +1,115 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg new file mode 100644 index 00000000..547d50d7 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg @@ -0,0 +1,170 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp new file mode 100644 index 00000000..d95b23de --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp @@ -0,0 +1,155 @@ +#include "TargetMemoryInspectionPane.hpp" + +#include +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" + +#include "src/Insight/InsightWorker/Tasks/ReadTargetMemory.hpp" + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" +#include "src/Logger/Logger.hpp" + +using namespace Bloom::Widgets; +using namespace Bloom::Exceptions; + +using Bloom::Targets::TargetMemoryDescriptor; +using Bloom::Targets::TargetMemoryType; + +TargetMemoryInspectionPane::TargetMemoryInspectionPane( + const TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + PanelWidget* parent +): QWidget(parent), parent(parent), targetMemoryDescriptor(targetMemoryDescriptor), insightWorker(insightWorker) { + this->setObjectName("target-memory-inspection-pane"); + + auto memoryInspectionPaneUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui" + ) + ); + + if (!memoryInspectionPaneUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open MemoryInspectionPane UI file"); + } + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&memoryInspectionPaneUiFile, this); + this->container->setFixedSize(parent->width(), parent->maximumHeight()); + + this->titleBar = this->container->findChild("title-bar"); + + this->titleBar->layout()->setContentsMargins(7, 0, 7, 0); + auto titleLabel = this->titleBar->findChild("title"); + titleLabel->setText("Internal RAM"); + + auto subContainerLayout = this->container->findChild("sub-container-layout"); + this->hexViewerWidget = new HexViewerWidget(this->targetMemoryDescriptor, this->insightWorker, this); + this->hexViewerWidget->setDisabled(true); + + subContainerLayout->addWidget(this->hexViewerWidget); + + this->connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &TargetMemoryInspectionPane::onTargetStateChanged + ); + + this->connect( + this->hexViewerWidget->refreshButton, + &QToolButton::clicked, + this, + [this] { + this->refreshMemoryValues(); + } + ); +} + +void TargetMemoryInspectionPane::refreshMemoryValues(std::optional> callback) { + this->hexViewerWidget->refreshButton->setDisabled(true); + + auto readMemoryTask = new ReadTargetMemory( + this->targetMemoryDescriptor.type, + this->targetMemoryDescriptor.addressRange.startAddress, + this->targetMemoryDescriptor.size() + ); + + this->connect( + readMemoryTask, + &ReadTargetMemory::targetMemoryRead, + this, + &TargetMemoryInspectionPane::onMemoryRead + ); + + this->connect( + readMemoryTask, + &InsightWorkerTask::completed, + this, + [this] { + this->hexViewerWidget->refreshButton->setDisabled(false); + } + ); + + if (callback.has_value()) { + this->connect( + readMemoryTask, + &InsightWorkerTask::completed, + this, + callback.value() + ); + } + + this->insightWorker.queueTask(readMemoryTask); +} + +void TargetMemoryInspectionPane::activate() { + this->show(); + this->activated = true; + this->postActivate(); +} + +void TargetMemoryInspectionPane::deactivate() { + this->hide(); + this->activated = false; + this->postDeactivate(); +} + +void TargetMemoryInspectionPane::resizeEvent(QResizeEvent* event) { + const auto parentSize = this->parent->size(); + const auto width = parentSize.width() - 1; + this->container->setFixedSize(width, parentSize.height()); +} + +void TargetMemoryInspectionPane::postActivate() { + if (this->targetState == Targets::TargetState::STOPPED) { + this->refreshMemoryValues(); + } +} + +void TargetMemoryInspectionPane::postDeactivate() { + +} + +void TargetMemoryInspectionPane::onTargetStateChanged(Targets::TargetState newState) { + using Targets::TargetState; + this->targetState = newState; + + if (this->activated) { + if (newState == TargetState::STOPPED) { + this->refreshMemoryValues([this] { + this->hexViewerWidget->setDisabled(false); + }); + + } else { + this->hexViewerWidget->setDisabled(true); + } + } +} + +void TargetMemoryInspectionPane::onMemoryRead(const Targets::TargetMemoryBuffer& buffer) { + this->hexViewerWidget->updateValues(buffer); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp new file mode 100644 index 00000000..b489f481 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +#include "HexViewerWidget/HexViewerWidget.hpp" + +#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetState.hpp" + +namespace Bloom::Widgets +{ + class TargetMemoryInspectionPane: public QWidget + { + Q_OBJECT + + public: + bool activated = false; + + TargetMemoryInspectionPane( + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + InsightWorker& insightWorker, + PanelWidget *parent + ); + + void refreshMemoryValues(std::optional> callback = std::nullopt); + + void activate(); + void deactivate(); + + protected: + void resizeEvent(QResizeEvent* event) override; + + virtual void postActivate(); + virtual void postDeactivate(); + + private: + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; + InsightWorker& insightWorker; + + PanelWidget* parent = nullptr; + QWidget* container = nullptr; + + QWidget* titleBar = nullptr; + HexViewerWidget* hexViewerWidget = nullptr; + + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + + private slots: + void onTargetStateChanged(Targets::TargetState newState); + void onMemoryRead(const Targets::TargetMemoryBuffer& buffer); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui new file mode 100644 index 00000000..93a2aa4c --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui @@ -0,0 +1,62 @@ + + + + + + 0 + + + 0 + + + + + 28 + + + 28 + + + + + + + 3 + + + 0 + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + + + 0 + + + + + + + + + + + + + + diff --git a/src/resources.qrc b/src/resources.qrc index 0f0ad1cf..cb0321a3 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -23,6 +23,8 @@ ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/TargetRegisterInspectorWindow.ui ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/UiFiles/RegisterHistoryWidget.ui ./Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui ./Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss @@ -47,5 +49,7 @@ ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Images/icon.svg ./Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Images/icon.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-disabled-icon.svg