Added primary highlighting in hex viewer
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace Widgets
|
||||
bool syncingScroll = false;
|
||||
bool syncingHover = false;
|
||||
bool syncingSelection = false;
|
||||
bool syncingHighlightedRanges = false;
|
||||
bool syncingHighlightedPrimaryRanges = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user