diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp index 0883788e..d374b498 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.cpp @@ -146,6 +146,73 @@ namespace Widgets return output; } + const ByteItem* HexViewerItemIndex::leftMostByteItemWithinRange(int yStart, int yEnd) const { + const ByteItem* leftMostByteItem = nullptr; + auto leftMostByteItemXPos = 0; + + for (const auto* item : this->items(yStart, yEnd)) { + const auto itemPos = item->position(); + + /* + * Remember, the HexViewerItemIndex::items() function can return items that are outside the given range, + * depending on the grid size of the Y-axis index. + * + * We need to ensure that we exclude those items here. + */ + if (itemPos.y() < yStart) { + continue; + } + + if (itemPos.y() > yEnd) { + break; + } + + const auto* byteItem = dynamic_cast(item); + + if (byteItem == nullptr) { + continue; + } + + if (leftMostByteItem == nullptr || itemPos.x() < leftMostByteItemXPos) { + leftMostByteItem = byteItem; + leftMostByteItemXPos = itemPos.x(); + } + } + + return leftMostByteItem; + } + + const ByteItem* HexViewerItemIndex::rightMostByteItemWithinRange(int yStart, int yEnd) const { + const ByteItem* rightMostByteItem = nullptr; + auto rightMostByteItemXPos = 0; + + for (const auto* item : this->items(yStart, yEnd)) { + const auto itemPos = item->position(); + + if (itemPos.y() < yStart) { + continue; + } + + if (itemPos.y() > yEnd) { + break; + } + + const auto* byteItem = dynamic_cast(item); + + if (byteItem == nullptr) { + continue; + } + + const auto itemXPos = item->position().x() + item->size().width(); + if (rightMostByteItem == nullptr || itemXPos > rightMostByteItemXPos) { + rightMostByteItem = byteItem; + rightMostByteItemXPos = itemXPos; + } + } + + return rightMostByteItem; + } + void HexViewerItemIndex::refreshFlattenedItems() { this->flattenedItems = this->topLevelGroupItem->flattenedItems(); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp index c0b7ca9b..ade5f49e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemIndex.hpp @@ -75,6 +75,24 @@ namespace Widgets */ std::vector intersectingByteItems(const QRectF& rect) const; + /** + * Returns the left-most (smallest position on the X-axis) byte item, within a Y-axis range. + * + * @param yStart + * @param yEnd + * @return + */ + const ByteItem* leftMostByteItemWithinRange(int yStart, int yEnd) const; + + /** + * Returns the right-most (largest position on the X-axis) byte item, within a Y-axis range. + * + * @param yStart + * @param yEnd + * @return + */ + const ByteItem* rightMostByteItemWithinRange(int yStart, int yEnd) const; + void refreshFlattenedItems(); void refreshIndex(); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp index 02e14620..6dc1a527 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerItemRenderer.cpp @@ -61,7 +61,7 @@ namespace Widgets break; } - if (startItemY != endItemY || endItemY < viewportYStart) { + if (endItemY < viewportYStart) { continue; } @@ -242,14 +242,34 @@ namespace Widgets 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 - ); + + if (startItemPos.y() != endItemPos.y()) { + // The highlighted range spans more than one line - draw the border around all lines containing the range + const auto leftMostItem = this->itemIndex.leftMostByteItemWithinRange(startItemPos.y(), endItemPos.y()); + const auto leftMostItemPos = leftMostItem->position(); + + const auto* rightMostItem = this->itemIndex.rightMostByteItemWithinRange(startItemPos.y(), endItemPos.y()); + const auto rightMostItemPos = rightMostItem->position(); + + painterPath.addRoundedRect( + leftMostItemPos.x() - padding, + startItemPos.y() - padding, + (rightMostItemPos.x() + rightMostItem->size().width()) - leftMostItemPos.x() + (padding * 2), + (endItemPos.y() + endItemSize.height()) - startItemPos.y() + (padding * 2), + rectRadius, + rectRadius + ); + + } else { + 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));