diff --git a/CMakeLists.txt b/CMakeLists.txt index 88ecaca4..caf16300 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,7 @@ add_executable(Bloom src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp # Memory region manager window src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp new file mode 100644 index 00000000..97c1aa93 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp @@ -0,0 +1,81 @@ +#include "AnnotationItem.hpp" + +#include + +#include "ByteItem.hpp" + +using namespace Bloom::Widgets; + +AnnotationItem::AnnotationItem(std::uint32_t startAddress, std::size_t size, QString labelText, AnnotationItemPosition position) +: QGraphicsItem(nullptr), +startAddress(startAddress), +size(size), +endAddress(static_cast(startAddress + size - 1)), +labelText(std::move(labelText)), +position(position), +width(static_cast((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)), +height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT) { + this->setAcceptHoverEvents(true); + this->setToolTip(this->labelText); +} + +void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + static auto lineColor = this->getLineColor(); + static auto labelFontColor = this->getLabelFontColor(); + + const auto isEnabled = this->isEnabled(); + + lineColor.setAlpha(isEnabled ? 255 : 100); + labelFontColor.setAlpha(isEnabled ? 255 : 100); + + const auto fontMetrics = painter->fontMetrics(); + auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText); + if (labelSize.width() > this->width) { + labelSize.setWidth(this->width); + this->labelText = fontMetrics.elidedText(this->labelText, Qt::TextElideMode::ElideRight, this->width); + } + + constexpr auto verticalLineLength = 5; + const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0 : AnnotationItem::TOP_HEIGHT; + const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ? + verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength; + + const auto labelRect = QRect( + (this->width - labelSize.width()) / 2, + verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6), + labelSize.width(), + labelSize.height() + ); + + painter->setPen(lineColor); + painter->drawLine(QLine( + ByteItem::WIDTH / 2, + verticalLineYStart, + ByteItem::WIDTH / 2, + verticalLineYEnd + )); + + painter->drawLine(QLine( + this->width - (ByteItem::WIDTH / 2), + verticalLineYStart, + this->width - (ByteItem::WIDTH / 2), + verticalLineYEnd + )); + + painter->drawLine(QLine( + ByteItem::WIDTH / 2, + verticalLineYEnd, + (ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH), + verticalLineYEnd + )); + + painter->drawLine(QLine( + this->width / 2, + verticalLineYEnd, + this->width / 2, + (this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4) + )); + + painter->setPen(labelFontColor); + painter->drawText(labelRect, Qt::AlignCenter, this->labelText); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp new file mode 100644 index 00000000..6a7bf8ff --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace Bloom::Widgets +{ + enum class AnnotationItemPosition: std::uint8_t + { + TOP, + BOTTOM, + }; + + class AnnotationItem: public QGraphicsItem + { + public: + static constexpr int TOP_HEIGHT = 26; + static constexpr int BOTTOM_HEIGHT = 26; + + const int width; + const int height; + const std::uint32_t startAddress; + const std::uint32_t endAddress; + AnnotationItemPosition position = AnnotationItemPosition::TOP; + + AnnotationItem( + std::uint32_t startAddress, + std::size_t size, + QString labelText, + AnnotationItemPosition position + ); + + [[nodiscard]] QRectF boundingRect() const override { + return QRectF(0, 0, this->width, this->height); + } + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + protected: + [[nodiscard]] QColor getLineColor() const { + return QColor(0x4F, 0x4F, 0x4F); + } + + [[nodiscard]] QColor getLabelFontColor() const { + return QColor(0x8A, 0x8A, 0x8D); + } + + private: + std::size_t size = 0; + QString labelText; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp index b1d6c477..2058674c 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp @@ -3,20 +3,20 @@ #include #include -#include "src/Logger/Logger.hpp" - using namespace Bloom::Widgets; ByteItem::ByteItem( std::size_t byteIndex, std::uint32_t address, std::optional& hoveredByteItem, + std::optional& hoveredAnnotationItem, const HexViewerWidgetSettings& settings ): QGraphicsItem(nullptr), byteIndex(byteIndex), address(address), hoveredByteItem(hoveredByteItem), +hoveredAnnotationItem(hoveredAnnotationItem), settings(settings) { this->setCacheMode( @@ -51,25 +51,40 @@ void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA); static auto font = QFont("'Ubuntu', sans-serif"); - static const auto stackMemoryBackgroundColor = QColor(0x5E, 0x50, 0x27, 255); + static const auto focusedRegionBackgroundColor = QColor(0x25, 0x5A, 0x49, 210); + static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210); static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70); static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30); + static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50); + + const auto isEnabled = this->isEnabled(); + auto textColor = this->valueChanged ? valueChangedTextColor : standardTextColor; + auto backgroundColor = std::optional(); font.setPixelSize(11); painter->setFont(font); - if (this->settings.highlightStackMemory && this->settings.stackPointerAddress.has_value() + if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) { + // This byte is within a focused region + backgroundColor = focusedRegionBackgroundColor; + + } else if (this->settings.highlightStackMemory && this->settings.stackPointerAddress.has_value() && this->address > this->settings.stackPointerAddress ) { // This byte is within the stack memory - painter->setBrush(stackMemoryBackgroundColor); - painter->drawRect(widgetRect); + backgroundColor = stackMemoryBackgroundColor; } const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr); + const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr); if (hoveredByteItem != nullptr) { if (hoveredByteItem == this) { - painter->setBrush(hoveredBackgroundColor); + if (backgroundColor.has_value()) { + backgroundColor->setAlpha(255); + + } else { + backgroundColor = hoveredBackgroundColor; + } } else if (this->settings.highlightHoveredRowAndCol && ( @@ -77,16 +92,34 @@ void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, || hoveredByteItem->currentRowIndex == this->currentRowIndex ) ) { - painter->setBrush(hoveredNeighbourBackgroundColor); + if (backgroundColor.has_value()) { + backgroundColor->setAlpha(220); + + } else { + backgroundColor = hoveredNeighbourBackgroundColor; + } } + } else if ( + !this->settings.highlightFocusedMemory + && hoveredAnnotationItem != nullptr + && this->address >= hoveredAnnotationItem->startAddress + && this->address <= hoveredAnnotationItem->endAddress + ) { + backgroundColor = hoveredAnnotationBackgroundColor; + } + + if (backgroundColor.has_value()) { + if (!isEnabled) { + backgroundColor->setAlpha(100); + } + + painter->setBrush(backgroundColor.value()); painter->drawRect(widgetRect); } - auto textColor = this->valueChanged ? valueChangedTextColor : standardTextColor; - - if (this->valueInitialised) { - if (!this->isEnabled()) { + if (this->valueInitialised && this->excludedMemoryRegion == nullptr) { + if (!isEnabled) { textColor.setAlpha(100); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp index 0027cc2b..6fdf57b9 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp @@ -5,8 +5,11 @@ #include #include -#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp" #include "HexViewerWidgetSettings.hpp" +#include "AnnotationItem.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.hpp" namespace Bloom::Widgets { @@ -28,10 +31,14 @@ namespace Bloom::Widgets std::size_t currentRowIndex = 0; std::size_t currentColumnIndex = 0; + const FocusedMemoryRegion* focusedMemoryRegion = nullptr; + const ExcludedMemoryRegion* excludedMemoryRegion = nullptr; + ByteItem( std::size_t byteIndex, std::uint32_t address, std::optional& hoveredByteItem, + std::optional& hoveredAnnotationItem, const HexViewerWidgetSettings& settings ); @@ -58,5 +65,6 @@ namespace Bloom::Widgets std::optional asciiValue; std::optional& hoveredByteItem; + std::optional& hoveredAnnotationItem; }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp index a7f7f7cf..e3d8e195 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp @@ -1,20 +1,13 @@ #include "ByteItemContainerGraphicsView.hpp" -#include -#include -#include -#include -#include - -#include "src/Logger/Logger.hpp" - using namespace Bloom::Widgets; -using namespace Bloom::Exceptions; using Bloom::Targets::TargetMemoryDescriptor; ByteItemContainerGraphicsView::ByteItemContainerGraphicsView( const TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, const HexViewerWidgetSettings& settings, QLabel* hoveredAddressLabel, @@ -29,6 +22,8 @@ ByteItemContainerGraphicsView::ByteItemContainerGraphicsView( this->scene = new ByteItemGraphicsScene( targetMemoryDescriptor, + focusedMemoryRegions, + excludedMemoryRegions, insightWorker, settings, hoveredAddressLabel, @@ -49,6 +44,6 @@ bool ByteItemContainerGraphicsView::event(QEvent* event) { } void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) { - this->scene->adjustByteWidgets(); QGraphicsView::resizeEvent(event); + this->scene->adjustSize(); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.hpp index f6a07be1..e785729f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.hpp @@ -3,18 +3,10 @@ #include #include #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" @@ -30,6 +22,8 @@ namespace Bloom::Widgets public: ByteItemContainerGraphicsView( const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, const HexViewerWidgetSettings& settings, QLabel* hoveredAddressLabel, diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp index a9bcc11d..fd022ddc 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp @@ -1,13 +1,7 @@ #include "ByteItemGraphicsScene.hpp" -#include -#include -#include -#include #include -#include "src/Logger/Logger.hpp" - using namespace Bloom::Widgets; using namespace Bloom::Exceptions; @@ -15,33 +9,34 @@ using Bloom::Targets::TargetMemoryDescriptor; ByteItemGraphicsScene::ByteItemGraphicsScene( const TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, const HexViewerWidgetSettings& settings, QLabel* hoveredAddressLabel, - QWidget* parent -): QGraphicsScene(parent), -targetMemoryDescriptor(targetMemoryDescriptor), -insightWorker(insightWorker), -settings(settings), -hoveredAddressLabel(hoveredAddressLabel), -parent(parent) { + QGraphicsView* parent +): + QGraphicsScene(parent), + targetMemoryDescriptor(targetMemoryDescriptor), + focusedMemoryRegions(focusedMemoryRegions), + excludedMemoryRegions(excludedMemoryRegions), + insightWorker(insightWorker), + settings(settings), + hoveredAddressLabel(hoveredAddressLabel), + parent(parent) +{ this->setObjectName("byte-widget-container"); this->byteAddressContainer = new ByteAddressContainer(); this->addItem(this->byteAddressContainer); - /* - * Construct ByteWidget objects - * - * No need to position them here - the subsequent call to resizeEvent() will do that. - */ + // Construct ByteWidget objects const auto memorySize = this->targetMemoryDescriptor.size(); const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress; - Logger::error("Constructing bytes begin"); for (std::uint32_t i = 0; i < memorySize; i++) { const auto address = startAddress + i; - auto* byteWidget = new ByteItem(i, address, this->hoveredByteWidget, settings); + auto* byteWidget = new ByteItem(i, address, this->hoveredByteWidget, this->hoveredAnnotationItem, settings); this->byteItemsByAddress.insert(std::pair( address, byteWidget @@ -49,8 +44,6 @@ parent(parent) { this->addItem(byteWidget); } - Logger::error("Constructing bytes end"); - this->adjustByteWidgets(); QObject::connect( &insightWorker, @@ -58,6 +51,9 @@ parent(parent) { this, &ByteItemGraphicsScene::onTargetStateChanged ); + + this->refreshRegions(); + this->adjustSize(); } void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) { @@ -67,61 +63,101 @@ void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buff } } -void ByteItemGraphicsScene::adjustByteWidgets() { - const auto margins = QMargins(10, 10, 10, 10); - const auto width = std::max(600, static_cast(this->parent->width())); +void ByteItemGraphicsScene::refreshRegions() { + for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) { + byteWidget->focusedMemoryRegion = nullptr; + byteWidget->excludedMemoryRegion = nullptr; - constexpr auto byteWidgetWidth = ByteItem::WIDTH + ByteItem::RIGHT_MARGIN; - constexpr auto byteWidgetHeight = ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN; - const auto rowCapacity = static_cast( - std::floor((width - margins.left() - margins.right() - ByteAddressContainer::WIDTH) / byteWidgetWidth) - ); - const auto rowCount = static_cast( - std::ceil(static_cast(this->byteItemsByAddress.size()) / static_cast(rowCapacity)) - ); + for (const auto& focusedRegion : this->focusedMemoryRegions) { + const auto addressRange = focusedRegion.getAbsoluteAddressRange(); + if (byteAddress >= addressRange.startAddress && byteAddress <= addressRange.endAddress) { + byteWidget->focusedMemoryRegion = &focusedRegion; + break; + } + } - this->setSceneRect( - 0, - 0, - width, - std::max(((rowCount * byteWidgetHeight) + margins.top() + margins.bottom()), this->parent->height()) - ); - - // Don't bother recalculating the byte item positions if the number of rows & columns have not changed. - if (rowCount == this->byteItemsByRowIndex.size() && rowCapacity == this->byteItemsByColumnIndex.size()) { - return; - } - - std::map> byteWidgetsByRowIndex; - std::map> byteWidgetsByColumnIndex; - - for (auto& [address, byteWidget] : this->byteItemsByAddress) { - 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->setPos( - static_cast(columnIndex * byteWidgetWidth + margins.left() + ByteAddressContainer::WIDTH), - static_cast(rowIndex * byteWidgetHeight + static_cast(margins.top())) - ); - - byteWidget->currentRowIndex = static_cast(rowIndex); - byteWidget->currentColumnIndex = static_cast(columnIndex); - - byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget); - byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget); + for (const auto& excludedRegion : this->excludedMemoryRegions) { + const auto addressRange = excludedRegion.getAbsoluteAddressRange(); + if (byteAddress >= addressRange.startAddress && byteAddress <= addressRange.endAddress) { + byteWidget->excludedMemoryRegion = &excludedRegion; + break; + } + } byteWidget->update(); } - this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex); - this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex); + // Refresh annotation items + for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + this->removeItem(annotationItem); + delete annotationItem; + } - this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex); + this->annotationItemsByStartAddress.clear(); + + for (const auto& focusedRegion : this->focusedMemoryRegions) { + const auto addressRange = focusedRegion.getAbsoluteAddressRange(); + auto* annotationItem = new AnnotationItem( + addressRange.startAddress, + addressRange.endAddress - addressRange.startAddress + 1, + focusedRegion.name, + AnnotationItemPosition::BOTTOM + ); + this->addItem(annotationItem); + this->annotationItemsByStartAddress.insert(std::pair(addressRange.startAddress, annotationItem)); + } + + this->adjustSize(true); +} + +void ByteItemGraphicsScene::adjustSize(bool forced) { + const auto width = this->getSceneWidth(); + + const auto columnCount = static_cast( + std::floor( + (width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN) + / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + ) + ); + const auto rowCount = static_cast( + std::ceil(static_cast(this->byteItemsByAddress.size()) / static_cast(columnCount)) + ); + + // Don't bother recalculating the byte item & annotation positions if the number of rows & columns have not changed. + if (this->byteItemsByAddress.empty() + || ( + !forced + && rowCount == this->byteItemsByRowIndex.size() + && columnCount == this->byteItemsByColumnIndex.size() + ) + ) { + this->setSceneRect( + 0, + 0, + width, + std::max(static_cast(this->sceneRect().height()), this->parent->viewport()->height()) + ); + + return; + } + + if (!this->byteItemsByAddress.empty()) { + this->adjustByteItemPositions(); + this->adjustAnnotationItemPositions(); + + const auto lastByteItemPosition = (--this->byteItemsByAddress.end())->second->pos(); + this->setSceneRect( + 0, + 0, + width, + std::max( + static_cast(lastByteItemPosition.y() + ByteItem::HEIGHT + this->margins.bottom()), + this->parent->height() + ) + ); + } + + this->update(); } void ByteItemGraphicsScene::setEnabled(bool enabled) { @@ -132,11 +168,178 @@ void ByteItemGraphicsScene::setEnabled(bool enabled) { byteItem->setEnabled(this->enabled); } + for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + annotationItem->setEnabled(this->enabled); + } + this->byteAddressContainer->setEnabled(enabled); + this->byteAddressContainer->update(); this->update(); } } +void ByteItemGraphicsScene::invalidateChildItemCaches() { + for (auto& [address, byteWidget] : this->byteItemsByAddress) { + byteWidget->update(); + } + + for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + annotationItem->update(); + } +} + +bool ByteItemGraphicsScene::event(QEvent* event) { + if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) { + this->onByteWidgetLeave(); + } + + return QGraphicsScene::event(event); +} + +void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { + auto hoveredItems = this->items(mouseEvent->scenePos()); + ByteItem* hoveredByteItem = nullptr; + AnnotationItem* hoveredAnnotationItem = nullptr; + + if (!hoveredItems.empty()) { + hoveredByteItem = dynamic_cast(hoveredItems.at(0)); + hoveredAnnotationItem = dynamic_cast(hoveredItems.at(0)); + } + + if (hoveredByteItem != nullptr) { + this->onByteWidgetEnter(hoveredByteItem); + return; + } + + if (this->hoveredByteWidget.has_value()) { + this->onByteWidgetLeave(); + } + + if (hoveredAnnotationItem != nullptr) { + this->onAnnotationItemEnter(hoveredAnnotationItem); + return; + } + + if (this->hoveredAnnotationItem.has_value()) { + this->onAnnotationItemLeave(); + } +} + +void ByteItemGraphicsScene::adjustByteItemPositions() { + const auto columnCount = static_cast( + std::floor( + (this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN) + / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + ) + ); + + std::map> byteWidgetsByRowIndex; + std::map> byteWidgetsByColumnIndex; + + auto rowIndicesWithTopAnnotations = std::set(); + auto rowIndicesWithBottomAnnotations = std::set(); + const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; + + for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + const auto firstByteRowIndex = static_cast( + std::ceil(static_cast((startAddress - memoryAddressRange.startAddress) + 1) + / static_cast(columnCount)) - 1 + ); + + const auto lastByteRowIndex = static_cast( + std::ceil(static_cast((annotationItem->endAddress - memoryAddressRange.startAddress) + 1) + / static_cast(columnCount)) - 1 + ); + + // We only display annotations that span a single row. + if (firstByteRowIndex == lastByteRowIndex) { + annotationItem->show(); + rowIndicesWithBottomAnnotations.insert(firstByteRowIndex); + + } else { + annotationItem->hide(); + } + } + + constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT; + constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT; + + std::size_t lastRowIndex = 0; + int rowYPosition = margins.top(); + auto currentRowAnnotatedTop = false; + + for (auto& [address, byteWidget] : this->byteItemsByAddress) { + const auto rowIndex = static_cast( + std::ceil(static_cast(byteWidget->byteIndex + 1) / static_cast(columnCount)) - 1 + ); + const auto columnIndex = static_cast( + static_cast(byteWidget->byteIndex) + - (std::floor(byteWidget->byteIndex / columnCount) * static_cast(columnCount)) + ); + + if (rowIndex != lastRowIndex) { + rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN; + currentRowAnnotatedTop = false; + + if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) { + rowYPosition += annotationBottomHeight; + } + } + + if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) { + rowYPosition += annotationTopHeight; + currentRowAnnotatedTop = true; + } + + byteWidget->setPos( + static_cast( + columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left() + ByteAddressContainer::WIDTH), + rowYPosition + ); + + byteWidget->currentRowIndex = rowIndex; + byteWidget->currentColumnIndex = columnIndex; + + byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget); + byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget); + + lastRowIndex = rowIndex; + } + + this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex); + this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex); + + this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex); +} + +void ByteItemGraphicsScene::adjustAnnotationItemPositions() { + if (this->byteItemsByAddress.empty()) { + return; + } + + for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + if (!this->byteItemsByAddress.contains(startAddress)) { + annotationItem->hide(); + continue; + } + + const auto firstByteItemPosition = this->byteItemsByAddress.at(startAddress)->pos(); + + if (annotationItem->position == AnnotationItemPosition::TOP) { + annotationItem->setPos( + firstByteItemPosition.x(), + firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT + ); + + } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { + annotationItem->setPos( + firstByteItemPosition.x(), + firstByteItemPosition.y() + ByteItem::HEIGHT + ); + } + } +} + void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) { using Targets::TargetState; this->targetState = newState; @@ -147,10 +350,9 @@ void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) { if (this->hoveredByteWidget.value() == widget) { // This byte item is already marked as hovered return; - - } else { - this->onByteWidgetLeave(); } + + this->onByteWidgetLeave(); } this->hoveredByteWidget = widget; @@ -174,7 +376,7 @@ void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) { } void ByteItemGraphicsScene::onByteWidgetLeave() { - const auto byteItem = this->hoveredByteWidget.value(); + auto* byteItem = this->hoveredByteWidget.value(); this->hoveredByteWidget = std::nullopt; this->hoveredAddressLabel->setText("Relative Address (Absolute Address):"); @@ -193,24 +395,35 @@ void ByteItemGraphicsScene::onByteWidgetLeave() { } } -bool ByteItemGraphicsScene::event(QEvent* event) { - if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) { - this->onByteWidgetLeave(); - } - - return QGraphicsScene::event(event); -} - -void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { - auto hoveredItems = this->items(mouseEvent->scenePos()); - if (!hoveredItems.empty()) { - auto hoveredByteWidget = dynamic_cast(hoveredItems.at(0)); - - if (hoveredByteWidget != nullptr) { - this->onByteWidgetEnter(hoveredByteWidget); +void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) { + if (this->hoveredAnnotationItem.has_value()) { + if (this->hoveredAnnotationItem.value() == annotationItem) { + return; } - } else if (this->hoveredByteWidget.has_value()) { - this->onByteWidgetLeave(); + this->onAnnotationItemLeave(); + } + + this->hoveredAnnotationItem = annotationItem; + + for ( + auto byteItemAddress = annotationItem->startAddress; + byteItemAddress <= annotationItem->endAddress; + byteItemAddress++ + ) { + this->byteItemsByAddress.at(byteItemAddress)->update(); + } +} + +void ByteItemGraphicsScene::onAnnotationItemLeave() { + auto* annotationItem = this->hoveredAnnotationItem.value(); + this->hoveredAnnotationItem = std::nullopt; + + for ( + auto byteItemAddress = annotationItem->startAddress; + byteItemAddress <= annotationItem->endAddress; + byteItemAddress++ + ) { + this->byteItemsByAddress.at(byteItemAddress)->update(); } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp index 44d01004..15187dab 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp @@ -1,15 +1,19 @@ #pragma once #include +#include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include "src/Targets/TargetMemory.hpp" @@ -19,8 +23,13 @@ #include "ByteItem.hpp" #include "ByteAddressContainer.hpp" +#include "AnnotationItem.hpp" #include "HexViewerWidgetSettings.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.hpp" + namespace Bloom::Widgets { class ByteItemGraphicsScene: public QGraphicsScene @@ -29,24 +38,23 @@ namespace Bloom::Widgets public: std::optional hoveredByteWidget; - - std::map byteItemsByAddress; - std::map> byteItemsByRowIndex; - std::map> byteItemsByColumnIndex; + std::optional hoveredAnnotationItem; ByteItemGraphicsScene( const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, const HexViewerWidgetSettings& settings, QLabel* hoveredAddressLabel, - QWidget* parent + QGraphicsView* parent ); void updateValues(const Targets::TargetMemoryBuffer& buffer); - - void adjustByteWidgets(); - + void refreshRegions(); + void adjustSize(bool forced = false); void setEnabled(bool enabled); + void invalidateChildItemCaches(); signals: void byteWidgetsAdjusted(); @@ -57,21 +65,43 @@ namespace Bloom::Widgets private: const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; + std::vector& focusedMemoryRegions; + std::vector& excludedMemoryRegions; + + std::map byteItemsByAddress; + std::map annotationItemsByStartAddress; + std::map> byteItemsByRowIndex; + std::map> byteItemsByColumnIndex; + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; InsightWorker& insightWorker; + const QMargins margins = QMargins(10, 10, 10, 10); const HexViewerWidgetSettings& settings; - QWidget* parent = nullptr; + QGraphicsView* parent = nullptr; QLabel* hoveredAddressLabel = nullptr; ByteAddressContainer* byteAddressContainer = nullptr; bool enabled = true; - private slots: + int getSceneWidth() { + /* + * Minus 2 for the QSS margin on the vertical scrollbar (which isn't accounted for during viewport + * size calculation). + * + * See https://bugreports.qt.io/browse/QTBUG-99189 for more on this. + */ + return std::max(this->parent->viewport()->width(), 400) - 2; + } + + void adjustByteItemPositions(); + void adjustAnnotationItemPositions(); void onTargetStateChanged(Targets::TargetState newState); void onByteWidgetEnter(Bloom::Widgets::ByteItem* widget); void onByteWidgetLeave(); + void onAnnotationItemEnter(Bloom::Widgets::AnnotationItem* annotationItem); + void onAnnotationItemLeave(); }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp index d64f0e1b..17ee4a4b 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp @@ -1,16 +1,9 @@ #include "HexViewerWidget.hpp" -#include -#include -#include -#include -#include - #include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" #include "src/Helpers/Paths.hpp" #include "src/Exceptions/Exception.hpp" -#include "src/Logger/Logger.hpp" using namespace Bloom::Widgets; using namespace Bloom::Exceptions; @@ -19,9 +12,17 @@ using Bloom::Targets::TargetMemoryDescriptor; HexViewerWidget::HexViewerWidget( const TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, QWidget* parent -): QWidget(parent), targetMemoryDescriptor(targetMemoryDescriptor), insightWorker(insightWorker) { +): + QWidget(parent), + targetMemoryDescriptor(targetMemoryDescriptor), + focusedMemoryRegions(focusedMemoryRegions), + excludedMemoryRegions(excludedMemoryRegions), + insightWorker(insightWorker) +{ this->setObjectName("hex-viewer-widget"); this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -63,8 +64,10 @@ HexViewerWidget::HexViewerWidget( this->byteItemGraphicsViewContainer = this->container->findChild("graphics-view-container"); this->byteItemGraphicsView = new ByteItemContainerGraphicsView( - targetMemoryDescriptor, - insightWorker, + this->targetMemoryDescriptor, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this->insightWorker, this->settings, this->hoveredAddressLabel, this->byteItemGraphicsViewContainer @@ -116,9 +119,13 @@ void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) { this->byteItemGraphicsScene->updateValues(buffer); } +void HexViewerWidget::refreshRegions() { + this->byteItemGraphicsScene->refreshRegions(); +} + void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) { this->settings.stackPointerAddress = stackPointer; - this->byteItemGraphicsScene->update(); + this->byteItemGraphicsScene->invalidateChildItemCaches(); } void HexViewerWidget::resizeEvent(QResizeEvent* event) { @@ -143,19 +150,19 @@ void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) { this->highlightStackMemoryButton->setChecked(enabled); this->settings.highlightStackMemory = enabled; - this->byteItemGraphicsScene->update(); + this->byteItemGraphicsScene->invalidateChildItemCaches(); } void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) { this->highlightHoveredRowAndColumnButton->setChecked(enabled); this->settings.highlightHoveredRowAndCol = enabled; - this->byteItemGraphicsScene->update(); + this->byteItemGraphicsScene->invalidateChildItemCaches(); } void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) { this->highlightFocusedMemoryButton->setChecked(enabled); this->settings.highlightFocusedMemory = enabled; - this->byteItemGraphicsScene->update(); + this->byteItemGraphicsScene->invalidateChildItemCaches(); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp index a419f314..5eef5be3 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp @@ -2,25 +2,23 @@ #include #include -#include -#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 "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" + #include "HexViewerWidgetSettings.hpp" #include "ByteItemContainerGraphicsView.hpp" -#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.hpp" namespace Bloom::Widgets { @@ -33,12 +31,14 @@ namespace Bloom::Widgets HexViewerWidget( const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, InsightWorker& insightWorker, QWidget* parent ); void updateValues(const Targets::TargetMemoryBuffer& buffer); - + void refreshRegions(); void setStackPointer(std::uint32_t stackPointer); protected: @@ -47,6 +47,9 @@ namespace Bloom::Widgets private: const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; + std::vector& focusedMemoryRegions; + std::vector& excludedMemoryRegions; + InsightWorker& insightWorker; HexViewerWidgetSettings settings = HexViewerWidgetSettings(); @@ -66,7 +69,6 @@ namespace Bloom::Widgets Targets::TargetState targetState = Targets::TargetState::UNKNOWN; - private slots: void onTargetStateChanged(Targets::TargetState newState); void setStackMemoryHighlightingEnabled(bool enabled); void setHoveredRowAndColumnHighlightingEnabled(bool enabled); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp index bce21054..13c8c9f8 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp @@ -382,6 +382,7 @@ void MemoryRegionManagerWindow::applyChanges() { this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions); this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions); this->close(); + emit this->changesApplied(); } void MemoryRegionManagerWindow::openHelpPage() { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp index e02aa757..fdde01f5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp @@ -37,6 +37,9 @@ namespace Bloom::Widgets void refreshRegions(); + signals: + void changesApplied(); + protected: void showEvent(QShowEvent* event) override; diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp index e0ec70a9..a4416374 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp @@ -49,7 +49,13 @@ TargetMemoryInspectionPane::TargetMemoryInspectionPane( auto* subContainerLayout = this->container->findChild("sub-container-layout"); this->manageMemoryRegionsButton = this->container->findChild("manage-memory-regions-btn"); - this->hexViewerWidget = new HexViewerWidget(this->targetMemoryDescriptor, this->insightWorker, this); + this->hexViewerWidget = new HexViewerWidget( + this->targetMemoryDescriptor, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this->insightWorker, + this + ); this->hexViewerWidget->setDisabled(true); subContainerLayout->addWidget(this->hexViewerWidget); @@ -187,6 +193,13 @@ void TargetMemoryInspectionPane::openMemoryRegionManagerWindow() { this->excludedMemoryRegions, this ); + + QObject::connect( + this->memoryRegionManagerWindow, + &MemoryRegionManagerWindow::changesApplied, + this, + &TargetMemoryInspectionPane::onMemoryRegionsChange + ); } if (!this->memoryRegionManagerWindow->isVisible()) { @@ -197,3 +210,7 @@ void TargetMemoryInspectionPane::openMemoryRegionManagerWindow() { this->memoryRegionManagerWindow->activateWindow(); } } + +void TargetMemoryInspectionPane::onMemoryRegionsChange() { + this->hexViewerWidget->refreshRegions(); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp index 604fe6e8..6ce4e019 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp @@ -64,5 +64,6 @@ namespace Bloom::Widgets void onTargetStateChanged(Targets::TargetState newState); void onMemoryRead(const Targets::TargetMemoryBuffer& buffer); void openMemoryRegionManagerWindow(); + void onMemoryRegionsChange(); }; }