Further refactoring of hex viewer item painting for additional performance gains
This commit is contained in:
@@ -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/StackMemoryGroupItem.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.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/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/HexViewerWidget/ContextMenuAction.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.cpp
|
||||||
|
|||||||
@@ -1,350 +1,8 @@
|
|||||||
#include "ByteItem.hpp"
|
#include "ByteItem.hpp"
|
||||||
|
|
||||||
#include <QFont>
|
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
namespace Bloom::Widgets
|
||||||
{
|
{
|
||||||
ByteItem::ByteItem(Targets::TargetMemoryAddress address)
|
ByteItem::ByteItem(Targets::TargetMemoryAddress address)
|
||||||
: HexViewerItem(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,50 +14,23 @@ namespace Bloom::Widgets
|
|||||||
class ByteItem: public HexViewerItem
|
class ByteItem: public HexViewerItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr int WIDTH = 27;
|
static constexpr int WIDTH = 28;
|
||||||
static constexpr int HEIGHT = 21;
|
static constexpr int HEIGHT = 22;
|
||||||
|
|
||||||
static constexpr int RIGHT_MARGIN = 5;
|
static constexpr int RIGHT_MARGIN = 6;
|
||||||
static constexpr int BOTTOM_MARGIN = 5;
|
static constexpr int BOTTOM_MARGIN = 6;
|
||||||
|
|
||||||
bool selected = false;
|
bool selected:1 = false;
|
||||||
bool excluded = false;
|
bool excluded:1 = false;
|
||||||
bool grouped = false;
|
bool grouped:1 = false;
|
||||||
bool stackMemory = false;
|
bool stackMemory:1 = false;
|
||||||
bool changed = false;
|
bool changed:1 = false;
|
||||||
|
|
||||||
explicit ByteItem(Targets::TargetMemoryAddress address);
|
explicit ByteItem(Targets::TargetMemoryAddress address);
|
||||||
|
|
||||||
QSize size() const override {
|
QSize size() const override {
|
||||||
return QSize(ByteItem::WIDTH, ByteItem::HEIGHT);
|
return QSize(ByteItem::WIDTH, ByteItem::HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void paint(
|
|
||||||
QPainter* painter,
|
|
||||||
const HexViewerSharedState* hexViewerState,
|
|
||||||
const QGraphicsItem* graphicsItem
|
|
||||||
) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static inline std::atomic<bool> pixmapCachesGenerated = false;
|
|
||||||
static inline std::mutex pixmapCacheMutex;
|
|
||||||
|
|
||||||
static inline std::vector<QPixmap> standardPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> selectedPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> groupedPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> stackMemoryPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> changedMemoryPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> standardAsciiPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> selectedAsciiPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> groupedAsciiPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> stackMemoryAsciiPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> changedMemoryAsciiPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> hoveredPrimaryPixmapsByValue = {};
|
|
||||||
static inline std::vector<QPixmap> hoveredPrimaryAsciiPixmapsByValue = {};
|
|
||||||
static inline std::optional<QPixmap> missingDataPixmap = {};
|
|
||||||
static inline std::optional<QPixmap> selectedMissingDataPixmap = {};
|
|
||||||
|
|
||||||
static void generatePixmapCaches();
|
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "FocusedRegionGroupItem.hpp"
|
#include "FocusedRegionGroupItem.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
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(
|
QMargins FocusedRegionGroupItem::groupMargins(
|
||||||
const HexViewerSharedState* hexViewerState,
|
const HexViewerSharedState* hexViewerState,
|
||||||
const int maximumWidth
|
const int maximumWidth
|
||||||
@@ -198,185 +174,4 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
return QMargins(0, 0, 0, 0);
|
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<int>(heightOffset);
|
|
||||||
const auto verticalLineYEnd = static_cast<int>(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<int>(heightOffset - 5);
|
|
||||||
const auto verticalLineYEnd = static_cast<int>(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ namespace Bloom::Widgets
|
|||||||
class FocusedRegionGroupItem: public GroupItem
|
class FocusedRegionGroupItem: public GroupItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr int ANNOTATION_HEIGHT = 35;
|
||||||
|
|
||||||
|
const FocusedMemoryRegion& focusedMemoryRegion;
|
||||||
|
std::optional<QString> valueLabel;
|
||||||
|
|
||||||
FocusedRegionGroupItem(
|
FocusedRegionGroupItem(
|
||||||
const FocusedMemoryRegion& focusedRegion,
|
const FocusedMemoryRegion& focusedRegion,
|
||||||
std::unordered_map<Targets::TargetMemoryAddress, ByteItem>& byteItemsByAddress,
|
std::unordered_map<Targets::TargetMemoryAddress, ByteItem>& byteItemsByAddress,
|
||||||
@@ -24,31 +29,7 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
void refreshValue(const HexViewerSharedState& hexViewerState);
|
void refreshValue(const HexViewerSharedState& hexViewerState);
|
||||||
|
|
||||||
void paint(
|
|
||||||
QPainter* painter,
|
|
||||||
const HexViewerSharedState* hexViewerState,
|
|
||||||
const QGraphicsItem* graphicsItem
|
|
||||||
) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override;
|
QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override;
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int ANNOTATION_HEIGHT = 35;
|
|
||||||
|
|
||||||
const FocusedMemoryRegion& focusedMemoryRegion;
|
|
||||||
std::optional<QString> 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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QGraphicsItem>
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,8 @@ namespace Bloom::Widgets
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::vector<HexViewerItem*> items;
|
std::vector<HexViewerItem*> items;
|
||||||
|
QSize groupSize = {};
|
||||||
|
bool multiLine = false;
|
||||||
|
|
||||||
~GroupItem();
|
~GroupItem();
|
||||||
|
|
||||||
@@ -28,18 +30,7 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
[[nodiscard]] std::vector<HexViewerItem*> flattenedItems() const;
|
[[nodiscard]] std::vector<HexViewerItem*> flattenedItems() const;
|
||||||
|
|
||||||
void paint(
|
|
||||||
QPainter* painter,
|
|
||||||
const HexViewerSharedState* hexViewerState,
|
|
||||||
const QGraphicsItem* graphicsItem
|
|
||||||
) const override {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSize groupSize = {};
|
|
||||||
bool multiLine = false;
|
|
||||||
|
|
||||||
GroupItem(
|
GroupItem(
|
||||||
Targets::TargetMemoryAddress startAddress,
|
Targets::TargetMemoryAddress startAddress,
|
||||||
HexViewerItem* parent = nullptr
|
HexViewerItem* parent = nullptr
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "HexViewerItem.hpp"
|
#include "HexViewerItem.hpp"
|
||||||
|
|
||||||
#include "GraphicsItem.hpp"
|
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
namespace Bloom::Widgets
|
||||||
{
|
{
|
||||||
HexViewerItem::HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent)
|
HexViewerItem::HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent)
|
||||||
@@ -9,12 +7,6 @@ namespace Bloom::Widgets
|
|||||||
, parent(parent)
|
, parent(parent)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
HexViewerItem::~HexViewerItem() {
|
|
||||||
if (this->allocatedGraphicsItem != nullptr) {
|
|
||||||
this->allocatedGraphicsItem->setHexViewerItem(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint HexViewerItem::position() const {
|
QPoint HexViewerItem::position() const {
|
||||||
if (this->parent != nullptr) {
|
if (this->parent != nullptr) {
|
||||||
return this->parent->position() + this->relativePosition;
|
return this->parent->position() + this->relativePosition;
|
||||||
|
|||||||
@@ -18,26 +18,15 @@ namespace Bloom::Widgets
|
|||||||
static constexpr int RIGHT_MARGIN = 5;
|
static constexpr int RIGHT_MARGIN = 5;
|
||||||
static constexpr int BOTTOM_MARGIN = 5;
|
static constexpr int BOTTOM_MARGIN = 5;
|
||||||
|
|
||||||
|
HexViewerItem* parent = nullptr;
|
||||||
|
|
||||||
const Targets::TargetMemoryAddress startAddress = 0;
|
const Targets::TargetMemoryAddress startAddress = 0;
|
||||||
|
|
||||||
QPoint relativePosition = {};
|
QPoint relativePosition = {};
|
||||||
|
|
||||||
HexViewerItem* parent = nullptr;
|
|
||||||
GraphicsItem* allocatedGraphicsItem = nullptr;
|
|
||||||
|
|
||||||
HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent = nullptr);
|
HexViewerItem(Targets::TargetMemoryAddress startAddress, HexViewerItem* parent = nullptr);
|
||||||
|
|
||||||
virtual ~HexViewerItem();
|
|
||||||
|
|
||||||
QPoint position() const;
|
QPoint position() const;
|
||||||
|
|
||||||
virtual QSize size() const = 0;
|
virtual QSize size() const = 0;
|
||||||
|
|
||||||
virtual void paint(
|
|
||||||
QPainter* painter,
|
|
||||||
const HexViewerSharedState* hexViewerState,
|
|
||||||
const QGraphicsItem* graphicsItem
|
|
||||||
) const = 0;
|
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#include "HexViewerItemIndex.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#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_cast<decltype(this->byteItemGrid)::size_type>(
|
||||||
|
std::floor(
|
||||||
|
static_cast<float>(yStart) / static_cast<float>(HexViewerItemIndex::GRID_SIZE)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (startGridPointIndex >= gridPointCount) {
|
||||||
|
return HexViewerItemIndex::ItemRangeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto endGridPointIndex = static_cast<decltype(this->byteItemGrid)::size_type>(std::min(
|
||||||
|
static_cast<decltype(this->byteItemGrid)::size_type>(
|
||||||
|
std::ceil(
|
||||||
|
static_cast<float>(yEnd) / static_cast<float>(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_cast<decltype(this->byteItemGrid)::size_type>(
|
||||||
|
std::floor(
|
||||||
|
static_cast<float>(std::max(position.y(), static_cast<qreal>(0)))
|
||||||
|
/ static_cast<float>(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<ByteItem*>(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_cast<decltype(this->byteItemGrid)::size_type>(
|
||||||
|
std::round(
|
||||||
|
static_cast<float>(yPosition) / static_cast<float>(HexViewerItemIndex::GRID_SIZE)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
assert(gridPointCount > gridPointIndex);
|
||||||
|
|
||||||
|
return static_cast<ByteItem*>(*(this->byteItemGrid[gridPointIndex]));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ByteItem*> HexViewerItemIndex::intersectingByteItems(const QRectF& rect) const {
|
||||||
|
auto output = std::vector<ByteItem*>();
|
||||||
|
|
||||||
|
const auto yStart = static_cast<int>(std::max(rect.y(), static_cast<qreal>(0)));
|
||||||
|
const auto yEnd = static_cast<int>(std::max(rect.y() + rect.height(), static_cast<qreal>(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<ByteItem*>(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<std::uint32_t>(
|
||||||
|
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<const ByteItem*>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <ranges>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#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<HexViewerItem*>;
|
||||||
|
using FlattenedItemItType = FlattenedItemType::const_iterator;
|
||||||
|
using ItemRangeType = std::ranges::subrange<FlattenedItemItType>;
|
||||||
|
|
||||||
|
std::vector<const ByteItem*> byteItemLines;
|
||||||
|
std::unordered_map<Targets::TargetMemoryAddress, int> 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<ByteItem*> 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<FlattenedItemItType> byteItemGrid;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,723 @@
|
|||||||
|
#include "HexViewerItemRenderer.hpp"
|
||||||
|
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
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<const ByteItem*>(item);
|
||||||
|
|
||||||
|
if (byteItem != nullptr) {
|
||||||
|
return this->paintByteItem(byteItem, painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* focusedRegionItem = dynamic_cast<const FocusedRegionGroupItem*>(item);
|
||||||
|
|
||||||
|
if (focusedRegionItem != nullptr) {
|
||||||
|
return this->paintFocusedRegionGroupItem(focusedRegionItem, painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* stackMemoryItem = dynamic_cast<const StackMemoryGroupItem*>(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<int>(heightOffset);
|
||||||
|
const auto verticalLineYEnd = position.y() + static_cast<int>(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<int>(heightOffset - 5);
|
||||||
|
const auto verticalLineYEnd = position.y() + static_cast<int>(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<float>(stackSize) / static_cast<float>(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<int>(position.y() + heightOffset - 5);
|
||||||
|
const auto verticalLineYEnd = static_cast<int>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QGraphicsItem>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#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<bool> pixmapCachesGenerated = false;
|
||||||
|
static inline std::mutex pixmapCacheMutex;
|
||||||
|
|
||||||
|
static inline std::vector<QPixmap> standardPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> selectedPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> groupedPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> stackMemoryPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> changedMemoryPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> standardAsciiPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> selectedAsciiPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> groupedAsciiPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> stackMemoryAsciiPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> changedMemoryAsciiPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> hoveredPrimaryPixmapsByValue = {};
|
||||||
|
static inline std::vector<QPixmap> hoveredPrimaryAsciiPixmapsByValue = {};
|
||||||
|
static inline std::optional<QPixmap> missingDataPixmap = {};
|
||||||
|
static inline std::optional<QPixmap> 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();
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -182,6 +182,8 @@ namespace Bloom::Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::init() {
|
void ItemGraphicsScene::init() {
|
||||||
|
this->byteAddressContainer->setPos(this->addressContainerPosition());
|
||||||
|
|
||||||
const auto constructHexViewerTopLevelGroupItem = QSharedPointer<ConstructHexViewerTopLevelGroupItem>(
|
const auto constructHexViewerTopLevelGroupItem = QSharedPointer<ConstructHexViewerTopLevelGroupItem>(
|
||||||
new ConstructHexViewerTopLevelGroupItem(
|
new ConstructHexViewerTopLevelGroupItem(
|
||||||
this->focusedMemoryRegions,
|
this->focusedMemoryRegions,
|
||||||
@@ -196,11 +198,16 @@ namespace Bloom::Widgets
|
|||||||
&ConstructHexViewerTopLevelGroupItem::topLevelGroupItem,
|
&ConstructHexViewerTopLevelGroupItem::topLevelGroupItem,
|
||||||
this,
|
this,
|
||||||
[this] (TopLevelGroupItem* item) {
|
[this] (TopLevelGroupItem* item) {
|
||||||
|
const auto margins = this->margins();
|
||||||
|
|
||||||
this->topLevelGroup.reset(item);
|
this->topLevelGroup.reset(item);
|
||||||
this->topLevelGroup->setPosition(
|
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<HexViewerItemIndex>(this->topLevelGroup.get(), this);
|
||||||
|
this->initRenderer();
|
||||||
|
|
||||||
emit this->ready();
|
emit this->ready();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -232,13 +239,14 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
void ItemGraphicsScene::rebuildItemHierarchy() {
|
void ItemGraphicsScene::rebuildItemHierarchy() {
|
||||||
this->topLevelGroup->rebuildItemHierarchy();
|
this->topLevelGroup->rebuildItemHierarchy();
|
||||||
this->flattenedItems = this->topLevelGroup->flattenedItems();
|
this->itemIndex->refreshFlattenedItems();
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::adjustSize() {
|
void ItemGraphicsScene::adjustSize() {
|
||||||
|
const auto margins = this->margins();
|
||||||
const auto width = this->getSceneWidth();
|
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();
|
auto hoverRectX = this->hoverRectX->rect();
|
||||||
hoverRectX.setWidth(width);
|
hoverRectX.setWidth(width);
|
||||||
@@ -249,38 +257,28 @@ namespace Bloom::Widgets
|
|||||||
this->hoverRectY->setRect(hoverRectY);
|
this->hoverRectY->setRect(hoverRectY);
|
||||||
|
|
||||||
this->topLevelGroup->adjustItemPositions(availableWidth);
|
this->topLevelGroup->adjustItemPositions(availableWidth);
|
||||||
this->refreshItemPositionIndices();
|
this->itemIndex->refreshIndex();
|
||||||
|
|
||||||
this->setSceneRect(
|
const auto sceneSize = QSize(
|
||||||
0,
|
|
||||||
0,
|
|
||||||
width,
|
width,
|
||||||
std::max(
|
std::max(
|
||||||
static_cast<int>(this->topLevelGroup->size().height())
|
static_cast<int>(this->topLevelGroup->size().height())
|
||||||
+ this->margins.top() + this->margins.bottom(),
|
+ margins.top() + margins.bottom(),
|
||||||
this->parent->height()
|
this->parent->height()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
this->setSceneRect(
|
||||||
this->byteAddressContainer->adjustAddressLabels(this->firstByteItemByLine);
|
0,
|
||||||
|
0,
|
||||||
const auto* view = this->views().first();
|
sceneSize.width(),
|
||||||
const auto itemsRequired = static_cast<std::uint32_t>(
|
sceneSize.height()
|
||||||
(availableWidth / (ByteItem::WIDTH + (ByteItem::RIGHT_MARGIN / 2)))
|
|
||||||
* (
|
|
||||||
(view->viewport()->height() + (4 * ItemGraphicsScene::GRID_SIZE))
|
|
||||||
/ (ByteItem::HEIGHT + (ByteItem::BOTTOM_MARGIN / 2))
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
while (this->graphicsItems.size() < itemsRequired) {
|
if (this->renderer != nullptr) {
|
||||||
auto* item = new GraphicsItem(&(this->state));
|
this->renderer->size = sceneSize;
|
||||||
item->setEnabled(this->enabled);
|
|
||||||
this->graphicsItems.push_back(item);
|
|
||||||
this->addItem(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->allocateGraphicsItems();
|
this->byteAddressContainer->adjustAddressLabels(this->itemIndex->byteItemLines);
|
||||||
this->update();
|
this->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,8 +289,8 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
this->enabled = enabled;
|
this->enabled = enabled;
|
||||||
|
|
||||||
for (auto& graphicsItem : this->graphicsItems) {
|
if (this->renderer != nullptr) {
|
||||||
graphicsItem->setEnabled(this->enabled);
|
this->renderer->setEnabled(this->enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->byteAddressContainer->setEnabled(enabled);
|
this->byteAddressContainer->setEnabled(enabled);
|
||||||
@@ -314,65 +312,6 @@ namespace Bloom::Widgets
|
|||||||
return QPointF();
|
return QPointF();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::allocateGraphicsItems() {
|
|
||||||
const auto verticalScrollBarValue = this->getScrollbarValue();
|
|
||||||
|
|
||||||
constexpr auto bufferPointSize = 2;
|
|
||||||
const auto gridPointIndex = static_cast<decltype(this->gridPoints)::size_type>(std::max(
|
|
||||||
static_cast<int>(
|
|
||||||
std::floor(
|
|
||||||
static_cast<float>(verticalScrollBarValue) / static_cast<float>(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) {
|
void ItemGraphicsScene::addExternalContextMenuAction(ContextMenuAction* action) {
|
||||||
QObject::connect(action, &QAction::triggered, this, [this, action] () {
|
QObject::connect(action, &QAction::triggered, this, [this, action] () {
|
||||||
emit action->invoked(this->selectedByteItemsByAddress);
|
emit action->invoked(this->selectedByteItemsByAddress);
|
||||||
@@ -381,6 +320,24 @@ namespace Bloom::Widgets
|
|||||||
this->externalContextMenuActions.push_back(action);
|
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) {
|
bool ItemGraphicsScene::event(QEvent* event) {
|
||||||
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->state.hoveredByteItem != nullptr) {
|
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->state.hoveredByteItem != nullptr) {
|
||||||
this->onByteItemLeave();
|
this->onByteItemLeave();
|
||||||
@@ -407,20 +364,7 @@ namespace Bloom::Widgets
|
|||||||
this->update();
|
this->update();
|
||||||
|
|
||||||
if (button == Qt::MouseButton::RightButton) {
|
if (button == Qt::MouseButton::RightButton) {
|
||||||
ByteItem* clickedByteItem = nullptr;
|
ByteItem* clickedByteItem = this->itemIndex->byteItemAt(mousePosition);
|
||||||
for (const auto& item : this->items(mousePosition)) {
|
|
||||||
auto* clickedGraphicsItem = dynamic_cast<GraphicsItem*>(item);
|
|
||||||
|
|
||||||
if (clickedGraphicsItem == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
clickedByteItem = dynamic_cast<ByteItem*>(clickedGraphicsItem->hexViewerItem);
|
|
||||||
|
|
||||||
if (clickedByteItem != nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clickedByteItem == nullptr || clickedByteItem->selected) {
|
if (clickedByteItem == nullptr || clickedByteItem->selected) {
|
||||||
return;
|
return;
|
||||||
@@ -447,26 +391,17 @@ namespace Bloom::Widgets
|
|||||||
this->clearByteItemSelection();
|
this->clearByteItemSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& item : this->items(mousePosition)) {
|
auto* clickedByteItem = this->itemIndex->byteItemAt(mousePosition);
|
||||||
auto* clickedGraphicsItem = dynamic_cast<GraphicsItem*>(item);
|
if (clickedByteItem != nullptr) {
|
||||||
|
|
||||||
if (clickedGraphicsItem == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* byteItem = dynamic_cast<ByteItem*>(clickedGraphicsItem->hexViewerItem);
|
|
||||||
|
|
||||||
if (byteItem == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((modifiers & Qt::ShiftModifier) != 0) {
|
if ((modifiers & Qt::ShiftModifier) != 0) {
|
||||||
for (
|
for (
|
||||||
auto i = byteItem->startAddress;
|
auto i = static_cast<std::int64_t>(clickedByteItem->startAddress);
|
||||||
i >= this->state.memoryDescriptor.addressRange.startAddress;
|
i >= this->state.memoryDescriptor.addressRange.startAddress;
|
||||||
--i
|
--i
|
||||||
) {
|
) {
|
||||||
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(i);
|
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(
|
||||||
|
static_cast<Targets::TargetMemoryAddress>(i)
|
||||||
|
);
|
||||||
|
|
||||||
if (byteItem.selected) {
|
if (byteItem.selected) {
|
||||||
break;
|
break;
|
||||||
@@ -479,15 +414,13 @@ namespace Bloom::Widgets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->toggleByteItemSelection(*byteItem);
|
this->toggleByteItemSelection(*clickedByteItem);
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
|
void ItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
|
||||||
const auto mousePosition = mouseEvent->scenePos();
|
const auto mousePosition = mouseEvent->scenePos();
|
||||||
auto hoveredItems = this->items(mousePosition);
|
|
||||||
|
|
||||||
if (this->rubberBandRectItem != nullptr && this->rubberBandInitPoint.has_value()) {
|
if (this->rubberBandRectItem != nullptr && this->rubberBandInitPoint.has_value()) {
|
||||||
this->update();
|
this->update();
|
||||||
@@ -504,52 +437,25 @@ namespace Bloom::Widgets
|
|||||||
this->clearByteItemSelection();
|
this->clearByteItemSelection();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const auto oldItems = this->items(oldRect, Qt::IntersectsItemShape);
|
const auto oldItems = this->itemIndex->intersectingByteItems(oldRect);
|
||||||
for (auto* item : oldItems) {
|
for (auto* byteItem : oldItems) {
|
||||||
auto* graphicsItem = dynamic_cast<GraphicsItem*>(item);
|
if (byteItem->selected) {
|
||||||
|
|
||||||
if (graphicsItem == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* byteItem = dynamic_cast<ByteItem*>(graphicsItem->hexViewerItem);
|
|
||||||
|
|
||||||
if (byteItem != nullptr && byteItem->selected) {
|
|
||||||
this->deselectByteItem(*byteItem);
|
this->deselectByteItem(*byteItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto items = this->items(this->rubberBandRectItem->rect(), Qt::IntersectsItemShape);
|
const auto items = this->itemIndex->intersectingByteItems(this->rubberBandRectItem->rect());
|
||||||
for (auto* item : items) {
|
for (auto& byteItem : items) {
|
||||||
auto* graphicsItem = dynamic_cast<GraphicsItem*>(item);
|
this->selectByteItem(*byteItem);
|
||||||
|
|
||||||
if (graphicsItem == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* byteItem = dynamic_cast<ByteItem*>(graphicsItem->hexViewerItem);
|
|
||||||
|
|
||||||
if (byteItem != nullptr && !byteItem->selected) {
|
|
||||||
this->selectByteItem(*byteItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& item : hoveredItems) {
|
auto* hoveredByteItem = this->itemIndex->byteItemAt(mousePosition);
|
||||||
auto* hoveredGraphicsItem = dynamic_cast<GraphicsItem*>(item);
|
if (hoveredByteItem != nullptr) {
|
||||||
|
this->onByteItemEnter(*hoveredByteItem);
|
||||||
if (hoveredGraphicsItem == nullptr) {
|
return;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* hoveredByteItem = dynamic_cast<ByteItem*>(hoveredGraphicsItem->hexViewerItem);
|
|
||||||
|
|
||||||
if (hoveredByteItem != nullptr) {
|
|
||||||
this->onByteItemEnter(*hoveredByteItem);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->state.hoveredByteItem != nullptr) {
|
if (this->state.hoveredByteItem != nullptr) {
|
||||||
@@ -638,47 +544,6 @@ namespace Bloom::Widgets
|
|||||||
menu->exec(event->screenPos());
|
menu->exec(event->screenPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::refreshItemPositionIndices() {
|
|
||||||
const auto pointsRequired = static_cast<std::uint32_t>(
|
|
||||||
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<const ByteItem*>(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() {
|
int ItemGraphicsScene::getScrollbarValue() {
|
||||||
return this->views().first()->verticalScrollBar()->value();
|
return this->views().first()->verticalScrollBar()->value();
|
||||||
}
|
}
|
||||||
@@ -713,19 +578,11 @@ namespace Bloom::Widgets
|
|||||||
this->hoverRectY->update();
|
this->hoverRectY->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byteItem.allocatedGraphicsItem != nullptr) {
|
|
||||||
byteItem.allocatedGraphicsItem->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->hoveredAddress(byteItem.startAddress);
|
emit this->hoveredAddress(byteItem.startAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::onByteItemLeave() {
|
void ItemGraphicsScene::onByteItemLeave() {
|
||||||
if (this->state.hoveredByteItem != nullptr) {
|
if (this->state.hoveredByteItem != nullptr) {
|
||||||
if (this->state.hoveredByteItem->allocatedGraphicsItem != nullptr) {
|
|
||||||
this->state.hoveredByteItem->allocatedGraphicsItem->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->state.hoveredByteItem = nullptr;
|
this->state.hoveredByteItem = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
|
|
||||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
|
||||||
|
|
||||||
#include "GraphicsItem.hpp"
|
#include "HexViewerItemIndex.hpp"
|
||||||
|
#include "HexViewerItemRenderer.hpp"
|
||||||
#include "TopLevelGroupItem.hpp"
|
#include "TopLevelGroupItem.hpp"
|
||||||
#include "GroupItem.hpp"
|
#include "GroupItem.hpp"
|
||||||
#include "ByteItem.hpp"
|
#include "ByteItem.hpp"
|
||||||
@@ -60,7 +61,6 @@ namespace Bloom::Widgets
|
|||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
void refreshValues();
|
void refreshValues();
|
||||||
QPointF getByteItemPositionByAddress(Targets::TargetMemoryAddress address);
|
QPointF getByteItemPositionByAddress(Targets::TargetMemoryAddress address);
|
||||||
void allocateGraphicsItems();
|
|
||||||
void addExternalContextMenuAction(ContextMenuAction* action);
|
void addExternalContextMenuAction(ContextMenuAction* action);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -69,8 +69,6 @@ namespace Bloom::Widgets
|
|||||||
void selectionChanged(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress);
|
void selectionChanged(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static constexpr auto GRID_SIZE = 100;
|
|
||||||
|
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
HexViewerSharedState state;
|
HexViewerSharedState state;
|
||||||
@@ -79,14 +77,9 @@ namespace Bloom::Widgets
|
|||||||
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
|
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
|
||||||
|
|
||||||
std::unique_ptr<TopLevelGroupItem> topLevelGroup = nullptr;
|
std::unique_ptr<TopLevelGroupItem> topLevelGroup = nullptr;
|
||||||
|
std::unique_ptr<HexViewerItemIndex> itemIndex = nullptr;
|
||||||
|
|
||||||
std::vector<HexViewerItem*> flattenedItems;
|
HexViewerItemRenderer* renderer = nullptr;
|
||||||
std::vector<decltype(ItemGraphicsScene::flattenedItems)::iterator> gridPoints;
|
|
||||||
std::vector<const ByteItem*> firstByteItemByLine;
|
|
||||||
|
|
||||||
std::vector<GraphicsItem*> graphicsItems;
|
|
||||||
|
|
||||||
const QMargins margins = QMargins(10, 10, 10, 10);
|
|
||||||
|
|
||||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||||
|
|
||||||
@@ -137,6 +130,9 @@ namespace Bloom::Widgets
|
|||||||
return std::max(this->parent->viewport()->width(), 200) - 2;
|
return std::max(this->parent->viewport()->width(), 200) - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void initRenderer();
|
||||||
|
virtual QMargins margins();
|
||||||
|
virtual QPointF addressContainerPosition();
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
||||||
@@ -144,7 +140,6 @@ namespace Bloom::Widgets
|
|||||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
|
||||||
void keyPressEvent(QKeyEvent* keyEvent) override;
|
void keyPressEvent(QKeyEvent* keyEvent) override;
|
||||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override;
|
void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override;
|
||||||
void refreshItemPositionIndices();
|
|
||||||
int getScrollbarValue();
|
int getScrollbarValue();
|
||||||
void onTargetStateChanged(Targets::TargetState newState);
|
void onTargetStateChanged(Targets::TargetState newState);
|
||||||
void onByteItemEnter(ByteItem& byteItem);
|
void onByteItemEnter(ByteItem& byteItem);
|
||||||
|
|||||||
@@ -90,12 +90,4 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
this->scene->adjustSize();
|
this->scene->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsView::scrollContentsBy(int dx, int dy) {
|
|
||||||
if (this->scene != nullptr) {
|
|
||||||
this->scene->allocateGraphicsItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QGraphicsView::scrollContentsBy(dx, dy);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,5 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
void scrollContentsBy(int dx, int dy) override;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "StackMemoryGroupItem.hpp"
|
#include "StackMemoryGroupItem.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
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<float>(stackSize) / static_cast<float>(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<int>(heightOffset - 5);
|
|
||||||
const auto verticalLineYEnd = static_cast<int>(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(
|
QMargins StackMemoryGroupItem::groupMargins(
|
||||||
const HexViewerSharedState* hexViewerState,
|
const HexViewerSharedState* hexViewerState,
|
||||||
const int maximumWidth
|
const int maximumWidth
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace Bloom::Widgets
|
|||||||
class StackMemoryGroupItem: public GroupItem
|
class StackMemoryGroupItem: public GroupItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Targets::TargetStackPointer stackPointer;
|
||||||
|
|
||||||
StackMemoryGroupItem(
|
StackMemoryGroupItem(
|
||||||
Targets::TargetStackPointer stackPointer,
|
Targets::TargetStackPointer stackPointer,
|
||||||
const HexViewerSharedState& hexViewerState,
|
const HexViewerSharedState& hexViewerState,
|
||||||
@@ -28,12 +30,6 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
void refreshValues();
|
void refreshValues();
|
||||||
|
|
||||||
void paint(
|
|
||||||
QPainter* painter,
|
|
||||||
const HexViewerSharedState* hexViewerState,
|
|
||||||
const QGraphicsItem* graphicsItem
|
|
||||||
) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override;
|
QMargins groupMargins(const HexViewerSharedState* hexViewerState, const int maximumWidth) const override;
|
||||||
|
|
||||||
@@ -42,9 +38,7 @@ namespace Bloom::Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Targets::TargetStackPointer stackPointer;
|
|
||||||
const HexViewerSharedState& hexViewerState;
|
const HexViewerSharedState& hexViewerState;
|
||||||
|
|
||||||
std::list<FocusedRegionGroupItem> focusedRegionGroupItems;
|
std::list<FocusedRegionGroupItem> focusedRegionGroupItems;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,23 +44,8 @@ namespace Bloom::Widgets
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportVerticalCenter() {
|
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportTop() {
|
||||||
const auto scrollBarValue = this->getScrollbarValue();
|
return this->itemIndex->closestByteItem(this->getScrollbarValue());
|
||||||
|
|
||||||
const auto midPosition = static_cast<qreal>(
|
|
||||||
scrollBarValue
|
|
||||||
);
|
|
||||||
|
|
||||||
const auto gridPointIndex = static_cast<decltype(this->gridPoints)::size_type>(
|
|
||||||
std::min(
|
|
||||||
static_cast<int>(
|
|
||||||
std::floor(static_cast<float>(midPosition) / static_cast<float>(ItemGraphicsScene::GRID_SIZE)) + 1
|
|
||||||
),
|
|
||||||
static_cast<int>(this->gridPoints.size() - 1)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return static_cast<ByteItem*>(*(this->gridPoints[gridPointIndex]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DifferentialItemGraphicsScene::onOtherHoveredAddress(
|
void DifferentialItemGraphicsScene::onOtherHoveredAddress(
|
||||||
@@ -83,8 +68,7 @@ namespace Bloom::Widgets
|
|||||||
const auto scrollbarValue = this->getScrollbarValue();
|
const auto scrollbarValue = this->getScrollbarValue();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
byteItem.allocatedGraphicsItem == nullptr
|
itemPosition < scrollbarValue
|
||||||
|| itemPosition < scrollbarValue
|
|
||||||
|| itemPosition > (scrollbarValue + this->views().first()->viewport()->height())
|
|| itemPosition > (scrollbarValue + this->views().first()->viewport()->height())
|
||||||
) {
|
) {
|
||||||
// The item isn't visible
|
// The item isn't visible
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace Bloom::Widgets
|
|||||||
);
|
);
|
||||||
|
|
||||||
void setOther(DifferentialItemGraphicsScene* other);
|
void setOther(DifferentialItemGraphicsScene* other);
|
||||||
ByteItem* byteItemAtViewportVerticalCenter();
|
ByteItem* byteItemAtViewportTop();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DifferentialHexViewerSharedState& diffHexViewerState;
|
DifferentialHexViewerSharedState& diffHexViewerState;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace Bloom::Widgets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* byteItem = this->differentialScene->byteItemAtViewportVerticalCenter();
|
auto* byteItem = this->differentialScene->byteItemAtViewportTop();
|
||||||
|
|
||||||
this->state.syncingScroll = true;
|
this->state.syncingScroll = true;
|
||||||
this->other->alignScroll(byteItem->startAddress, byteItem->position().y() - this->verticalScrollBar()->value());
|
this->other->alignScroll(byteItem->startAddress, byteItem->position().y() - this->verticalScrollBar()->value());
|
||||||
|
|||||||
Reference in New Issue
Block a user