Refactored TargetRegistersPane widget to improve memory footprint
This commit is contained in:
@@ -202,25 +202,6 @@ QScrollBar::sub-line:vertical {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#target-registers-side-pane #register-group-header[selected=true],
|
||||
#target-registers-side-pane #register-item[selected=true] {
|
||||
background-color: #355A80;
|
||||
}
|
||||
|
||||
#target-registers-side-pane #register-item #value {
|
||||
font-size: 13px;
|
||||
color: #8a8a8d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#target-registers-side-pane #register-item[selected=false] #value[changed=true] {
|
||||
color: #547fba;
|
||||
}
|
||||
|
||||
#target-registers-side-pane #register-item[selected=true] #value[changed=true] {
|
||||
color: #8a8a8d;
|
||||
}
|
||||
|
||||
/* Bottom menu bar & panels */
|
||||
#bottom-menu-bar {
|
||||
background-color: transparent;
|
||||
|
||||
@@ -27,12 +27,11 @@ namespace Bloom::Widgets
|
||||
TargetRegisterInspectorWindow::TargetRegisterInspectorWindow(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
TargetState currentTargetState,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& registerValue,
|
||||
QWidget* parent
|
||||
)
|
||||
: QWidget(parent)
|
||||
, registerDescriptor(registerDescriptor)
|
||||
, registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0)))
|
||||
, registerValue(Targets::TargetMemoryBuffer(registerDescriptor.size, 0))
|
||||
{
|
||||
this->setWindowFlag(Qt::Window);
|
||||
auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper();
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Bloom::Widgets
|
||||
TargetRegisterInspectorWindow(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
Targets::TargetState currentTargetState,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& registerValue = std::nullopt,
|
||||
QWidget* parent = nullptr
|
||||
);
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#include "ItemWidget.hpp"
|
||||
|
||||
#include <QStyle>
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) {
|
||||
auto onClick = [this] {
|
||||
this->setSelected(true);
|
||||
};
|
||||
|
||||
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
|
||||
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
|
||||
|
||||
this->setSelected(false);
|
||||
}
|
||||
|
||||
void ItemWidget::setSelected(bool selected) {
|
||||
this->setProperty("selected", selected);
|
||||
this->style()->unpolish(this);
|
||||
this->style()->polish(this);
|
||||
|
||||
if (selected) {
|
||||
emit this->selected(this);
|
||||
}
|
||||
|
||||
this->postSetSelected(selected);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ClickableWidget.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class ItemWidget: public ClickableWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ItemWidget(QWidget *parent);
|
||||
|
||||
public slots:
|
||||
void setSelected(bool selected);
|
||||
|
||||
signals:
|
||||
void selected(ItemWidget*);
|
||||
|
||||
protected:
|
||||
virtual void postSetSelected(bool selected) {};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#include "RegisterGroupItem.hpp"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QSvgRenderer>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.hpp"
|
||||
#include "src/Services/PathService.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
RegisterGroupItem::RegisterGroupItem(
|
||||
QString name,
|
||||
const std::set<Targets::TargetRegisterDescriptor>& registerDescriptors,
|
||||
std::unordered_map<Targets::TargetRegisterDescriptor, RegisterItem*>& registerItemsByDescriptor
|
||||
)
|
||||
: groupName(name)
|
||||
{
|
||||
for (const auto& registerDescriptor : registerDescriptors) {
|
||||
auto* registerItem = new RegisterItem(registerDescriptor);
|
||||
registerItem->setParentItem(this);
|
||||
registerItem->setVisible(this->isExpanded());
|
||||
|
||||
this->registerItems.push_back(registerItem);
|
||||
registerItemsByDescriptor.insert(std::pair(registerDescriptor, registerItem));
|
||||
}
|
||||
|
||||
if (!RegisterGroupItem::registerGroupIconPixmap.has_value()) {
|
||||
this->generatePixmaps();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterGroupItem::setExpanded(bool expanded) {
|
||||
if (expanded == this->expanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->expanded = expanded;
|
||||
|
||||
for (auto& registerItem : this->registerItems) {
|
||||
registerItem->setVisible(this->expanded);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterGroupItem::refreshGeometry() {
|
||||
const auto groupWidth = this->size.width();
|
||||
|
||||
const auto startXPosition = 0;
|
||||
auto startYPosition = RegisterGroupItem::GROUP_LIST_ITEM_HEIGHT;
|
||||
|
||||
if (this->isExpanded()) {
|
||||
for (auto& registerItem : this->registerItems) {
|
||||
if (!registerItem->isVisible() || registerItem->excluded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
registerItem->size.setWidth(groupWidth);
|
||||
registerItem->setPos(startXPosition, startYPosition);
|
||||
|
||||
registerItem->onGeometryChanged();
|
||||
|
||||
startYPosition += registerItem->size.height();
|
||||
}
|
||||
}
|
||||
|
||||
this->size.setHeight(startYPosition);
|
||||
}
|
||||
|
||||
void RegisterGroupItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||
static constexpr auto selectedBackgroundColor = QColor(0x35, 0x5A, 0x80);
|
||||
|
||||
static constexpr auto itemLeftPadding = 3;
|
||||
static constexpr auto itemTopPadding = 4;
|
||||
static constexpr auto iconLeftMargin = 5;
|
||||
static constexpr auto labelLeftMargin = 6;
|
||||
|
||||
static const auto nameLabelFont = QFont("'Ubuntu', sans-serif", 11);
|
||||
static constexpr auto nameLabelFontColor = QColor(0xAF, 0xB1, 0xB3);
|
||||
|
||||
if (this->selected) {
|
||||
painter->setBrush(selectedBackgroundColor);
|
||||
painter->setPen(Qt::PenStyle::NoPen);
|
||||
painter->drawRect(0, 0, this->size.width(), RegisterGroupItem::GROUP_LIST_ITEM_HEIGHT);
|
||||
}
|
||||
|
||||
const auto& collapsedArrowIconPixmap = *(RegisterGroupItem::collapsedArrowIconPixmap);
|
||||
const auto& expandedArrowIconPixmap = *(RegisterGroupItem::expandedArrowIconPixmap);
|
||||
const auto& registerGroupIconPixmap = *(RegisterGroupItem::registerGroupIconPixmap);
|
||||
|
||||
const QPixmap& arrowPixmap = this->isExpanded() ? expandedArrowIconPixmap : collapsedArrowIconPixmap;
|
||||
|
||||
painter->drawPixmap(itemLeftPadding, itemTopPadding + 2, arrowPixmap);
|
||||
painter->drawPixmap(
|
||||
arrowPixmap.width() + itemLeftPadding + iconLeftMargin,
|
||||
itemTopPadding + 1,
|
||||
registerGroupIconPixmap
|
||||
);
|
||||
|
||||
painter->setFont(nameLabelFont);
|
||||
painter->setPen(nameLabelFontColor);
|
||||
|
||||
const auto fontMetrics = painter->fontMetrics();
|
||||
|
||||
const auto nameLabelSize = fontMetrics.size(Qt::TextSingleLine, this->groupName);
|
||||
const auto nameLabelRect = QRect(
|
||||
arrowPixmap.width() + itemLeftPadding + registerGroupIconPixmap.width() + itemLeftPadding + labelLeftMargin,
|
||||
itemTopPadding,
|
||||
nameLabelSize.width(),
|
||||
nameLabelSize.height()
|
||||
);
|
||||
|
||||
painter->drawText(nameLabelRect, Qt::AlignLeft, this->groupName);
|
||||
}
|
||||
|
||||
void RegisterGroupItem::generatePixmaps() const {
|
||||
auto svgRenderer = QSvgRenderer();
|
||||
|
||||
{
|
||||
svgRenderer.load(QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
|
||||
));
|
||||
|
||||
auto registerGroupIconPixmap = QPixmap(svgRenderer.defaultSize());
|
||||
registerGroupIconPixmap.fill(Qt::GlobalColor::transparent);
|
||||
|
||||
auto painter = QPainter(®isterGroupIconPixmap);
|
||||
svgRenderer.render(&painter);
|
||||
|
||||
RegisterGroupItem::registerGroupIconPixmap = registerGroupIconPixmap;
|
||||
}
|
||||
|
||||
{
|
||||
svgRenderer.load(QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
|
||||
));
|
||||
|
||||
const auto iconSize = svgRenderer.defaultSize();
|
||||
auto arrowIconPixmap = QPixmap(iconSize);
|
||||
arrowIconPixmap.fill(Qt::GlobalColor::transparent);
|
||||
|
||||
auto painter = QPainter(&arrowIconPixmap);
|
||||
svgRenderer.render(&painter);
|
||||
|
||||
RegisterGroupItem::collapsedArrowIconPixmap = arrowIconPixmap;
|
||||
|
||||
painter.translate(
|
||||
std::ceil(static_cast<float>(iconSize.width() / 2)),
|
||||
std::ceil(static_cast<float>(iconSize.height() / 2))
|
||||
);
|
||||
painter.rotate(90);
|
||||
painter.translate(
|
||||
-std::ceil(static_cast<float>(iconSize.width() / 2)),
|
||||
-std::ceil(static_cast<float>(iconSize.height() / 2))
|
||||
);
|
||||
arrowIconPixmap.fill(Qt::GlobalColor::transparent);
|
||||
svgRenderer.render(&painter);
|
||||
|
||||
RegisterGroupItem::expandedArrowIconPixmap = arrowIconPixmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <QPixmap>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListItem.hpp"
|
||||
|
||||
#include "RegisterItem.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class RegisterGroupItem: public ListItem
|
||||
{
|
||||
public:
|
||||
const QString groupName;
|
||||
std::vector<RegisterItem*> registerItems;
|
||||
|
||||
explicit RegisterGroupItem(
|
||||
QString name,
|
||||
const std::set<Targets::TargetRegisterDescriptor>& registerDescriptors,
|
||||
std::unordered_map<Targets::TargetRegisterDescriptor, RegisterItem*>& registerItemsByDescriptor
|
||||
);
|
||||
|
||||
bool isExpanded() const {
|
||||
return this->expanded;
|
||||
}
|
||||
|
||||
void setExpanded(bool expanded);
|
||||
|
||||
void onGeometryChanged() override {
|
||||
return this->refreshGeometry();
|
||||
}
|
||||
|
||||
void refreshGeometry();
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
|
||||
|
||||
private:
|
||||
static constexpr int GROUP_LIST_ITEM_HEIGHT = 25;
|
||||
static inline std::optional<QPixmap> registerGroupIconPixmap = std::nullopt;
|
||||
static inline std::optional<QPixmap> collapsedArrowIconPixmap = std::nullopt;
|
||||
static inline std::optional<QPixmap> expandedArrowIconPixmap = std::nullopt;
|
||||
|
||||
bool expanded = false;
|
||||
|
||||
void generatePixmaps() const;
|
||||
};
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
#include "RegisterGroupWidget.hpp"
|
||||
|
||||
#include <QUiLoader>
|
||||
#include <QStyle>
|
||||
#include <utility>
|
||||
|
||||
#include "RegisterWidget.hpp"
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
using Bloom::Targets::TargetRegisterDescriptor;
|
||||
|
||||
RegisterGroupWidget::RegisterGroupWidget(
|
||||
QString name,
|
||||
const std::set<TargetRegisterDescriptor>& registerDescriptors,
|
||||
TargetRegistersPaneWidget* parent
|
||||
)
|
||||
: ItemWidget(parent)
|
||||
, name(std::move(name))
|
||||
{
|
||||
this->setObjectName(this->name);
|
||||
|
||||
this->headerWidget->setObjectName("register-group-header");
|
||||
this->headerWidget->setFixedHeight(25);
|
||||
auto* headerLayout = new QHBoxLayout(this->headerWidget);
|
||||
headerLayout->setContentsMargins(5, 0, 0, 0);
|
||||
|
||||
this->label->setText(this->name);
|
||||
|
||||
this->arrowIcon->setObjectName("arrow-icon");
|
||||
auto static arrowIconPath = QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
|
||||
);
|
||||
this->arrowIcon->setSvgFilePath(arrowIconPath);
|
||||
this->arrowIcon->setContainerHeight(15);
|
||||
this->arrowIcon->setContainerWidth(14);
|
||||
|
||||
this->registerGroupIcon->setObjectName("register-group-icon");
|
||||
auto static registerIconPath = QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
|
||||
);
|
||||
this->registerGroupIcon->setSvgFilePath(registerIconPath);
|
||||
this->registerGroupIcon->setContainerHeight(15);
|
||||
this->registerGroupIcon->setContainerWidth(15);
|
||||
|
||||
headerLayout->addWidget(this->arrowIcon);
|
||||
headerLayout->addWidget(this->registerGroupIcon);
|
||||
headerLayout->addWidget(this->label);
|
||||
|
||||
auto* bodyLayout = new QVBoxLayout(this->bodyWidget);
|
||||
bodyLayout->setContentsMargins(0, 0,0,0);
|
||||
bodyLayout->setSpacing(0);
|
||||
|
||||
for (const auto& descriptor : registerDescriptors) {
|
||||
if (!descriptor.name.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!descriptor.readable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* registerWidget = new RegisterWidget(descriptor, this->bodyWidget);
|
||||
bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop);
|
||||
|
||||
QObject::connect(
|
||||
registerWidget,
|
||||
&ItemWidget::selected,
|
||||
parent,
|
||||
&TargetRegistersPaneWidget::onItemSelectionChange
|
||||
);
|
||||
|
||||
this->registerWidgets.insert(registerWidget);
|
||||
this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget));
|
||||
}
|
||||
|
||||
bodyLayout->addStretch(1);
|
||||
|
||||
this->layout->setContentsMargins(0,0,0,0);
|
||||
this->layout->setSpacing(0);
|
||||
this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop);
|
||||
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop);
|
||||
this->layout->addStretch(1);
|
||||
|
||||
this->collapse();
|
||||
|
||||
QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] {
|
||||
if (this->collapsed) {
|
||||
this->expand();
|
||||
|
||||
} else {
|
||||
this->collapse();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
this->headerWidget,
|
||||
&ItemWidget::selected,
|
||||
parent,
|
||||
&TargetRegistersPaneWidget::onItemSelectionChange
|
||||
);
|
||||
}
|
||||
|
||||
void RegisterGroupWidget::collapse() {
|
||||
this->arrowIcon->setAngle(0);
|
||||
this->bodyWidget->setVisible(false);
|
||||
this->collapsed = true;
|
||||
}
|
||||
|
||||
void RegisterGroupWidget::expand() {
|
||||
this->arrowIcon->setAngle(90);
|
||||
this->bodyWidget->setVisible(true);
|
||||
this->collapsed = false;
|
||||
}
|
||||
|
||||
void RegisterGroupWidget::setAllRegistersVisible(bool visible) {
|
||||
for (const auto& registerWidget : this->registerWidgets) {
|
||||
registerWidget->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterGroupWidget::filterRegisters(const QString& keyword) {
|
||||
int matchingWidgetCount = 0;
|
||||
for (const auto& registerWidget : this->registerWidgets) {
|
||||
if (keyword.isEmpty() || (registerWidget->searchKeywords.contains(keyword, Qt::CaseInsensitive))) {
|
||||
matchingWidgetCount++;
|
||||
registerWidget->setVisible(true);
|
||||
|
||||
} else {
|
||||
registerWidget->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingWidgetCount == 0) {
|
||||
this->collapse();
|
||||
this->setVisible(false);
|
||||
|
||||
} else {
|
||||
this->setVisible(true);
|
||||
if (!keyword.isEmpty()) {
|
||||
this->expand();
|
||||
|
||||
} else {
|
||||
this->collapse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <unordered_set>
|
||||
#include <QVBoxLayout>
|
||||
#include <QSize>
|
||||
#include <set>
|
||||
|
||||
#include "TargetRegistersPaneWidget.hpp"
|
||||
#include "ItemWidget.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class RegisterWidget;
|
||||
class RegisterGroupWidget: public ItemWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString name;
|
||||
bool collapsed = true;
|
||||
|
||||
std::set<RegisterWidget*> registerWidgets;
|
||||
std::map<Targets::TargetRegisterDescriptor, RegisterWidget*> registerWidgetsMappedByDescriptor;
|
||||
|
||||
RegisterGroupWidget(
|
||||
QString name,
|
||||
const std::set<Targets::TargetRegisterDescriptor>& registerDescriptors,
|
||||
TargetRegistersPaneWidget* parent
|
||||
);
|
||||
|
||||
void collapse();
|
||||
void expand();
|
||||
void setAllRegistersVisible(bool visible);
|
||||
|
||||
void filterRegisters(const QString& keyword);
|
||||
|
||||
private:
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
ItemWidget* headerWidget = new ItemWidget(this);
|
||||
SvgWidget* arrowIcon = new SvgWidget(this->headerWidget);
|
||||
SvgWidget* registerGroupIcon = new SvgWidget(this->headerWidget);
|
||||
Label* label = new Label(this->headerWidget);
|
||||
QWidget* bodyWidget = new QWidget(this);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "RegisterItem.hpp"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QSvgRenderer>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
RegisterItem::RegisterItem(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor
|
||||
)
|
||||
: registerDescriptor(registerDescriptor)
|
||||
, registerName(QString::fromStdString(registerDescriptor.name.value()).toUpper())
|
||||
, searchKeywords(this->registerName + " " + QString::fromStdString(registerDescriptor.description.value_or("")))
|
||||
{
|
||||
this->size = QSize(0, RegisterItem::HEIGHT);
|
||||
|
||||
if (!RegisterItem::registerIconPixmap.has_value()) {
|
||||
this->generatePixmaps();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterItem::setValue(const Targets::TargetMemoryBuffer& value) {
|
||||
const auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(value.data()),
|
||||
static_cast<qsizetype>(value.size())
|
||||
);
|
||||
|
||||
auto hexValueByteArray = valueByteArray.toHex();
|
||||
this->valueText = ": 0x" + QString(hexValueByteArray).toUpper()
|
||||
+ " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16));
|
||||
|
||||
if (value.size() == 1 && value[0] >= 32 && value[0] <= 126) {
|
||||
this->valueText += " | '" + QString(valueByteArray) + "'";
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||
if (this->excluded) {
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr auto selectedBackgroundColor = QColor(0x35, 0x5A, 0x80);
|
||||
|
||||
static constexpr auto itemLeftPadding = 41;
|
||||
static constexpr auto itemTopPadding = 4;
|
||||
static constexpr auto labelLeftMargin = 5;
|
||||
|
||||
static const auto nameLabelFont = QFont("'Ubuntu', sans-serif", 11);
|
||||
static constexpr auto nameLabelFontColor = QColor(0xAF, 0xB1, 0xB3);
|
||||
static const auto valueLabelFont = QFont("'Ubuntu', sans-serif", 10, -1, true);
|
||||
static constexpr auto valueLabelFontColor = QColor(0x8A, 0x8A, 0x8D);
|
||||
static constexpr auto highlightedValueLabelFontColor = QColor(0x54, 0x7F, 0xBA);
|
||||
|
||||
if (this->selected) {
|
||||
painter->setBrush(selectedBackgroundColor);
|
||||
painter->setPen(Qt::PenStyle::NoPen);
|
||||
painter->drawRect(QRect(QPoint(0, 0), this->size));
|
||||
}
|
||||
|
||||
const auto& registerIconPixmap = *(RegisterItem::registerIconPixmap);
|
||||
painter->drawPixmap(itemLeftPadding, itemTopPadding + 4, registerIconPixmap);
|
||||
|
||||
painter->setFont(nameLabelFont);
|
||||
painter->setPen(nameLabelFontColor);
|
||||
|
||||
const auto fontMetrics = painter->fontMetrics();
|
||||
|
||||
const auto nameLabelSize = fontMetrics.size(Qt::TextSingleLine, this->registerName);
|
||||
const auto nameLabelRect = QRect(
|
||||
registerIconPixmap.width() + itemLeftPadding + labelLeftMargin,
|
||||
itemTopPadding,
|
||||
nameLabelSize.width(),
|
||||
nameLabelSize.height()
|
||||
);
|
||||
|
||||
painter->drawText(nameLabelRect, Qt::AlignLeft, this->registerName);
|
||||
|
||||
if (!this->valueText.isEmpty()) {
|
||||
painter->setFont(valueLabelFont);
|
||||
painter->setPen(
|
||||
this->valueChanged && !this->selected ? highlightedValueLabelFontColor : valueLabelFontColor
|
||||
);
|
||||
|
||||
const auto fontMetrics = painter->fontMetrics();
|
||||
|
||||
const auto valueLabelSize = fontMetrics.size(Qt::TextSingleLine, this->valueText);
|
||||
const auto valueLabelRect = QRect(
|
||||
nameLabelRect.right() + labelLeftMargin,
|
||||
itemTopPadding + 1,
|
||||
valueLabelSize.width(),
|
||||
valueLabelSize.height()
|
||||
);
|
||||
|
||||
painter->drawText(valueLabelRect, Qt::AlignLeft, this->valueText);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterItem::generatePixmaps() const {
|
||||
auto svgRegisterIconRenderer = QSvgRenderer(QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
|
||||
));
|
||||
|
||||
const auto registerIconSize = svgRegisterIconRenderer.defaultSize();
|
||||
|
||||
auto registerIconPixmap = QPixmap(registerIconSize);
|
||||
registerIconPixmap.fill(Qt::GlobalColor::transparent);
|
||||
|
||||
auto painter = QPainter(®isterIconPixmap);
|
||||
svgRegisterIconRenderer.render(&painter);
|
||||
|
||||
RegisterItem::registerIconPixmap = registerIconPixmap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListItem.hpp"
|
||||
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class RegisterItem: public ListItem
|
||||
{
|
||||
public:
|
||||
const Targets::TargetRegisterDescriptor registerDescriptor;
|
||||
const QString registerName;
|
||||
const QString searchKeywords;
|
||||
bool excluded = false;
|
||||
bool valueChanged = false;
|
||||
|
||||
explicit RegisterItem(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor
|
||||
);
|
||||
|
||||
void setValue(const Targets::TargetMemoryBuffer& value);
|
||||
|
||||
void clearValue() {
|
||||
this->valueText.clear();
|
||||
}
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
|
||||
|
||||
private:
|
||||
static constexpr int HEIGHT = 25;
|
||||
static inline std::optional<QPixmap> registerIconPixmap = std::nullopt;
|
||||
|
||||
QString valueText = "";
|
||||
|
||||
void generatePixmaps() const;
|
||||
};
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "RegisterWidget.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QMenu>
|
||||
#include <QStyle>
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
|
||||
#include "src/Insight/InsightSignals.hpp"
|
||||
#include "src/Insight/InsightWorker/InsightWorker.hpp"
|
||||
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
using Bloom::Targets::TargetRegisterDescriptor;
|
||||
|
||||
RegisterWidget::RegisterWidget(
|
||||
TargetRegisterDescriptor descriptor,
|
||||
QWidget *parent
|
||||
)
|
||||
: ItemWidget(parent)
|
||||
, descriptor(std::move(descriptor))
|
||||
, searchKeywords(QString::fromStdString(
|
||||
this->descriptor.name.value_or("") + this->descriptor.description.value_or("")
|
||||
).toLower())
|
||||
{
|
||||
this->setObjectName("register-item");
|
||||
this->setFixedHeight(25);
|
||||
|
||||
this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper());
|
||||
this->valueLabel->setObjectName("value");
|
||||
|
||||
this->registerIcon->setObjectName("register-icon");
|
||||
auto static registerIconPath = QString::fromStdString(
|
||||
Services::PathService::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
|
||||
);
|
||||
this->registerIcon->setSvgFilePath(registerIconPath);
|
||||
this->registerIcon->setContainerHeight(15);
|
||||
this->registerIcon->setContainerWidth(15);
|
||||
|
||||
this->layout->setContentsMargins(47, 0, 0, 0);
|
||||
this->layout->setSpacing(0);
|
||||
this->layout->addWidget(this->registerIcon);
|
||||
this->layout->addSpacing(7);
|
||||
this->layout->addWidget(this->nameLabel);
|
||||
this->layout->addSpacing(5);
|
||||
this->layout->addWidget(this->valueLabel);
|
||||
this->layout->addStretch(1);
|
||||
|
||||
QObject::connect(this, &ClickableWidget::doubleClicked, this, &RegisterWidget::openInspectionWindow);
|
||||
QObject::connect(this->openInspectionWindowAction, &QAction::triggered, this, &RegisterWidget::openInspectionWindow);
|
||||
QObject::connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue);
|
||||
QObject::connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName);
|
||||
QObject::connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex);
|
||||
QObject::connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal);
|
||||
QObject::connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary);
|
||||
|
||||
QObject::connect(
|
||||
InsightSignals::instance(),
|
||||
&InsightSignals::targetStateUpdated,
|
||||
this,
|
||||
&RegisterWidget::onTargetStateChange
|
||||
);
|
||||
}
|
||||
|
||||
void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) {
|
||||
const auto valueChanged = this->currentRegister.has_value()
|
||||
&& this->currentRegister.value().value != targetRegister.value;
|
||||
this->currentRegister = targetRegister;
|
||||
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(targetRegister.value.data()),
|
||||
static_cast<qsizetype>(targetRegister.value.size())
|
||||
);
|
||||
|
||||
auto hexValueByteArray = valueByteArray.toHex();
|
||||
auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper()
|
||||
+ " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16));
|
||||
|
||||
if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) {
|
||||
registerValue += " | '" + QString(valueByteArray) + "'";
|
||||
}
|
||||
|
||||
this->valueLabel->setProperty("changed", valueChanged);
|
||||
this->valueLabel->style()->unpolish(this->valueLabel);
|
||||
this->valueLabel->style()->polish(this->valueLabel);
|
||||
|
||||
this->valueLabel->setText(registerValue);
|
||||
|
||||
if (this->inspectWindow != nullptr) {
|
||||
this->inspectWindow->setValue(targetRegister.value);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::clearInlineValue() {
|
||||
this->valueLabel->clear();
|
||||
}
|
||||
|
||||
void RegisterWidget::contextMenuEvent(QContextMenuEvent* event) {
|
||||
this->setSelected(true);
|
||||
|
||||
auto* menu = new QMenu(this);
|
||||
menu->addAction(this->openInspectionWindowAction);
|
||||
menu->addAction(this->refreshValueAction);
|
||||
menu->addSeparator();
|
||||
|
||||
auto* copyMenu = new QMenu("Copy", this);
|
||||
copyMenu->addAction(this->copyValueNameAction);
|
||||
copyMenu->addSeparator();
|
||||
copyMenu->addAction(this->copyValueDecimalAction);
|
||||
copyMenu->addAction(this->copyValueHexAction);
|
||||
copyMenu->addAction(this->copyValueBinaryAction);
|
||||
|
||||
menu->addMenu(copyMenu);
|
||||
|
||||
this->openInspectionWindowAction->setEnabled(TargetRegisterInspectorWindow::registerSupported(this->descriptor));
|
||||
|
||||
const auto targetStopped = this->targetState == Targets::TargetState::STOPPED;
|
||||
const auto targetStoppedAndValuePresent = targetStopped && this->currentRegister.has_value();
|
||||
this->refreshValueAction->setEnabled(targetStopped);
|
||||
this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent);
|
||||
this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent);
|
||||
this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent);
|
||||
|
||||
menu->exec(event->globalPos());
|
||||
}
|
||||
|
||||
void RegisterWidget::openInspectionWindow() {
|
||||
if (!TargetRegisterInspectorWindow::registerSupported(this->descriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->inspectWindow == nullptr) {
|
||||
this->inspectWindow = new TargetRegisterInspectorWindow(
|
||||
this->descriptor,
|
||||
this->targetState,
|
||||
this->currentRegister.has_value() ? std::optional(this->currentRegister->value)
|
||||
: std::nullopt,
|
||||
this
|
||||
);
|
||||
|
||||
} else {
|
||||
if (this->currentRegister.has_value()) {
|
||||
this->inspectWindow->setValue(this->currentRegister->value);
|
||||
}
|
||||
|
||||
if (!this->inspectWindow->isVisible()) {
|
||||
this->inspectWindow->show();
|
||||
|
||||
} else {
|
||||
this->inspectWindow->activateWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::refreshValue() {
|
||||
auto* readRegisterTask = new ReadTargetRegisters({this->descriptor});
|
||||
|
||||
QObject::connect(
|
||||
readRegisterTask,
|
||||
&ReadTargetRegisters::targetRegistersRead,
|
||||
this,
|
||||
[this] (Targets::TargetRegisters registers) {
|
||||
for (const auto& targetRegister : registers) {
|
||||
if (targetRegister.descriptor == this->descriptor) {
|
||||
this->setRegisterValue(targetRegister);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
InsightWorker::queueTask(readRegisterTask);
|
||||
}
|
||||
|
||||
void RegisterWidget::copyName() {
|
||||
if (this->nameLabel != nullptr) {
|
||||
QApplication::clipboard()->setText(this->nameLabel->text());
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::copyValueHex() {
|
||||
if (this->currentRegister.has_value()) {
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(this->currentRegister.value().value.size())
|
||||
).toHex();
|
||||
QApplication::clipboard()->setText(QString(valueByteArray).toUpper());
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::copyValueDecimal() {
|
||||
if (this->currentRegister.has_value()) {
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
|
||||
static_cast<qsizetype>(this->currentRegister.value().value.size())
|
||||
).toHex();
|
||||
QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16)));
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::copyValueBinary() {
|
||||
if (this->currentRegister.has_value()) {
|
||||
const auto registerValueSize = static_cast<qsizetype>(this->currentRegister.value().size());
|
||||
auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
|
||||
registerValueSize
|
||||
).toHex();
|
||||
|
||||
auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2);
|
||||
|
||||
if (bitString.size() < (registerValueSize * 8)) {
|
||||
bitString = bitString.rightJustified((registerValueSize * 8), '0');
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(bitString);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterWidget::postSetSelected(bool selected) {
|
||||
auto valueLabelStyle = this->valueLabel->style();
|
||||
valueLabelStyle->unpolish(this->valueLabel);
|
||||
valueLabelStyle->polish(this->valueLabel);
|
||||
}
|
||||
|
||||
void RegisterWidget::onTargetStateChange(Targets::TargetState newState) {
|
||||
this->targetState = newState;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <unordered_set>
|
||||
#include <optional>
|
||||
#include <QHBoxLayout>
|
||||
#include <QAction>
|
||||
#include <QSize>
|
||||
#include <QEvent>
|
||||
#include <QString>
|
||||
|
||||
#include "ItemWidget.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp"
|
||||
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class RegisterWidget: public ItemWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Targets::TargetRegisterDescriptor descriptor;
|
||||
QString searchKeywords;
|
||||
std::optional<Targets::TargetRegister> currentRegister;
|
||||
|
||||
RegisterWidget(
|
||||
Targets::TargetRegisterDescriptor descriptor,
|
||||
QWidget *parent
|
||||
);
|
||||
|
||||
[[nodiscard]] QSize minimumSizeHint() const override {
|
||||
auto size = QSize(this->parentWidget()->width(), 25);
|
||||
return size;
|
||||
}
|
||||
|
||||
void setRegisterValue(const Targets::TargetRegister& targetRegister);
|
||||
void clearInlineValue();
|
||||
|
||||
void contextMenuEvent(QContextMenuEvent* event) override;
|
||||
|
||||
public slots:
|
||||
void openInspectionWindow();
|
||||
void refreshValue();
|
||||
void copyName();
|
||||
void copyValueHex();
|
||||
void copyValueDecimal();
|
||||
void copyValueBinary();
|
||||
|
||||
private:
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
SvgWidget* registerIcon = new SvgWidget(this);
|
||||
Label* nameLabel = new Label(this);
|
||||
Label* valueLabel = new Label(this);
|
||||
|
||||
// Context-menu actions
|
||||
QAction* openInspectionWindowAction = new QAction("Inspect", this);
|
||||
QAction* refreshValueAction = new QAction("Refresh Value", this);
|
||||
QAction* copyValueNameAction = new QAction("Copy Register Name", this);
|
||||
QAction* copyValueHexAction = new QAction("Copy Hexadecimal Value", this);
|
||||
QAction* copyValueDecimalAction = new QAction("Copy Decimal Value", this);
|
||||
QAction* copyValueBinaryAction = new QAction("Copy Binary Value", this);
|
||||
|
||||
TargetRegisterInspectorWindow* inspectWindow = nullptr;
|
||||
|
||||
void postSetSelected(bool selected) override;
|
||||
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
|
||||
private slots:
|
||||
void onTargetStateChange(Targets::TargetState newState);
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
#include "TargetRegistersPaneWidget.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <set>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
|
||||
#include "src/Insight/InsightSignals.hpp"
|
||||
#include "src/Insight/InsightWorker/InsightWorker.hpp"
|
||||
#include "RegisterGroupWidget.hpp"
|
||||
#include "RegisterWidget.hpp"
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
@@ -47,6 +48,7 @@ namespace Bloom::Widgets
|
||||
auto uiLoader = UiLoader(this);
|
||||
this->container = uiLoader.load(&targetRegistersPaneUiFile, this);
|
||||
this->container->setFixedSize(parent->width(), parent->maximumHeight());
|
||||
auto* containerLayout = this->container->findChild<QVBoxLayout*>();
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -70,48 +72,126 @@ namespace Bloom::Widgets
|
||||
this->filterRegisters(this->searchInput->text());
|
||||
});
|
||||
|
||||
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
|
||||
const auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
|
||||
|
||||
this->itemContainer = this->container->findChild<QWidget*>("item-container");
|
||||
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
|
||||
auto registerDescriptorsByGroupName = std::map<QString, std::set<TargetRegisterDescriptor>>({
|
||||
{
|
||||
"CPU General Purpose",
|
||||
std::set<TargetRegisterDescriptor>(
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(),
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end()
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
|
||||
this->renderedDescriptors = std::set<TargetRegisterDescriptor>(
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(),
|
||||
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end()
|
||||
);
|
||||
|
||||
auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget(
|
||||
"CPU General Purpose",
|
||||
this->renderedDescriptors,
|
||||
this
|
||||
);
|
||||
|
||||
itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop);
|
||||
this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget);
|
||||
|
||||
auto registerDescriptorsByGroupName = std::map<std::string, std::set<TargetRegisterDescriptor>>();
|
||||
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) {
|
||||
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
|
||||
const auto groupName = QString::fromStdString(registerDescriptor.groupName.value_or("other")).toUpper();
|
||||
registerDescriptorsByGroupName[groupName].insert(registerDescriptor);
|
||||
}
|
||||
|
||||
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) {
|
||||
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
|
||||
const auto groupName = QString::fromStdString(registerDescriptor.groupName.value_or("other")).toUpper();
|
||||
registerDescriptorsByGroupName[groupName].insert(registerDescriptor);
|
||||
}
|
||||
|
||||
for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) {
|
||||
auto* registerGroupWidget = new RegisterGroupWidget(
|
||||
QString::fromStdString(groupName).toUpper(),
|
||||
this->registerGroupItems.emplace_back(new RegisterGroupItem(
|
||||
groupName,
|
||||
registerDescriptors,
|
||||
this
|
||||
);
|
||||
this->registerItemsByDescriptor
|
||||
));
|
||||
|
||||
itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop);
|
||||
this->registerGroupWidgets.insert(registerGroupWidget);
|
||||
this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
|
||||
this->registerDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
|
||||
}
|
||||
|
||||
itemLayout->addStretch(1);
|
||||
this->registerListView = new ListView(
|
||||
std::vector<ListItem*>(this->registerGroupItems.begin(), this->registerGroupItems.end()),
|
||||
this
|
||||
);
|
||||
|
||||
this->registerListScene = this->registerListView->listScene();
|
||||
|
||||
containerLayout->addWidget(this->registerListView);
|
||||
|
||||
QObject::connect(
|
||||
this->registerListScene,
|
||||
&ListScene::itemDoubleClicked,
|
||||
this,
|
||||
&TargetRegistersPaneWidget::onItemDoubleClicked
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->registerListScene,
|
||||
&ListScene::itemContextMenu,
|
||||
this,
|
||||
&TargetRegistersPaneWidget::onItemContextMenu
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->openInspectionWindowAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->openInspectionWindow(this->contextMenuRegisterItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->refreshValueAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->refreshRegisterValues(this->contextMenuRegisterItem->registerDescriptor, std::nullopt);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->copyNameAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->copyRegisterName(this->contextMenuRegisterItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->copyValueHexAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->copyRegisterValueHex(this->contextMenuRegisterItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->copyValueDecimalAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->copyRegisterValueDecimal(this->contextMenuRegisterItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->copyValueBinaryAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
[this] {
|
||||
if (this->contextMenuRegisterItem != nullptr) {
|
||||
this->copyRegisterValueBinary(this->contextMenuRegisterItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
auto* insightSignals = InsightSignals::instance();
|
||||
|
||||
@@ -142,45 +222,56 @@ namespace Bloom::Widgets
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::filterRegisters(const QString& keyword) {
|
||||
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
|
||||
// If the group name matches the keyword, then don't bother iterating through all the register widgets
|
||||
if (keyword.isEmpty() || registerGroupWidget->name.contains(keyword, Qt::CaseInsensitive)) {
|
||||
registerGroupWidget->setVisible(true);
|
||||
registerGroupWidget->setAllRegistersVisible(true);
|
||||
for (const auto& groupItem : this->registerGroupItems) {
|
||||
auto visibleItems = std::uint32_t{0};
|
||||
auto displayEntireGroup = keyword.isEmpty() || groupItem->groupName.contains(keyword, Qt::CaseInsensitive);
|
||||
|
||||
if (!keyword.isEmpty()) {
|
||||
registerGroupWidget->expand();
|
||||
for (auto& registerItem : groupItem->registerItems) {
|
||||
registerItem->excluded = !displayEntireGroup
|
||||
&& !registerItem->searchKeywords.contains(keyword, Qt::CaseInsensitive);
|
||||
|
||||
} else {
|
||||
registerGroupWidget->collapse();
|
||||
if (!registerItem->excluded) {
|
||||
++visibleItems;
|
||||
}
|
||||
|
||||
} else {
|
||||
registerGroupWidget->filterRegisters(keyword);
|
||||
}
|
||||
|
||||
groupItem->setVisible(visibleItems > 0 || keyword.isEmpty());
|
||||
groupItem->setExpanded(visibleItems > 0 && !keyword.isEmpty());
|
||||
}
|
||||
|
||||
this->registerListScene->refreshGeometry();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::collapseAllRegisterGroups() {
|
||||
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
|
||||
registerGroupWidget->collapse();
|
||||
for (auto& registerGroupItem : this->registerGroupItems) {
|
||||
registerGroupItem->setExpanded(false);
|
||||
}
|
||||
|
||||
this->registerListScene->refreshGeometry();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::expandAllRegisterGroups() {
|
||||
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
|
||||
registerGroupWidget->expand();
|
||||
for (auto& registerGroupItem : this->registerGroupItems) {
|
||||
registerGroupItem->setExpanded(true);
|
||||
}
|
||||
|
||||
this->registerListScene->refreshGeometry();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::refreshRegisterValues(std::optional<std::function<void(void)>> callback) {
|
||||
auto& descriptors = this->renderedDescriptors;
|
||||
|
||||
if (descriptors.empty()) {
|
||||
void TargetRegistersPaneWidget::refreshRegisterValues(
|
||||
std::optional<Targets::TargetRegisterDescriptor> registerDescriptor,
|
||||
std::optional<std::function<void(void)>> callback
|
||||
) {
|
||||
if (!registerDescriptor.has_value() && this->registerDescriptors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* readRegisterTask = new ReadTargetRegisters(descriptors);
|
||||
auto* readRegisterTask = new ReadTargetRegisters(
|
||||
registerDescriptor.has_value()
|
||||
? Targets::TargetRegisterDescriptors({*registerDescriptor})
|
||||
: this->registerDescriptors
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
readRegisterTask,
|
||||
&ReadTargetRegisters::targetRegistersRead,
|
||||
@@ -200,17 +291,6 @@ namespace Bloom::Widgets
|
||||
InsightWorker::queueTask(readRegisterTask);
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) {
|
||||
// Only one item in the target registers pane can be selected at any given time.
|
||||
if (this->selectedItemWidget != newlySelectedWidget) {
|
||||
if (this->selectedItemWidget != nullptr) {
|
||||
this->selectedItemWidget->setSelected(false);
|
||||
}
|
||||
|
||||
this->selectedItemWidget = newlySelectedWidget;
|
||||
}
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) {
|
||||
const auto parentSize = this->parentPanel->size();
|
||||
const auto width = parentSize.width() - 1;
|
||||
@@ -221,11 +301,65 @@ namespace Bloom::Widgets
|
||||
* In order to avoid the panel resize handle overlapping the scroll bar handle, we reduce the size of
|
||||
* the scroll area.
|
||||
*/
|
||||
this->itemScrollArea->setFixedWidth(width - this->parentPanel->getHandleSize());
|
||||
this->registerListView->setFixedWidth(width - this->parentPanel->getHandleSize());
|
||||
|
||||
PaneWidget::resizeEvent(event);
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::onItemDoubleClicked(ListItem* clickedItem) {
|
||||
auto* registerGroupItem = dynamic_cast<RegisterGroupItem*>(clickedItem);
|
||||
|
||||
if (registerGroupItem != nullptr) {
|
||||
registerGroupItem->setExpanded(!registerGroupItem->isExpanded());
|
||||
this->registerListScene->refreshGeometry();
|
||||
}
|
||||
|
||||
auto* registerItem = dynamic_cast<RegisterItem*>(clickedItem);
|
||||
|
||||
if (registerItem != nullptr) {
|
||||
this->openInspectionWindow(registerItem->registerDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::onItemContextMenu(ListItem* item, QPoint sourcePosition) {
|
||||
auto* registerItem = dynamic_cast<RegisterItem*>(item);
|
||||
|
||||
if (registerItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->contextMenuRegisterItem = registerItem;
|
||||
|
||||
auto* menu = new QMenu(this);
|
||||
menu->addAction(this->openInspectionWindowAction);
|
||||
menu->addAction(this->refreshValueAction);
|
||||
menu->addSeparator();
|
||||
|
||||
auto* copyMenu = new QMenu("Copy", this);
|
||||
copyMenu->addAction(this->copyNameAction);
|
||||
copyMenu->addSeparator();
|
||||
copyMenu->addAction(this->copyValueDecimalAction);
|
||||
copyMenu->addAction(this->copyValueHexAction);
|
||||
copyMenu->addAction(this->copyValueBinaryAction);
|
||||
|
||||
menu->addMenu(copyMenu);
|
||||
|
||||
this->openInspectionWindowAction->setEnabled(
|
||||
TargetRegisterInspectorWindow::registerSupported(this->contextMenuRegisterItem->registerDescriptor)
|
||||
);
|
||||
|
||||
const auto targetStopped = this->targetState == Targets::TargetState::STOPPED;
|
||||
const auto targetStoppedAndValuePresent = targetStopped
|
||||
&& this->currentRegisterValues.contains(this->contextMenuRegisterItem->registerDescriptor);
|
||||
|
||||
this->refreshValueAction->setEnabled(targetStopped);
|
||||
this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent);
|
||||
this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent);
|
||||
this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent);
|
||||
|
||||
menu->exec(sourcePosition);
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) {
|
||||
if (this->targetState == newState) {
|
||||
return;
|
||||
@@ -241,23 +375,121 @@ namespace Bloom::Widgets
|
||||
void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) {
|
||||
for (const auto& targetRegister : registers) {
|
||||
const auto& descriptor = targetRegister.descriptor;
|
||||
const auto& previousValueIt = this->currentRegisterValues.find(descriptor);
|
||||
const auto& registerItemIt = this->registerItemsByDescriptor.find(descriptor);
|
||||
|
||||
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
|
||||
const auto registerWidgetit = registerGroupWidget->registerWidgetsMappedByDescriptor.find(descriptor);
|
||||
if (registerItemIt != this->registerItemsByDescriptor.end()) {
|
||||
auto& registerItem = registerItemIt->second;
|
||||
|
||||
if (registerWidgetit != registerGroupWidget->registerWidgetsMappedByDescriptor.end()) {
|
||||
registerWidgetit->second->setRegisterValue(targetRegister);
|
||||
break;
|
||||
}
|
||||
registerItem->setValue(targetRegister.value);
|
||||
registerItem->valueChanged = previousValueIt != this->currentRegisterValues.end()
|
||||
? previousValueIt->second != targetRegister.value
|
||||
: false;
|
||||
}
|
||||
|
||||
this->currentRegisterValues[descriptor] = targetRegister.value;
|
||||
}
|
||||
|
||||
this->registerListScene->update();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::clearInlineRegisterValues() {
|
||||
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
|
||||
for (auto* registerWidget : registerGroupWidget->registerWidgets) {
|
||||
registerWidget->clearInlineValue();
|
||||
}
|
||||
for (auto& [registerDescriptor, registerItem] : this->registerItemsByDescriptor) {
|
||||
registerItem->clearValue();
|
||||
}
|
||||
|
||||
this->registerListScene->update();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::openInspectionWindow(const TargetRegisterDescriptor& registerDescriptor) {
|
||||
if (!TargetRegisterInspectorWindow::registerSupported(registerDescriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TargetRegisterInspectorWindow* inspectionWindow = nullptr;
|
||||
|
||||
const auto& currentValueIt = this->currentRegisterValues.find(registerDescriptor);
|
||||
const auto& inspectionWindowIt = this->inspectionWindowsByDescriptor.find(registerDescriptor);
|
||||
|
||||
if (inspectionWindowIt != this->inspectionWindowsByDescriptor.end()) {
|
||||
inspectionWindow = inspectionWindowIt->second;
|
||||
|
||||
} else {
|
||||
inspectionWindow = new TargetRegisterInspectorWindow(
|
||||
registerDescriptor,
|
||||
this->targetState,
|
||||
this
|
||||
);
|
||||
|
||||
this->inspectionWindowsByDescriptor.insert(std::pair(
|
||||
registerDescriptor,
|
||||
inspectionWindow
|
||||
));
|
||||
}
|
||||
|
||||
if (currentValueIt != this->currentRegisterValues.end()) {
|
||||
inspectionWindow->setValue(currentValueIt->second);
|
||||
}
|
||||
|
||||
inspectionWindow->show();
|
||||
inspectionWindow->activateWindow();
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::copyRegisterName(const TargetRegisterDescriptor& registerDescriptor) {
|
||||
QApplication::clipboard()->setText(QString::fromStdString(registerDescriptor.name.value_or("")).toUpper());
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::copyRegisterValueHex(const TargetRegisterDescriptor& registerDescriptor) {
|
||||
const auto& valueIt = this->currentRegisterValues.find(registerDescriptor);
|
||||
|
||||
if (valueIt == this->currentRegisterValues.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& value = valueIt->second;
|
||||
const auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(value.data()),
|
||||
static_cast<qsizetype>(value.size())
|
||||
).toHex();
|
||||
|
||||
QApplication::clipboard()->setText(QString(valueByteArray).toUpper());
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::copyRegisterValueDecimal(const TargetRegisterDescriptor& registerDescriptor) {
|
||||
const auto& valueIt = this->currentRegisterValues.find(registerDescriptor);
|
||||
|
||||
if (valueIt == this->currentRegisterValues.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& value = valueIt->second;
|
||||
const auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(value.data()),
|
||||
static_cast<qsizetype>(value.size())
|
||||
).toHex();
|
||||
|
||||
QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16)));
|
||||
}
|
||||
|
||||
void TargetRegistersPaneWidget::copyRegisterValueBinary(const TargetRegisterDescriptor& registerDescriptor) {
|
||||
const auto& valueIt = this->currentRegisterValues.find(registerDescriptor);
|
||||
|
||||
if (valueIt == this->currentRegisterValues.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& value = valueIt->second;
|
||||
const auto valueByteArray = QByteArray(
|
||||
reinterpret_cast<const char*>(value.data()),
|
||||
static_cast<qsizetype>(value.size())
|
||||
).toHex();
|
||||
|
||||
auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2);
|
||||
|
||||
if (bitString.size() < (value.size() * 8)) {
|
||||
bitString = bitString.rightJustified(static_cast<qsizetype>(value.size() * 8), '0');
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(bitString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,26 @@
|
||||
#include <QLineEdit>
|
||||
#include <QScrollArea>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
#include <QEvent>
|
||||
#include <QAction>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PaneWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.hpp"
|
||||
|
||||
#include "ItemWidget.hpp"
|
||||
#include "RegisterItem.hpp"
|
||||
#include "RegisterGroupItem.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/TargetRegisterInspectorWindow.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListView.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp"
|
||||
#include "src/Targets/TargetState.hpp"
|
||||
#include "src/Targets/TargetDescriptor.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class RegisterGroupWidget;
|
||||
class TargetRegistersPaneWidget: public PaneWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -35,9 +39,10 @@ namespace Bloom::Widgets
|
||||
void collapseAllRegisterGroups();
|
||||
void expandAllRegisterGroups();
|
||||
|
||||
void refreshRegisterValues(std::optional<std::function<void(void)>> callback = std::nullopt);
|
||||
|
||||
void onItemSelectionChange(Bloom::Widgets::ItemWidget* newlySelectedWidget);
|
||||
void refreshRegisterValues(
|
||||
std::optional<Targets::TargetRegisterDescriptor> registerDescriptor = std::nullopt,
|
||||
std::optional<std::function<void(void)>> callback = std::nullopt
|
||||
);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
@@ -52,18 +57,36 @@ namespace Bloom::Widgets
|
||||
SvgToolButton* expandAllButton = nullptr;
|
||||
|
||||
QLineEdit* searchInput = nullptr;
|
||||
QScrollArea* itemScrollArea = nullptr;
|
||||
QWidget* itemContainer = nullptr;
|
||||
ListView* registerListView = nullptr;
|
||||
ListScene* registerListScene = nullptr;
|
||||
|
||||
ItemWidget* selectedItemWidget = nullptr;
|
||||
|
||||
std::set<RegisterGroupWidget*> registerGroupWidgets;
|
||||
Targets::TargetRegisterDescriptors renderedDescriptors;
|
||||
Targets::TargetRegisterDescriptors registerDescriptors;
|
||||
std::vector<RegisterGroupItem*> registerGroupItems;
|
||||
std::unordered_map<Targets::TargetRegisterDescriptor, RegisterItem*> registerItemsByDescriptor;
|
||||
std::unordered_map<Targets::TargetRegisterDescriptor, TargetRegisterInspectorWindow*> inspectionWindowsByDescriptor;
|
||||
std::unordered_map<Targets::TargetRegisterDescriptor, Targets::TargetMemoryBuffer> currentRegisterValues;
|
||||
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
|
||||
// Context-menu actions
|
||||
QAction* openInspectionWindowAction = new QAction("Inspect", this);
|
||||
QAction* refreshValueAction = new QAction("Refresh Value", this);
|
||||
QAction* copyNameAction = new QAction("Copy Register Name", this);
|
||||
QAction* copyValueHexAction = new QAction("Copy Hexadecimal Value", this);
|
||||
QAction* copyValueDecimalAction = new QAction("Copy Decimal Value", this);
|
||||
QAction* copyValueBinaryAction = new QAction("Copy Binary Value", this);
|
||||
|
||||
RegisterItem* contextMenuRegisterItem = nullptr;
|
||||
|
||||
void onItemDoubleClicked(ListItem* clickedItem);
|
||||
void onItemContextMenu(ListItem* item, QPoint sourcePosition);
|
||||
void onTargetStateChanged(Targets::TargetState newState);
|
||||
void onRegistersRead(const Targets::TargetRegisters& registers);
|
||||
void clearInlineRegisterValues();
|
||||
void openInspectionWindow(const Targets::TargetRegisterDescriptor& registerDescriptor);
|
||||
void copyRegisterName(const Targets::TargetRegisterDescriptor& registerDescriptor);
|
||||
void copyRegisterValueHex(const Targets::TargetRegisterDescriptor& registerDescriptor);
|
||||
void copyRegisterValueDecimal(const Targets::TargetRegisterDescriptor& registerDescriptor);
|
||||
void copyRegisterValueBinary(const Targets::TargetRegisterDescriptor& registerDescriptor);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,31 +116,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="item-scroll-area">
|
||||
<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">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"/>
|
||||
</property>
|
||||
|
||||
<widget class="QWidget" name="item-container">
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
||||
|
||||
Reference in New Issue
Block a user