Refactored hex viewer widget to use QGraphicsView items as opposed to widgets, for byte items in the hex viewer.

This requirement became apparent when testing the initial approach with large memory sizes - the GUI became unresponsive due to the number of widgets being constructed.
This was along side other performance issues that arose from the large number of widgets
This commit is contained in:
Nav
2021-10-24 20:40:53 +01:00
parent 8d82af3689
commit e2c4dcb97f
13 changed files with 364 additions and 312 deletions

View File

@@ -177,8 +177,9 @@ add_executable(Bloom
# Target memory inspection pane # Target memory inspection pane
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.cpp
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidgetContainer.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemContainerGraphicsView.cpp
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp
) )
set_target_properties(Bloom PROPERTIES OUTPUT_NAME bloom) set_target_properties(Bloom PROPERTIES OUTPUT_NAME bloom)

View File

@@ -179,6 +179,7 @@ QScrollBar:vertical {
QScrollBar:handle:vertical { QScrollBar:handle:vertical {
border: none; border: none;
background-color: rgba(69, 69, 66, 0.6); background-color: rgba(69, 69, 66, 0.6);
min-height: 30px;
} }
QScrollBar:handle:vertical:hover { QScrollBar:handle:vertical:hover {
@@ -379,26 +380,29 @@ QScrollBar::sub-line:vertical {
} }
#hex-viewer-container QScrollArea, #hex-viewer-container QScrollArea,
#hex-viewer-container #byte-widget-scroll-area-container { #hex-viewer-container #graphics-view-container,
#hex-viewer-container #graphics-view-container #graphics-view {
background-color: #323330; background-color: #323330;
border: none; border: none;
font-size: 11px;
}
#hex-viewer-container #graphics-view-container {
background-color: #50504b;
border: none;
} }
#hex-viewer-container #byte-widget-scroll-area-container #address-container { #hex-viewer-container #graphics-view-container #address-container {
background-color: #353633; background-color: #353633;
border-right: 1px solid #41423f; border-right: 1px solid #41423f;
} }
#hex-viewer-container #byte-widget-scroll-area-container #address-container QLabel { #hex-viewer-container #graphics-view-container #address-container QLabel {
background-color: transparent; background-color: transparent;
font-size: 12px; font-size: 12px;
color: rgba(175, 177, 179, 0.72); color: rgba(175, 177, 179, 0.72);
} }
#hex-viewer-container #byte-widget-scroll-area-container #address-container QLabel:disabled { #hex-viewer-container #graphics-view-container #address-container QLabel:disabled {
color: rgba(175, 177, 179, 0.3); color: rgba(175, 177, 179, 0.3);
} }
#hex-viewer-container #byte {
font-size: 11px;
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <QGraphicsItem>
#include <cstdint>
#include <QEvent>
#include <QGraphicsScene>
#include <optional>
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp"
namespace Bloom::Widgets
{
class ByteAddressContainer: public QGraphicsItem
{
public:
static constexpr int WIDTH = 85;
static constexpr int RIGHT_MARGIN = 5;
static constexpr int BOTTOM_MARGIN = 5;
std::size_t byteIndex;
unsigned char value = 0x00;
std::uint32_t address = 0x00;
QString addressHex;
QString relativeAddressHex;
bool valueInitialised = false;
std::size_t currentRowIndex = 0;
std::size_t currentColumnIndex = 0;
ByteAddressContainer();
[[nodiscard]] QRectF boundingRect() const override {
return QRectF(
0,
0,
ByteAddressContainer::WIDTH,
this->scene()->height()
);
}
private:
};
}

View File

@@ -0,0 +1,68 @@
#include "ByteItem.hpp"
#include <QPainter>
#include <QStyle>
#include "src/Logger/Logger.hpp"
using namespace Bloom::Widgets;
ByteItem::ByteItem(
std::size_t byteIndex,
std::uint32_t address,
std::optional<ByteItem*>& hoveredByteItem
): QGraphicsItem(nullptr), byteIndex(byteIndex), address(address), hoveredByteItem(hoveredByteItem) {
this->setAcceptHoverEvents(true);
this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(8, '0').toUpper();
this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(8, '0').toUpper();
this->setSelected(false);
}
void ByteItem::setValue(unsigned char value) {
this->valueChanged = this->valueInitialised && this->value != value;
this->value = value;
this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper();
this->asciiValue = (this->value >= 32 && this->value <= 126)
? std::optional(QString(QChar(this->value))) : std::nullopt;
this->valueInitialised = true;
}
void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
painter->setPen(Qt::PenStyle::NoPen);
static const auto widgetRect = this->boundingRect();
if (this->hoveredByteItem.has_value()) {
const auto hoveredWidget = this->hoveredByteItem.value();
if (hoveredWidget->currentColumnIndex == this->currentColumnIndex
|| hoveredWidget->currentRowIndex == this->currentRowIndex
){
painter->setBrush(QColor(0x8E, 0x8B, 0x83, hoveredWidget == this ? 70 : 30));
painter->drawRect(widgetRect);
}
}
auto textColor = QColor(this->valueChanged ? "#547fba" : "#afb1b3");
if (this->valueInitialised) {
if (!this->isEnabled()) {
textColor.setAlpha(100);
}
painter->setPen(textColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue);
} else {
textColor.setAlpha(100);
painter->setPen(textColor);
static const auto placeholderString = QString("??");
painter->drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
}

View File

@@ -2,16 +2,15 @@
#include <cstdint> #include <cstdint>
#include <QEvent> #include <QEvent>
#include <QGraphicsItem>
#include <optional> #include <optional>
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp"
namespace Bloom::Widgets namespace Bloom::Widgets
{ {
class ByteWidget: public ClickableWidget class ByteItem: public QGraphicsItem
{ {
Q_OBJECT
public: public:
static constexpr int WIDTH = 25; static constexpr int WIDTH = 25;
static constexpr int HEIGHT = 20; static constexpr int HEIGHT = 20;
@@ -29,35 +28,34 @@ namespace Bloom::Widgets
std::size_t currentRowIndex = 0; std::size_t currentRowIndex = 0;
std::size_t currentColumnIndex = 0; std::size_t currentColumnIndex = 0;
ByteWidget( ByteItem(
std::size_t byteNumber, std::size_t byteNumber,
std::uint32_t address, std::uint32_t address,
std::optional<ByteWidget*>& hoveredByteWidget, std::optional<ByteItem*>& hoveredByteItem
QWidget* parent
); );
void setValue(unsigned char value); void setValue(unsigned char value);
public slots: [[nodiscard]] QRectF boundingRect() const override {
void setSelected(bool selected); return QRectF(
0,
0,
ByteItem::WIDTH,
ByteItem::HEIGHT
);
}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
signals: signals:
void selected(Bloom::Widgets::ByteWidget*); void selected(Bloom::Widgets::ByteItem*);
void enter(Bloom::Widgets::ByteWidget*); void enter(Bloom::Widgets::ByteItem*);
void leave(Bloom::Widgets::ByteWidget*); void leave(Bloom::Widgets::ByteItem*);
protected:
virtual void postSetSelected(bool selected) {};
bool event(QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void drawWidget(QPainter& painter);
private: private:
bool hoverActive = false;
bool valueChanged = false; bool valueChanged = false;
QString hexValue; QString hexValue;
std::optional<QString> asciiValue; std::optional<QString> asciiValue;
std::optional<ByteWidget*>& hoveredByteWidget; std::optional<ByteItem*>& hoveredByteItem;
}; };
} }

View File

@@ -0,0 +1,41 @@
#include "ByteItemContainerGraphicsView.hpp"
#include <QVBoxLayout>
#include <QTableWidget>
#include <QScrollBar>
#include <QPainter>
#include <cmath>
#include "src/Logger/Logger.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetMemoryDescriptor;
ByteItemContainerGraphicsView::ByteItemContainerGraphicsView(
const TargetMemoryDescriptor& targetMemoryDescriptor,
InsightWorker& insightWorker,
QLabel* hoveredAddressLabel,
QWidget* parent
): QGraphicsView(parent) {
this->setObjectName("graphics-view");
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->scene = new ByteItemGraphicsScene(
targetMemoryDescriptor,
insightWorker,
hoveredAddressLabel,
this
);
this->setScene(this->scene);
}
void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) {
Logger::warning("Resizing");
this->scene->adjustByteWidgets();
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <QGraphicsView>
#include <QWidget>
#include <QLabel>
#include <QToolButton>
#include <QVBoxLayout>
#include <map>
#include <vector>
#include <QSize>
#include <QString>
#include <QEvent>
#include <QGraphicsSceneMouseEvent>
#include <optional>
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetState.hpp"
#include "src/Insight/InsightWorker/InsightWorker.hpp"
#include "ByteItemGraphicsScene.hpp"
namespace Bloom::Widgets
{
class ByteItemContainerGraphicsView: public QGraphicsView
{
Q_OBJECT
public:
ByteItemContainerGraphicsView(
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
InsightWorker& insightWorker,
QLabel* hoveredAddressLabel,
QWidget* parent
);
[[nodiscard]] ByteItemGraphicsScene* getScene() const {
return this->scene;
}
protected:
void resizeEvent(QResizeEvent* event) override;
private:
ByteItemGraphicsScene* scene = nullptr;
};
}

View File

@@ -1,4 +1,4 @@
#include "ByteWidgetContainer.hpp" #include "ByteItemGraphicsScene.hpp"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QTableWidget> #include <QTableWidget>
@@ -6,30 +6,24 @@
#include <QPainter> #include <QPainter>
#include <cmath> #include <cmath>
#include "src/Logger/Logger.hpp"
using namespace Bloom::Widgets; using namespace Bloom::Widgets;
using namespace Bloom::Exceptions; using namespace Bloom::Exceptions;
using Bloom::Targets::TargetMemoryDescriptor; using Bloom::Targets::TargetMemoryDescriptor;
ByteWidgetContainer::ByteWidgetContainer( ByteItemGraphicsScene::ByteItemGraphicsScene(
const TargetMemoryDescriptor& targetMemoryDescriptor, const TargetMemoryDescriptor& targetMemoryDescriptor,
InsightWorker& insightWorker, InsightWorker& insightWorker,
QLabel* hoveredAddressLabel, QLabel* hoveredAddressLabel,
QWidget* parent QWidget* parent
): QWidget(parent), ): QGraphicsScene(parent),
targetMemoryDescriptor(targetMemoryDescriptor), targetMemoryDescriptor(targetMemoryDescriptor),
insightWorker(insightWorker), insightWorker(insightWorker),
hoveredAddressLabel(hoveredAddressLabel), hoveredAddressLabel(hoveredAddressLabel),
parent(parent) { parent(parent) {
this->setObjectName("byte-widget-container"); this->setObjectName("byte-widget-container");
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored);
this->setContentsMargins(
10,
10,
10,
10
);
/* /*
* Construct ByteWidget objects * Construct ByteWidget objects
@@ -38,49 +32,45 @@ parent(parent) {
*/ */
const auto memorySize = this->targetMemoryDescriptor.size(); const auto memorySize = this->targetMemoryDescriptor.size();
const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress; const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress;
Logger::error("Constructing bytes begin");
for (std::size_t i = 0; i < memorySize; i++) { for (std::size_t i = 0; i < memorySize; i++) {
const auto address = static_cast<std::uint32_t>(startAddress + i); const auto address = static_cast<std::uint32_t>(startAddress + i);
auto byteWidget = new ByteWidget(i, address, this->hoveredByteWidget, this); auto byteWidget = new ByteItem(i, address, this->hoveredByteWidget);
this->byteWidgetsByAddress.insert(std::pair( this->byteWidgetsByAddress.insert(std::pair(
address, address,
byteWidget byteWidget
)); ));
QObject::connect(byteWidget, &ByteWidget::enter, this, &ByteWidgetContainer::onByteWidgetEnter); this->addItem(byteWidget);
QObject::connect(byteWidget, &ByteWidget::leave, this, &ByteWidgetContainer::onByteWidgetLeave);
} }
Logger::error("Constructing bytes end");
this->adjustByteWidgets();
QObject::connect( QObject::connect(
&insightWorker, &insightWorker,
&InsightWorker::targetStateUpdated, &InsightWorker::targetStateUpdated,
this, this,
&ByteWidgetContainer::onTargetStateChanged &ByteItemGraphicsScene::onTargetStateChanged
); );
this->show();
} }
void ByteWidgetContainer::updateValues(const Targets::TargetMemoryBuffer& buffer) { void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) {
for (auto& [address, byteWidget] : this->byteWidgetsByAddress) { for (auto& [address, byteWidget] : this->byteWidgetsByAddress) {
byteWidget->setValue(buffer.at(byteWidget->byteIndex)); byteWidget->setValue(buffer.at(byteWidget->byteIndex));
byteWidget->update(); byteWidget->update();
} }
} }
void ByteWidgetContainer::resizeEvent(QResizeEvent* event) { void ByteItemGraphicsScene::adjustByteWidgets() {
this->adjustByteWidgets(); std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
} std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
void ByteWidgetContainer::adjustByteWidgets() { const auto margins = QMargins(10, 10, 10, 10);
std::map<std::size_t, std::vector<ByteWidget*>> byteWidgetsByRowIndex; const auto width = std::max(600, static_cast<int>(this->parent->width()));
std::map<std::size_t, std::vector<ByteWidget*>> byteWidgetsByColumnIndex;
const auto margins = this->contentsMargins(); constexpr auto byteWidgetWidth = ByteItem::WIDTH + ByteItem::RIGHT_MARGIN;
const auto width = this->width(); constexpr auto byteWidgetHeight = ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN;
constexpr auto byteWidgetWidth = ByteWidget::WIDTH + ByteWidget::RIGHT_MARGIN;
constexpr auto byteWidgetHeight = ByteWidget::HEIGHT + ByteWidget::BOTTOM_MARGIN;
const auto rowCapacity = static_cast<std::size_t>( const auto rowCapacity = static_cast<std::size_t>(
std::floor((width - margins.left() - margins.right()) / byteWidgetWidth) std::floor((width - margins.left() - margins.right()) / byteWidgetWidth)
); );
@@ -97,12 +87,10 @@ void ByteWidgetContainer::adjustByteWidgets() {
- (std::floor(byteWidget->byteIndex / rowCapacity) * static_cast<double>(rowCapacity)) - (std::floor(byteWidget->byteIndex / rowCapacity) * static_cast<double>(rowCapacity))
); );
byteWidget->setGeometry(QRect( byteWidget->setPos(
static_cast<int>(columnIndex * byteWidgetWidth + static_cast<std::size_t>(margins.left())), static_cast<int>(columnIndex * byteWidgetWidth + static_cast<std::size_t>(margins.left())),
static_cast<int>(rowIndex * byteWidgetHeight + static_cast<std::size_t>(margins.top())), static_cast<int>(rowIndex * byteWidgetHeight + static_cast<std::size_t>(margins.top()))
ByteWidget::WIDTH, );
ByteWidget::HEIGHT
));
byteWidget->currentRowIndex = static_cast<std::size_t>(rowIndex); byteWidget->currentRowIndex = static_cast<std::size_t>(rowIndex);
byteWidget->currentColumnIndex = static_cast<std::size_t>(columnIndex); byteWidget->currentColumnIndex = static_cast<std::size_t>(columnIndex);
@@ -114,8 +102,9 @@ void ByteWidgetContainer::adjustByteWidgets() {
} }
const auto minHeight = (rowCount * byteWidgetHeight) + margins.top() + margins.bottom(); const auto minHeight = (rowCount * byteWidgetHeight) + margins.top() + margins.bottom();
this->setMinimumHeight(minHeight); // this->setMinimumHeight(minHeight);
this->parent->setMinimumHeight(minHeight); this->setSceneRect(0, 0, width, minHeight);
// this->parent->setMinimumHeight(minHeight);
this->byteWidgetsByRowIndex = std::move(byteWidgetsByRowIndex); this->byteWidgetsByRowIndex = std::move(byteWidgetsByRowIndex);
this->byteWidgetsByColumnIndex = std::move(byteWidgetsByColumnIndex); this->byteWidgetsByColumnIndex = std::move(byteWidgetsByColumnIndex);
@@ -123,12 +112,22 @@ void ByteWidgetContainer::adjustByteWidgets() {
emit this->byteWidgetsAdjusted(); emit this->byteWidgetsAdjusted();
} }
void ByteWidgetContainer::onTargetStateChanged(Targets::TargetState newState) { void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState; using Targets::TargetState;
this->targetState = newState; this->targetState = newState;
} }
void ByteWidgetContainer::onByteWidgetEnter(ByteWidget* widget) { void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) {
if (this->hoveredByteWidget.has_value()) {
if (this->hoveredByteWidget.value() == widget) {
// This byte item is already marked as hovered
return;
} else {
this->onByteWidgetLeave();
}
}
this->hoveredByteWidget = widget; this->hoveredByteWidget = widget;
this->hoveredAddressLabel->setText( this->hoveredAddressLabel->setText(
@@ -146,18 +145,33 @@ void ByteWidgetContainer::onByteWidgetEnter(ByteWidget* widget) {
} }
} }
void ByteWidgetContainer::onByteWidgetLeave(ByteWidget* widget) { void ByteItemGraphicsScene::onByteWidgetLeave() {
const auto byteItem = this->hoveredByteWidget.value();
this->hoveredByteWidget = std::nullopt; this->hoveredByteWidget = std::nullopt;
this->hoveredAddressLabel->setText("Relative Address (Absolute Address):"); this->hoveredAddressLabel->setText("Relative Address (Absolute Address):");
if (!this->byteWidgetsByRowIndex.empty()) { if (!this->byteWidgetsByRowIndex.empty()) {
for (auto& byteWidget : this->byteWidgetsByColumnIndex.at(widget->currentColumnIndex)) { for (auto& byteWidget : this->byteWidgetsByColumnIndex.at(byteItem->currentColumnIndex)) {
byteWidget->update(); byteWidget->update();
} }
for (auto& byteWidget : this->byteWidgetsByRowIndex.at(widget->currentRowIndex)) { for (auto& byteWidget : this->byteWidgetsByRowIndex.at(byteItem->currentRowIndex)) {
byteWidget->update(); byteWidget->update();
} }
} }
} }
void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
auto hoveredItems = this->items(mouseEvent->scenePos());
if (!hoveredItems.empty()) {
auto hoveredByteWidget = dynamic_cast<ByteItem*>(hoveredItems.at(0));
if (hoveredByteWidget != nullptr) {
this->onByteWidgetEnter(hoveredByteWidget);
}
} else if (this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QGraphicsScene>
#include <QWidget> #include <QWidget>
#include <QLabel> #include <QLabel>
#include <QToolButton> #include <QToolButton>
@@ -8,7 +9,7 @@
#include <vector> #include <vector>
#include <QSize> #include <QSize>
#include <QString> #include <QString>
#include <QEvent> #include <QGraphicsSceneMouseEvent>
#include <optional> #include <optional>
#include "src/Targets/TargetMemory.hpp" #include "src/Targets/TargetMemory.hpp"
@@ -16,22 +17,22 @@
#include "src/Insight/InsightWorker/InsightWorker.hpp" #include "src/Insight/InsightWorker/InsightWorker.hpp"
#include "ByteWidget.hpp" #include "ByteItem.hpp"
namespace Bloom::Widgets namespace Bloom::Widgets
{ {
class ByteWidgetContainer: public QWidget class ByteItemGraphicsScene: public QGraphicsScene
{ {
Q_OBJECT Q_OBJECT
public: public:
std::optional<ByteWidget*> hoveredByteWidget; std::optional<ByteItem*> hoveredByteWidget;
std::map<std::uint32_t, ByteWidget*> byteWidgetsByAddress; std::map<std::uint32_t, ByteItem*> byteWidgetsByAddress;
std::map<std::size_t, std::vector<ByteWidget*>> byteWidgetsByRowIndex; std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
std::map<std::size_t, std::vector<ByteWidget*>> byteWidgetsByColumnIndex; std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
ByteWidgetContainer( ByteItemGraphicsScene(
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor, const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
InsightWorker& insightWorker, InsightWorker& insightWorker,
QLabel* hoveredAddressLabel, QLabel* hoveredAddressLabel,
@@ -40,11 +41,13 @@ namespace Bloom::Widgets
void updateValues(const Targets::TargetMemoryBuffer& buffer); void updateValues(const Targets::TargetMemoryBuffer& buffer);
void adjustByteWidgets();
signals: signals:
void byteWidgetsAdjusted(); void byteWidgetsAdjusted();
protected: protected:
void resizeEvent(QResizeEvent* event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
private: private:
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor; const Targets::TargetMemoryDescriptor& targetMemoryDescriptor;
@@ -54,11 +57,9 @@ namespace Bloom::Widgets
QWidget* parent = nullptr; QWidget* parent = nullptr;
QLabel* hoveredAddressLabel = nullptr; QLabel* hoveredAddressLabel = nullptr;
void adjustByteWidgets();
private slots: private slots:
void onTargetStateChanged(Targets::TargetState newState); void onTargetStateChanged(Targets::TargetState newState);
void onByteWidgetEnter(Bloom::Widgets::ByteWidget* widget); void onByteWidgetEnter(Bloom::Widgets::ByteItem* widget);
void onByteWidgetLeave(Bloom::Widgets::ByteWidget* widget); void onByteWidgetLeave();
}; };
} }

View File

@@ -1,114 +0,0 @@
#include "ByteWidget.hpp"
#include <QPainter>
#include <QStyle>
using namespace Bloom::Widgets;
ByteWidget::ByteWidget(
std::size_t byteIndex,
std::uint32_t address,
std::optional<ByteWidget*>& hoveredByteWidget,
QWidget* parent
): ClickableWidget(parent), byteIndex(byteIndex), address(address), hoveredByteWidget(hoveredByteWidget) {
this->setObjectName("byte");
auto onClick = [this] {
this->setSelected(true);
};
this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(8, '0').toUpper();
this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(8, '0').toUpper();
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
void ByteWidget::setValue(unsigned char value) {
this->valueChanged = this->valueInitialised && this->value != value;
this->value = value;
this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper();
this->asciiValue = (this->value >= 32 && this->value <= 126)
? std::optional(QString(QChar(this->value))) : std::nullopt;
this->valueInitialised = true;
}
void ByteWidget::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
}
this->postSetSelected(selected);
}
bool ByteWidget::event(QEvent* event) {
if (this->isEnabled()) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
emit this->enter(this);
this->update();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
emit this->leave(this);
this->update();
break;
}
default: {
break;
}
}
}
return QWidget::event(event);
}
void ByteWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void ByteWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
painter.setPen(Qt::PenStyle::NoPen);
static const auto widgetRect = QRect(0, 0, ByteWidget::WIDTH, ByteWidget::HEIGHT);
if (this->hoveredByteWidget.has_value()
&& (
this->hoveredByteWidget.value()->currentColumnIndex == this->currentColumnIndex
|| this->hoveredByteWidget.value()->currentRowIndex == this->currentRowIndex
)
) {
painter.setBrush(QColor(0x8E, 0x8B, 0x83, this->hoverActive ? 70 : 30));
painter.drawRect(widgetRect);
}
auto textColor = QColor(this->valueChanged ? "#547fba" : "#afb1b3");
if (this->valueInitialised) {
if (!this->isEnabled()) {
textColor.setAlpha(100);
}
painter.setPen(textColor);
painter.drawText(widgetRect, Qt::AlignCenter, this->hexValue);
} else {
textColor.setAlpha(100);
painter.setPen(textColor);
static const auto placeholderString = QString("??");
painter.drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
}

View File

@@ -1,7 +1,6 @@
#include "HexViewerWidget.hpp" #include "HexViewerWidget.hpp"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QTableWidget>
#include <QScrollBar> #include <QScrollBar>
#include <QScrollArea> #include <QScrollArea>
#include <QPainter> #include <QPainter>
@@ -11,6 +10,7 @@
#include "src/Helpers/Paths.hpp" #include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp" #include "src/Exceptions/Exception.hpp"
#include "src/Logger/Logger.hpp"
using namespace Bloom::Widgets; using namespace Bloom::Widgets;
using namespace Bloom::Exceptions; using namespace Bloom::Exceptions;
@@ -53,35 +53,19 @@ HexViewerWidget::HexViewerWidget(
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label"); this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
this->byteWidgetScrollArea = this->container->findChild<QScrollArea*>("byte-widget-scroll-area"); auto byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("graphics-view-container");
auto byteWidgetScrollAreaWidgetContainer = this->byteWidgetScrollArea->findChild<QWidget*>( auto byteItemGraphicsViewLayout = byteItemGraphicsViewContainer->findChild<QVBoxLayout*>(
"byte-widget-scroll-area-container" "byte-item-container-layout"
); );
auto byteWidgetScrollAreaHorizontalLayout = byteWidgetScrollAreaWidgetContainer->findChild<QHBoxLayout*>( this->byteItemGraphicsView = new ByteItemContainerGraphicsView(
"byte-widget-scroll-area-horizontal-layout"
);
this->byteWidgetContainer = new ByteWidgetContainer(
targetMemoryDescriptor, targetMemoryDescriptor,
insightWorker, insightWorker,
this->hoveredAddressLabel, this->hoveredAddressLabel,
byteWidgetScrollAreaHorizontalLayout->parentWidget() byteItemGraphicsViewContainer
); );
this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene();
byteItemGraphicsViewLayout->insertWidget(0, this->byteItemGraphicsView);
byteWidgetScrollAreaHorizontalLayout->addWidget(this->byteWidgetContainer);
this->byteWidgetAddressContainer = byteWidgetScrollAreaWidgetContainer->findChild<QWidget*>(
"address-container"
);
this->byteWidgetAddressLayout = this->byteWidgetAddressContainer->findChild<QVBoxLayout*>();
this->byteWidgetAddressLayout->setContentsMargins(5, 10, 0, 5);
QObject::connect(
this->byteWidgetContainer,
&ByteWidgetContainer::byteWidgetsAdjusted,
this,
&HexViewerWidget::onByteWidgetsAdjusted
);
QObject::connect( QObject::connect(
&insightWorker, &insightWorker,
&InsightWorker::targetStateUpdated, &InsightWorker::targetStateUpdated,
@@ -93,7 +77,7 @@ HexViewerWidget::HexViewerWidget(
} }
void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) { void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) {
this->byteWidgetContainer->updateValues(buffer); this->byteItemGraphicsScene->updateValues(buffer);
} }
void HexViewerWidget::resizeEvent(QResizeEvent* event) { void HexViewerWidget::resizeEvent(QResizeEvent* event) {
@@ -109,36 +93,36 @@ void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) {
} }
void HexViewerWidget::onByteWidgetsAdjusted() { void HexViewerWidget::onByteWidgetsAdjusted() {
const auto& byteWidgetsByRowIndex = this->byteWidgetContainer->byteWidgetsByRowIndex; // const auto& byteWidgetsByRowIndex = this->byteWidgetContainer->byteWidgetsByRowIndex;
//
int layoutItemMaxIndex = this->byteWidgetAddressLayout->count() - 1; // int layoutItemMaxIndex = this->byteWidgetAddressLayout->count() - 1;
for (const auto& mappingPair : byteWidgetsByRowIndex) { // for (const auto& mappingPair : byteWidgetsByRowIndex) {
const auto rowIndex = static_cast<int>(mappingPair.first); // const auto rowIndex = static_cast<int>(mappingPair.first);
const auto& byteWidgets = mappingPair.second; // const auto& byteWidgets = mappingPair.second;
//
if (byteWidgets.empty()) { // if (byteWidgets.empty()) {
continue; // continue;
} // }
//
QLabel* labelWidget; // QLabel* labelWidget;
if (rowIndex > layoutItemMaxIndex) { // if (rowIndex > layoutItemMaxIndex) {
labelWidget = new QLabel(this->byteWidgetAddressContainer); // labelWidget = new QLabel(this->byteWidgetAddressContainer);
labelWidget->setFixedSize(75, 20); // labelWidget->setFixedSize(75, 20);
labelWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // labelWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
//
this->byteWidgetAddressLayout->addWidget(labelWidget); // this->byteWidgetAddressLayout->addWidget(labelWidget);
layoutItemMaxIndex++; // layoutItemMaxIndex++;
//
} else { // } else {
labelWidget = qobject_cast<QLabel*>(this->byteWidgetAddressLayout->itemAt(rowIndex)->widget()); // labelWidget = qobject_cast<QLabel*>(this->byteWidgetAddressLayout->itemAt(rowIndex)->widget());
} // }
//
labelWidget->setText(byteWidgets.front()->relativeAddressHex); // labelWidget->setText(byteWidgets.front()->relativeAddressHex);
} // }
//
const auto rowCount = static_cast<int>(byteWidgetsByRowIndex.size()); // const auto rowCount = static_cast<int>(byteWidgetsByRowIndex.size());
QLayoutItem* labelItem; // QLayoutItem* labelItem;
while ((labelItem = this->byteWidgetAddressLayout->takeAt(rowCount)) != nullptr) { // while ((labelItem = this->byteWidgetAddressLayout->takeAt(rowCount)) != nullptr) {
labelItem->widget()->deleteLater(); // labelItem->widget()->deleteLater();
} // }
} }

View File

@@ -2,7 +2,7 @@
#include <QWidget> #include <QWidget>
#include <QLabel> #include <QLabel>
#include <QToolButton> #include <QGraphicsView>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <set> #include <set>
#include <map> #include <map>
@@ -16,7 +16,7 @@
#include "src/Insight/InsightWorker/InsightWorker.hpp" #include "src/Insight/InsightWorker/InsightWorker.hpp"
#include "ByteWidgetContainer.hpp" #include "ByteItemContainerGraphicsView.hpp"
namespace Bloom::Widgets namespace Bloom::Widgets
{ {
@@ -46,7 +46,8 @@ namespace Bloom::Widgets
QWidget* toolBar = nullptr; QWidget* toolBar = nullptr;
QWidget* bottomBar = nullptr; QWidget* bottomBar = nullptr;
ByteWidgetContainer* byteWidgetContainer = nullptr; ByteItemContainerGraphicsView* byteItemGraphicsView = nullptr;
ByteItemGraphicsScene* byteItemGraphicsScene = nullptr;
QWidget* byteWidgetScrollArea = nullptr; QWidget* byteWidgetScrollArea = nullptr;
QWidget* byteWidgetAddressContainer = nullptr; QWidget* byteWidgetAddressContainer = nullptr;
QVBoxLayout* byteWidgetAddressLayout = nullptr; QVBoxLayout* byteWidgetAddressLayout = nullptr;

View File

@@ -53,64 +53,25 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="ExpandingHeightScrollAreaWidget" name="byte-widget-scroll-area"> <widget class="QWidget" name="graphics-view-container">
<property name="widgetResizable"><bool>true</bool></property>
<property name="verticalScrollBarPolicy"><enum>Qt::ScrollBarAsNeeded</enum></property>
<property name="sizeAdjustPolicy"><enum>QAbstractScrollArea::AdjustToContents</enum></property>
<!-- <property name="horizontalScrollBarPolicy"><enum>Qt::ScrollBarAlwaysOff</enum></property>-->
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Ignored"/>
</property> </property>
<layout class="QVBoxLayout" name="byte-item-container-layout">
<widget class="QWidget" name="byte-widget-scroll-area-container"> <property name="spacing">
<property name="sizePolicy"> <number>0</number>
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Ignored"/>
</property> </property>
<layout class="QVBoxLayout" name="byte-widget-scroll-area-vertical-layout"> <property name="margin">
<property name="spacing"> <number>0</number>
<number>0</number> </property>
</property> <item>
<property name="margin"> <spacer name="vertical-spacer">
<number>0</number> <property name="orientation">
</property> <enum>Qt::Vertical</enum>
<item> </property>
<layout class="QHBoxLayout" name="byte-widget-scroll-area-horizontal-layout"> </spacer>
<property name="spacing"> </item>
<number>0</number> </layout>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="address-container">
<property name="minimumWidth">
<number>85</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Ignored"/>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="margin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="vertical-spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
<item> <item>