Added primary highlighting in hex viewer

This commit is contained in:
Nav
2023-08-25 19:53:24 +01:00
parent fc6decc1df
commit d20a0f0ed5
12 changed files with 148 additions and 38 deletions

View File

@@ -25,7 +25,7 @@ namespace Widgets
bool grouped:1 = false;
bool stackMemory:1 = false;
bool changed:1 = false;
bool highlighted:1 = false;
bool primaryHighlighted:1 = false;
explicit ByteItem(Targets::TargetMemoryAddress address);

View File

@@ -2,15 +2,18 @@
#include <QScrollBar>
#include <QColor>
#include <QPainterPath>
namespace Widgets
{
HexViewerItemRenderer::HexViewerItemRenderer(
const HexViewerSharedState& hexViewerState,
const TopLevelGroupItem& topLevelGroupItem,
const HexViewerItemIndex& itemIndex,
const QGraphicsView* view
)
: hexViewerState(hexViewerState)
, topLevelGroupItem(topLevelGroupItem)
, itemIndex(itemIndex)
, view(view)
, viewport(view->viewport())
@@ -24,11 +27,10 @@ namespace Widgets
}
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()
);
const auto viewportYStart = this->view->verticalScrollBar()->value();
const auto viewportYEnd = viewportYStart + this->viewport->size().height();
const auto visibleItems = this->itemIndex.items(viewportYStart, viewportYEnd);
painter->setRenderHints(QPainter::RenderHint::Antialiasing, false);
@@ -46,6 +48,26 @@ namespace Widgets
painter->setOpacity(1);
this->paintItem(item, painter);
}
if (this->hexViewerState.highlightingEnabled) {
for (const auto& range : this->hexViewerState.highlightedPrimaryAddressRanges) {
const auto& startItem = this->topLevelGroupItem.byteItemsByAddress.at(range.startAddress);
const auto& endItem = this->topLevelGroupItem.byteItemsByAddress.at(range.endAddress);
const auto startItemY = startItem.position().y();
const auto endItemY = endItem.position().y();
if (startItemY > viewportYEnd) {
break;
}
if (startItemY != endItemY || endItemY < viewportYStart) {
continue;
}
this->paintPrimaryHighlightBorder(&startItem, &endItem, painter);
}
}
}
void HexViewerItemRenderer::paintItem(const HexViewerItem* item, QPainter* painter) {
@@ -75,7 +97,7 @@ namespace Widgets
painter->setOpacity(
!this->isEnabled()
|| (item->excluded && !item->selected)
|| (this->hexViewerState.highlightingEnabled && !item->highlighted)
|| (this->hexViewerState.highlightingEnabled && !item->primaryHighlighted)
? 0.6
: 1
);
@@ -86,6 +108,11 @@ namespace Widgets
return;
}
if (this->hexViewerState.highlightingEnabled && item->primaryHighlighted) {
painter->drawPixmap(boundingRect, HexViewerItemRenderer::primaryHighlightedMissingDataPixmap.value());
return;
}
painter->drawPixmap(boundingRect, HexViewerItemRenderer::missingDataPixmap.value());
return;
}
@@ -104,6 +131,14 @@ namespace Widgets
return;
}
if (this->hexViewerState.highlightingEnabled && item->primaryHighlighted) {
painter->drawPixmap(
boundingRect,
HexViewerItemRenderer::primaryHighlightedAsciiPixmapsByValue[value]
);
return;
}
if (item->changed) {
painter->drawPixmap(
boundingRect,
@@ -148,6 +183,14 @@ namespace Widgets
return;
}
if (this->hexViewerState.highlightingEnabled && item->primaryHighlighted) {
painter->drawPixmap(
boundingRect,
HexViewerItemRenderer::primaryHighlightedPixmapsByValue[value]
);
return;
}
if (item->changed) {
painter->drawPixmap(
boundingRect,
@@ -186,6 +229,34 @@ namespace Widgets
);
}
void HexViewerItemRenderer::paintPrimaryHighlightBorder(
const ByteItem* startItem,
const ByteItem* endItem,
QPainter* painter
) {
constexpr auto padding = 6;
constexpr auto rectRadius = 4;
const auto startItemPos = startItem->position();
const auto endItemPos = endItem->position();
const auto endItemSize = endItem->size();
auto painterPath = QPainterPath();
painterPath.addRoundedRect(
startItemPos.x() - padding,
startItemPos.y() - padding,
(endItemPos.x() + endItemSize.width()) - startItemPos.x() + (padding * 2),
(endItemPos.y() + endItemSize.height()) - startItemPos.y() + (padding * 2),
rectRadius,
rectRadius
);
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
painter->setPen(QPen(QColor(0x78, 0x78, 0x78), 2));
painter->setBrush(Qt::BrushStyle::NoBrush);
painter->drawPath(painterPath);
}
void HexViewerItemRenderer::paintFocusedRegionGroupItem(const FocusedRegionGroupItem* item, QPainter* painter) {
if (!this->hexViewerState.settings.displayAnnotations) {
return;
@@ -517,8 +588,8 @@ namespace Widgets
}
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 primaryHighlightedBackgroundColor = QColor(0x3B, 0x59, 0x37, 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);
@@ -545,8 +616,8 @@ namespace Widgets
auto standardTemplatePixmap = QPixmap(byteItemSize);
standardTemplatePixmap.fill(standardBackgroundColor);
auto highlightedTemplatePixmap = QPixmap(byteItemSize);
highlightedTemplatePixmap.fill(highlightedBackgroundColor);
auto primaryHighlightedTemplatePixmap = QPixmap(byteItemSize);
primaryHighlightedTemplatePixmap.fill(primaryHighlightedBackgroundColor);
auto selectedTemplatePixmap = QPixmap(byteItemSize);
selectedTemplatePixmap.fill(selectedBackgroundColor);
@@ -604,6 +675,16 @@ namespace Widgets
HexViewerItemRenderer::selectedPixmapsByValue.emplace_back(std::move(selectedPixmap));
}
{
auto primaryHighlightedPixmap = primaryHighlightedTemplatePixmap;
auto painter = QPainter(&primaryHighlightedPixmap);
painter.setFont(font);
painter.setPen(standardFontColor);
painter.drawText(byteItemRect, Qt::AlignCenter, hexValue);
HexViewerItemRenderer::primaryHighlightedPixmapsByValue.emplace_back(std::move(primaryHighlightedPixmap));
}
{
auto groupedPixmap = groupedTemplatePixmap;
auto painter = QPainter(&groupedPixmap);
@@ -664,6 +745,18 @@ namespace Widgets
HexViewerItemRenderer::selectedAsciiPixmapsByValue.emplace_back(std::move(selectedAsciiPixmap));
}
{
auto primaryHighlightedAsciiPixmap = primaryHighlightedTemplatePixmap;
auto painter = QPainter(&primaryHighlightedAsciiPixmap);
painter.setFont(font);
painter.setPen(asciiValue.has_value() ? asciiFontColor : fadedFontColor);
painter.drawText(byteItemRect, Qt::AlignCenter, asciiValue.value_or(hexValue));
HexViewerItemRenderer::primaryHighlightedAsciiPixmapsByValue.emplace_back(
std::move(primaryHighlightedAsciiPixmap)
);
}
{
auto groupedAsciiPixmap = groupedTemplatePixmap;
auto painter = QPainter(&groupedAsciiPixmap);
@@ -724,6 +817,14 @@ namespace Widgets
painter.drawText(byteItemRect, Qt::AlignCenter, "??");
}
{
HexViewerItemRenderer::primaryHighlightedMissingDataPixmap = primaryHighlightedTemplatePixmap;
auto painter = QPainter(&HexViewerItemRenderer::primaryHighlightedMissingDataPixmap.value());
painter.setFont(font);
painter.setPen(standardFontColor);
painter.drawText(byteItemRect, Qt::AlignCenter, "??");
}
HexViewerItemRenderer::pixmapCachesGenerated = true;
}
}

View File

@@ -31,6 +31,7 @@ namespace Widgets
HexViewerItemRenderer(
const HexViewerSharedState& hexViewerState,
const TopLevelGroupItem& topLevelGroupItem,
const HexViewerItemIndex& itemIndex,
const QGraphicsView* view
);
@@ -43,6 +44,7 @@ namespace Widgets
protected:
const HexViewerSharedState& hexViewerState;
const TopLevelGroupItem& topLevelGroupItem;
const HexViewerItemIndex& itemIndex;
const QGraphicsView* view;
@@ -53,11 +55,13 @@ namespace Widgets
static inline std::vector<QPixmap> standardPixmapsByValue = {};
static inline std::vector<QPixmap> selectedPixmapsByValue = {};
static inline std::vector<QPixmap> primaryHighlightedPixmapsByValue = {};
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> primaryHighlightedAsciiPixmapsByValue = {};
static inline std::vector<QPixmap> groupedAsciiPixmapsByValue = {};
static inline std::vector<QPixmap> stackMemoryAsciiPixmapsByValue = {};
static inline std::vector<QPixmap> changedMemoryAsciiPixmapsByValue = {};
@@ -65,9 +69,15 @@ namespace Widgets
static inline std::vector<QPixmap> hoveredPrimaryAsciiPixmapsByValue = {};
static inline std::optional<QPixmap> missingDataPixmap = {};
static inline std::optional<QPixmap> selectedMissingDataPixmap = {};
static inline std::optional<QPixmap> primaryHighlightedMissingDataPixmap = {};
inline void paintItem(const HexViewerItem* item, QPainter* painter) __attribute__((__always_inline__));
inline void paintByteItem(const ByteItem* item, QPainter* painter) __attribute__((__always_inline__));
inline void paintPrimaryHighlightBorder(
const ByteItem* startItem,
const ByteItem* endItem,
QPainter* painter
) __attribute__((__always_inline__));
inline void paintFocusedRegionGroupItem(
const FocusedRegionGroupItem* item,
QPainter* painter

View File

@@ -20,7 +20,7 @@ namespace Widgets
ByteItem* hoveredByteItem = nullptr;
std::optional<Targets::TargetStackPointer> currentStackPointer;
bool highlightingEnabled = false;
std::set<Targets::TargetMemoryAddressRange> highlightedAddressRanges;
std::set<Targets::TargetMemoryAddressRange> highlightedPrimaryAddressRanges;
HexViewerSharedState(
const Targets::TargetMemoryDescriptor& memoryDescriptor,

View File

@@ -242,19 +242,16 @@ namespace Widgets
this->byteItemGraphicsScene->addExternalContextMenuAction(action);
}
void HexViewerWidget::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
void HexViewerWidget::highlightPrimaryByteItemRanges(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
) {
this->byteItemGraphicsScene->highlightPrimaryByteItemRanges(addressRanges);
}
void HexViewerWidget::clearHighlighting() {
this->byteItemGraphicsScene->clearByteItemHighlighting();
}
void HexViewerWidget::selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
this->byteItemGraphicsScene->selectByteItemRanges(addressRanges);
}
void HexViewerWidget::centerOnByte(Targets::TargetMemoryAddress address) {
this->byteItemGraphicsView->scrollToByteItemAtAddress(address);
}

View File

@@ -42,9 +42,8 @@ namespace Widgets
void refreshRegions();
void setStackPointer(Targets::TargetStackPointer stackPointer);
void addExternalContextMenuAction(ContextMenuAction* action);
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void highlightPrimaryByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void clearHighlighting();
void selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void centerOnByte(Targets::TargetMemoryAddress address);
signals:

View File

@@ -241,7 +241,9 @@ namespace Widgets
return this->selectByteItems(this->addressRangesToAddresses(addressRanges));
}
void ItemGraphicsScene::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
void ItemGraphicsScene::highlightPrimaryByteItemRanges(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
) {
/*
* Don't bother updating the byte items if addressRanges is empty - updating the this->state.highlightingEnabled
* flag will prevent the highlighting, and the byte items will be updated the next time we actually want to
@@ -252,19 +254,19 @@ namespace Widgets
if (!addressRanges.empty()) {
const auto addresses = this->addressRangesToAddresses(addressRanges);
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
byteItem.highlighted = addresses.contains(address);
byteItem.primaryHighlighted = addresses.contains(address);
}
}
this->state.highlightingEnabled = !addressRanges.empty();
this->state.highlightedAddressRanges = std::move(addressRanges);
this->state.highlightedPrimaryAddressRanges = std::move(addressRanges);
this->update();
emit this->highlightingChanged(addressRanges);
emit this->primaryHighlightingChanged(addressRanges);
}
void ItemGraphicsScene::clearByteItemHighlighting() {
this->highlightByteItemRanges({});
this->highlightPrimaryByteItemRanges({});
}
void ItemGraphicsScene::rebuildItemHierarchy() {
@@ -353,6 +355,7 @@ namespace Widgets
void ItemGraphicsScene::initRenderer() {
this->renderer = new HexViewerItemRenderer(
this->state,
*(this->topLevelGroup.get()),
*(this->itemIndex.get()),
this->views().first()
);

View File

@@ -57,7 +57,7 @@ namespace Widgets
void updateStackPointer(Targets::TargetStackPointer stackPointer);
void selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
void selectByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void highlightPrimaryByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void clearByteItemHighlighting();
void rebuildItemHierarchy();
void adjustSize();
@@ -70,7 +70,7 @@ namespace Widgets
void ready();
void hoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void selectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
void highlightingChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void primaryHighlightingChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
protected:
bool enabled = true;

View File

@@ -106,10 +106,10 @@ namespace Widgets
const auto* item = dynamic_cast<ChangeListItem*>(selectedItems.front());
this->hexViewerWidgetA->selectAndHighlightBytes({item->addressRange});
this->hexViewerWidgetA->highlightPrimaryByteItemRanges({item->addressRange});
this->hexViewerWidgetA->centerOnByte(item->addressRange.startAddress);
this->hexViewerWidgetB->selectAndHighlightBytes({item->addressRange});
this->hexViewerWidgetB->highlightPrimaryByteItemRanges({item->addressRange});
this->hexViewerWidgetB->centerOnByte(item->addressRange.startAddress);
}

View File

@@ -12,6 +12,6 @@ namespace Widgets
bool syncingScroll = false;
bool syncingHover = false;
bool syncingSelection = false;
bool syncingHighlightedRanges = false;
bool syncingHighlightedPrimaryRanges = false;
};
}

View File

@@ -47,9 +47,9 @@ namespace Widgets
QObject::connect(
this->other,
&DifferentialItemGraphicsScene::highlightingChanged,
&DifferentialItemGraphicsScene::primaryHighlightingChanged,
this,
&DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged
&DifferentialItemGraphicsScene::onOtherHighlightedPrimaryByteRangesChanged
);
}
@@ -115,16 +115,16 @@ namespace Widgets
this->diffHexViewerState.syncingSelection = false;
}
void DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged(
void DifferentialItemGraphicsScene::onOtherHighlightedPrimaryByteRangesChanged(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
) {
if (this->diffHexViewerState.syncingHighlightedRanges) {
if (this->diffHexViewerState.syncingHighlightedPrimaryRanges) {
return;
}
this->diffHexViewerState.syncingHighlightedRanges = true;
this->highlightByteItemRanges(addressRanges);
this->diffHexViewerState.syncingHighlightedRanges = false;
this->diffHexViewerState.syncingHighlightedPrimaryRanges = true;
this->highlightPrimaryByteItemRanges(addressRanges);
this->diffHexViewerState.syncingHighlightedPrimaryRanges = false;
this->update();
}

View File

@@ -42,6 +42,6 @@ namespace Widgets
void onOtherHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void onOtherSelectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
void onOtherHighlightedByteRangesChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void onOtherHighlightedPrimaryByteRangesChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
};
}