From b80f6aad6cc4659766d00f29bef3f08bf731c8c0 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 6 May 2023 21:02:30 +0100 Subject: [PATCH] Further refactoring of hex viewer item painting for additional performance gains --- src/Insight/CMakeLists.txt | 2 + .../HexViewerWidget/ByteItem.cpp | 344 +-------- .../HexViewerWidget/ByteItem.hpp | 45 +- .../FocusedRegionGroupItem.cpp | 205 ----- .../FocusedRegionGroupItem.hpp | 29 +- .../HexViewerWidget/GraphicsItem.hpp | 69 -- .../HexViewerWidget/GroupItem.hpp | 13 +- .../HexViewerWidget/HexViewerItem.cpp | 8 - .../HexViewerWidget/HexViewerItem.hpp | 15 +- .../HexViewerWidget/HexViewerItemIndex.cpp | 184 +++++ .../HexViewerWidget/HexViewerItemIndex.hpp | 108 +++ .../HexViewerWidget/HexViewerItemRenderer.cpp | 723 ++++++++++++++++++ .../HexViewerWidget/HexViewerItemRenderer.hpp | 82 ++ .../HexViewerWidget/ItemGraphicsScene.cpp | 265 ++----- .../HexViewerWidget/ItemGraphicsScene.hpp | 19 +- .../HexViewerWidget/ItemGraphicsView.cpp | 8 - .../HexViewerWidget/ItemGraphicsView.hpp | 1 - .../HexViewerWidget/StackMemoryGroupItem.cpp | 147 ---- .../HexViewerWidget/StackMemoryGroupItem.hpp | 10 +- .../DifferentialItemGraphicsScene.cpp | 22 +- .../DifferentialItemGraphicsScene.hpp | 2 +- .../DifferentialItemGraphicsView.cpp | 2 +- 22 files changed, 1193 insertions(+), 1110 deletions(-) delete mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GraphicsItem.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.hpp diff --git a/src/Insight/CMakeLists.txt b/src/Insight/CMakeLists.txt index c9129504..20f0c5ae 100755 --- a/src/Insight/CMakeLists.txt +++ b/src/Insight/CMakeLists.txt @@ -99,6 +99,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.cpp diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp index 68bd04da..2a6aef7d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp @@ -1,350 +1,8 @@ #include "ByteItem.hpp" -#include -#include - namespace Bloom::Widgets { ByteItem::ByteItem(Targets::TargetMemoryAddress address) : HexViewerItem(address) - { - if (!ByteItem::pixmapCachesGenerated) { - ByteItem::generatePixmapCaches(); - } - } - - void ByteItem::paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const { - const auto boundingRect = QRect(0, 0, ByteItem::WIDTH, ByteItem::HEIGHT); - - if (!graphicsItem->isEnabled() || (this->excluded && !this->selected)) { - painter->setOpacity(0.6); - } - - if (this->excluded || !hexViewerState->data.has_value()) { - if (this->selected) { - painter->drawPixmap(boundingRect, ByteItem::selectedMissingDataPixmap.value()); - return; - } - - painter->drawPixmap(boundingRect, ByteItem::missingDataPixmap.value()); - return; - } - - const auto byteIndex = this->startAddress - hexViewerState->memoryDescriptor.addressRange.startAddress; - const auto value = (*hexViewerState->data)[byteIndex]; - - const auto hoveredPrimary = hexViewerState->hoveredByteItem == this; - - if (hexViewerState->settings.displayAsciiValues) { - if (this->selected) { - painter->drawPixmap( - boundingRect, - ByteItem::selectedAsciiPixmapsByValue[value] - ); - return; - } - - if (this->changed) { - painter->drawPixmap( - boundingRect, - ByteItem::changedMemoryAsciiPixmapsByValue[value] - ); - return; - } - - if (this->stackMemory && hexViewerState->settings.groupStackMemory) { - painter->drawPixmap( - boundingRect, - ByteItem::stackMemoryAsciiPixmapsByValue[value] - ); - return; - } - - if (this->grouped && hexViewerState->settings.highlightFocusedMemory) { - painter->drawPixmap( - boundingRect, - ByteItem::groupedAsciiPixmapsByValue[value] - ); - return; - } - - if (hoveredPrimary) { - painter->drawPixmap( - boundingRect, - ByteItem::hoveredPrimaryAsciiPixmapsByValue[value] - ); - return; - } - - painter->drawPixmap(boundingRect, this->standardAsciiPixmapsByValue[value]); - return; - } - - if (this->selected) { - painter->drawPixmap( - boundingRect, - ByteItem::selectedPixmapsByValue[value] - ); - return; - } - - if (this->changed) { - painter->drawPixmap( - boundingRect, - ByteItem::changedMemoryPixmapsByValue[value] - ); - return; - } - - if (this->stackMemory && hexViewerState->settings.groupStackMemory) { - painter->drawPixmap( - boundingRect, - ByteItem::stackMemoryPixmapsByValue[value] - ); - return; - } - - if (this->grouped && hexViewerState->settings.highlightFocusedMemory) { - painter->drawPixmap( - boundingRect, - ByteItem::groupedPixmapsByValue[value] - ); - return; - } - - if (hoveredPrimary) { - painter->drawPixmap( - boundingRect, - ByteItem::hoveredPrimaryPixmapsByValue[value] - ); - return; - } - - painter->drawPixmap( - boundingRect, - this->standardPixmapsByValue[value] - ); - } - - void ByteItem::generatePixmapCaches() { - const auto lock = std::unique_lock(ByteItem::pixmapCacheMutex); - - if (ByteItem::pixmapCachesGenerated) { - return; - } - - static constexpr auto standardBackgroundColor = QColor(0x32, 0x33, 0x30, 255); - static constexpr auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); - static constexpr auto selectedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); - static constexpr auto groupedBackgroundColor = QColor(0x44, 0x44, 0x41, 255); - static constexpr auto stackMemoryBackgroundColor = QColor(0x44, 0x44, 0x41, 200); - static constexpr auto stackMemoryBarColor = QColor(0x67, 0x57, 0x20, 255); - static constexpr auto changedMemoryBackgroundColor = QColor(0x5C, 0x49, 0x5D, 200); - static constexpr auto changedMemoryFadedBackgroundColor = QColor(0x5C, 0x49, 0x5D, 125); - - static const auto hoveredStackMemoryBackgroundColor = QColor( - stackMemoryBackgroundColor.red(), - stackMemoryBackgroundColor.green(), - stackMemoryBackgroundColor.blue(), - 255 - ); - - static constexpr auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70); - - static constexpr auto standardFontColor = QColor(0xAF, 0xB1, 0xB3); - static constexpr auto fadedFontColor = QColor(0xAF, 0xB1, 0xB3, 100); - static constexpr auto asciiFontColor = QColor(0xA7, 0x77, 0x26); - static constexpr auto changedMemoryAsciiFontColor = QColor(0xB7, 0x7F, 0x21); - - const auto byteItemRect = QRect(0, 0, ByteItem::WIDTH, ByteItem::HEIGHT); - const auto byteItemSize = byteItemRect.size(); - - auto standardTemplatePixmap = QPixmap(byteItemSize); - standardTemplatePixmap.fill(standardBackgroundColor); - - auto highlightedTemplatePixmap = QPixmap(byteItemSize); - highlightedTemplatePixmap.fill(highlightedBackgroundColor); - - auto selectedTemplatePixmap = QPixmap(byteItemSize); - selectedTemplatePixmap.fill(selectedBackgroundColor); - - auto groupedTemplatePixmap = QPixmap(byteItemSize); - groupedTemplatePixmap.fill(groupedBackgroundColor); - - auto stackMemoryTemplatePixmap = QPixmap(byteItemSize); - stackMemoryTemplatePixmap.fill(stackMemoryBackgroundColor); - - { - auto painter = QPainter(&stackMemoryTemplatePixmap); - painter.setBrush(stackMemoryBarColor); - painter.setPen(Qt::PenStyle::NoPen); - painter.drawRect(0, byteItemSize.height() - 3, byteItemSize.width(), 3); - } - - auto changedMemoryTemplatePixmap = QPixmap(byteItemSize); - changedMemoryTemplatePixmap.fill(changedMemoryBackgroundColor); - - auto changedMemoryFadedTemplatePixmap = QPixmap(byteItemSize); - changedMemoryFadedTemplatePixmap.fill(changedMemoryFadedBackgroundColor); - - auto hoveredStackMemoryTemplatePixmap = QPixmap(byteItemSize); - hoveredStackMemoryTemplatePixmap.fill(hoveredStackMemoryBackgroundColor); - - auto hoveredPrimaryTemplatePixmap = QPixmap(byteItemSize); - hoveredPrimaryTemplatePixmap.fill(hoveredBackgroundColor); - - static auto font = QFont("'Ubuntu', sans-serif", 8); - - for (std::uint16_t value = 0x00; value <= 0xFF; ++value) { - const auto hexValue = QString::number(value, 16).rightJustified(2, '0').toUpper(); - const auto asciiValue = value >= 32 && value <= 126 - ? std::optional("'" + QString(QChar(value)) + "'") - : std::nullopt; - - { - auto standardPixmap = standardTemplatePixmap; - auto painter = QPainter(&standardPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::standardPixmapsByValue.emplace_back(std::move(standardPixmap)); - } - - { - auto selectedPixmap = selectedTemplatePixmap; - auto painter = QPainter(&selectedPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::selectedPixmapsByValue.emplace_back(std::move(selectedPixmap)); - } - - { - auto groupedPixmap = groupedTemplatePixmap; - auto painter = QPainter(&groupedPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::groupedPixmapsByValue.emplace_back(std::move(groupedPixmap)); - } - - { - auto stackMemoryPixmap = stackMemoryTemplatePixmap; - auto painter = QPainter(&stackMemoryPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::stackMemoryPixmapsByValue.emplace_back(std::move(stackMemoryPixmap)); - } - - { - auto changedMemoryPixmap = changedMemoryTemplatePixmap; - auto painter = QPainter(&changedMemoryPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::changedMemoryPixmapsByValue.emplace_back(std::move(changedMemoryPixmap)); - } - - { - auto hoveredPrimaryPixmap = hoveredPrimaryTemplatePixmap; - auto painter = QPainter(&hoveredPrimaryPixmap); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); - - ByteItem::hoveredPrimaryPixmapsByValue.emplace_back(std::move(hoveredPrimaryPixmap)); - } - - { - auto standardAsciiPixmap = standardTemplatePixmap; - auto painter = QPainter(&standardAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::standardAsciiPixmapsByValue.emplace_back(std::move(standardAsciiPixmap)); - } - - { - auto selectedAsciiPixmap = selectedTemplatePixmap; - auto painter = QPainter(&selectedAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::selectedAsciiPixmapsByValue.emplace_back(std::move(selectedAsciiPixmap)); - } - - { - auto groupedAsciiPixmap = groupedTemplatePixmap; - auto painter = QPainter(&groupedAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::groupedAsciiPixmapsByValue.emplace_back(std::move(groupedAsciiPixmap)); - } - - { - auto stackMemoryAsciiPixmap = stackMemoryTemplatePixmap; - auto painter = QPainter(&stackMemoryAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::stackMemoryAsciiPixmapsByValue.emplace_back(std::move(stackMemoryAsciiPixmap)); - } - - { - auto changedMemoryAsciiPixmap = asciiValue.has_value() - ? changedMemoryTemplatePixmap - : changedMemoryFadedTemplatePixmap; - - auto painter = QPainter(&changedMemoryAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? changedMemoryAsciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::changedMemoryAsciiPixmapsByValue.emplace_back(std::move(changedMemoryAsciiPixmap)); - } - - { - auto hoveredPrimaryAsciiPixmap = hoveredPrimaryTemplatePixmap; - auto painter = QPainter(&hoveredPrimaryAsciiPixmap); - painter.setFont(font); - painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); - - ByteItem::hoveredPrimaryAsciiPixmapsByValue.emplace_back(std::move(hoveredPrimaryAsciiPixmap)); - } - } - - { - ByteItem::missingDataPixmap = standardTemplatePixmap; - auto painter = QPainter(&ByteItem::missingDataPixmap.value()); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, "??"); - } - - { - ByteItem::selectedMissingDataPixmap = selectedTemplatePixmap; - auto painter = QPainter(&ByteItem::selectedMissingDataPixmap.value()); - painter.setFont(font); - painter.setPen(standardFontColor); - painter.drawText(byteItemRect, Qt::AlignCenter, "??"); - } - - ByteItem::pixmapCachesGenerated = true; - } + {} } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp index 1e497e56..d8043822 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp @@ -14,50 +14,23 @@ namespace Bloom::Widgets class ByteItem: public HexViewerItem { public: - static constexpr int WIDTH = 27; - static constexpr int HEIGHT = 21; + static constexpr int WIDTH = 28; + static constexpr int HEIGHT = 22; - static constexpr int RIGHT_MARGIN = 5; - static constexpr int BOTTOM_MARGIN = 5; + static constexpr int RIGHT_MARGIN = 6; + static constexpr int BOTTOM_MARGIN = 6; - bool selected = false; - bool excluded = false; - bool grouped = false; - bool stackMemory = false; - bool changed = false; + bool selected:1 = false; + bool excluded:1 = false; + bool grouped:1 = false; + bool stackMemory:1 = false; + bool changed:1 = false; explicit ByteItem(Targets::TargetMemoryAddress address); QSize size() const override { return QSize(ByteItem::WIDTH, ByteItem::HEIGHT); } - - void paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const override; - - private: - static inline std::atomic pixmapCachesGenerated = false; - static inline std::mutex pixmapCacheMutex; - - static inline std::vector standardPixmapsByValue = {}; - static inline std::vector selectedPixmapsByValue = {}; - static inline std::vector groupedPixmapsByValue = {}; - static inline std::vector stackMemoryPixmapsByValue = {}; - static inline std::vector changedMemoryPixmapsByValue = {}; - static inline std::vector standardAsciiPixmapsByValue = {}; - static inline std::vector selectedAsciiPixmapsByValue = {}; - static inline std::vector groupedAsciiPixmapsByValue = {}; - static inline std::vector stackMemoryAsciiPixmapsByValue = {}; - static inline std::vector changedMemoryAsciiPixmapsByValue = {}; - static inline std::vector hoveredPrimaryPixmapsByValue = {}; - static inline std::vector hoveredPrimaryAsciiPixmapsByValue = {}; - static inline std::optional missingDataPixmap = {}; - static inline std::optional selectedMissingDataPixmap = {}; - - static void generatePixmapCaches(); }; #pragma pack(pop) } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.cpp index d462e10d..b0ecfc70 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.cpp @@ -1,7 +1,6 @@ #include "FocusedRegionGroupItem.hpp" #include -#include namespace Bloom::Widgets { @@ -137,29 +136,6 @@ namespace Bloom::Widgets } } - void FocusedRegionGroupItem::paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const { - if (!hexViewerState->settings.displayAnnotations) { - return; - } - - painter->setRenderHints(QPainter::RenderHint::Antialiasing, false); - - if (!graphicsItem->isEnabled()) { - painter->setOpacity(0.5); - } - - this->paintRegionNameAnnotation(painter, hexViewerState, graphicsItem); - - if (this->focusedMemoryRegion.dataType != MemoryRegionDataType::UNKNOWN) { - // Paint the value annotation - this->paintValueAnnotation(painter, hexViewerState, graphicsItem); - } - } - QMargins FocusedRegionGroupItem::groupMargins( const HexViewerSharedState* hexViewerState, const int maximumWidth @@ -198,185 +174,4 @@ namespace Bloom::Widgets return QMargins(0, 0, 0, 0); } - - void FocusedRegionGroupItem::paintRegionNameAnnotation( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const { - auto labelText = this->focusedMemoryRegion.name; - - static constexpr auto lineColor = QColor(0x4F, 0x4F, 0x4F); - static constexpr auto labelFontColor = QColor(0x68, 0x68, 0x68); - - static auto labelFont = QFont("'Ubuntu', sans-serif"); - labelFont.setPixelSize(12); - - painter->setFont(labelFont); - - const auto groupWidth = this->groupSize.width(); - const auto fontMetrics = painter->fontMetrics(); - auto labelSize = fontMetrics.size(Qt::TextSingleLine, labelText); - if (labelSize.width() > groupWidth) { - labelSize.setWidth(groupWidth); - labelText = fontMetrics.elidedText( - labelText, - Qt::TextElideMode::ElideRight, - groupWidth - ); - } - - const auto heightOffset = this->groupSize.height() - FocusedRegionGroupItem::ANNOTATION_HEIGHT + 4; - - const auto verticalLineYStart = static_cast(heightOffset); - const auto verticalLineYEnd = static_cast(heightOffset + 5); - - const auto labelRect = QRect( - (groupWidth - labelSize.width()) / 2, - verticalLineYEnd + 10, - labelSize.width(), - labelSize.height() - ); - - painter->setPen(lineColor); - - if (this->focusedMemoryRegion.addressRange.startAddress != this->focusedMemoryRegion.addressRange.endAddress) { - const auto lineStartX = this->items.front()->relativePosition.x() + (ByteItem::WIDTH / 2); - const auto lineEndX = this->multiLine - ? groupWidth - + (ByteItem::WIDTH / 2) - : this->items.back()->relativePosition.x() + (ByteItem::WIDTH / 2); - - painter->drawLine(QLine( - lineStartX, - verticalLineYStart, - lineStartX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineEndX, - verticalLineYStart, - lineEndX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineStartX, - verticalLineYEnd, - lineEndX, - verticalLineYEnd - )); - } - - painter->drawLine(QLine( - groupWidth / 2, - verticalLineYEnd, - groupWidth / 2, - verticalLineYEnd + 4 - )); - - painter->setPen(labelFontColor); - painter->drawText(labelRect, Qt::AlignCenter, labelText); - } - - void FocusedRegionGroupItem::paintValueAnnotation( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const { - using Targets::TargetMemoryEndianness; - - auto labelText = this->valueLabel.value_or("??"); - - static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); - static const auto labelFontColor = QColor(0x94, 0x6F, 0x30); - - static auto labelFont = QFont("'Ubuntu', sans-serif"); - labelFont.setPixelSize(12); - labelFont.setItalic(true); - - painter->setFont(labelFont); - - const auto groupWidth = this->groupSize.width(); - const auto fontMetrics = painter->fontMetrics(); - auto labelSize = fontMetrics.size(Qt::TextSingleLine, labelText); - if (labelSize.width() > groupWidth) { - labelSize.setWidth(groupWidth); - labelText = fontMetrics.elidedText( - labelText, - Qt::TextElideMode::ElideRight, - groupWidth - ); - } - - const auto heightOffset = FocusedRegionGroupItem::ANNOTATION_HEIGHT - 4; - - const auto verticalLineYStart = static_cast(heightOffset - 5); - const auto verticalLineYEnd = static_cast(heightOffset); - - const auto labelRect = QRect( - (groupWidth - labelSize.width()) / 2, - verticalLineYStart - 10 - labelSize.height(), - labelSize.width(), - labelSize.height() - ); - - painter->setPen(lineColor); - - if (this->focusedMemoryRegion.addressRange.startAddress != this->focusedMemoryRegion.addressRange.endAddress) { - const auto lineStartX = this->items.front()->relativePosition.x() + (ByteItem::WIDTH / 2); - const auto lineEndX = this->multiLine - ? groupWidth - + (ByteItem::WIDTH / 2) - : this->items.back()->relativePosition.x() + (ByteItem::WIDTH / 2); - - painter->drawLine(QLine( - lineStartX, - verticalLineYStart, - lineStartX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineEndX, - verticalLineYStart, - lineEndX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineStartX, - verticalLineYStart, - lineEndX, - verticalLineYStart - )); - - /* - * Draw a circle just above the first byte item of the region, to indicate the first byte used to generate - * the value (which will depend on the configured endianness of the region). - */ - painter->setBrush(lineColor); - - constexpr auto radius = 2; - painter->drawEllipse( - QPoint( - this->focusedMemoryRegion.endianness == TargetMemoryEndianness::BIG ? lineStartX : lineEndX, - !this->multiLine || this->focusedMemoryRegion.endianness == TargetMemoryEndianness::BIG - ? verticalLineYStart - : this->groupSize.height() - FocusedRegionGroupItem::ANNOTATION_HEIGHT + 4 + 5 - ), - radius, - radius - ); - } - - painter->drawLine(QLine( - groupWidth / 2, - verticalLineYStart - 4, - groupWidth / 2, - verticalLineYStart - )); - - painter->setPen(labelFontColor); - painter->drawText(labelRect, Qt::AlignCenter, labelText); - } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.hpp index e45f4156..0be15d27 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/FocusedRegionGroupItem.hpp @@ -14,6 +14,11 @@ namespace Bloom::Widgets class FocusedRegionGroupItem: public GroupItem { public: + static constexpr int ANNOTATION_HEIGHT = 35; + + const FocusedMemoryRegion& focusedMemoryRegion; + std::optional valueLabel; + FocusedRegionGroupItem( const FocusedMemoryRegion& focusedRegion, std::unordered_map& byteItemsByAddress, @@ -24,31 +29,7 @@ namespace Bloom::Widgets void refreshValue(const HexViewerSharedState& hexViewerState); - void paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const override; - protected: QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override; - - private: - static constexpr int ANNOTATION_HEIGHT = 35; - - const FocusedMemoryRegion& focusedMemoryRegion; - std::optional valueLabel; - - __attribute__((always_inline)) inline void paintRegionNameAnnotation( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const; - - __attribute__((always_inline)) inline void paintValueAnnotation( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const; }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GraphicsItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GraphicsItem.hpp deleted file mode 100644 index 91061676..00000000 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GraphicsItem.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -#include "HexViewerItem.hpp" -#include "HexViewerSharedState.hpp" - -namespace Bloom::Widgets -{ - class GraphicsItem: public QGraphicsItem - { - public: - HexViewerItem* hexViewerItem = nullptr; - - GraphicsItem(const HexViewerSharedState* hexViewerState) - : hexViewerState(hexViewerState) - { - this->setAcceptHoverEvents(true); - this->setCacheMode(QGraphicsItem::CacheMode::NoCache); - } - - ~GraphicsItem() { - if (this->hexViewerItem != nullptr) { - this->hexViewerItem->allocatedGraphicsItem = nullptr; - } - } - - void setHexViewerItem(HexViewerItem* item) { - if (this->hexViewerItem != nullptr) { - this->hexViewerItem->allocatedGraphicsItem = nullptr; - } - - this->hexViewerItem = item; - - if (this->hexViewerItem == nullptr) { - this->setVisible(false); - return; - } - - if (this->hexViewerItem->allocatedGraphicsItem != nullptr) { - this->hexViewerItem->allocatedGraphicsItem->setHexViewerItem(nullptr); - } - - this->hexViewerItem->allocatedGraphicsItem = this; - this->setPos(this->hexViewerItem->position()); - this->setVisible(true); - this->update(); - } - - [[nodiscard]] QRectF boundingRect() const override { - if (this->hexViewerItem != nullptr) { - const auto itemSize = this->hexViewerItem->size(); - return QRectF(0, 0, itemSize.width(), itemSize.height()); - } - - return QRectF(); - } - - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override { - if (this->hexViewerItem != nullptr) { - return this->hexViewerItem->paint(painter, this->hexViewerState, this); - } - } - - private: - const HexViewerSharedState* hexViewerState; - }; -} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GroupItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GroupItem.hpp index 24c937e7..db95059e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GroupItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/GroupItem.hpp @@ -17,6 +17,8 @@ namespace Bloom::Widgets { public: std::vector items; + QSize groupSize = {}; + bool multiLine = false; ~GroupItem(); @@ -28,18 +30,7 @@ namespace Bloom::Widgets [[nodiscard]] std::vector flattenedItems() const; - void paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const override { - return; - } - protected: - QSize groupSize = {}; - bool multiLine = false; - GroupItem( Targets::TargetMemoryAddress startAddress, HexViewerItem* parent = nullptr diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.cpp index b417d5a7..1ea53d60 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.cpp @@ -1,7 +1,5 @@ #include "HexViewerItem.hpp" -#include "GraphicsItem.hpp" - namespace Bloom::Widgets { HexViewerItem::HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent) @@ -9,12 +7,6 @@ namespace Bloom::Widgets , parent(parent) {} - HexViewerItem::~HexViewerItem() { - if (this->allocatedGraphicsItem != nullptr) { - this->allocatedGraphicsItem->setHexViewerItem(nullptr); - } - } - QPoint HexViewerItem::position() const { if (this->parent != nullptr) { return this->parent->position() + this->relativePosition; diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.hpp index 9b0a9536..82e3440d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItem.hpp @@ -18,26 +18,15 @@ namespace Bloom::Widgets static constexpr int RIGHT_MARGIN = 5; static constexpr int BOTTOM_MARGIN = 5; + HexViewerItem* parent = nullptr; + const Targets::TargetMemoryAddress startAddress = 0; QPoint relativePosition = {}; - HexViewerItem* parent = nullptr; - GraphicsItem* allocatedGraphicsItem = nullptr; - HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent = nullptr); - - virtual ~HexViewerItem(); - QPoint position() const; - virtual QSize size() const = 0; - - virtual void paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const = 0; }; #pragma pack(pop) } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp new file mode 100644 index 00000000..30bdc4a9 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp @@ -0,0 +1,184 @@ +#include "HexViewerItemIndex.hpp" + +#include +#include +#include "src/Logger/Logger.hpp" + +namespace Bloom::Widgets +{ + HexViewerItemIndex::HexViewerItemIndex( + const TopLevelGroupItem* topLevelGroupItem, + const QGraphicsScene* hexViewerScene + ) + : topLevelGroupItem(topLevelGroupItem) + , hexViewerScene(hexViewerScene) + { + this->refreshFlattenedItems(); + } + + HexViewerItemIndex::ItemRangeType HexViewerItemIndex::items(int yStart, int yEnd) const { + assert(!this->byteItemGrid.empty()); + + const auto gridPointCount = this->byteItemGrid.size(); + + const auto startGridPointIndex = static_castbyteItemGrid)::size_type>( + std::floor( + static_cast(yStart) / static_cast(HexViewerItemIndex::GRID_SIZE) + ) + ); + + if (startGridPointIndex >= gridPointCount) { + return HexViewerItemIndex::ItemRangeType(); + } + + const auto endGridPointIndex = static_castbyteItemGrid)::size_type>(std::min( + static_castbyteItemGrid)::size_type>( + std::ceil( + static_cast(yEnd) / static_cast(HexViewerItemIndex::GRID_SIZE) + ) + ), + gridPointCount - 1 + )); + + return HexViewerItemIndex::ItemRangeType( + this->byteItemGrid[startGridPointIndex], + endGridPointIndex == gridPointCount - 1 + ? this->flattenedItems.end() + : this->byteItemGrid[endGridPointIndex] + ); + } + + ByteItem* HexViewerItemIndex::byteItemAt(const QPointF& position) const { + const auto gridPointCount = this->byteItemGrid.size(); + + const auto startGridPointIndex = static_castbyteItemGrid)::size_type>( + std::floor( + static_cast(std::max(position.y(), static_cast(0))) + / static_cast(HexViewerItemIndex::GRID_SIZE) + ) + ); + + if (startGridPointIndex >= gridPointCount) { + return nullptr; + } + + const auto startItemIt = this->byteItemGrid[startGridPointIndex]; + + for (auto itemIt = startItemIt; itemIt < this->flattenedItems.end(); ++itemIt) { + auto* item = *itemIt; + + const auto itemPosition = item->position(); + + if (itemPosition.y() > position.y()) { + break; + } + + const auto itemSize = item->size(); + + if ( + (itemPosition.y() + itemSize.height()) < position.y() + || itemPosition.x() > position.x() + || (itemPosition.x() + itemSize.width()) < position.x() + ) { + continue; + } + + auto* byteItem = dynamic_cast(item); + if (byteItem != nullptr) { + return byteItem; + } + } + + return nullptr; + } + + ByteItem* HexViewerItemIndex::closestByteItem(int yPosition) const { + assert(!this->byteItemGrid.empty()); + + const auto gridPointCount = this->byteItemGrid.size(); + + const auto gridPointIndex = static_castbyteItemGrid)::size_type>( + std::round( + static_cast(yPosition) / static_cast(HexViewerItemIndex::GRID_SIZE) + ) + ); + + // Sanity check + assert(gridPointCount > gridPointIndex); + + return static_cast(*(this->byteItemGrid[gridPointIndex])); + } + + std::vector HexViewerItemIndex::intersectingByteItems(const QRectF& rect) const { + auto output = std::vector(); + + const auto yStart = static_cast(std::max(rect.y(), static_cast(0))); + const auto yEnd = static_cast(std::max(rect.y() + rect.height(), static_cast(0))); + + const auto items = this->items(yStart, yEnd); + + for (auto& item : items) { + const auto itemRect = QRectF(item->position(), item->size()); + + if (itemRect.y() > yEnd) { + break; + } + + if (!itemRect.intersects(rect)) { + continue; + } + + auto* byteItem = dynamic_cast(item); + if (byteItem != nullptr) { + output.push_back(byteItem); + } + } + + return output; + } + + void HexViewerItemIndex::refreshFlattenedItems() { + this->flattenedItems = this->topLevelGroupItem->flattenedItems(); + } + + void HexViewerItemIndex::refreshIndex() { + const auto pointsRequired = static_cast( + this->hexViewerScene->sceneRect().height() / HexViewerItemIndex::GRID_SIZE + ); + + this->byteItemGrid.clear(); + this->byteItemGrid.reserve(pointsRequired); + + this->byteItemLines.clear(); + this->byteItemYStartPositionsByAddress.clear(); + + auto currentByteItemGridPoint = 0; + auto currentLineYPosition = 0; + + for (auto itemIt = this->flattenedItems.begin(); itemIt != this->flattenedItems.end(); ++itemIt) { + auto& item = *itemIt; + + const auto byteItem = dynamic_cast(item); + + if (byteItem == nullptr) { + continue; + } + + const auto itemYStartPosition = byteItem->position().y(); + const auto itemYEndPosition = itemYStartPosition + byteItem->size().height(); + + this->byteItemYStartPositionsByAddress.emplace(byteItem->startAddress, itemYStartPosition); + + if (itemYStartPosition > currentLineYPosition) { + this->byteItemLines.push_back(byteItem); + currentLineYPosition = itemYStartPosition; + } + + if (itemYEndPosition >= currentByteItemGridPoint) { + // This byte item is the first to exceed or intersect with the currentByteItemGridPoint + this->byteItemGrid.push_back(itemIt); + currentByteItemGridPoint += HexViewerItemIndex::GRID_SIZE; + } + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp new file mode 100644 index 00000000..b875ce73 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "HexViewerItem.hpp" +#include "TopLevelGroupItem.hpp" +#include "ByteItem.hpp" + +namespace Bloom::Widgets +{ + /** + * This class maintains indices of hex viewer item positions and provides fast lookups for items within certain + * positions. + */ + class HexViewerItemIndex + { + public: + using FlattenedItemType = std::vector; + using FlattenedItemItType = FlattenedItemType::const_iterator; + using ItemRangeType = std::ranges::subrange; + + std::vector byteItemLines; + std::unordered_map byteItemYStartPositionsByAddress; + + explicit HexViewerItemIndex( + const TopLevelGroupItem* topLevelGroupItem, + const QGraphicsScene* hexViewerScene + ); + + /** + * Identifies the items between two points on the Y axis, and returns them in the form of a subrange, in + * constant-time. + * + * This member function can return items that are outside of the range, by HexViewerItemIndex::GRID_SIZE. + * The caller should tolerate this. + * + * CAUTION: The returned range can be invalidated! This member function should only be used immediately before + * you intend to do work on the returned range. Do **NOT** keep hold of the returned range. You should consider + * any data returned by this function to be invalid as soon as program control has returned to the main event + * loop. + * + * @param yStart + * @param yEnd + * + * @return + */ + ItemRangeType items(int yStart, int yEnd) const; + + /** + * Returns the byte item at the given position. Byte items do not overlap. + * + * @param position + * @return + */ + ByteItem* byteItemAt(const QPointF& position) const; + + /** + * Returns the closest byte item from the given position on the Y-axis. + * * + * @param yPosition + * @return + */ + ByteItem* closestByteItem(int yPosition) const; + + /** + * Returns all byte items that intersect with the given rectangle. + * + * @param rect + * @return + */ + std::vector intersectingByteItems(const QRectF& rect) const; + + void refreshFlattenedItems(); + void refreshIndex(); + + protected: + static constexpr auto GRID_SIZE = 100; + + const TopLevelGroupItem* topLevelGroupItem; + const QGraphicsScene* hexViewerScene; + + /** + * An std::vector of all HexViewerItems along with their parents and children, sorted by position. + * + * Some of the lookup member functions return subranges from this container. + */ + FlattenedItemType flattenedItems; + + /** + * Byte item Y-axis grid (one-dimensional index) + * + * Each element in this std::vector represents a point on the Y-axis grid. The distance between each point is + * equal to HexViewerItemIndex::GRID_SIZE. + * + * The value of each element is an iterator addressing the HexViewerItem* at that point on the grid. + * + * Although the iterators address hex viewer items, all elements can be safely cast to ByteItem*, as we only + * consider byte items when populating this grid. See HexViewerItemIndex::refreshIndex() for more. + * + * We use an std::vector here because it provides constant-time access to any element. + */ + std::vector byteItemGrid; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp new file mode 100644 index 00000000..240109bf --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp @@ -0,0 +1,723 @@ +#include "HexViewerItemRenderer.hpp" + +#include +#include + +namespace Bloom::Widgets +{ + HexViewerItemRenderer::HexViewerItemRenderer( + const HexViewerSharedState& hexViewerState, + const HexViewerItemIndex& itemIndex, + const QGraphicsView* view + ) + : hexViewerState(hexViewerState) + , itemIndex(itemIndex) + , view(view) + , viewport(view->viewport()) + { + this->setAcceptHoverEvents(true); + this->setCacheMode(QGraphicsItem::CacheMode::NoCache); + + if (!HexViewerItemRenderer::pixmapCachesGenerated) { + HexViewerItemRenderer::generatePixmapCaches(); + } + } + + void HexViewerItemRenderer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + const auto vScrollBarValue = this->view->verticalScrollBar()->value(); + const auto visibleItems = this->itemIndex.items( + vScrollBarValue, + vScrollBarValue + this->viewport->size().height() + ); + + painter->setRenderHints(QPainter::RenderHint::Antialiasing, false); + + // Paint the ancestors of the first visible item + const auto& firstItem = *(visibleItems.begin()); + + auto* parentItem = firstItem->parent; + while (parentItem != nullptr) { + painter->setOpacity(1); + this->paintItem(parentItem, painter); + parentItem = parentItem->parent; + } + + for (auto& item : visibleItems) { + painter->setOpacity(1); + this->paintItem(item, painter); + } + } + + void HexViewerItemRenderer::paintItem(const HexViewerItem* item, QPainter* painter) { + const auto* byteItem = dynamic_cast(item); + + if (byteItem != nullptr) { + return this->paintByteItem(byteItem, painter); + } + + const auto* focusedRegionItem = dynamic_cast(item); + + if (focusedRegionItem != nullptr) { + return this->paintFocusedRegionGroupItem(focusedRegionItem, painter); + } + + const auto* stackMemoryItem = dynamic_cast(item); + + if (stackMemoryItem != nullptr) { + return this->paintStackMemoryGroupItem(stackMemoryItem, painter); + } + } + + void HexViewerItemRenderer::paintByteItem(const ByteItem* item, QPainter* painter) { + const auto position = item->position(); + const auto boundingRect = QRect(position.x(), position.y(), ByteItem::WIDTH, ByteItem::HEIGHT); + + painter->setOpacity(!this->isEnabled() || (item->excluded && !item->selected) ? 0.6 : 1); + + if (item->excluded || !this->hexViewerState.data.has_value()) { + if (item->selected) { + painter->drawPixmap(boundingRect, HexViewerItemRenderer::selectedMissingDataPixmap.value()); + return; + } + + painter->drawPixmap(boundingRect, HexViewerItemRenderer::missingDataPixmap.value()); + return; + } + + const auto byteIndex = item->startAddress - this->hexViewerState.memoryDescriptor.addressRange.startAddress; + const auto value = (*(this->hexViewerState.data))[byteIndex]; + + const auto hoveredPrimary = this->hexViewerState.hoveredByteItem == item; + + if (this->hexViewerState.settings.displayAsciiValues) { + if (item->selected) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::selectedAsciiPixmapsByValue[value] + ); + return; + } + + if (item->changed) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::changedMemoryAsciiPixmapsByValue[value] + ); + return; + } + + if (item->stackMemory && this->hexViewerState.settings.groupStackMemory) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::stackMemoryAsciiPixmapsByValue[value] + ); + return; + } + + if (item->grouped && this->hexViewerState.settings.highlightFocusedMemory) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::groupedAsciiPixmapsByValue[value] + ); + return; + } + + if (hoveredPrimary) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::hoveredPrimaryAsciiPixmapsByValue[value] + ); + return; + } + + painter->drawPixmap(boundingRect, HexViewerItemRenderer::standardAsciiPixmapsByValue[value]); + return; + } + + if (item->selected) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::selectedPixmapsByValue[value] + ); + return; + } + + if (item->changed) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::changedMemoryPixmapsByValue[value] + ); + return; + } + + if (item->stackMemory && this->hexViewerState.settings.groupStackMemory) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::stackMemoryPixmapsByValue[value] + ); + return; + } + + if (item->grouped && this->hexViewerState.settings.highlightFocusedMemory) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::groupedPixmapsByValue[value] + ); + return; + } + + if (hoveredPrimary) { + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::hoveredPrimaryPixmapsByValue[value] + ); + return; + } + + painter->drawPixmap( + boundingRect, + HexViewerItemRenderer::standardPixmapsByValue[value] + ); + } + + void HexViewerItemRenderer::paintFocusedRegionGroupItem(const FocusedRegionGroupItem* item, QPainter* painter) { + if (!this->hexViewerState.settings.displayAnnotations) { + return; + } + + const auto position = item->position(); + + // The region name label + { + auto labelText = item->focusedMemoryRegion.name; + + static constexpr auto lineColor = QColor(0x4F, 0x4F, 0x4F); + static constexpr auto labelFontColor = QColor(0x68, 0x68, 0x68); + + static auto labelFont = QFont("'Ubuntu', sans-serif"); + labelFont.setPixelSize(12); + + painter->setFont(labelFont); + + const auto groupWidth = item->groupSize.width(); + const auto fontMetrics = painter->fontMetrics(); + auto labelSize = fontMetrics.size(Qt::TextSingleLine, labelText); + if (labelSize.width() > groupWidth) { + labelSize.setWidth(groupWidth); + labelText = fontMetrics.elidedText( + labelText, + Qt::TextElideMode::ElideRight, + groupWidth + ); + } + + const auto heightOffset = item->groupSize.height() - FocusedRegionGroupItem::ANNOTATION_HEIGHT + 4; + + const auto verticalLineYStart = position.y() + static_cast(heightOffset); + const auto verticalLineYEnd = position.y() + static_cast(heightOffset + 5); + + const auto labelRect = QRect( + position.x() + (groupWidth - labelSize.width()) / 2, + verticalLineYEnd + 10, + labelSize.width(), + labelSize.height() + ); + + painter->setPen(lineColor); + + if (item->focusedMemoryRegion.addressRange.startAddress != + item->focusedMemoryRegion.addressRange.endAddress) { + const auto lineStartX = + position.x() + item->items.front()->relativePosition.x() + (ByteItem::WIDTH / 2); + const auto lineEndX = item->multiLine + ? position.x() + groupWidth - (ByteItem::WIDTH / 2) + : position.x() + item->items.back()->relativePosition.x() + (ByteItem::WIDTH / 2); + + painter->drawLine(QLine( + lineStartX, + verticalLineYStart, + lineStartX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineEndX, + verticalLineYStart, + lineEndX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineStartX, + verticalLineYEnd, + lineEndX, + verticalLineYEnd + )); + } + + painter->drawLine(QLine( + position.x() + groupWidth / 2, + verticalLineYEnd, + position.x() + groupWidth / 2, + verticalLineYEnd + 4 + )); + + painter->setPen(labelFontColor); + painter->drawText(labelRect, Qt::AlignCenter, labelText); + } + + // The value label + if (item->focusedMemoryRegion.dataType != MemoryRegionDataType::UNKNOWN) { + using Targets::TargetMemoryEndianness; + + auto labelText = item->valueLabel.value_or("??"); + + static const auto lineColor = QColor(0x4F, 0x4F, 0x4F); + static const auto labelFontColor = QColor(0x94, 0x6F, 0x30); + + static auto labelFont = QFont("'Ubuntu', sans-serif"); + labelFont.setPixelSize(12); + labelFont.setItalic(true); + + painter->setFont(labelFont); + + const auto groupWidth = item->groupSize.width(); + const auto fontMetrics = painter->fontMetrics(); + auto labelSize = fontMetrics.size(Qt::TextSingleLine, labelText); + if (labelSize.width() > groupWidth) { + labelSize.setWidth(groupWidth); + labelText = fontMetrics.elidedText( + labelText, + Qt::TextElideMode::ElideRight, + groupWidth + ); + } + + const auto heightOffset = FocusedRegionGroupItem::ANNOTATION_HEIGHT - 4; + + const auto verticalLineYStart = position.y() + static_cast(heightOffset - 5); + const auto verticalLineYEnd = position.y() + static_cast(heightOffset); + + const auto labelRect = QRect( + position.x() + (groupWidth - labelSize.width()) / 2, + verticalLineYStart - 10 - labelSize.height(), + labelSize.width(), + labelSize.height() + ); + + painter->setPen(lineColor); + + if (item->focusedMemoryRegion.addressRange.startAddress != item->focusedMemoryRegion.addressRange.endAddress) { + const auto lineStartX = position.x() + item->items.front()->relativePosition.x() + (ByteItem::WIDTH / 2); + const auto lineEndX = item->multiLine + ? position.x() + groupWidth - + (ByteItem::WIDTH / 2) + : position.x() + item->items.back()->relativePosition.x() + (ByteItem::WIDTH / 2); + + painter->drawLine(QLine( + lineStartX, + verticalLineYStart, + lineStartX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineEndX, + verticalLineYStart, + lineEndX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineStartX, + verticalLineYStart, + lineEndX, + verticalLineYStart + )); + + /* + * Draw a circle just above the first byte item of the region, to indicate the first byte used to generate + * the value (which will depend on the configured endianness of the region). + */ + painter->setBrush(lineColor); + + constexpr auto radius = 2; + painter->drawEllipse( + QPoint( + item->focusedMemoryRegion.endianness == TargetMemoryEndianness::BIG ? lineStartX : lineEndX, + !item->multiLine || item->focusedMemoryRegion.endianness == TargetMemoryEndianness::BIG + ? verticalLineYStart + : position.y() + item->groupSize.height() - FocusedRegionGroupItem::ANNOTATION_HEIGHT + 4 + 5 + ), + radius, + radius + ); + } + + painter->drawLine(QLine( + position.x() + groupWidth / 2, + verticalLineYStart - 4, + position.x() + groupWidth / 2, + verticalLineYStart + )); + + painter->setPen(labelFontColor); + painter->drawText(labelRect, Qt::AlignCenter, labelText); + } + } + + void HexViewerItemRenderer::paintStackMemoryGroupItem(const StackMemoryGroupItem* item, QPainter* painter) { + const auto position = item->position(); + + const auto headingText = QString("Stack Memory"); + + const auto stackSize = this->hexViewerState.memoryDescriptor.addressRange.endAddress - item->startAddress + 1; + const auto& memoryCapacity = this->hexViewerState.memoryDescriptor.size(); + + const auto stackSizeHeadingText = QString("Stack size:"); + const auto stackSizeValueText = QString::number(stackSize) + " byte(s) (" + + QString::number(static_cast(stackSize) / static_cast(memoryCapacity / 100), 'f' , 1) + + "% of memory capacity)"; + + const auto stackPointerHeadingText = QString("Stack pointer:"); + const auto stackPointerValueText = "0x" + QString::number( + item->stackPointer, + 16 + ).rightJustified(8, '0').toUpper(); + + static constexpr auto lineColor = QColor(0x4F, 0x4F, 0x4F); + static constexpr auto headingLabelFontColor = QColor(0x6F, 0x6F, 0x6F); + static constexpr auto valueLabelFontColor = QColor(0x94, 0x6F, 0x30, 230); + + static auto headingLabelFont = QFont("'Ubuntu', sans-serif"); + headingLabelFont.setPixelSize(13); + + static auto valueFont = QFont("'Ubuntu', sans-serif"); + valueFont.setPixelSize(13); + valueFont.setItalic(true); + + painter->setFont(headingLabelFont); + + const auto groupWidth = item->groupSize.width(); + const auto fontMetrics = painter->fontMetrics(); + + auto headingLabelSize = fontMetrics.size(Qt::TextSingleLine, headingText); + + auto stackSizeHeadingLabelSize = fontMetrics.size(Qt::TextSingleLine, stackSizeHeadingText); + auto stackSizeLabelSize = fontMetrics.size(Qt::TextSingleLine, stackSizeValueText); + + auto stackPointerHeadingLabelSize = fontMetrics.size(Qt::TextSingleLine, stackPointerHeadingText); + auto stackPointerLabelSize = fontMetrics.size(Qt::TextSingleLine, stackPointerValueText); + + static constexpr auto labelLineHeight = 4; + static constexpr auto labelBottomMargin = 10; + static constexpr auto labelRightMargin = 3; + + const auto heightOffset = headingLabelSize.height() + stackSizeHeadingLabelSize.height() + + stackPointerHeadingLabelSize.height() + (labelBottomMargin * 3) + 15; + + const auto verticalLineYStart = static_cast(position.y() + heightOffset - 5); + const auto verticalLineYEnd = static_cast(position.y() + heightOffset); + + const auto lineStartX = position.x() + ByteItem::WIDTH / 2; + const auto lineEndX = position.x() + groupWidth - (ByteItem::WIDTH / 2); + + const auto labelRect = QRect( + position.x() + (groupWidth - headingLabelSize.width()) / 2, + verticalLineYStart - stackPointerHeadingLabelSize.height() - stackSizeHeadingLabelSize.height() + - headingLabelSize.height() - labelLineHeight - (labelBottomMargin * 2) - 3, + headingLabelSize.width(), + headingLabelSize.height() + ); + + const auto stackPointerHeadingLabelRect = QRect( + labelRect.left() + (labelRect.width() / 2) - ( + (stackPointerHeadingLabelSize.width() + stackPointerLabelSize.width()) / 2 + ), + labelRect.bottom() + labelBottomMargin, + stackPointerHeadingLabelSize.width(), + stackPointerHeadingLabelSize.height() + ); + + const auto stackPointerValueLabelRect = QRect( + stackPointerHeadingLabelRect.right() + labelRightMargin, + labelRect.bottom() + labelBottomMargin, + stackPointerLabelSize.width(), + stackPointerLabelSize.height() + ); + + const auto stackSizeHeadingLabelRect = QRect( + labelRect.left() + (labelRect.width() / 2) - ( + (stackSizeHeadingLabelSize.width() + stackSizeLabelSize.width()) / 2 + ), + stackPointerHeadingLabelRect.bottom() + labelBottomMargin, + stackSizeHeadingLabelSize.width(), + stackSizeHeadingLabelSize.height() + ); + + const auto stackSizeValueLabelRect = QRect( + stackSizeHeadingLabelRect.right() + labelRightMargin, + stackPointerHeadingLabelRect.bottom() + labelBottomMargin, + stackSizeLabelSize.width(), + stackSizeLabelSize.height() + ); + + painter->setPen(lineColor); + + painter->drawLine(QLine( + lineStartX, + verticalLineYStart, + lineStartX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineEndX, + verticalLineYStart, + lineEndX, + verticalLineYEnd + )); + + painter->drawLine(QLine( + lineStartX, + verticalLineYStart, + lineEndX, + verticalLineYStart + )); + + painter->drawLine(QLine( + position.x() + groupWidth / 2, + verticalLineYStart - labelLineHeight, + position.x() + groupWidth / 2, + verticalLineYStart + )); + + painter->setPen(headingLabelFontColor); + + painter->drawText(labelRect, Qt::AlignCenter, headingText); + painter->drawText(stackSizeHeadingLabelRect, Qt::AlignCenter, stackSizeHeadingText); + painter->drawText(stackPointerHeadingLabelRect, Qt::AlignCenter, stackPointerHeadingText); + + painter->setFont(valueFont); + painter->setPen(valueLabelFontColor); + painter->drawText(stackSizeValueLabelRect, Qt::AlignCenter, stackSizeValueText); + painter->drawText(stackPointerValueLabelRect, Qt::AlignCenter, stackPointerValueText); + } + + void HexViewerItemRenderer::generatePixmapCaches() { + const auto lock = std::unique_lock(HexViewerItemRenderer::pixmapCacheMutex); + + if (HexViewerItemRenderer::pixmapCachesGenerated) { + return; + } + + static constexpr auto standardBackgroundColor = QColor(0x32, 0x33, 0x30, 0); + static constexpr auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); + static constexpr auto selectedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); + static constexpr auto groupedBackgroundColor = QColor(0x44, 0x44, 0x41, 255); + static constexpr auto stackMemoryBackgroundColor = QColor(0x44, 0x44, 0x41, 200); + static constexpr auto stackMemoryBarColor = QColor(0x67, 0x57, 0x20, 255); + static constexpr auto changedMemoryBackgroundColor = QColor(0x5C, 0x49, 0x5D, 200); + static constexpr auto changedMemoryFadedBackgroundColor = QColor(0x5C, 0x49, 0x5D, 125); + + static const auto hoveredStackMemoryBackgroundColor = QColor( + stackMemoryBackgroundColor.red(), + stackMemoryBackgroundColor.green(), + stackMemoryBackgroundColor.blue(), + 255 + ); + + static constexpr auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70); + + static constexpr auto standardFontColor = QColor(0xAF, 0xB1, 0xB3); + static constexpr auto fadedFontColor = QColor(0xAF, 0xB1, 0xB3, 100); + static constexpr auto asciiFontColor = QColor(0xA7, 0x77, 0x26); + static constexpr auto changedMemoryAsciiFontColor = QColor(0xB7, 0x7F, 0x21); + + const auto byteItemRect = QRect(0, 0, ByteItem::WIDTH, ByteItem::HEIGHT); + const auto byteItemSize = byteItemRect.size(); + + auto standardTemplatePixmap = QPixmap(byteItemSize); + standardTemplatePixmap.fill(standardBackgroundColor); + + auto highlightedTemplatePixmap = QPixmap(byteItemSize); + highlightedTemplatePixmap.fill(highlightedBackgroundColor); + + auto selectedTemplatePixmap = QPixmap(byteItemSize); + selectedTemplatePixmap.fill(selectedBackgroundColor); + + auto groupedTemplatePixmap = QPixmap(byteItemSize); + groupedTemplatePixmap.fill(groupedBackgroundColor); + + auto stackMemoryTemplatePixmap = QPixmap(byteItemSize); + stackMemoryTemplatePixmap.fill(stackMemoryBackgroundColor); + + { + auto painter = QPainter(&stackMemoryTemplatePixmap); + painter.setBrush(stackMemoryBarColor); + painter.setPen(Qt::PenStyle::NoPen); + painter.drawRect(0, byteItemSize.height() - 3, byteItemSize.width(), 3); + } + + auto changedMemoryTemplatePixmap = QPixmap(byteItemSize); + changedMemoryTemplatePixmap.fill(changedMemoryBackgroundColor); + + auto changedMemoryFadedTemplatePixmap = QPixmap(byteItemSize); + changedMemoryFadedTemplatePixmap.fill(changedMemoryFadedBackgroundColor); + + auto hoveredStackMemoryTemplatePixmap = QPixmap(byteItemSize); + hoveredStackMemoryTemplatePixmap.fill(hoveredStackMemoryBackgroundColor); + + auto hoveredPrimaryTemplatePixmap = QPixmap(byteItemSize); + hoveredPrimaryTemplatePixmap.fill(hoveredBackgroundColor); + + static auto font = QFont("'Ubuntu', sans-serif", 8); + + for (std::uint16_t value = 0x00; value <= 0xFF; ++value) { + const auto hexValue = QString::number(value, 16).rightJustified(2, '0').toUpper(); + const auto asciiValue = value >= 32 && value <= 126 + ? std::optional("'" + QString(QChar(value)) + "'") + : std::nullopt; + + { + auto standardPixmap = standardTemplatePixmap; + auto painter = QPainter(&standardPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::standardPixmapsByValue.emplace_back(std::move(standardPixmap)); + } + + { + auto selectedPixmap = selectedTemplatePixmap; + auto painter = QPainter(&selectedPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::selectedPixmapsByValue.emplace_back(std::move(selectedPixmap)); + } + + { + auto groupedPixmap = groupedTemplatePixmap; + auto painter = QPainter(&groupedPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::groupedPixmapsByValue.emplace_back(std::move(groupedPixmap)); + } + + { + auto stackMemoryPixmap = stackMemoryTemplatePixmap; + auto painter = QPainter(&stackMemoryPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::stackMemoryPixmapsByValue.emplace_back(std::move(stackMemoryPixmap)); + } + + { + auto changedMemoryPixmap = changedMemoryTemplatePixmap; + auto painter = QPainter(&changedMemoryPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::changedMemoryPixmapsByValue.emplace_back(std::move(changedMemoryPixmap)); + } + + { + auto hoveredPrimaryPixmap = hoveredPrimaryTemplatePixmap; + auto painter = QPainter(&hoveredPrimaryPixmap); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, hexValue); + + HexViewerItemRenderer::hoveredPrimaryPixmapsByValue.emplace_back(std::move(hoveredPrimaryPixmap)); + } + + { + auto standardAsciiPixmap = standardTemplatePixmap; + auto painter = QPainter(&standardAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::standardAsciiPixmapsByValue.emplace_back(std::move(standardAsciiPixmap)); + } + + { + auto selectedAsciiPixmap = selectedTemplatePixmap; + auto painter = QPainter(&selectedAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::selectedAsciiPixmapsByValue.emplace_back(std::move(selectedAsciiPixmap)); + } + + { + auto groupedAsciiPixmap = groupedTemplatePixmap; + auto painter = QPainter(&groupedAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::groupedAsciiPixmapsByValue.emplace_back(std::move(groupedAsciiPixmap)); + } + + { + auto stackMemoryAsciiPixmap = stackMemoryTemplatePixmap; + auto painter = QPainter(&stackMemoryAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::stackMemoryAsciiPixmapsByValue.emplace_back(std::move(stackMemoryAsciiPixmap)); + } + + { + auto changedMemoryAsciiPixmap = asciiValue.has_value() + ? changedMemoryTemplatePixmap + : changedMemoryFadedTemplatePixmap; + + auto painter = QPainter(&changedMemoryAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? changedMemoryAsciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::changedMemoryAsciiPixmapsByValue.emplace_back(std::move(changedMemoryAsciiPixmap)); + } + + { + auto hoveredPrimaryAsciiPixmap = hoveredPrimaryTemplatePixmap; + auto painter = QPainter(&hoveredPrimaryAsciiPixmap); + painter.setFont(font); + painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue)); + + HexViewerItemRenderer::hoveredPrimaryAsciiPixmapsByValue.emplace_back(std::move(hoveredPrimaryAsciiPixmap)); + } + } + + { + HexViewerItemRenderer::missingDataPixmap = standardTemplatePixmap; + auto painter = QPainter(&HexViewerItemRenderer::missingDataPixmap.value()); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, "??"); + } + + { + HexViewerItemRenderer::selectedMissingDataPixmap = selectedTemplatePixmap; + auto painter = QPainter(&HexViewerItemRenderer::selectedMissingDataPixmap.value()); + painter.setFont(font); + painter.setPen(standardFontColor); + painter.drawText(byteItemRect, Qt::AlignCenter, "??"); + } + + HexViewerItemRenderer::pixmapCachesGenerated = true; + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.hpp new file mode 100644 index 00000000..e0ccb71f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HexViewerItemIndex.hpp" +#include "HexViewerSharedState.hpp" +#include "HexViewerItem.hpp" +#include "TopLevelGroupItem.hpp" +#include "ByteItem.hpp" +#include "FocusedRegionGroupItem.hpp" +#include "StackMemoryGroupItem.hpp" + +namespace Bloom::Widgets +{ + /** + * Renders hex viewer items in a QGraphicsScene. + */ + class HexViewerItemRenderer: public QGraphicsItem + { + public: + QSize size; + + HexViewerItemRenderer( + const HexViewerSharedState& hexViewerState, + const HexViewerItemIndex& itemIndex, + const QGraphicsView* view + ); + + [[nodiscard]] QRectF boundingRect() const override { + return QRectF(0, 0, this->size.width(), this->size.height()); + } + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + protected: + const HexViewerSharedState& hexViewerState; + const HexViewerItemIndex& itemIndex; + + const QGraphicsView* view; + const QWidget* viewport; + + static inline std::atomic pixmapCachesGenerated = false; + static inline std::mutex pixmapCacheMutex; + + static inline std::vector standardPixmapsByValue = {}; + static inline std::vector selectedPixmapsByValue = {}; + static inline std::vector groupedPixmapsByValue = {}; + static inline std::vector stackMemoryPixmapsByValue = {}; + static inline std::vector changedMemoryPixmapsByValue = {}; + static inline std::vector standardAsciiPixmapsByValue = {}; + static inline std::vector selectedAsciiPixmapsByValue = {}; + static inline std::vector groupedAsciiPixmapsByValue = {}; + static inline std::vector stackMemoryAsciiPixmapsByValue = {}; + static inline std::vector changedMemoryAsciiPixmapsByValue = {}; + static inline std::vector hoveredPrimaryPixmapsByValue = {}; + static inline std::vector hoveredPrimaryAsciiPixmapsByValue = {}; + static inline std::optional missingDataPixmap = {}; + static inline std::optional selectedMissingDataPixmap = {}; + + void paintItem(const HexViewerItem* item, QPainter* painter); + inline void paintByteItem(const ByteItem* item, QPainter* painter) __attribute__((__always_inline__)); + inline void paintFocusedRegionGroupItem( + const FocusedRegionGroupItem* item, + QPainter* painter + ) __attribute__((__always_inline__)); + inline void paintStackMemoryGroupItem( + const StackMemoryGroupItem* item, + QPainter* painter + ) __attribute__((__always_inline__)); + + static void generatePixmapCaches(); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.cpp index 7ee3271e..dd903caf 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.cpp @@ -182,6 +182,8 @@ namespace Bloom::Widgets } void ItemGraphicsScene::init() { + this->byteAddressContainer->setPos(this->addressContainerPosition()); + const auto constructHexViewerTopLevelGroupItem = QSharedPointer( new ConstructHexViewerTopLevelGroupItem( this->focusedMemoryRegions, @@ -196,11 +198,16 @@ namespace Bloom::Widgets &ConstructHexViewerTopLevelGroupItem::topLevelGroupItem, this, [this] (TopLevelGroupItem* item) { + const auto margins = this->margins(); + this->topLevelGroup.reset(item); this->topLevelGroup->setPosition( - QPoint(ByteAddressContainer::WIDTH + this->margins.left(), this->margins.top()) + QPoint(ByteAddressContainer::WIDTH + margins.left(), margins.top()) ); - this->flattenedItems = this->topLevelGroup->flattenedItems(); + + this->itemIndex = std::make_unique(this->topLevelGroup.get(), this); + this->initRenderer(); + emit this->ready(); } ); @@ -232,13 +239,14 @@ namespace Bloom::Widgets void ItemGraphicsScene::rebuildItemHierarchy() { this->topLevelGroup->rebuildItemHierarchy(); - this->flattenedItems = this->topLevelGroup->flattenedItems(); + this->itemIndex->refreshFlattenedItems(); this->adjustSize(); } void ItemGraphicsScene::adjustSize() { + const auto margins = this->margins(); const auto width = this->getSceneWidth(); - const auto availableWidth = width - ByteAddressContainer::WIDTH - this->margins.left() - this->margins.right(); + const auto availableWidth = width - ByteAddressContainer::WIDTH - margins.left() - margins.right(); auto hoverRectX = this->hoverRectX->rect(); hoverRectX.setWidth(width); @@ -249,38 +257,28 @@ namespace Bloom::Widgets this->hoverRectY->setRect(hoverRectY); this->topLevelGroup->adjustItemPositions(availableWidth); - this->refreshItemPositionIndices(); + this->itemIndex->refreshIndex(); - this->setSceneRect( - 0, - 0, + const auto sceneSize = QSize( width, std::max( static_cast(this->topLevelGroup->size().height()) - + this->margins.top() + this->margins.bottom(), + + margins.top() + margins.bottom(), this->parent->height() ) ); - - this->byteAddressContainer->adjustAddressLabels(this->firstByteItemByLine); - - const auto* view = this->views().first(); - const auto itemsRequired = static_cast( - (availableWidth / (ByteItem::WIDTH + (ByteItem::RIGHT_MARGIN / 2))) - * ( - (view->viewport()->height() + (4 * ItemGraphicsScene::GRID_SIZE)) - / (ByteItem::HEIGHT + (ByteItem::BOTTOM_MARGIN / 2)) - ) + this->setSceneRect( + 0, + 0, + sceneSize.width(), + sceneSize.height() ); - while (this->graphicsItems.size() < itemsRequired) { - auto* item = new GraphicsItem(&(this->state)); - item->setEnabled(this->enabled); - this->graphicsItems.push_back(item); - this->addItem(item); + if (this->renderer != nullptr) { + this->renderer->size = sceneSize; } - this->allocateGraphicsItems(); + this->byteAddressContainer->adjustAddressLabels(this->itemIndex->byteItemLines); this->update(); } @@ -291,8 +289,8 @@ namespace Bloom::Widgets this->enabled = enabled; - for (auto& graphicsItem : this->graphicsItems) { - graphicsItem->setEnabled(this->enabled); + if (this->renderer != nullptr) { + this->renderer->setEnabled(this->enabled); } this->byteAddressContainer->setEnabled(enabled); @@ -314,65 +312,6 @@ namespace Bloom::Widgets return QPointF(); } - void ItemGraphicsScene::allocateGraphicsItems() { - const auto verticalScrollBarValue = this->getScrollbarValue(); - - constexpr auto bufferPointSize = 2; - const auto gridPointIndex = static_castgridPoints)::size_type>(std::max( - static_cast( - std::floor( - static_cast(verticalScrollBarValue) / static_cast(ItemGraphicsScene::GRID_SIZE) - ) - ) - 1 - bufferPointSize, - 0 - )); - - // Sanity check - assert(this->gridPoints.size() > gridPointIndex); - - const auto& allocatableGraphicsItems = this->graphicsItems; - auto allocatableGraphicsItemsCount = allocatableGraphicsItems.size(); - - const auto allocateRangeStartItemIt = this->gridPoints[gridPointIndex]; - const auto allocateRangeEndItemIt = this->flattenedItems.end(); - - const auto& firstItem = *allocateRangeStartItemIt; - - /* - * Ensure that a graphics item for each parent, grandparent, etc. is allocated for the first item in the - * allocatable range. - */ - auto* parentItem = firstItem->parent; - - while ( - parentItem != nullptr - && parentItem != this->topLevelGroup.get() - && allocatableGraphicsItemsCount > 0 - ) { - allocatableGraphicsItems[allocatableGraphicsItemsCount - 1]->setHexViewerItem(parentItem); - --allocatableGraphicsItemsCount; - parentItem = parentItem->parent; - } - - for (auto itemIt = allocateRangeStartItemIt; itemIt != allocateRangeEndItemIt; ++itemIt) { - if (allocatableGraphicsItemsCount < 1) { - // No more graphics items available to allocate - break; - } - - allocatableGraphicsItems[allocatableGraphicsItemsCount - 1]->setHexViewerItem(*itemIt); - --allocatableGraphicsItemsCount; - } - - // If we still have some available graphics items, clear them - while (allocatableGraphicsItemsCount > 0) { - allocatableGraphicsItems[allocatableGraphicsItemsCount - 1]->setHexViewerItem(nullptr); - --allocatableGraphicsItemsCount; - } - - this->update(); - } - void ItemGraphicsScene::addExternalContextMenuAction(ContextMenuAction* action) { QObject::connect(action, &QAction::triggered, this, [this, action] () { emit action->invoked(this->selectedByteItemsByAddress); @@ -381,6 +320,24 @@ namespace Bloom::Widgets this->externalContextMenuActions.push_back(action); } + void ItemGraphicsScene::initRenderer() { + this->renderer = new HexViewerItemRenderer( + this->state, + *(this->itemIndex.get()), + this->views().first() + ); + this->renderer->setPos(0, 0); + this->addItem(this->renderer); + } + + QMargins ItemGraphicsScene::margins() { + return QMargins(10, 10, 10, 10); + } + + QPointF ItemGraphicsScene::addressContainerPosition() { + return QPointF(0, 0); + } + bool ItemGraphicsScene::event(QEvent* event) { if (event->type() == QEvent::Type::GraphicsSceneLeave && this->state.hoveredByteItem != nullptr) { this->onByteItemLeave(); @@ -407,20 +364,7 @@ namespace Bloom::Widgets this->update(); if (button == Qt::MouseButton::RightButton) { - ByteItem* clickedByteItem = nullptr; - for (const auto& item : this->items(mousePosition)) { - auto* clickedGraphicsItem = dynamic_cast(item); - - if (clickedGraphicsItem == nullptr) { - continue; - } - - clickedByteItem = dynamic_cast(clickedGraphicsItem->hexViewerItem); - - if (clickedByteItem != nullptr) { - break; - } - } + ByteItem* clickedByteItem = this->itemIndex->byteItemAt(mousePosition); if (clickedByteItem == nullptr || clickedByteItem->selected) { return; @@ -447,26 +391,17 @@ namespace Bloom::Widgets this->clearByteItemSelection(); } - for (const auto& item : this->items(mousePosition)) { - auto* clickedGraphicsItem = dynamic_cast(item); - - if (clickedGraphicsItem == nullptr) { - continue; - } - - auto* byteItem = dynamic_cast(clickedGraphicsItem->hexViewerItem); - - if (byteItem == nullptr) { - continue; - } - + auto* clickedByteItem = this->itemIndex->byteItemAt(mousePosition); + if (clickedByteItem != nullptr) { if ((modifiers & Qt::ShiftModifier) != 0) { for ( - auto i = byteItem->startAddress; + auto i = static_cast(clickedByteItem->startAddress); i >= this->state.memoryDescriptor.addressRange.startAddress; --i ) { - auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(i); + auto& byteItem = this->topLevelGroup->byteItemsByAddress.at( + static_cast(i) + ); if (byteItem.selected) { break; @@ -479,15 +414,13 @@ namespace Bloom::Widgets return; } - this->toggleByteItemSelection(*byteItem); + this->toggleByteItemSelection(*clickedByteItem); emit this->selectionChanged(this->selectedByteItemsByAddress); - break; } } void ItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { const auto mousePosition = mouseEvent->scenePos(); - auto hoveredItems = this->items(mousePosition); if (this->rubberBandRectItem != nullptr && this->rubberBandInitPoint.has_value()) { this->update(); @@ -504,52 +437,25 @@ namespace Bloom::Widgets this->clearByteItemSelection(); } else { - const auto oldItems = this->items(oldRect, Qt::IntersectsItemShape); - for (auto* item : oldItems) { - auto* graphicsItem = dynamic_cast(item); - - if (graphicsItem == nullptr) { - continue; - } - - auto* byteItem = dynamic_cast(graphicsItem->hexViewerItem); - - if (byteItem != nullptr && byteItem->selected) { + const auto oldItems = this->itemIndex->intersectingByteItems(oldRect); + for (auto* byteItem : oldItems) { + if (byteItem->selected) { this->deselectByteItem(*byteItem); } } } - const auto items = this->items(this->rubberBandRectItem->rect(), Qt::IntersectsItemShape); - for (auto* item : items) { - auto* graphicsItem = dynamic_cast(item); - - if (graphicsItem == nullptr) { - continue; - } - - auto* byteItem = dynamic_cast(graphicsItem->hexViewerItem); - - if (byteItem != nullptr && !byteItem->selected) { - this->selectByteItem(*byteItem); - } + const auto items = this->itemIndex->intersectingByteItems(this->rubberBandRectItem->rect()); + for (auto& byteItem : items) { + this->selectByteItem(*byteItem); } emit this->selectionChanged(this->selectedByteItemsByAddress); } - for (const auto& item : hoveredItems) { - auto* hoveredGraphicsItem = dynamic_cast(item); - - if (hoveredGraphicsItem == nullptr) { - continue; - } - - auto* hoveredByteItem = dynamic_cast(hoveredGraphicsItem->hexViewerItem); - - if (hoveredByteItem != nullptr) { - this->onByteItemEnter(*hoveredByteItem); - return; - } + auto* hoveredByteItem = this->itemIndex->byteItemAt(mousePosition); + if (hoveredByteItem != nullptr) { + this->onByteItemEnter(*hoveredByteItem); + return; } if (this->state.hoveredByteItem != nullptr) { @@ -638,47 +544,6 @@ namespace Bloom::Widgets menu->exec(event->screenPos()); } - void ItemGraphicsScene::refreshItemPositionIndices() { - const auto pointsRequired = static_cast( - this->sceneRect().height() / ItemGraphicsScene::GRID_SIZE - ); - - this->gridPoints.clear(); - this->gridPoints.reserve(pointsRequired); - - this->firstByteItemByLine.clear(); - - auto currentPoint = 0; - auto currentLineYPosition = 0; - for (auto itemIt = this->flattenedItems.begin(); itemIt != this->flattenedItems.end(); ++itemIt) { - auto& item = *itemIt; - - if (item->allocatedGraphicsItem != nullptr) { - item->allocatedGraphicsItem->setPos(item->position()); - } - - const auto byteItem = dynamic_cast(item); - - if (byteItem == nullptr) { - continue; - } - - const auto itemYStartPosition = byteItem->position().y(); - const auto itemYEndPosition = itemYStartPosition + byteItem->size().height(); - - if (itemYStartPosition > currentLineYPosition) { - this->firstByteItemByLine.push_back(byteItem); - currentLineYPosition = itemYStartPosition; - } - - if (itemYEndPosition >= currentPoint) { - // This byte item is the first to exceed or intersect with the currentPoint - this->gridPoints.push_back(itemIt); - currentPoint += ItemGraphicsScene::GRID_SIZE; - } - } - } - int ItemGraphicsScene::getScrollbarValue() { return this->views().first()->verticalScrollBar()->value(); } @@ -713,19 +578,11 @@ namespace Bloom::Widgets this->hoverRectY->update(); } - if (byteItem.allocatedGraphicsItem != nullptr) { - byteItem.allocatedGraphicsItem->update(); - } - emit this->hoveredAddress(byteItem.startAddress); } void ItemGraphicsScene::onByteItemLeave() { if (this->state.hoveredByteItem != nullptr) { - if (this->state.hoveredByteItem->allocatedGraphicsItem != nullptr) { - this->state.hoveredByteItem->allocatedGraphicsItem->update(); - } - this->state.hoveredByteItem = nullptr; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp index 0eaf5f36..b815731f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp @@ -22,7 +22,8 @@ #include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp" -#include "GraphicsItem.hpp" +#include "HexViewerItemIndex.hpp" +#include "HexViewerItemRenderer.hpp" #include "TopLevelGroupItem.hpp" #include "GroupItem.hpp" #include "ByteItem.hpp" @@ -60,7 +61,6 @@ namespace Bloom::Widgets void setEnabled(bool enabled); void refreshValues(); QPointF getByteItemPositionByAddress(Targets::TargetMemoryAddress address); - void allocateGraphicsItems(); void addExternalContextMenuAction(ContextMenuAction* action); signals: @@ -69,8 +69,6 @@ namespace Bloom::Widgets void selectionChanged(const std::unordered_map& selectedByteItemsByAddress); protected: - static constexpr auto GRID_SIZE = 100; - bool enabled = true; HexViewerSharedState state; @@ -79,14 +77,9 @@ namespace Bloom::Widgets const std::vector& excludedMemoryRegions; std::unique_ptr topLevelGroup = nullptr; + std::unique_ptr itemIndex = nullptr; - std::vector flattenedItems; - std::vector gridPoints; - std::vector firstByteItemByLine; - - std::vector graphicsItems; - - const QMargins margins = QMargins(10, 10, 10, 10); + HexViewerItemRenderer* renderer = nullptr; Targets::TargetState targetState = Targets::TargetState::UNKNOWN; @@ -137,6 +130,9 @@ namespace Bloom::Widgets return std::max(this->parent->viewport()->width(), 200) - 2; } + virtual void initRenderer(); + virtual QMargins margins(); + virtual QPointF addressContainerPosition(); bool event(QEvent* event) override; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) override; void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override; @@ -144,7 +140,6 @@ namespace Bloom::Widgets void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) override; void keyPressEvent(QKeyEvent* keyEvent) override; void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - void refreshItemPositionIndices(); int getScrollbarValue(); void onTargetStateChanged(Targets::TargetState newState); void onByteItemEnter(ByteItem& byteItem); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.cpp index 6ab0310f..547bb4bf 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.cpp @@ -90,12 +90,4 @@ namespace Bloom::Widgets this->scene->adjustSize(); } - - void ItemGraphicsView::scrollContentsBy(int dx, int dy) { - if (this->scene != nullptr) { - this->scene->allocateGraphicsItems(); - } - - return QGraphicsView::scrollContentsBy(dx, dy); - } } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp index d0a4ca08..89b9cf33 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp @@ -48,6 +48,5 @@ namespace Bloom::Widgets bool event(QEvent* event) override; void resizeEvent(QResizeEvent* event) override; - void scrollContentsBy(int dx, int dy) override; }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp index 1e7ab32f..83949e7d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.cpp @@ -1,7 +1,6 @@ #include "StackMemoryGroupItem.hpp" #include -#include namespace Bloom::Widgets { @@ -89,152 +88,6 @@ namespace Bloom::Widgets } } - void StackMemoryGroupItem::paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const { - painter->setRenderHints(QPainter::RenderHint::Antialiasing, false); - - if (!graphicsItem->isEnabled()) { - painter->setOpacity(0.5); - } - - const auto headingText = QString("Stack Memory"); - - const auto stackSize = hexViewerState->memoryDescriptor.addressRange.endAddress - this->startAddress + 1; - const auto& memoryCapacity = hexViewerState->memoryDescriptor.size(); - - const auto stackSizeHeadingText = QString("Stack size:"); - const auto stackSizeValueText = QString::number(stackSize) + " byte(s) (" - + QString::number(static_cast(stackSize) / static_cast(memoryCapacity / 100), 'f' , 1) - + "% of memory capacity)"; - - const auto stackPointerHeadingText = QString("Stack pointer:"); - const auto stackPointerValueText = "0x" + QString::number( - this->stackPointer, - 16 - ).rightJustified(8, '0').toUpper(); - - static constexpr auto lineColor = QColor(0x4F, 0x4F, 0x4F); - static constexpr auto headingLabelFontColor = QColor(0x6F, 0x6F, 0x6F); - static constexpr auto valueLabelFontColor = QColor(0x94, 0x6F, 0x30, 230); - - static auto headingLabelFont = QFont("'Ubuntu', sans-serif"); - headingLabelFont.setPixelSize(13); - - static auto valueFont = QFont("'Ubuntu', sans-serif"); - valueFont.setPixelSize(13); - valueFont.setItalic(true); - - painter->setFont(headingLabelFont); - - const auto groupWidth = this->groupSize.width(); - const auto fontMetrics = painter->fontMetrics(); - - auto headingLabelSize = fontMetrics.size(Qt::TextSingleLine, headingText); - - auto stackSizeHeadingLabelSize = fontMetrics.size(Qt::TextSingleLine, stackSizeHeadingText); - auto stackSizeLabelSize = fontMetrics.size(Qt::TextSingleLine, stackSizeValueText); - - auto stackPointerHeadingLabelSize = fontMetrics.size(Qt::TextSingleLine, stackPointerHeadingText); - auto stackPointerLabelSize = fontMetrics.size(Qt::TextSingleLine, stackPointerValueText); - - static constexpr auto labelLineHeight = 4; - static constexpr auto labelBottomMargin = 10; - static constexpr auto labelRightMargin = 3; - - const auto heightOffset = headingLabelSize.height() + stackSizeHeadingLabelSize.height() - + stackPointerHeadingLabelSize.height() + (labelBottomMargin * 3) + 15; - - const auto verticalLineYStart = static_cast(heightOffset - 5); - const auto verticalLineYEnd = static_cast(heightOffset); - - const auto lineStartX = ByteItem::WIDTH / 2; - const auto lineEndX = groupWidth - + (ByteItem::WIDTH / 2); - - const auto labelRect = QRect( - (groupWidth - headingLabelSize.width()) / 2, - verticalLineYStart - stackPointerHeadingLabelSize.height() - stackSizeHeadingLabelSize.height() - - headingLabelSize.height() - labelLineHeight - (labelBottomMargin * 2) - 3, - headingLabelSize.width(), - headingLabelSize.height() - ); - - const auto stackPointerHeadingLabelRect = QRect( - labelRect.left() + (labelRect.width() / 2) - ( - (stackPointerHeadingLabelSize.width() + stackPointerLabelSize.width()) / 2 - ), - labelRect.bottom() + labelBottomMargin, - stackPointerHeadingLabelSize.width(), - stackPointerHeadingLabelSize.height() - ); - - const auto stackPointerValueLabelRect = QRect( - stackPointerHeadingLabelRect.right() + labelRightMargin, - labelRect.bottom() + labelBottomMargin, - stackPointerLabelSize.width(), - stackPointerLabelSize.height() - ); - - const auto stackSizeHeadingLabelRect = QRect( - labelRect.left() + (labelRect.width() / 2) - ( - (stackSizeHeadingLabelSize.width() + stackSizeLabelSize.width()) / 2 - ), - stackPointerHeadingLabelRect.bottom() + labelBottomMargin, - stackSizeHeadingLabelSize.width(), - stackSizeHeadingLabelSize.height() - ); - - const auto stackSizeValueLabelRect = QRect( - stackSizeHeadingLabelRect.right() + labelRightMargin, - stackPointerHeadingLabelRect.bottom() + labelBottomMargin, - stackSizeLabelSize.width(), - stackSizeLabelSize.height() - ); - - painter->setPen(lineColor); - - painter->drawLine(QLine( - lineStartX, - verticalLineYStart, - lineStartX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineEndX, - verticalLineYStart, - lineEndX, - verticalLineYEnd - )); - - painter->drawLine(QLine( - lineStartX, - verticalLineYStart, - lineEndX, - verticalLineYStart - )); - - painter->drawLine(QLine( - groupWidth / 2, - verticalLineYStart - labelLineHeight, - groupWidth / 2, - verticalLineYStart - )); - - painter->setPen(headingLabelFontColor); - - painter->drawText(labelRect, Qt::AlignCenter, headingText); - painter->drawText(stackSizeHeadingLabelRect, Qt::AlignCenter, stackSizeHeadingText); - painter->drawText(stackPointerHeadingLabelRect, Qt::AlignCenter, stackPointerHeadingText); - - painter->setFont(valueFont); - painter->setPen(valueLabelFontColor); - painter->drawText(stackSizeValueLabelRect, Qt::AlignCenter, stackSizeValueText); - painter->drawText(stackPointerValueLabelRect, Qt::AlignCenter, stackPointerValueText); - } - QMargins StackMemoryGroupItem::groupMargins( const HexViewerSharedState* hexViewerState, const int maximumWidth diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.hpp index f1c44de0..56929751 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/StackMemoryGroupItem.hpp @@ -14,6 +14,8 @@ namespace Bloom::Widgets class StackMemoryGroupItem: public GroupItem { public: + Targets::TargetStackPointer stackPointer; + StackMemoryGroupItem( Targets::TargetStackPointer stackPointer, const HexViewerSharedState& hexViewerState, @@ -28,12 +30,6 @@ namespace Bloom::Widgets void refreshValues(); - void paint( - QPainter* painter, - const HexViewerSharedState* hexViewerState, - const QGraphicsItem* graphicsItem - ) const override; - protected: QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override; @@ -42,9 +38,7 @@ namespace Bloom::Widgets } private: - Targets::TargetStackPointer stackPointer; const HexViewerSharedState& hexViewerState; - std::list focusedRegionGroupItems; }; } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp index 85c55dc3..dec9aad4 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp @@ -44,23 +44,8 @@ namespace Bloom::Widgets ); } - ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportVerticalCenter() { - const auto scrollBarValue = this->getScrollbarValue(); - - const auto midPosition = static_cast( - scrollBarValue - ); - - const auto gridPointIndex = static_castgridPoints)::size_type>( - std::min( - static_cast( - std::floor(static_cast(midPosition) / static_cast(ItemGraphicsScene::GRID_SIZE)) + 1 - ), - static_cast(this->gridPoints.size() - 1) - ) - ); - - return static_cast(*(this->gridPoints[gridPointIndex])); + ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportTop() { + return this->itemIndex->closestByteItem(this->getScrollbarValue()); } void DifferentialItemGraphicsScene::onOtherHoveredAddress( @@ -83,8 +68,7 @@ namespace Bloom::Widgets const auto scrollbarValue = this->getScrollbarValue(); if ( - byteItem.allocatedGraphicsItem == nullptr - || itemPosition < scrollbarValue + itemPosition < scrollbarValue || itemPosition > (scrollbarValue + this->views().first()->viewport()->height()) ) { // The item isn't visible diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp index b172de41..9267f4c0 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp @@ -25,7 +25,7 @@ namespace Bloom::Widgets ); void setOther(DifferentialItemGraphicsScene* other); - ByteItem* byteItemAtViewportVerticalCenter(); + ByteItem* byteItemAtViewportTop(); protected: DifferentialHexViewerSharedState& diffHexViewerState; diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp index a3fc6dcc..5fedbebc 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp @@ -74,7 +74,7 @@ namespace Bloom::Widgets return; } - auto* byteItem = this->differentialScene->byteItemAtViewportVerticalCenter(); + auto* byteItem = this->differentialScene->byteItemAtViewportTop(); this->state.syncingScroll = true; this->other->alignScroll(byteItem->startAddress, byteItem->position().y() - this->verticalScrollBar()->value());