From 798f6913a498daa23a754a564847f4708e6fa5aa Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 3 Sep 2022 20:36:38 +0100 Subject: [PATCH] Implemented rubber-band selection rectangle in memory inspection hex viewer --- .../HexViewerWidget/ByteItem.cpp | 16 ++ .../HexViewerWidget/ByteItem.hpp | 1 + .../HexViewerWidget/ByteItemGraphicsScene.cpp | 144 +++++++++++++++++- .../HexViewerWidget/ByteItemGraphicsScene.hpp | 13 ++ 4 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp index 9341cc7e..4cddd19e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp @@ -94,6 +94,7 @@ namespace Bloom::Widgets * color variant, so that we don't have to make copies or call QColor::setAlpha() for each ByteItem. */ static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); + static const auto selectedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255); static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255); static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210); @@ -104,6 +105,13 @@ namespace Bloom::Widgets 100 ); + static const auto disabledSelectedBackgroundColor = QColor( + selectedBackgroundColor.red(), + selectedBackgroundColor.green(), + selectedBackgroundColor.blue(), + 100 + ); + static const auto disabledFocusedRegionBackgroundColor = QColor( focusedRegionBackgroundColor.red(), focusedRegionBackgroundColor.green(), @@ -133,6 +141,10 @@ namespace Bloom::Widgets return &(highlightedBackgroundColor); } + if (this->selected) { + return &(selectedBackgroundColor); + } + const auto* hoveredByteItem = *(this->hoveredByteItem); const auto hovered = hoveredByteItem == this; const auto hoveredNeighbour = @@ -169,6 +181,10 @@ namespace Bloom::Widgets return &(disabledHighlightedBackgroundColor); } + if (this->selected) { + return &(disabledSelectedBackgroundColor); + } + if ( this->settings.highlightStackMemory && this->currentStackPointer.has_value() diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp index ff45bb13..24d763e7 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.hpp @@ -34,6 +34,7 @@ namespace Bloom::Widgets std::size_t currentColumnIndex = 0; bool highlighted = false; + bool selected = false; const FocusedMemoryRegion* focusedMemoryRegion = nullptr; const ExcludedMemoryRegion* excludedMemoryRegion = nullptr; diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp index c59a0eff..f5e78f3e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp @@ -248,14 +248,109 @@ namespace Bloom::Widgets return QGraphicsScene::event(event); } + void ByteItemGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) { + static const auto rubberBandRectBackgroundColor = QColor(0x3C, 0x59, 0x5C, 0x82); + static const auto rubberBandRectBorderColor = QColor(0x3C, 0x59, 0x5C, 255); + + const auto mousePosition = mouseEvent->buttonDownScenePos(Qt::MouseButton::LeftButton); + + if (mousePosition.x() <= this->byteAddressContainer->boundingRect().width()) { + return; + } + + this->clearSelectionRectItem(); + + this->rubberBandInitPoint = std::move(mousePosition); + this->rubberBandRectItem = new QGraphicsRectItem( + this->rubberBandInitPoint->x(), + this->rubberBandInitPoint->y(), + 1, + 1 + ); + this->rubberBandRectItem->setBrush(rubberBandRectBackgroundColor); + this->rubberBandRectItem->setPen(rubberBandRectBorderColor); + this->addItem(this->rubberBandRectItem); + + const auto modifiers = mouseEvent->modifiers(); + if ((modifiers & (Qt::ControlModifier | Qt::ShiftModifier)) == 0) { + this->clearByteItemSelection(); + } + + auto clickedItems = this->items(mousePosition); + + if (!clickedItems.empty()) { + auto* clickedByteItem = dynamic_cast(clickedItems.last()); + + if (clickedByteItem != nullptr) { + + if ((modifiers & Qt::ShiftModifier) != 0) { + for ( + auto i = clickedByteItem->address; + i >= this->targetMemoryDescriptor.addressRange.startAddress; + --i + ) { + auto* byteItem = this->byteItemsByAddress.at(i); + + if (byteItem->selected) { + break; + } + + this->toggleByteItemSelection(byteItem); + } + + return; + } + + this->toggleByteItemSelection(clickedByteItem); + } + } + + } + void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { - auto hoveredItems = this->items(mouseEvent->scenePos()); + const auto mousePosition = mouseEvent->scenePos(); + auto hoveredItems = this->items(mousePosition); + + if (this->rubberBandRectItem != nullptr && this->rubberBandInitPoint.has_value()) { + const auto oldRect = this->rubberBandRectItem->rect(); + + this->rubberBandRectItem->setRect( + qMin(mousePosition.x(), this->rubberBandInitPoint->x()), + qMin(mousePosition.y(), this->rubberBandInitPoint->y()), + qAbs(mousePosition.x() - this->rubberBandInitPoint->x()), + qAbs(mousePosition.y() - this->rubberBandInitPoint->y()) + ); + + if ((mouseEvent->modifiers() & Qt::ControlModifier) == 0) { + this->clearByteItemSelection(); + + } else { + const auto oldItems = this->items(oldRect, Qt::IntersectsItemShape); + for (auto* item : oldItems) { + auto* byteItem = dynamic_cast(item); + + if (byteItem != nullptr && byteItem->selected) { + this->deselectByteItem(byteItem); + } + } + } + + const auto items = this->items(this->rubberBandRectItem->rect(), Qt::IntersectsItemShape); + for (auto* item : items) { + auto* byteItem = dynamic_cast(item); + + if (byteItem != nullptr && !byteItem->selected) { + this->selectByteItem(byteItem); + } + } + } + ByteItem* hoveredByteItem = nullptr; AnnotationItem* hoveredAnnotationItem = nullptr; if (!hoveredItems.empty()) { - hoveredByteItem = dynamic_cast(hoveredItems.at(0)); - hoveredAnnotationItem = dynamic_cast(hoveredItems.at(0)); + hoveredByteItem = dynamic_cast(hoveredItems.last()); + hoveredAnnotationItem = dynamic_cast(hoveredItems.last()); } if (hoveredByteItem != nullptr) { @@ -277,6 +372,10 @@ namespace Bloom::Widgets } } + void ByteItemGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) { + this->clearSelectionRectItem(); + } + void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) { const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress; for (auto* valueAnnotationItem : this->valueAnnotationItems) { @@ -504,4 +603,43 @@ namespace Bloom::Widgets this->byteItemsByAddress.at(byteItemAddress)->update(); } } + + void ByteItemGraphicsScene::clearSelectionRectItem() { + if (this->rubberBandRectItem != nullptr) { + this->removeItem(this->rubberBandRectItem); + delete this->rubberBandRectItem; + this->rubberBandRectItem = nullptr; + } + } + + void ByteItemGraphicsScene::selectByteItem(ByteItem* byteItem) { + byteItem->selected = true; + this->selectedByteItems.insert(byteItem); + byteItem->update(); + } + + void ByteItemGraphicsScene::deselectByteItem(ByteItem* byteItem) { + byteItem->selected = false; + this->selectedByteItems.erase(byteItem); + byteItem->update(); + } + + void ByteItemGraphicsScene::toggleByteItemSelection(ByteItem* byteItem) { + if (byteItem->selected) { + this->deselectByteItem(byteItem); + return; + } + + this->selectByteItem(byteItem); + } + + void ByteItemGraphicsScene::clearByteItemSelection() { + for (auto* byteItem : this->selectedByteItems) { + byteItem->selected = false; + byteItem->update(); + } + + this->selectedByteItems.clear(); + } + } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp index 969eb7a8..501ae1f5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "src/Targets/TargetMemory.hpp" #include "src/Targets/TargetState.hpp" @@ -63,7 +65,9 @@ namespace Bloom::Widgets protected: bool event(QEvent* event) override; + void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override; void mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) override; private: const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; @@ -97,6 +101,10 @@ namespace Bloom::Widgets ByteAddressContainer* byteAddressContainer = nullptr; std::set highlightedByteItems; + std::set selectedByteItems; + + QGraphicsRectItem* rubberBandRectItem = nullptr; + std::optional rubberBandInitPoint = std::nullopt; int getSceneWidth() { /* @@ -116,5 +124,10 @@ namespace Bloom::Widgets void onByteWidgetLeave(); void onAnnotationItemEnter(Bloom::Widgets::AnnotationItem* annotationItem); void onAnnotationItemLeave(); + void clearSelectionRectItem(); + void selectByteItem(ByteItem* byteItem); + void deselectByteItem(ByteItem* byteItem); + void toggleByteItemSelection(ByteItem* byteItem); + void clearByteItemSelection(); }; }