From c0269e8378cada7db7d9290164fbd98bb9a3f523 Mon Sep 17 00:00:00 2001 From: Nav Date: Mon, 1 May 2023 15:05:34 +0100 Subject: [PATCH] Initial work for the SnapshotDiff window --- src/Insight/CMakeLists.txt | 19 +- .../Widgets/ListView/ListScene.cpp | 36 +- .../DifferentialHexViewerSharedState.hpp | 12 + .../DifferentialHexViewerWidget.cpp | 120 +++++++ .../DifferentialHexViewerWidget.hpp | 46 +++ .../DifferentialHexViewerWidgetType.hpp | 12 + .../DifferentialItemGraphicsScene.cpp | 120 +++++++ .../DifferentialItemGraphicsScene.hpp | 40 +++ .../DifferentialItemGraphicsView.cpp | 83 +++++ .../DifferentialItemGraphicsView.hpp | 42 +++ .../Images/sync-hex-viewer-hover.svg | 209 ++++++++++++ .../Images/sync-hex-viewer-scroll.svg | 195 +++++++++++ .../Images/sync-hex-viewer-selection.svg | 209 ++++++++++++ .../Images/sync-hex-viewer-settings.svg | 181 ++++++++++ .../SnapshotDiff/SnapshotDiff.cpp | 241 ++++++++++++++ .../SnapshotDiff/SnapshotDiff.hpp | 94 ++++++ .../SnapshotDiff/SnapshotDiffSettings.hpp | 12 + .../SnapshotDiff/Stylesheets/SnapshotDiff.qss | 72 ++++ .../SnapshotDiff/UiFiles/SnapshotDiff.ui | 309 ++++++++++++++++++ .../SnapshotManager/SnapshotManager.cpp | 101 +++++- .../SnapshotManager/SnapshotManager.hpp | 8 +- 21 files changed, 2129 insertions(+), 32 deletions(-) create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerSharedState.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidgetType.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui diff --git a/src/Insight/CMakeLists.txt b/src/Insight/CMakeLists.txt index bd53e547..c9129504 100755 --- a/src/Insight/CMakeLists.txt +++ b/src/Insight/CMakeLists.txt @@ -109,6 +109,10 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/CreateSnapshotWindow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/SnapshotViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/MemoryRegionItem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp # Memory region manager window ${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp @@ -182,18 +186,29 @@ qt_add_resources( "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/display-annotations-disabled.svg" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/ascii-view.svg" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/ascii-view-disabled.svg" - "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg" # Hex viewer widget "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Stylesheets/HexViewerWidget.qss" - # Memory snapshots + # Memory snapshot manager "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg" + + # Memory snapshot viewer "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss" + # Memory snapshot diff + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg" + "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg" + + # Memory region manager window "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui" "./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss" diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.cpp index a979b079..7c82080e 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.cpp @@ -87,6 +87,22 @@ namespace Bloom::Widgets void ListScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) { const auto button = mouseEvent->button(); + const auto mousePosition = mouseEvent->buttonDownScenePos(button); + + const auto items = this->items(mousePosition); + if (items.empty()) { + return; + } + + auto* clickedListItem = dynamic_cast(items.first()); + if (clickedListItem == nullptr) { + return; + } + + if (clickedListItem->selected && button == Qt::MouseButton::RightButton) { + return; + } + const auto selectedItemCount = this->selectedItems.size(); if (selectedItemCount > 0) { const auto ctrlModifierEnabled = (mouseEvent->modifiers() & Qt::ControlModifier) != 0; @@ -109,18 +125,6 @@ namespace Bloom::Widgets } } - const auto mousePosition = mouseEvent->buttonDownScenePos(button); - - const auto items = this->items(mousePosition); - if (items.empty()) { - return; - } - - auto* clickedListItem = dynamic_cast(items.first()); - if (clickedListItem == nullptr) { - return; - } - if (this->selectionLimit > 0) { this->selectedItems.push_back(clickedListItem); clickedListItem->selected = true; @@ -148,6 +152,14 @@ namespace Bloom::Widgets return; } + if (!clickedListItem->selected) { + /* + * Sometimes, QT won't trigger a press event when the user double clicks. This usually happens when the + * first click closes a context menu. + */ + this->mousePressEvent(mouseEvent); + } + emit this->itemDoubleClicked(clickedListItem); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerSharedState.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerSharedState.hpp new file mode 100644 index 00000000..302829bf --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerSharedState.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace Bloom::Widgets +{ + struct DifferentialHexViewerSharedState + { + bool syncingSettings = false; + bool syncingScroll = false; + bool syncingHover = false; + bool syncingSelection = false; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp new file mode 100644 index 00000000..ac10ecda --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp @@ -0,0 +1,120 @@ +#include "DifferentialHexViewerWidget.hpp" + +#include +#include + +namespace Bloom::Widgets +{ + DifferentialHexViewerWidget::DifferentialHexViewerWidget( + DifferentialHexViewerWidgetType type, + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + HexViewerWidgetSettings& settings, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + QWidget* parent + ) + : HexViewerWidget( + targetMemoryDescriptor, + data, + settings, + focusedMemoryRegions, + excludedMemoryRegions, + parent + ) + , type(type) + , state(state) + , snapshotDiffSettings(snapshotDiffSettings) + {} + + void DifferentialHexViewerWidget::init() { + this->differentialView = new DifferentialItemGraphicsView( + this->state, + this->snapshotDiffSettings, + this->targetMemoryDescriptor, + this->data, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this->settings, + this->container + ); + + if (this->type == DifferentialHexViewerWidgetType::PRIMARY) { + this->differentialView->setLayoutDirection(Qt::LayoutDirection::RightToLeft); + } + + this->byteItemGraphicsView = this->differentialView; + this->byteItemGraphicsView->hide(); + + auto* containerLayout = this->container->findChild("hex-viewer-layout"); + containerLayout->insertWidget(2, this->byteItemGraphicsView); + + QObject::connect( + this->byteItemGraphicsView, + &ItemGraphicsView::sceneReady, + this, + [this] { + this->differentialScene = dynamic_cast( + this->byteItemGraphicsView->getScene() + ); + this->byteItemGraphicsScene = this->differentialScene; + + QObject::connect( + this->byteItemGraphicsScene, + &ItemGraphicsScene::hoveredAddress, + this, + &DifferentialHexViewerWidget::onHoveredAddress + ); + + QObject::connect( + this->byteItemGraphicsScene, + &ItemGraphicsScene::selectionChanged, + this, + &DifferentialHexViewerWidget::onByteSelectionChanged + ); + + this->loadingHexViewerLabel->hide(); + this->byteItemGraphicsView->show(); + + emit this->ready(); + } + ); + + this->byteItemGraphicsView->initScene(); + } + + void DifferentialHexViewerWidget::setOther(DifferentialHexViewerWidget* other) { + assert(other->byteItemGraphicsView != nullptr); + assert(other->byteItemGraphicsScene != nullptr); + + this->other = other; + this->differentialView->setOther(this->other->differentialView); + + QObject::connect( + this->other, + &HexViewerWidget::settingsChanged, + this, + &DifferentialHexViewerWidget::onOtherSettingsChanged + ); + } + + void DifferentialHexViewerWidget::onOtherSettingsChanged(const HexViewerWidgetSettings& settings) { + if (!this->snapshotDiffSettings.syncHexViewerSettings || this->state.syncingSettings) { + return; + } + + this->state.syncingSettings = true; + + this->setStackMemoryGroupingEnabled(settings.groupStackMemory); + this->setFocusedMemoryHighlightingEnabled(settings.highlightFocusedMemory); + this->setHoveredRowAndColumnHighlightingEnabled(settings.highlightHoveredRowAndCol); + this->setDisplayAsciiEnabled(settings.displayAsciiValues); + this->setAnnotationsEnabled(settings.displayAnnotations); + + this->state.syncingSettings = false; + + this->differentialView->update(); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp new file mode 100644 index 00000000..2ce1016a --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp" + +#include "DifferentialHexViewerWidgetType.hpp" +#include "DifferentialHexViewerSharedState.hpp" +#include "DifferentialItemGraphicsView.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp" + +namespace Bloom::Widgets +{ + class DifferentialHexViewerWidget final: public HexViewerWidget + { + Q_OBJECT + + public: + DifferentialHexViewerWidget( + DifferentialHexViewerWidgetType type, + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + HexViewerWidgetSettings& settings, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + QWidget* parent + ); + + void init() override; + + void setOther(DifferentialHexViewerWidget* other); + + private: + DifferentialHexViewerWidgetType type; + DifferentialHexViewerSharedState& state; + + const SnapshotDiffSettings& snapshotDiffSettings; + DifferentialItemGraphicsView* differentialView = nullptr; + DifferentialItemGraphicsScene* differentialScene = nullptr; + + DifferentialHexViewerWidget* other = nullptr; + + void onOtherSettingsChanged(const HexViewerWidgetSettings& settings); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidgetType.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidgetType.hpp new file mode 100644 index 00000000..6bd83a36 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidgetType.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Bloom::Widgets +{ + enum class DifferentialHexViewerWidgetType: std::uint8_t + { + PRIMARY, + SECONDARY, + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp new file mode 100644 index 00000000..85c55dc3 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp @@ -0,0 +1,120 @@ +#include "DifferentialItemGraphicsScene.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp" + +namespace Bloom::Widgets +{ + DifferentialItemGraphicsScene::DifferentialItemGraphicsScene( + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + HexViewerWidgetSettings& settings, + QGraphicsView* parent + ) + : ItemGraphicsScene( + targetMemoryDescriptor, + data, + focusedMemoryRegions, + excludedMemoryRegions, + settings, + parent + ) + , diffHexViewerState(state) + , snapshotDiffSettings(snapshotDiffSettings) + {} + + void DifferentialItemGraphicsScene::setOther(DifferentialItemGraphicsScene* other) { + this->other = other; + + QObject::connect( + this->other, + &DifferentialItemGraphicsScene::hoveredAddress, + this, + &DifferentialItemGraphicsScene::onOtherHoveredAddress + ); + + QObject::connect( + this->other, + &DifferentialItemGraphicsScene::selectionChanged, + this, + &DifferentialItemGraphicsScene::onOtherSelectionChanged + ); + } + + ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportVerticalCenter() { + const auto scrollBarValue = this->getScrollbarValue(); + + const auto midPosition = static_cast( + scrollBarValue + ); + + const auto gridPointIndex = static_castgridPoints)::size_type>( + std::min( + static_cast( + std::floor(static_cast(midPosition) / static_cast(ItemGraphicsScene::GRID_SIZE)) + 1 + ), + static_cast(this->gridPoints.size() - 1) + ) + ); + + return static_cast(*(this->gridPoints[gridPointIndex])); + } + + void DifferentialItemGraphicsScene::onOtherHoveredAddress( + const std::optional& address + ) { + if (!this->snapshotDiffSettings.syncHexViewerHover || this->diffHexViewerState.syncingHover) { + return; + } + + if (!address.has_value()) { + if (this->state.hoveredByteItem != nullptr) { + this->onByteItemLeave(); + } + + return; + } + + auto& byteItem = this->topLevelGroup->byteItemsByAddress.find(*address)->second; + const auto itemPosition = byteItem.position().y(); + const auto scrollbarValue = this->getScrollbarValue(); + + if ( + byteItem.allocatedGraphicsItem == nullptr + || itemPosition < scrollbarValue + || itemPosition > (scrollbarValue + this->views().first()->viewport()->height()) + ) { + // The item isn't visible + return; + } + + this->diffHexViewerState.syncingHover = true; + this->onByteItemEnter(byteItem); + this->diffHexViewerState.syncingHover = false; + } + + void DifferentialItemGraphicsScene::onOtherSelectionChanged( + const std::unordered_map& selectedByteItemsByAddress + ) { + if (!this->snapshotDiffSettings.syncHexViewerSelection || this->diffHexViewerState.syncingSelection) { + return; + } + + this->diffHexViewerState.syncingSelection = true; + this->clearByteItemSelection(); + + for (const auto& [address, otherByteItem] : selectedByteItemsByAddress) { + auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address); + byteItem.selected = true; + this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem)); + } + + emit this->selectionChanged(this->selectedByteItemsByAddress); + this->diffHexViewerState.syncingSelection = false; + + this->update(); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp new file mode 100644 index 00000000..b172de41 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp" + +#include "DifferentialHexViewerSharedState.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp" + +namespace Bloom::Widgets +{ + class DifferentialItemGraphicsScene final: public ItemGraphicsScene + { + Q_OBJECT + + public: + DifferentialItemGraphicsScene( + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + HexViewerWidgetSettings& settings, + QGraphicsView* parent + ); + + void setOther(DifferentialItemGraphicsScene* other); + ByteItem* byteItemAtViewportVerticalCenter(); + + protected: + DifferentialHexViewerSharedState& diffHexViewerState; + const SnapshotDiffSettings& snapshotDiffSettings; + DifferentialItemGraphicsScene* other = nullptr; + + void onOtherHoveredAddress(const std::optional& address); + void onOtherSelectionChanged( + const std::unordered_map& selectedByteItemsByAddress + ); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp new file mode 100644 index 00000000..a3fc6dcc --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp @@ -0,0 +1,83 @@ +#include "DifferentialItemGraphicsView.hpp" + +namespace Bloom::Widgets +{ + DifferentialItemGraphicsView::DifferentialItemGraphicsView( + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + HexViewerWidgetSettings& settings, + QWidget* parent + ) + : ItemGraphicsView( + targetMemoryDescriptor, + data, + focusedMemoryRegions, + excludedMemoryRegions, + settings, + parent + ) + , state(state) + , snapshotDiffSettings(snapshotDiffSettings) + {} + + void DifferentialItemGraphicsView::initScene() { + this->differentialScene = new DifferentialItemGraphicsScene( + this->state, + this->snapshotDiffSettings, + this->targetMemoryDescriptor, + this->data, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this->settings, + this + ); + + this->scene = this->differentialScene; + this->setScene(this->scene); + + QObject::connect( + this->scene, + &ItemGraphicsScene::ready, + this, + [this] { + this->scene->setEnabled(this->isEnabled()); + emit this->sceneReady(); + } + ); + + this->scene->init(); + } + + void DifferentialItemGraphicsView::setOther(DifferentialItemGraphicsView* other) { + this->other = other; + this->differentialScene->setOther(this->other->differentialScene); + } + + void DifferentialItemGraphicsView::alignScroll( + Targets::TargetMemoryAddress otherByteItemAddress, + int otherByteItemYOffset + ) { + const auto byteItemPosition = this->differentialScene->getByteItemPositionByAddress(otherByteItemAddress); + this->verticalScrollBar()->setValue( + std::max(static_cast(byteItemPosition.y() - otherByteItemYOffset), 0) + ); + } + + void DifferentialItemGraphicsView::scrollContentsBy(int dx, int dy) { + ItemGraphicsView::scrollContentsBy(dx, dy); + + if (!this->snapshotDiffSettings.syncHexViewerScroll || this->state.syncingScroll) { + return; + } + + auto* byteItem = this->differentialScene->byteItemAtViewportVerticalCenter(); + + this->state.syncingScroll = true; + this->other->alignScroll(byteItem->startAddress, byteItem->position().y() - this->verticalScrollBar()->value()); + this->state.syncingScroll = false; + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.hpp new file mode 100644 index 00000000..60bc8313 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp" + +#include "DifferentialHexViewerSharedState.hpp" +#include "DifferentialItemGraphicsScene.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp" + +namespace Bloom::Widgets +{ + class DifferentialItemGraphicsView final: public ItemGraphicsView + { + Q_OBJECT + + public: + DifferentialItemGraphicsView( + DifferentialHexViewerSharedState& state, + const SnapshotDiffSettings& snapshotDiffSettings, + const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, + const std::optional& data, + const std::vector& focusedMemoryRegions, + const std::vector& excludedMemoryRegions, + HexViewerWidgetSettings& settings, + QWidget* parent + ); + + void initScene() override; + + void setOther(DifferentialItemGraphicsView* other); + void alignScroll(Targets::TargetMemoryAddress otherByteItemAddress, int otherByteItemYOffset); + + protected: + DifferentialHexViewerSharedState& state; + const SnapshotDiffSettings& snapshotDiffSettings; + + DifferentialItemGraphicsScene* differentialScene = nullptr; + DifferentialItemGraphicsView* other = nullptr; + + void scrollContentsBy(int dx, int dy) override; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg new file mode 100644 index 00000000..56729816 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg @@ -0,0 +1,209 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg new file mode 100644 index 00000000..e431075f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg @@ -0,0 +1,195 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg new file mode 100644 index 00000000..e7113947 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg @@ -0,0 +1,209 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg new file mode 100644 index 00000000..424aefcb --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg @@ -0,0 +1,181 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp new file mode 100644 index 00000000..aa2a3c6c --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp @@ -0,0 +1,241 @@ +#include "SnapshotDiff.hpp" + +#include +#include +#include + +#include "src/Insight/InsightWorker/Tasks/WriteTargetMemory.hpp" +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ConfirmationDialog.hpp" + +#include "src/Services/PathService.hpp" +#include "src/Exceptions/Exception.hpp" + +#include "src/Insight/InsightSignals.hpp" +#include "src/Logger/Logger.hpp" + +namespace Bloom::Widgets +{ + using Bloom::Exceptions::Exception; + + SnapshotDiff::SnapshotDiff( + MemorySnapshot& snapshotA, + MemorySnapshot& snapshotB, + const Targets::TargetMemoryDescriptor& memoryDescriptor, + QWidget* parent + ) + : QWidget(parent) + , memoryDescriptor(memoryDescriptor) + , hexViewerDataA(snapshotA.data) + , focusedRegionsA(snapshotA.focusedRegions) + , excludedRegionsA(snapshotA.excludedRegions) + , stackPointerA(snapshotA.stackPointer) + , hexViewerDataB(snapshotB.data) + , focusedRegionsB(snapshotB.focusedRegions) + , excludedRegionsB(snapshotB.excludedRegions) + , stackPointerB(snapshotB.stackPointer) + { + this->init(); + + this->setWindowTitle( + "Comparing snapshot \"" + snapshotA.name + "\" with snapshot \"" + snapshotB.name + "\"" + ); + + this->dataAPrimaryLabel->setText(snapshotA.name); + this->dataBPrimaryLabel->setText(snapshotB.name); + this->dataASecondaryLabel->setText(snapshotA.createdDate.toString("dd/MM/yyyy hh:mm")); + this->dataBSecondaryLabel->setText(snapshotB.createdDate.toString("dd/MM/yyyy hh:mm")); + } + + void SnapshotDiff::showEvent(QShowEvent* event) { + QWidget::showEvent(event); + } + + void SnapshotDiff::resizeEvent(QResizeEvent* event) { + this->container->setFixedSize(this->size()); + + QWidget::resizeEvent(event); + } + + void SnapshotDiff::init() { + this->setWindowFlag(Qt::Window); + this->setObjectName("snapshot-diff"); + + auto windowUiFile = QFile( + QString::fromStdString(Services::PathService::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui" + ) + ); + + auto stylesheetFile = QFile( + QString::fromStdString(Services::PathService::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss" + ) + ); + + if (!windowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open SnapshotDiff UI file"); + } + + if (!stylesheetFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open SnapshotDiff stylesheet file"); + } + + // Set ideal window size + this->setFixedSize(1600, 910); + this->setMinimumSize(700, 600); + this->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + + auto uiLoader = UiLoader(this); + const auto styleSheet = stylesheetFile.readAll(); + this->container = uiLoader.load(&windowUiFile, this); + this->container->setStyleSheet(styleSheet); + + auto* toolBar = this->container->findChild("tool-bar"); + + this->syncHexViewerSettingsButton = toolBar->findChild("sync-settings-btn"); + this->syncHexViewerScrollButton = toolBar->findChild("sync-scroll-btn"); + this->syncHexViewerHoverButton = toolBar->findChild("sync-hover-btn"); + this->syncHexViewerSelectionButton = toolBar->findChild("sync-selection-btn"); + + this->restoreBytesAction = new ContextMenuAction("Restore Selection", std::nullopt, this); + + this->dataAContainer = this->container->findChild("data-a-container"); + this->dataBContainer = this->container->findChild("data-b-container"); + + this->dataAPrimaryLabel = this->dataAContainer->findChild("primary-label"); + this->dataASecondaryLabel = this->dataAContainer->findChild("secondary-label"); + this->dataBPrimaryLabel = this->dataBContainer->findChild("primary-label"); + this->dataBSecondaryLabel = this->dataBContainer->findChild("secondary-label"); + + auto snapshotAContainerLayout = this->dataAContainer->findChild(); + auto snapshotBContainerLayout = this->dataBContainer->findChild(); + + this->hexViewerWidgetA = new DifferentialHexViewerWidget( + DifferentialHexViewerWidgetType::PRIMARY, + this->differentialHexViewerSharedState, + this->settings, + this->memoryDescriptor, + this->hexViewerDataA, + this->hexViewerWidgetSettingsA, + this->focusedRegionsA, + this->excludedRegionsA, + this + ); + + this->hexViewerWidgetB = new DifferentialHexViewerWidget( + DifferentialHexViewerWidgetType::SECONDARY, + this->differentialHexViewerSharedState, + this->settings, + this->memoryDescriptor, + this->hexViewerDataB, + this->hexViewerWidgetSettingsB, + this->focusedRegionsB, + this->excludedRegionsB, + this + ); + + this->hexViewerWidgetA->setObjectName("differential-hex-viewer-widget-a"); + this->hexViewerWidgetB->setObjectName("differential-hex-viewer-widget-b"); + + this->hexViewerWidgetA->setStyleSheet(this->hexViewerWidgetA->styleSheet() + styleSheet); + this->hexViewerWidgetB->setStyleSheet(this->hexViewerWidgetB->styleSheet() + styleSheet); + + snapshotAContainerLayout->addWidget(this->hexViewerWidgetA); + snapshotBContainerLayout->addWidget(this->hexViewerWidgetB); + + this->bottomBar = this->container->findChild("bottom-bar"); + this->bottomBarLayout = this->bottomBar->findChild(); + + this->setSyncHexViewerSettingsEnabled(this->settings.syncHexViewerSettings); + this->setSyncHexViewerScrollEnabled(this->settings.syncHexViewerScroll); + this->setSyncHexViewerHoverEnabled(this->settings.syncHexViewerHover); + this->setSyncHexViewerSelectionEnabled(this->settings.syncHexViewerSelection); + + QObject::connect( + this->syncHexViewerSettingsButton, + &QToolButton::clicked, + this, + [this] { + this->setSyncHexViewerSettingsEnabled(!this->settings.syncHexViewerSettings); + } + ); + + QObject::connect( + this->syncHexViewerScrollButton, + &QToolButton::clicked, + this, + [this] { + this->setSyncHexViewerScrollEnabled(!this->settings.syncHexViewerScroll); + } + ); + + QObject::connect( + this->syncHexViewerHoverButton, + &QToolButton::clicked, + this, + [this] { + this->setSyncHexViewerHoverEnabled(!this->settings.syncHexViewerHover); + } + ); + + QObject::connect( + this->syncHexViewerSelectionButton, + &QToolButton::clicked, + this, + [this] { + this->setSyncHexViewerSelectionEnabled(!this->settings.syncHexViewerSelection); + } + ); + + QObject::connect(this->hexViewerWidgetA, &HexViewerWidget::ready, this, &SnapshotDiff::onHexViewerAReady); + QObject::connect(this->hexViewerWidgetB, &HexViewerWidget::ready, this, &SnapshotDiff::onHexViewerBReady); + + this->hexViewerWidgetA->init(); + this->hexViewerWidgetB->init(); + + this->move(this->parentWidget()->window()->geometry().center() - this->rect().center()); + } + + void SnapshotDiff::onHexViewerAReady() { + this->hexViewerWidgetB->setOther(this->hexViewerWidgetA); + +// this->hexViewerWidgetA->addExternalContextMenuAction(this->restoreBytesAction); + if (this->stackPointerA.has_value()) { + this->hexViewerWidgetA->setStackPointer(*(this->stackPointerA)); + } + } + + void SnapshotDiff::onHexViewerBReady() { + this->hexViewerWidgetA->setOther(this->hexViewerWidgetB); + + if (this->stackPointerB.has_value()) { + this->hexViewerWidgetB->setStackPointer(*(this->stackPointerB)); + } + } + + void SnapshotDiff::setSyncHexViewerSettingsEnabled(bool enabled) { + this->settings.syncHexViewerSettings = enabled; + this->syncHexViewerSettingsButton->setChecked(this->settings.syncHexViewerSettings); + } + + void SnapshotDiff::setSyncHexViewerScrollEnabled(bool enabled) { + this->settings.syncHexViewerScroll = enabled; + this->syncHexViewerScrollButton->setChecked(this->settings.syncHexViewerScroll); + } + + void SnapshotDiff::setSyncHexViewerHoverEnabled(bool enabled) { + this->settings.syncHexViewerHover = enabled; + this->syncHexViewerHoverButton->setChecked(this->settings.syncHexViewerHover); + } + + void SnapshotDiff::setSyncHexViewerSelectionEnabled(bool enabled) { + this->settings.syncHexViewerSelection = enabled; + this->syncHexViewerSelectionButton->setChecked(this->settings.syncHexViewerSelection); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.hpp new file mode 100644 index 00000000..8c7a1fa2 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "SnapshotDiffSettings.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PushButton.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListView.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TaskProgressIndicator/TaskProgressIndicator.hpp" + +#include "src/Targets/TargetMemory.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp" +#include "DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.hpp" + +namespace Bloom::Widgets +{ + class SnapshotDiff: public QWidget + { + Q_OBJECT + + public: + SnapshotDiff( + MemorySnapshot& snapshotA, + MemorySnapshot& snapshotB, + const Targets::TargetMemoryDescriptor& memoryDescriptor, + QWidget* parent = nullptr + ); + + protected: + void showEvent(QShowEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + + private: + SnapshotDiffSettings settings; + + const Targets::TargetMemoryDescriptor& memoryDescriptor; + + QWidget* container = nullptr; + + SvgToolButton* syncHexViewerSettingsButton = nullptr; + SvgToolButton* syncHexViewerScrollButton = nullptr; + SvgToolButton* syncHexViewerHoverButton = nullptr; + SvgToolButton* syncHexViewerSelectionButton = nullptr; + + QWidget* dataAContainer = nullptr; + QWidget* dataBContainer = nullptr; + + Label* dataAPrimaryLabel = nullptr; + Label* dataBPrimaryLabel = nullptr; + + Label* dataASecondaryLabel = nullptr; + Label* dataBSecondaryLabel = nullptr; + + DifferentialHexViewerSharedState differentialHexViewerSharedState; + + std::optional hexViewerDataA; + std::vector focusedRegionsA; + std::vector excludedRegionsA; + std::optional stackPointerA; + DifferentialHexViewerWidget* hexViewerWidgetA = nullptr; + HexViewerWidgetSettings hexViewerWidgetSettingsA = HexViewerWidgetSettings(); + + std::optional hexViewerDataB; + std::vector focusedRegionsB; + std::vector excludedRegionsB; + std::optional stackPointerB; + DifferentialHexViewerWidget* hexViewerWidgetB = nullptr; + HexViewerWidgetSettings hexViewerWidgetSettingsB = HexViewerWidgetSettings(); + + ContextMenuAction* restoreBytesAction = nullptr; + + QWidget* bottomBar = nullptr; + QHBoxLayout* bottomBarLayout = nullptr; + + void init(); + + void onHexViewerAReady(); + void onHexViewerBReady(); + + void setSyncHexViewerSettingsEnabled(bool enabled); + void setSyncHexViewerScrollEnabled(bool enabled); + void setSyncHexViewerHoverEnabled(bool enabled); + void setSyncHexViewerSelectionEnabled(bool enabled); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp new file mode 100644 index 00000000..91f95892 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace Bloom::Widgets +{ + struct SnapshotDiffSettings + { + bool syncHexViewerSettings = true; + bool syncHexViewerScroll = true; + bool syncHexViewerHover = true; + bool syncHexViewerSelection = false; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss new file mode 100644 index 00000000..d7b44414 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss @@ -0,0 +1,72 @@ +#snapshot-diff, +#snapshot-diff #container { + background-color: #373835; + border: none; +} + +#snapshot-diff #tool-bar { + border-bottom: 1px solid #41423f; + border-left: none; +} + +#snapshot-diff #tool-bar #sync-label { + color: #8a8a8d; + font-size: 12px; +} + +#snapshot-diff #tool-bar QToolButton { + background-color: transparent; + border: none; + padding: 0; + qproperty-buttonWidth: 33; + qproperty-buttonHeight: 23; +} + +#snapshot-diff #tool-bar QToolButton:hover { + background-color: #41413b; + border: none; + padding: 0; +} + +#snapshot-diff #tool-bar QToolButton:checked { + background-color: #43433d; + border: none; +} + +#snapshot-diff #tool-bar #separator { + background-color: transparent; + border-right: 1px solid #41423f; + padding: 0; + min-width: 1px; + max-width: 1px; +} + +#snapshot-diff #data-details-container { + border-bottom: 1px solid #41423f; + color: #8a8a8d; +} + +#snapshot-diff #data-details-container #primary-label, +#snapshot-diff #data-details-container #secondary-label { + color: #8a8a8d; +} + +#snapshot-diff #snapshot-a-container, +#snapshot-diff #snapshot-a-container #data-details-container { + border-right: 1px solid #41423f; +} + +#snapshot-diff #differential-hex-viewer-widget-a #hex-viewer-container QScrollBar { + margin: 0; + padding: 1px 3px 0 3px; + width: 15px; + border-right: 1px solid #41423f; +} + +#snapshot-diff #differential-hex-viewer-widget-a #hex-viewer-container #graphics-view { + border-right: 1px solid #41423f; +} + +#snapshot-diff #bottom-bar { + border-top: 1px solid #41423f; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui new file mode 100644 index 00000000..b0a8c5fa --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui @@ -0,0 +1,309 @@ + + + + + + 0 + + + 0 + + + + + 32 + + + 32 + + + + + + + 3 + + + 0 + + + + + + 6 + + + + QSizePolicy::Fixed + + + + + + + Synchronisation: + + + + + + + + 3 + + + + QSizePolicy::Fixed + + + + + + + true + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg + + + Sync settings + + + + + + + true + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg + + + Sync scrolling + + + + + + + true + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg + + + Sync hover + + + + + + + true + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg + + + Sync selection + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + + + 28 + + + 28 + + + + + + + 3 + + + 0 + + + + + + 6 + + + + QSizePolicy::Fixed + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + 6 + + + + QSizePolicy::Fixed + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + 28 + + + 28 + + + + + + + 3 + + + 0 + + + + + + 6 + + + + QSizePolicy::Fixed + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + 6 + + + + QSizePolicy::Fixed + + + + + + + + + + + + + + + + 28 + + + 28 + + + + + + + 3 + + + 0 + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp index d3d39f0c..d4428bf4 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp @@ -100,6 +100,13 @@ namespace Bloom::Widgets } ); + QObject::connect( + this->snapshotListScene, + &ListScene::selectionChanged, + this, + &SnapshotManager::onSnapshotItemSelectionChanged + ); + QObject::connect( this->snapshotListScene, &ListScene::itemDoubleClicked, @@ -125,9 +132,29 @@ namespace Bloom::Widgets &QAction::triggered, this, [this] { - if (this->contextMenuSnapshotItem != nullptr) { - this->openSnapshotViewer(this->contextMenuSnapshotItem->memorySnapshot.id); + if (this->selectedSnapshotItems.size() != 1) { + return; } + + this->openSnapshotViewer(this->selectedSnapshotItems.front()->memorySnapshot.id); + } + ); + + QObject::connect( + this->openSnapshotDiffAction, + &QAction::triggered, + this, + [this] { + if (this->selectedSnapshotItems.size() != 2) { + return; + } + + const auto& first = this->selectedSnapshotItems.front()->memorySnapshot; + const auto& second = this->selectedSnapshotItems.back()->memorySnapshot; + this->openSnapshotDiff( + first.createdDate < second.createdDate ? first.id : second.id, + first.createdDate < second.createdDate ? second.id : first.id + ); } ); @@ -136,9 +163,11 @@ namespace Bloom::Widgets &QAction::triggered, this, [this] { - if (this->contextMenuSnapshotItem != nullptr) { - this->deleteSnapshot(this->contextMenuSnapshotItem->memorySnapshot.id, true); + if (this->selectedSnapshotItems.size() != 1) { + return; } + + this->deleteSnapshot(this->selectedSnapshotItems.front()->memorySnapshot.id, true); } ); @@ -147,9 +176,11 @@ namespace Bloom::Widgets &QAction::triggered, this, [this] { - if (this->contextMenuSnapshotItem != nullptr) { - this->restoreSnapshot(this->contextMenuSnapshotItem->memorySnapshot.id, true); + if (this->selectedSnapshotItems.size() != 1) { + return; } + + this->restoreSnapshot(this->selectedSnapshotItems.front()->memorySnapshot.id, true); } ); @@ -247,12 +278,17 @@ namespace Bloom::Widgets this->snapshotListScene->addListItem(snapshotItem); } - void SnapshotManager::onSnapshotItemSelected(MemorySnapshotItem* item) { - if (this->selectedItem != nullptr && this->selectedItem != item) { - this->selectedItem->setSelected(false); - } + void SnapshotManager::onSnapshotItemSelectionChanged(const std::list& selectedItems) { + this->selectedSnapshotItems.clear(); - this->selectedItem = item; + std::transform( + selectedItems.begin(), + selectedItems.end(), + std::inserter(this->selectedSnapshotItems, this->selectedSnapshotItems.end()), + [] (ListItem* item) { + return dynamic_cast(item); + } + ); } void SnapshotManager::openSnapshotViewer(const QString& snapshotId) { @@ -276,6 +312,34 @@ namespace Bloom::Widgets snapshotViewer->activateWindow(); } + void SnapshotManager::openSnapshotDiff(const QString& snapshotIdA, const QString& snapshotIdB) { + const auto diffKey = snapshotIdA + snapshotIdB; + auto snapshotDiffIt = this->snapshotDiffs.find(diffKey); + + if (snapshotDiffIt == this->snapshotDiffs.end()) { + const auto& snapshotItA = this->snapshotsById.find(snapshotIdA); + const auto& snapshotItB = this->snapshotsById.find(snapshotIdB); + + if (snapshotItA == this->snapshotsById.end() || snapshotItB == this->snapshotsById.end()) { + return; + } + + snapshotDiffIt = this->snapshotDiffs.insert( + diffKey, + new SnapshotDiff( + snapshotItA.value(), + snapshotItB.value(), + this->memoryDescriptor, + this + ) + ); + } + + auto* snapshotDiff = snapshotDiffIt.value(); + snapshotDiff->show(); + snapshotDiff->activateWindow(); + } + void SnapshotManager::deleteSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) { const auto& snapshotIt = this->snapshotsById.find(snapshotId); @@ -460,17 +524,22 @@ namespace Bloom::Widgets return; } - this->contextMenuSnapshotItem = snapshotItem; - auto* menu = new QMenu(this); + menu->addAction(this->openSnapshotViewerAction); menu->addAction(this->deleteSnapshotAction); - menu->addSeparator(); - menu->addAction(this->restoreSnapshotAction); - this->restoreSnapshotAction->setEnabled(this->targetState == Targets::TargetState::STOPPED); + this->openSnapshotViewerAction->setEnabled(this->selectedSnapshotItems.size() == 1); + this->deleteSnapshotAction->setEnabled(this->selectedSnapshotItems.size() == 1); + this->restoreSnapshotAction->setEnabled( + this->selectedSnapshotItems.size() == 1 && this->targetState == Targets::TargetState::STOPPED + ); + + if (this->selectedSnapshotItems.size() == 2) { + menu->addAction(this->openSnapshotDiffAction); + } menu->exec(sourcePosition); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp index 81c5027b..19442ca9 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.hpp @@ -23,6 +23,7 @@ #include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp" #include "MemorySnapshotItem.hpp" #include "SnapshotViewer/SnapshotViewer.hpp" +#include "SnapshotDiff/SnapshotDiff.hpp" namespace Bloom::Widgets { @@ -65,6 +66,7 @@ namespace Bloom::Widgets QMap snapshotsById; QMap snapshotItemsById; QMap snapshotViewersById; + QMap snapshotDiffs; QWidget* container = nullptr; QWidget* toolBar = nullptr; @@ -75,9 +77,10 @@ namespace Bloom::Widgets ListView* snapshotListView = nullptr; ListScene* snapshotListScene = nullptr; - MemorySnapshotItem* contextMenuSnapshotItem = nullptr; + std::list selectedSnapshotItems; QAction* openSnapshotViewerAction = new QAction("Open", this); + QAction* openSnapshotDiffAction = new QAction("Compare Selection", this); QAction* deleteSnapshotAction = new QAction("Delete", this); QAction* restoreSnapshotAction = new QAction("Restore", this); @@ -89,8 +92,9 @@ namespace Bloom::Widgets ); void addSnapshot(MemorySnapshot&& snapshotTmp); - void onSnapshotItemSelected(MemorySnapshotItem* item); + void onSnapshotItemSelectionChanged(const std::list& selectedItems); void openSnapshotViewer(const QString& snapshotId); + void openSnapshotDiff(const QString& snapshotIdA, const QString& snapshotIdB); void deleteSnapshot(const QString& snapshotId, bool confirmationPromptEnabled); void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled); void onSnapshotItemDoubleClick(MemorySnapshotItem* item);