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