Removed using namespace directive for class member function definitions in source files

This commit is contained in:
Nav
2022-02-05 15:32:08 +00:00
parent 9bbc534973
commit 53a3c815d7
116 changed files with 13113 additions and 12664 deletions

View File

@@ -1,22 +1,23 @@
#include "ClickableWidget.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->clicked();
void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->clicked();
} else if (event->button() == Qt::MouseButton::RightButton) {
emit this->rightClicked();
}
} else if (event->button() == Qt::MouseButton::RightButton) {
emit this->rightClicked();
QWidget::mouseReleaseEvent(event);
}
QWidget::mouseReleaseEvent(event);
}
void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->doubleClicked();
}
void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->doubleClicked();
QWidget::mouseDoubleClickEvent(event);
}
QWidget::mouseDoubleClickEvent(event);
}

View File

@@ -7,58 +7,64 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Bloom::Exceptions::Exception;
using Bloom::Exceptions::Exception;
ErrorDialogue::ErrorDialogue(
const QString& windowTitle,
const QString& errorMessage,
QWidget* parent
): QDialog(parent) {
this->setObjectName("error-dialogue");
this->setAttribute(Qt::WA_DeleteOnClose, true);
this->setWindowTitle(windowTitle);
ErrorDialogue::ErrorDialogue(
const QString& windowTitle,
const QString& errorMessage,
QWidget* parent
): QDialog(parent) {
this->setObjectName("error-dialogue");
this->setAttribute(Qt::WA_DeleteOnClose, true);
this->setWindowTitle(windowTitle);
auto dialogueUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui"
)
);
auto dialogueUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui"
)
);
auto dialogueStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss"
)
);
auto dialogueStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss"
)
);
if (!dialogueUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue UI file");
}
if (!dialogueUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue UI file");
if (!dialogueStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue stylesheet file");
}
this->setStyleSheet(dialogueStylesheet.readAll());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&dialogueUiFile, this);
this->errorMessageDescriptionLabel = this->container->findChild<QLabel*>(
"error-message-description-label"
);
this->okButton = this->container->findChild<QPushButton*>("ok-btn");
this->container->setContentsMargins(15, 10, 15, 15);
this->errorMessageDescriptionLabel->setText(errorMessage);
QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close);
}
if (!dialogueStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue stylesheet file");
void ErrorDialogue::showEvent(QShowEvent* event) {
const auto containerSize = this->container->sizeHint();
const auto windowSize = QSize(
std::max(containerSize.width(), 500),
std::max(containerSize.height(), 100)
);
this->setFixedSize(windowSize);
this->container->setFixedSize(windowSize);
}
this->setStyleSheet(dialogueStylesheet.readAll());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&dialogueUiFile, this);
this->errorMessageDescriptionLabel = this->container->findChild<QLabel*>("error-message-description-label");
this->okButton = this->container->findChild<QPushButton*>("ok-btn");
this->container->setContentsMargins(15, 10, 15, 15);
this->errorMessageDescriptionLabel->setText(errorMessage);
QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close);
}
void ErrorDialogue::showEvent(QShowEvent* event) {
const auto containerSize = this->container->sizeHint();
const auto windowSize = QSize(std::max(containerSize.width(), 500), std::max(containerSize.height(), 100));
this->setFixedSize(windowSize);
this->container->setFixedSize(windowSize);
}

View File

@@ -1,35 +1,36 @@
#include "LabeledSeparator.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) {
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT);
}
LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) {
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT);
}
void LabeledSeparator::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void LabeledSeparator::drawWidget(QPainter& painter) {
const auto fontMetrics = painter.fontMetrics();
const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title);
const auto titleRect = QRect(
QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2),
titleSize
);
const auto lineYPosition = titleRect.y() + (titleRect.height() / 2);
const auto line = QLine(
titleRect.right() + 8,
lineYPosition,
this->width() - this->marginRight,
lineYPosition
);
painter.drawText(titleRect, Qt::AlignCenter, this->title);
painter.setPen(this->lineColor);
painter.drawLine(line);
void LabeledSeparator::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void LabeledSeparator::drawWidget(QPainter& painter) {
const auto fontMetrics = painter.fontMetrics();
const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title);
const auto titleRect = QRect(
QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2),
titleSize
);
const auto lineYPosition = titleRect.y() + (titleRect.height() / 2);
const auto line = QLine(
titleRect.right() + 8,
lineYPosition,
this->width() - this->marginRight,
lineYPosition
);
painter.drawText(titleRect, Qt::AlignCenter, this->title);
painter.setPen(this->lineColor);
painter.drawLine(line);
}
}

View File

@@ -2,138 +2,139 @@
#include <QLayout>
using namespace Bloom::Widgets;
PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) {
this->setMouseTracking(false);
this->setAttribute(Qt::WA_Hover, true);
}
void PanelWidget::setMinimumResize(int minimumResize) {
this->minimumResize = minimumResize;
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) {
this->setFixedWidth(this->minimumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) {
this->setFixedHeight(this->minimumResize);
namespace Bloom::Widgets
{
PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) {
this->setMouseTracking(false);
this->setAttribute(Qt::WA_Hover, true);
}
}
void PanelWidget::setMaximumResize(int maximumResize) {
this->maximumResize = maximumResize;
void PanelWidget::setMinimumResize(int minimumResize) {
this->minimumResize = minimumResize;
const auto currentSize = this->size();
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) {
this->setFixedWidth(this->maximumResize);
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) {
this->setFixedWidth(this->minimumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) {
this->setFixedHeight(this->maximumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) {
this->setFixedHeight(this->minimumResize);
}
}
}
void PanelWidget::setPanelType(PanelWidgetType panelType) {
this->panelType = panelType;
void PanelWidget::setMaximumResize(int maximumResize) {
this->maximumResize = maximumResize;
if (this->panelType == PanelWidgetType::LEFT) {
this->resizeCursor = Qt::SplitHCursor;
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
const auto currentSize = this->size();
} else {
this->resizeCursor = Qt::SplitVCursor;
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) {
this->setFixedWidth(this->maximumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) {
this->setFixedHeight(this->maximumResize);
}
}
}
bool PanelWidget::event(QEvent* event) {
if (event->type() == QEvent::Type::HoverMove) {
auto hoverEvent = static_cast<QHoverEvent*>(event);
if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) {
this->setCursor(this->resizeCursor);
void PanelWidget::setPanelType(PanelWidgetType panelType) {
this->panelType = panelType;
if (this->panelType == PanelWidgetType::LEFT) {
this->resizeCursor = Qt::SplitHCursor;
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
} else {
this->resizeCursor = Qt::SplitVCursor;
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
}
}
bool PanelWidget::event(QEvent* event) {
if (event->type() == QEvent::Type::HoverMove) {
auto hoverEvent = static_cast<QHoverEvent*>(event);
if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) {
this->setCursor(this->resizeCursor);
} else {
this->setCursor(Qt::ArrowCursor);
}
}
return QFrame::event(event);
}
void PanelWidget::mousePressEvent(QMouseEvent* event) {
const auto position = event->pos();
if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) {
this->resizingActive = true;
switch (this->panelType) {
case PanelWidgetType::LEFT: {
this->resizingOffset = this->width() - position.x();
break;
}
case PanelWidgetType::BOTTOM: {
this->resizingOffset = position.y();
break;
}
}
}
}
void PanelWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->resizingActive) {
this->resizingActive = false;
this->resizingOffset = 0;
this->setCursor(Qt::ArrowCursor);
}
}
return QFrame::event(event);
}
void PanelWidget::mouseMoveEvent(QMouseEvent* event) {
if (this->resizingActive) {
const auto position = event->pos();
void PanelWidget::mousePressEvent(QMouseEvent* event) {
const auto position = event->pos();
if (this->panelType == PanelWidgetType::LEFT) {
this->setFixedWidth(
std::max(
this->minimumResize,
std::min(this->maximumResize, position.x() + this->resizingOffset)
)
);
if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) {
this->resizingActive = true;
switch (this->panelType) {
case PanelWidgetType::LEFT: {
this->resizingOffset = this->width() - position.x();
break;
}
case PanelWidgetType::BOTTOM: {
this->resizingOffset = position.y();
break;
} else if (this->panelType == PanelWidgetType::BOTTOM) {
this->setFixedHeight(
std::max(
this->minimumResize,
std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset)
)
);
}
}
}
}
void PanelWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->resizingActive) {
this->resizingActive = false;
this->resizingOffset = 0;
this->setCursor(Qt::ArrowCursor);
}
}
void PanelWidget::mouseMoveEvent(QMouseEvent* event) {
if (this->resizingActive) {
const auto position = event->pos();
std::pair<QPoint, QPoint> PanelWidget::getHandleArea() const {
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT) {
this->setFixedWidth(
std::max(
this->minimumResize,
std::min(this->maximumResize, position.x() + this->resizingOffset)
)
return std::pair(
QPoint(currentSize.width() - this->handleSize, 0),
QPoint(currentSize.width(), currentSize.height())
);
} else if (this->panelType == PanelWidgetType::BOTTOM) {
this->setFixedHeight(
std::max(
this->minimumResize,
std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset)
)
} else {
return std::pair(
QPoint(0, 0),
QPoint(currentSize.width(), this->handleSize)
);
}
}
}
std::pair<QPoint, QPoint> PanelWidget::getHandleArea() const {
const auto currentSize = this->size();
bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const {
const auto handleArea = this->getHandleArea();
if (this->panelType == PanelWidgetType::LEFT) {
return std::pair(
QPoint(currentSize.width() - this->handleSize, 0),
QPoint(currentSize.width(), currentSize.height())
);
} else {
return std::pair(
QPoint(0, 0),
QPoint(currentSize.width(), this->handleSize)
return (
position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x()
&& position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y()
);
}
}
bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const {
const auto handleArea = this->getHandleArea();
return (
position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x()
&& position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y()
);
}

View File

@@ -2,60 +2,61 @@
#include <QPainter>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void RotatableLabel::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
static auto containerSize = this->getContainerSize();
static auto textSize = QLabel::minimumSizeHint();
static auto margins = this->contentsMargins();
painter.setClipRect(0, 0, containerSize.width(), containerSize.height());
painter.save();
painter.setPen(Qt::PenStyle::SolidLine);
painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484"));
painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2));
painter.rotate(this->angle);
painter.drawText(
-(textSize.width() / 2) + margins.left(),
(textSize.height() / 2) + margins.top(),
this->text()
);
void RotatableLabel::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
static auto containerSize = this->getContainerSize();
static auto textSize = QLabel::minimumSizeHint();
static auto margins = this->contentsMargins();
painter.setClipRect(0, 0, containerSize.width(), containerSize.height());
painter.save();
painter.setPen(Qt::PenStyle::SolidLine);
painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484"));
painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2));
painter.rotate(this->angle);
painter.drawText(
-(textSize.width() / 2) + margins.left(),
(textSize.height() / 2) + margins.top(),
this->text()
);
painter.restore();
}
QSize RotatableLabel::getContainerSize() const {
auto size = QSize();
auto textSize = QLabel::sizeHint();
if (this->angle % 360 == 0 || this->angle % 180 == 0) {
size = textSize;
} else if (this->angle % 90 == 0) {
size.setHeight(textSize.width());
size.setWidth(textSize.height());
} else {
auto angle = this->angle;
if (angle > 90) {
float angleMultiplier = static_cast<float>(angle) / 90;
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
angle = static_cast<int>(90 * angleMultiplier);
}
auto angleRadians = angle * (M_PI / 180);
size.setWidth(static_cast<int>(
std::cos(angleRadians) * textSize.width()
+ std::ceil(std::sin(angleRadians) * textSize.height())
))
;
size.setHeight(static_cast<int>(
std::sin(angleRadians) * textSize.width()
+ std::ceil(std::cos(angleRadians) * textSize.height())
));
painter.restore();
}
return size;
QSize RotatableLabel::getContainerSize() const {
auto size = QSize();
auto textSize = QLabel::sizeHint();
if (this->angle % 360 == 0 || this->angle % 180 == 0) {
size = textSize;
} else if (this->angle % 90 == 0) {
size.setHeight(textSize.width());
size.setWidth(textSize.height());
} else {
auto angle = this->angle;
if (angle > 90) {
float angleMultiplier = static_cast<float>(angle) / 90;
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
angle = static_cast<int>(90 * angleMultiplier);
}
auto angleRadians = angle * (M_PI / 180);
size.setWidth(static_cast<int>(
std::cos(angleRadians) * textSize.width()
+ std::ceil(std::sin(angleRadians) * textSize.height())
))
;
size.setHeight(static_cast<int>(
std::sin(angleRadians) * textSize.width()
+ std::ceil(std::cos(angleRadians) * textSize.height())
));
}
return size;
}
}

View File

@@ -2,22 +2,23 @@
#include <QMenu>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) {
this->setButtonWidth(10);
this->setButtonHeight(10);
}
SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) {
this->setButtonWidth(10);
this->setButtonHeight(10);
}
void SvgToolButton::childEvent(QChildEvent* childEvent) {
if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) {
/*
* If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button
* via QToolButton::setMenu().
*/
auto* menuWidget = qobject_cast<QMenu*>(childEvent->child());
if (menuWidget != nullptr && menuWidget != this->menu()) {
this->setMenu(menuWidget);
void SvgToolButton::childEvent(QChildEvent* childEvent) {
if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) {
/*
* If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button
* via QToolButton::setMenu().
*/
auto* menuWidget = qobject_cast<QMenu*>(childEvent->child());
if (menuWidget != nullptr && menuWidget != this->menu()) {
this->setMenu(menuWidget);
}
}
}
}

View File

@@ -3,71 +3,76 @@
#include <QPainter>
#include <cmath>
using namespace Bloom::Widgets;
SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) {
this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
void SvgWidget::startSpin() {
if (this->spinningAnimation == nullptr) {
this->spinningAnimation = new QPropertyAnimation(this, "angle", this);
this->spinningAnimation->setDuration(2000);
this->spinningAnimation->setStartValue(0);
this->spinningAnimation->setEndValue(360);
QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] {
this->spinningAnimation->start();
});
namespace Bloom::Widgets
{
SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) {
this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
this->spinningAnimation->start();
}
void SvgWidget::startSpin() {
if (this->spinningAnimation == nullptr) {
this->spinningAnimation = new QPropertyAnimation(this, "angle", this);
this->spinningAnimation->setDuration(2000);
this->spinningAnimation->setStartValue(0);
this->spinningAnimation->setEndValue(360);
void SvgWidget::stopSpin() {
if (this->spinningAnimation != nullptr) {
this->spinningAnimation->stop();
this->setAngle(0);
}
}
void SvgWidget::paintEvent(QPaintEvent* paintEvent) {
auto painter = QPainter(this);
auto svgSize = this->renderer.defaultSize();
auto margins = this->contentsMargins();
const auto containerSize = this->frameSize();
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
if (this->angle % 360 != 0) {
painter.translate(
std::ceil(static_cast<float>(containerSize.width() / 2)),
std::ceil(static_cast<float>(containerSize.height() / 2))
);
painter.rotate(this->angle);
painter.translate(
-std::ceil(static_cast<float>(containerSize.width() / 2)),
-std::ceil(static_cast<float>(containerSize.height() / 2))
);
}
this->renderer.render(&painter, QRectF(
std::ceil(static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())),
std::ceil(static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(margins.top())),
svgSize.width(),
svgSize.height()
));
}
void SvgWidget::changeEvent(QEvent* event) {
if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) {
if (!this->isEnabled()) {
this->renderer.load(this->disabledSvgFilePath);
} else {
this->renderer.load(this->svgFilePath);
QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] {
this->spinningAnimation->start();
});
}
this->repaint();
this->spinningAnimation->start();
}
void SvgWidget::stopSpin() {
if (this->spinningAnimation != nullptr) {
this->spinningAnimation->stop();
this->setAngle(0);
}
}
void SvgWidget::paintEvent(QPaintEvent* paintEvent) {
auto painter = QPainter(this);
auto svgSize = this->renderer.defaultSize();
auto margins = this->contentsMargins();
const auto containerSize = this->frameSize();
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
if (this->angle % 360 != 0) {
painter.translate(
std::ceil(static_cast<float>(containerSize.width() / 2)),
std::ceil(static_cast<float>(containerSize.height() / 2))
);
painter.rotate(this->angle);
painter.translate(
-std::ceil(static_cast<float>(containerSize.width() / 2)),
-std::ceil(static_cast<float>(containerSize.height() / 2))
);
}
this->renderer.render(&painter, QRectF(
std::ceil(
static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())
),
std::ceil(
static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(margins.top())
),
svgSize.width(),
svgSize.height()
));
}
void SvgWidget::changeEvent(QEvent* event) {
if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) {
if (!this->isEnabled()) {
this->renderer.load(this->disabledSvgFilePath);
} else {
this->renderer.load(this->svgFilePath);
}
this->repaint();
}
}
}

View File

@@ -4,109 +4,115 @@
#include "ByteItem.hpp"
using namespace Bloom::Widgets;
AnnotationItem::AnnotationItem(
std::uint32_t startAddress,
std::size_t size,
QString labelText,
AnnotationItemPosition position
):
QGraphicsItem(nullptr),
startAddress(startAddress),
size(size),
endAddress(static_cast<std::uint32_t>(startAddress + size - 1)),
labelText(std::move(labelText)),
position(position),
width(static_cast<int>((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)),
height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT
) {
this->setAcceptHoverEvents(true);
this->setToolTip(this->labelText);
}
AnnotationItem::AnnotationItem(
const Targets::TargetMemoryAddressRange& addressRange,
const QString& labelText,
AnnotationItemPosition position
): AnnotationItem(
addressRange.startAddress,
addressRange.endAddress - addressRange.startAddress + 1,
labelText,
position
) {}
AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position)
: AnnotationItem(
focusedMemoryRegion.addressRange,
focusedMemoryRegion.name,
position
) {}
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto lineColor = this->getLineColor();
auto labelFontColor = this->getLabelFontColor();
auto font = painter->font();
font.setPixelSize(this->getLabelFontSize());
painter->setFont(font);
const auto isEnabled = this->isEnabled();
lineColor.setAlpha(isEnabled ? 255 : 100);
labelFontColor.setAlpha(isEnabled ? 255 : 100);
const auto fontMetrics = painter->fontMetrics();
auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText);
if (labelSize.width() > this->width) {
labelSize.setWidth(this->width);
this->labelText = fontMetrics.elidedText(this->labelText, Qt::TextElideMode::ElideRight, this->width);
namespace Bloom::Widgets
{
AnnotationItem::AnnotationItem(
std::uint32_t startAddress,
std::size_t size,
QString labelText,
AnnotationItemPosition position
):
QGraphicsItem(nullptr),
startAddress(startAddress),
size(size),
endAddress(static_cast<std::uint32_t>(startAddress + size - 1)),
labelText(std::move(labelText)),
position(position),
width(static_cast<int>((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)),
height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT
) {
this->setAcceptHoverEvents(true);
this->setToolTip(this->labelText);
}
constexpr auto verticalLineLength = 5;
const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0 : AnnotationItem::TOP_HEIGHT;
const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ?
verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength;
AnnotationItem::AnnotationItem(
const Targets::TargetMemoryAddressRange& addressRange,
const QString& labelText,
AnnotationItemPosition position
): AnnotationItem(
addressRange.startAddress,
addressRange.endAddress - addressRange.startAddress + 1,
labelText,
position
) {}
const auto labelRect = QRect(
(this->width - labelSize.width()) / 2,
verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6),
labelSize.width(),
labelSize.height()
);
AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position)
: AnnotationItem(
focusedMemoryRegion.addressRange,
focusedMemoryRegion.name,
position
) {}
painter->setPen(lineColor);
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto lineColor = this->getLineColor();
auto labelFontColor = this->getLabelFontColor();
if (this->size > 1) {
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYStart,
ByteItem::WIDTH / 2,
verticalLineYEnd
));
auto font = painter->font();
font.setPixelSize(this->getLabelFontSize());
painter->setFont(font);
const auto isEnabled = this->isEnabled();
lineColor.setAlpha(isEnabled ? 255 : 100);
labelFontColor.setAlpha(isEnabled ? 255 : 100);
const auto fontMetrics = painter->fontMetrics();
auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText);
if (labelSize.width() > this->width) {
labelSize.setWidth(this->width);
this->labelText = fontMetrics.elidedText(
this->labelText,
Qt::TextElideMode::ElideRight,
this->width
);
}
constexpr auto verticalLineLength = 5;
const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0
: AnnotationItem::TOP_HEIGHT;
const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ?
verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength;
const auto labelRect = QRect(
(this->width - labelSize.width()) / 2,
verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6),
labelSize.width(),
labelSize.height()
);
painter->setPen(lineColor);
if (this->size > 1) {
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYStart,
ByteItem::WIDTH / 2,
verticalLineYEnd
));
painter->drawLine(QLine(
this->width - (ByteItem::WIDTH / 2),
verticalLineYStart,
this->width - (ByteItem::WIDTH / 2),
verticalLineYEnd
));
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYEnd,
(ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH),
verticalLineYEnd
));
}
painter->drawLine(QLine(
this->width - (ByteItem::WIDTH / 2),
verticalLineYStart,
this->width - (ByteItem::WIDTH / 2),
verticalLineYEnd
));
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
this->width / 2,
verticalLineYEnd,
(ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH),
verticalLineYEnd
this->width / 2,
(this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4)
));
painter->setPen(labelFontColor);
painter->drawText(labelRect, Qt::AlignCenter, this->labelText);
}
painter->drawLine(QLine(
this->width / 2,
verticalLineYEnd,
this->width / 2,
(this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4)
));
painter->setPen(labelFontColor);
painter->drawText(labelRect, Qt::AlignCenter, this->labelText);
}

View File

@@ -1,71 +1,72 @@
#include "ByteAddressContainer.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ByteAddressContainer::adjustAddressLabels(
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
) {
static const auto margins = QMargins(0, 10, 0, 0);
void ByteAddressContainer::adjustAddressLabels(
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
) {
static const auto margins = QMargins(0, 10, 0, 0);
const auto newRowCount = byteItemsByRowIndex.size();
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
const auto newRowCount = byteItemsByRowIndex.size();
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
for (const auto& mappingPair : byteItemsByRowIndex) {
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
const auto& byteItems = mappingPair.second;
for (const auto& mappingPair : byteItemsByRowIndex) {
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
const auto& byteItems = mappingPair.second;
if (byteItems.empty()) {
continue;
}
if (byteItems.empty()) {
continue;
ByteAddressItem* addressLabel = nullptr;
if (static_cast<int>(rowIndex) > layoutItemMaxIndex) {
addressLabel = new ByteAddressItem(this);
this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel));
} else {
addressLabel = this->addressItemsByRowIndex.at(rowIndex);
}
const auto& firstByteItem = byteItems.front();
addressLabel->setAddressHex(firstByteItem->relativeAddressHex);
addressLabel->setPos(
0,
firstByteItem->pos().y()
);
}
ByteAddressItem* addressLabel = nullptr;
if (static_cast<int>(rowIndex) > layoutItemMaxIndex) {
addressLabel = new ByteAddressItem(this);
this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel));
// Delete any address items we no longer need
const auto addressItemCount = this->addressItemsByRowIndex.size();
} else {
addressLabel = this->addressItemsByRowIndex.at(rowIndex);
if (newRowCount > 0 && newRowCount < addressItemCount) {
for (auto i = (addressItemCount - 1); i >= newRowCount; i--) {
delete this->addressItemsByRowIndex.at(i);
this->addressItemsByRowIndex.erase(i);
}
}
const auto& firstByteItem = byteItems.front();
addressLabel->setAddressHex(firstByteItem->relativeAddressHex);
addressLabel->setPos(
this->update();
}
void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
static const auto backgroundColor = QColor(0x35, 0x36, 0x33);
static const auto borderColor = QColor(0x41, 0x42, 0x3F);
painter->setPen(Qt::PenStyle::NoPen);
painter->setBrush(backgroundColor);
painter->drawRect(
0,
firstByteItem->pos().y()
0,
ByteAddressContainer::WIDTH,
static_cast<int>(this->boundingRect().height())
);
painter->setPen(borderColor);
painter->drawLine(
ByteAddressContainer::WIDTH - 1,
0,
ByteAddressContainer::WIDTH - 1,
static_cast<int>(this->boundingRect().height())
);
}
// Delete any address items we no longer need
const auto addressItemCount = this->addressItemsByRowIndex.size();
if (newRowCount > 0 && newRowCount < addressItemCount) {
for (auto i = (addressItemCount - 1); i >= newRowCount; i--) {
delete this->addressItemsByRowIndex.at(i);
this->addressItemsByRowIndex.erase(i);
}
}
this->update();
}
void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
static const auto backgroundColor = QColor(0x35, 0x36, 0x33);
static const auto borderColor = QColor(0x41, 0x42, 0x3F);
painter->setPen(Qt::PenStyle::NoPen);
painter->setBrush(backgroundColor);
painter->drawRect(
0,
0,
ByteAddressContainer::WIDTH,
static_cast<int>(this->boundingRect().height())
);
painter->setPen(borderColor);
painter->drawLine(
ByteAddressContainer::WIDTH - 1,
0,
ByteAddressContainer::WIDTH - 1,
static_cast<int>(this->boundingRect().height())
);
}

View File

@@ -1,28 +1,31 @@
#include "ByteAddressItem.hpp"
#include <QPainter>
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ByteAddressItem::setAddressHex(const QString& addressHex) {
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT)
);
this->addressHex = addressHex;
}
void ByteAddressItem::setAddressHex(const QString& addressHex) {
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT)
);
this->addressHex = addressHex;
}
void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
static const auto widgetRect = this->boundingRect();
static auto fontColor = QColor(0x8F, 0x91, 0x92);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(12);
fontColor.setAlpha(!this->isEnabled() ? 100 : 255);
painter->setFont(font);
painter->setPen(fontColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex);
void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
static const auto widgetRect = this->boundingRect();
static auto fontColor = QColor(0x8F, 0x91, 0x92);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(12);
fontColor.setAlpha(!this->isEnabled() ? 100 : 255);
painter->setFont(font);
painter->setPen(fontColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex);
}
}

View File

@@ -1,166 +1,175 @@
#include "ByteItem.hpp"
#include <QPainter>
#include <QStyle>
using namespace Bloom::Widgets;
ByteItem::ByteItem(
std::size_t byteIndex,
std::uint32_t address,
std::optional<std::uint32_t>& currentStackPointer,
std::optional<ByteItem*>& hoveredByteItem,
std::optional<AnnotationItem*>& hoveredAnnotationItem,
std::set<std::uint32_t>& highlightedAddresses,
const HexViewerWidgetSettings& settings
):
QGraphicsItem(nullptr),
byteIndex(byteIndex),
address(address),
currentStackPointer(currentStackPointer),
hoveredByteItem(hoveredByteItem),
hoveredAnnotationItem(hoveredAnnotationItem),
highlightedAddresses(highlightedAddresses),
settings(settings)
namespace Bloom::Widgets
{
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteItem::WIDTH, ByteItem::HEIGHT)
);
this->setAcceptHoverEvents(true);
ByteItem::ByteItem(
std::size_t byteIndex,
std::uint32_t address,
std::optional<std::uint32_t>& currentStackPointer,
std::optional<ByteItem*>& hoveredByteItem,
std::optional<AnnotationItem*>& hoveredAnnotationItem,
std::set<std::uint32_t>& highlightedAddresses,
const HexViewerWidgetSettings& settings
)
: QGraphicsItem(nullptr)
, byteIndex(byteIndex)
, address(address)
, currentStackPointer(currentStackPointer)
, hoveredByteItem(hoveredByteItem)
, hoveredAnnotationItem(hoveredAnnotationItem)
, highlightedAddresses(highlightedAddresses)
, settings(settings)
{
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteItem::WIDTH, ByteItem::HEIGHT)
);
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->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 = this->excludedMemoryRegion == nullptr;
this->update();
}
void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
painter->setPen(Qt::PenStyle::NoPen);
// TODO: This code could do with some tidying. It's getting quite messy.
static const auto widgetRect = this->boundingRect();
static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3);
static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA);
static auto font = QFont("'Ubuntu', sans-serif");
static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255);
static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255);
static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210);
static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70);
static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30);
static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50);
const auto isEnabled = this->isEnabled();
const auto highlightingEnabled = !this->highlightedAddresses.empty();
const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address);
auto textColor = standardTextColor;
auto asciiTextColor = QColor(0xA7, 0x77, 0x26);
auto backgroundColor = std::optional<QColor>();
font.setPixelSize(11);
painter->setFont(font);
if (highlightedByte) {
backgroundColor = highlightedBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value()
&& this->address > this->currentStackPointer
) {
// This byte is within the stack memory
backgroundColor = stackMemoryBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) {
// This byte is within a focused region
backgroundColor = focusedRegionBackgroundColor;
this->setSelected(false);
}
const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr);
const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr);
if (hoveredByteItem != nullptr) {
if (hoveredByteItem == this) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(255);
void ByteItem::setValue(unsigned char value) {
this->valueChanged = this->valueInitialised && this->value != value;
} else {
backgroundColor = hoveredBackgroundColor;
}
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;
} else if (this->settings.highlightHoveredRowAndCol
&& (
hoveredByteItem->currentColumnIndex == this->currentColumnIndex
|| hoveredByteItem->currentRowIndex == this->currentRowIndex
)
this->valueInitialised = this->excludedMemoryRegion == nullptr;
this->update();
}
void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
painter->setPen(Qt::PenStyle::NoPen);
// TODO: This code could do with some tidying. It's getting quite messy.
static const auto widgetRect = this->boundingRect();
static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3);
static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA);
static auto font = QFont("'Ubuntu', sans-serif");
static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255);
static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255);
static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210);
static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70);
static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30);
static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50);
const auto isEnabled = this->isEnabled();
const auto highlightingEnabled = !this->highlightedAddresses.empty();
const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address);
auto textColor = standardTextColor;
auto asciiTextColor = QColor(0xA7, 0x77, 0x26);
auto backgroundColor = std::optional<QColor>();
font.setPixelSize(11);
painter->setFont(font);
if (highlightedByte) {
backgroundColor = highlightedBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value()
&& this->address > this->currentStackPointer
) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(220);
// This byte is within the stack memory
backgroundColor = stackMemoryBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) {
// This byte is within a focused region
backgroundColor = focusedRegionBackgroundColor;
}
const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr);
const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr);
if (hoveredByteItem != nullptr) {
if (hoveredByteItem == this) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(255);
} else {
backgroundColor = hoveredBackgroundColor;
}
} else if (this->settings.highlightHoveredRowAndCol
&& (
hoveredByteItem->currentColumnIndex == this->currentColumnIndex
|| hoveredByteItem->currentRowIndex == this->currentRowIndex
)
) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(220);
} else {
backgroundColor = hoveredNeighbourBackgroundColor;
}
}
} else if (
!this->settings.highlightFocusedMemory
&& hoveredAnnotationItem != nullptr
&& this->address >= hoveredAnnotationItem->startAddress
&& this->address <= hoveredAnnotationItem->endAddress
) {
backgroundColor = hoveredAnnotationBackgroundColor;
}
if (backgroundColor.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
backgroundColor->setAlpha(100);
}
painter->setBrush(backgroundColor.value());
painter->drawRect(widgetRect);
}
if (this->valueInitialised && this->excludedMemoryRegion == nullptr) {
if (this->settings.displayAsciiValues && this->asciiValue.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
asciiTextColor.setAlpha(100);
}
painter->setPen(asciiTextColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value());
} else {
backgroundColor = hoveredNeighbourBackgroundColor;
if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) {
textColor.setAlpha(100);
}
painter->setPen(textColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue);
return;
}
}
} else if (
!this->settings.highlightFocusedMemory
&& hoveredAnnotationItem != nullptr
&& this->address >= hoveredAnnotationItem->startAddress
&& this->address <= hoveredAnnotationItem->endAddress
) {
backgroundColor = hoveredAnnotationBackgroundColor;
}
if (backgroundColor.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
backgroundColor->setAlpha(100);
}
painter->setBrush(backgroundColor.value());
painter->drawRect(widgetRect);
}
if (this->valueInitialised && this->excludedMemoryRegion == nullptr) {
if (this->settings.displayAsciiValues && this->asciiValue.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
asciiTextColor.setAlpha(100);
}
painter->setPen(asciiTextColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value());
} else {
if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) {
textColor.setAlpha(100);
}
textColor.setAlpha(100);
painter->setPen(textColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue);
return;
static const auto placeholderString = QString("??");
painter->drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
} else {
textColor.setAlpha(100);
painter->setPen(textColor);
static const auto placeholderString = QString("??");
painter->drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
}

View File

@@ -1,53 +1,54 @@
#include "ByteItemContainerGraphicsView.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Bloom::Targets::TargetMemoryDescriptor;
using Bloom::Targets::TargetMemoryDescriptor;
ByteItemContainerGraphicsView::ByteItemContainerGraphicsView(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QWidget* parent
): QGraphicsView(parent) {
this->setObjectName("graphics-view");
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
ByteItemContainerGraphicsView::ByteItemContainerGraphicsView(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QWidget* parent
): QGraphicsView(parent) {
this->setObjectName("graphics-view");
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
this->scene = new ByteItemGraphicsScene(
targetMemoryDescriptor,
focusedMemoryRegions,
excludedMemoryRegions,
insightWorker,
settings,
hoveredAddressLabel,
this
);
this->scene = new ByteItemGraphicsScene(
targetMemoryDescriptor,
focusedMemoryRegions,
excludedMemoryRegions,
insightWorker,
settings,
hoveredAddressLabel,
this
);
this->setScene(this->scene);
this->setScene(this->scene);
}
void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) {
this->centerOn(this->scene->getByteItemPositionByAddress(address));
}
bool ByteItemContainerGraphicsView::event(QEvent* event) {
const auto eventType = event->type();
if (eventType == QEvent::Type::EnabledChange) {
this->scene->setEnabled(this->isEnabled());
}
return QGraphicsView::event(event);
}
void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) {
this->centerOn(this->scene->getByteItemPositionByAddress(address));
}
void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) {
QGraphicsView::resizeEvent(event);
this->scene->adjustSize();
bool ByteItemContainerGraphicsView::event(QEvent* event) {
const auto eventType = event->type();
if (eventType == QEvent::Type::EnabledChange) {
this->scene->setEnabled(this->isEnabled());
}
return QGraphicsView::event(event);
}
void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) {
QGraphicsView::resizeEvent(event);
this->scene->adjustSize();
}
}

View File

@@ -2,494 +2,497 @@
#include <cmath>
using namespace Bloom::Widgets;
using Bloom::Targets::TargetMemoryDescriptor;
ByteItemGraphicsScene::ByteItemGraphicsScene(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QGraphicsView* parent
):
QGraphicsScene(parent),
targetMemoryDescriptor(targetMemoryDescriptor),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions),
insightWorker(insightWorker),
settings(settings),
hoveredAddressLabel(hoveredAddressLabel),
parent(parent)
namespace Bloom::Widgets
{
this->setObjectName("byte-widget-container");
using Bloom::Targets::TargetMemoryDescriptor;
this->byteAddressContainer = new ByteAddressContainer();
this->addItem(this->byteAddressContainer);
ByteItemGraphicsScene::ByteItemGraphicsScene(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QGraphicsView* parent
)
: QGraphicsScene(parent)
, targetMemoryDescriptor(targetMemoryDescriptor)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
, insightWorker(insightWorker)
, settings(settings)
, hoveredAddressLabel(hoveredAddressLabel)
, parent(parent)
{
this->setObjectName("byte-widget-container");
// Construct ByteWidget objects
const auto memorySize = this->targetMemoryDescriptor.size();
const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (std::uint32_t i = 0; i < memorySize; i++) {
const auto address = startAddress + i;
this->byteAddressContainer = new ByteAddressContainer();
this->addItem(this->byteAddressContainer);
auto* byteWidget = new ByteItem(
i,
address,
this->currentStackPointer,
this->hoveredByteWidget,
this->hoveredAnnotationItem,
this->highlightedAddresses,
settings
// Construct ByteWidget objects
const auto memorySize = this->targetMemoryDescriptor.size();
const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (std::uint32_t i = 0; i < memorySize; i++) {
const auto address = startAddress + i;
auto* byteWidget = new ByteItem(
i,
address,
this->currentStackPointer,
this->hoveredByteWidget,
this->hoveredAnnotationItem,
this->highlightedAddresses,
settings
);
this->byteItemsByAddress.insert(std::pair(
address,
byteWidget
));
this->addItem(byteWidget);
}
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&ByteItemGraphicsScene::onTargetStateChanged
);
this->byteItemsByAddress.insert(std::pair(
address,
byteWidget
));
this->addItem(byteWidget);
this->refreshRegions();
this->adjustSize();
}
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&ByteItemGraphicsScene::onTargetStateChanged
);
void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->setValue(buffer.at(byteWidget->byteIndex));
}
this->refreshRegions();
this->adjustSize();
}
void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->setValue(buffer.at(byteWidget->byteIndex));
this->updateAnnotationValues(buffer);
this->lastValueBuffer = buffer;
}
this->updateAnnotationValues(buffer);
this->lastValueBuffer = buffer;
}
void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
this->currentStackPointer = stackPointer;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
this->currentStackPointer = stackPointer;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::setHighlightedAddresses(const std::set<std::uint32_t>& highlightedAddresses) {
this->highlightedAddresses = highlightedAddresses;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::setHighlightedAddresses(const std::set<std::uint32_t>& highlightedAddresses) {
this->highlightedAddresses = highlightedAddresses;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::refreshRegions() {
for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) {
byteWidget->focusedMemoryRegion = nullptr;
byteWidget->excludedMemoryRegion = nullptr;
void ByteItemGraphicsScene::refreshRegions() {
for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) {
byteWidget->focusedMemoryRegion = nullptr;
byteWidget->excludedMemoryRegion = nullptr;
for (const auto& focusedRegion : this->focusedMemoryRegions) {
if (byteAddress >= focusedRegion.addressRange.startAddress
&& byteAddress <= focusedRegion.addressRange.endAddress
) {
byteWidget->focusedMemoryRegion = &focusedRegion;
break;
}
}
for (const auto& excludedRegion : this->excludedMemoryRegions) {
if (byteAddress >= excludedRegion.addressRange.startAddress
&& byteAddress <= excludedRegion.addressRange.endAddress
) {
byteWidget->excludedMemoryRegion = &excludedRegion;
break;
}
}
byteWidget->update();
}
// Refresh annotation items
this->hoveredAnnotationItem = std::nullopt;
for (auto* annotationItem : this->annotationItems) {
this->removeItem(annotationItem);
delete annotationItem;
}
this->annotationItems.clear();
this->valueAnnotationItems.clear();
for (const auto& focusedRegion : this->focusedMemoryRegions) {
if (byteAddress >= focusedRegion.addressRange.startAddress
&& byteAddress <= focusedRegion.addressRange.endAddress
) {
byteWidget->focusedMemoryRegion = &focusedRegion;
break;
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
annotationItem->setEnabled(this->enabled);
this->addItem(annotationItem);
this->annotationItems.emplace_back(annotationItem);
if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) {
auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion);
valueAnnotationItem->setEnabled(this->enabled);
this->addItem(valueAnnotationItem);
this->annotationItems.emplace_back(valueAnnotationItem);
this->valueAnnotationItems.emplace_back(valueAnnotationItem);
}
}
for (const auto& excludedRegion : this->excludedMemoryRegions) {
if (byteAddress >= excludedRegion.addressRange.startAddress
&& byteAddress <= excludedRegion.addressRange.endAddress
) {
byteWidget->excludedMemoryRegion = &excludedRegion;
break;
if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) {
this->updateAnnotationValues(this->lastValueBuffer);
}
this->adjustSize(true);
}
void ByteItemGraphicsScene::adjustSize(bool forced) {
const auto width = this->getSceneWidth();
const auto columnCount = static_cast<std::size_t>(
std::floor(
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH
+ ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto rowCount = static_cast<int>(
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(columnCount))
);
// Don't bother recalculating the byte & annotation positions if the number of rows & columns haven't changed.
if (this->byteItemsByAddress.empty()
|| (
!forced
&& rowCount == this->byteItemsByRowIndex.size()
&& columnCount == this->byteItemsByColumnIndex.size()
)
) {
this->setSceneRect(
0,
0,
width,
std::max(static_cast<int>(this->sceneRect().height()), this->parent->viewport()->height())
);
return;
}
if (!this->byteItemsByAddress.empty()) {
this->adjustByteItemPositions();
if (this->settings.displayAnnotations) {
this->adjustAnnotationItemPositions();
}
const auto* lastByteItem = (--this->byteItemsByAddress.end())->second;
const auto sceneHeight = static_cast<int>(
lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom()
);
this->setSceneRect(
0,
0,
width,
std::max(sceneHeight, this->parent->height())
);
}
byteWidget->update();
this->update();
}
// Refresh annotation items
this->hoveredAnnotationItem = std::nullopt;
for (auto* annotationItem : this->annotationItems) {
this->removeItem(annotationItem);
delete annotationItem;
}
void ByteItemGraphicsScene::setEnabled(bool enabled) {
if (this->enabled != enabled) {
this->enabled = enabled;
this->annotationItems.clear();
this->valueAnnotationItems.clear();
for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) {
byteItem->setEnabled(this->enabled);
}
for (const auto& focusedRegion : this->focusedMemoryRegions) {
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
annotationItem->setEnabled(this->enabled);
this->addItem(annotationItem);
this->annotationItems.emplace_back(annotationItem);
for (auto* annotationItem : this->annotationItems) {
annotationItem->setEnabled(this->enabled);
}
if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) {
auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion);
valueAnnotationItem->setEnabled(this->enabled);
this->addItem(valueAnnotationItem);
this->annotationItems.emplace_back(valueAnnotationItem);
this->valueAnnotationItems.emplace_back(valueAnnotationItem);
this->byteAddressContainer->setEnabled(enabled);
this->byteAddressContainer->update();
this->update();
}
}
if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) {
this->updateAnnotationValues(this->lastValueBuffer);
}
this->adjustSize(true);
}
void ByteItemGraphicsScene::adjustSize(bool forced) {
const auto width = this->getSceneWidth();
const auto columnCount = static_cast<std::size_t>(
std::floor(
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN)
/ (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto rowCount = static_cast<int>(
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(columnCount))
);
// Don't bother recalculating the byte item & annotation positions if the number of rows & columns have not changed.
if (this->byteItemsByAddress.empty()
|| (
!forced
&& rowCount == this->byteItemsByRowIndex.size()
&& columnCount == this->byteItemsByColumnIndex.size()
)
) {
this->setSceneRect(
0,
0,
width,
std::max(static_cast<int>(this->sceneRect().height()), this->parent->viewport()->height())
);
return;
}
if (!this->byteItemsByAddress.empty()) {
this->adjustByteItemPositions();
if (this->settings.displayAnnotations) {
this->adjustAnnotationItemPositions();
}
const auto* lastByteItem = (--this->byteItemsByAddress.end())->second;
const auto sceneHeight = static_cast<int>(
lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom()
);
this->setSceneRect(
0,
0,
width,
std::max(sceneHeight, this->parent->height())
);
}
this->update();
}
void ByteItemGraphicsScene::setEnabled(bool enabled) {
if (this->enabled != enabled) {
this->enabled = enabled;
for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) {
byteItem->setEnabled(this->enabled);
void ByteItemGraphicsScene::invalidateChildItemCaches() {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->update();
}
for (auto* annotationItem : this->annotationItems) {
annotationItem->setEnabled(this->enabled);
annotationItem->update();
}
}
QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) {
if (this->byteItemsByAddress.contains(address)) {
return this->byteItemsByAddress.at(address)->pos();
}
this->byteAddressContainer->setEnabled(enabled);
this->byteAddressContainer->update();
this->update();
}
}
void ByteItemGraphicsScene::invalidateChildItemCaches() {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->update();
return QPointF();
}
for (auto* annotationItem : this->annotationItems) {
annotationItem->update();
}
}
QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) {
if (this->byteItemsByAddress.contains(address)) {
return this->byteItemsByAddress.at(address)->pos();
}
return QPointF();
}
bool ByteItemGraphicsScene::event(QEvent* event) {
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
return QGraphicsScene::event(event);
}
void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
auto hoveredItems = this->items(mouseEvent->scenePos());
ByteItem* hoveredByteItem = nullptr;
AnnotationItem* hoveredAnnotationItem = nullptr;
if (!hoveredItems.empty()) {
hoveredByteItem = dynamic_cast<ByteItem*>(hoveredItems.at(0));
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(hoveredItems.at(0));
}
if (hoveredByteItem != nullptr) {
this->onByteWidgetEnter(hoveredByteItem);
return;
}
if (this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
if (hoveredAnnotationItem != nullptr) {
this->onAnnotationItemEnter(hoveredAnnotationItem);
return;
}
if (this->hoveredAnnotationItem.has_value()) {
this->onAnnotationItemLeave();
}
}
void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) {
const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (auto* valueAnnotationItem : this->valueAnnotationItems) {
if (valueAnnotationItem->size > buffer.size()) {
continue;
bool ByteItemGraphicsScene::event(QEvent* event) {
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress;
const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress;
valueAnnotationItem->setValue(Targets::TargetMemoryBuffer(
buffer.begin() + relativeStartAddress,
buffer.begin() + relativeEndAddress + 1
));
return QGraphicsScene::event(event);
}
}
void ByteItemGraphicsScene::adjustByteItemPositions() {
const auto columnCount = static_cast<std::size_t>(
std::floor(
(this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN)
/ (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
auto hoveredItems = this->items(mouseEvent->scenePos());
ByteItem* hoveredByteItem = nullptr;
AnnotationItem* hoveredAnnotationItem = nullptr;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
if (!hoveredItems.empty()) {
hoveredByteItem = dynamic_cast<ByteItem*>(hoveredItems.at(0));
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(hoveredItems.at(0));
}
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (hoveredByteItem != nullptr) {
this->onByteWidgetEnter(hoveredByteItem);
return;
}
for (auto* annotationItem : this->annotationItems) {
const auto firstByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
if (this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
if (hoveredAnnotationItem != nullptr) {
this->onAnnotationItemEnter(hoveredAnnotationItem);
return;
}
if (this->hoveredAnnotationItem.has_value()) {
this->onAnnotationItemLeave();
}
}
void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) {
const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (auto* valueAnnotationItem : this->valueAnnotationItems) {
if (valueAnnotationItem->size > buffer.size()) {
continue;
}
const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress;
const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress;
valueAnnotationItem->setValue(Targets::TargetMemoryBuffer(
buffer.begin() + relativeStartAddress,
buffer.begin() + relativeEndAddress + 1
));
}
}
void ByteItemGraphicsScene::adjustByteItemPositions() {
const auto columnCount = static_cast<std::size_t>(
std::floor(
(this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH
+ ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto lastByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
// We only display annotations that span a single row.
if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) {
annotationItem->show();
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
for (auto* annotationItem : this->annotationItems) {
const auto firstByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
const auto lastByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
// We only display annotations that span a single row.
if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) {
annotationItem->show();
if (annotationItem->position == AnnotationItemPosition::TOP) {
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
}
} else {
annotationItem->hide();
}
}
constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT;
constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT;
std::size_t lastRowIndex = 0;
int rowYPosition = margins.top();
auto currentRowAnnotatedTop = false;
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
const auto rowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
);
const auto columnIndex = static_cast<std::size_t>(
static_cast<double>(byteWidget->byteIndex)
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(columnCount))
);
if (rowIndex != lastRowIndex) {
rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN;
currentRowAnnotatedTop = false;
if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) {
rowYPosition += annotationBottomHeight;
}
}
if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) {
rowYPosition += annotationTopHeight;
currentRowAnnotatedTop = true;
}
byteWidget->setPos(
static_cast<int>(
columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left()
+ ByteAddressContainer::WIDTH
),
rowYPosition
);
byteWidget->currentRowIndex = rowIndex;
byteWidget->currentColumnIndex = columnIndex;
byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget);
byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget);
lastRowIndex = rowIndex;
}
this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex);
this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex);
this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex);
}
void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) {
return;
}
for (auto* annotationItem : this->annotationItems) {
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
annotationItem->hide();
continue;
}
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
if (annotationItem->position == AnnotationItemPosition::TOP) {
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
);
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() + ByteItem::HEIGHT
);
}
}
}
void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) {
if (this->hoveredByteWidget.has_value()) {
if (this->hoveredByteWidget.value() == widget) {
// This byte item is already marked as hovered
return;
}
this->onByteWidgetLeave();
}
this->hoveredByteWidget = widget;
this->hoveredAddressLabel->setText(
"Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")"
);
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) {
byteWidget->update();
}
} else {
annotationItem->hide();
widget->update();
}
}
constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT;
constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT;
void ByteItemGraphicsScene::onByteWidgetLeave() {
auto* byteItem = this->hoveredByteWidget.value();
this->hoveredByteWidget = std::nullopt;
std::size_t lastRowIndex = 0;
int rowYPosition = margins.top();
auto currentRowAnnotatedTop = false;
this->hoveredAddressLabel->setText("Relative Address (Absolute Address):");
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
const auto rowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
);
const auto columnIndex = static_cast<std::size_t>(
static_cast<double>(byteWidget->byteIndex)
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(columnCount))
);
if (rowIndex != lastRowIndex) {
rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN;
currentRowAnnotatedTop = false;
if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) {
rowYPosition += annotationBottomHeight;
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) {
byteWidget->update();
}
} else {
byteItem->update();
}
if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) {
rowYPosition += annotationTopHeight;
currentRowAnnotatedTop = true;
}
byteWidget->setPos(
static_cast<int>(
columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left() + ByteAddressContainer::WIDTH),
rowYPosition
);
byteWidget->currentRowIndex = rowIndex;
byteWidget->currentColumnIndex = columnIndex;
byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget);
byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget);
lastRowIndex = rowIndex;
}
this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex);
this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex);
void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) {
if (this->hoveredAnnotationItem.has_value()) {
if (this->hoveredAnnotationItem.value() == annotationItem) {
return;
}
this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex);
}
void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) {
return;
}
for (auto* annotationItem : this->annotationItems) {
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
annotationItem->hide();
continue;
this->onAnnotationItemLeave();
}
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
this->hoveredAnnotationItem = annotationItem;
if (annotationItem->position == AnnotationItemPosition::TOP) {
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
);
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() + ByteItem::HEIGHT
);
void ByteItemGraphicsScene::onAnnotationItemLeave() {
auto* annotationItem = this->hoveredAnnotationItem.value();
this->hoveredAnnotationItem = std::nullopt;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
}
void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) {
if (this->hoveredByteWidget.has_value()) {
if (this->hoveredByteWidget.value() == widget) {
// This byte item is already marked as hovered
return;
}
this->onByteWidgetLeave();
}
this->hoveredByteWidget = widget;
this->hoveredAddressLabel->setText(
"Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")"
);
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) {
byteWidget->update();
}
} else {
widget->update();
}
}
void ByteItemGraphicsScene::onByteWidgetLeave() {
auto* byteItem = this->hoveredByteWidget.value();
this->hoveredByteWidget = std::nullopt;
this->hoveredAddressLabel->setText("Relative Address (Absolute Address):");
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) {
byteWidget->update();
}
} else {
byteItem->update();
}
}
void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) {
if (this->hoveredAnnotationItem.has_value()) {
if (this->hoveredAnnotationItem.value() == annotationItem) {
return;
}
this->onAnnotationItemLeave();
}
this->hoveredAnnotationItem = annotationItem;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
void ByteItemGraphicsScene::onAnnotationItemLeave() {
auto* annotationItem = this->hoveredAnnotationItem.value();
this->hoveredAnnotationItem = std::nullopt;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}

View File

@@ -5,240 +5,244 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetMemoryDescriptor;
HexViewerWidget::HexViewerWidget(
const TargetMemoryDescriptor& targetMemoryDescriptor,
HexViewerWidgetSettings& settings,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
QWidget* parent
):
QWidget(parent),
targetMemoryDescriptor(targetMemoryDescriptor),
settings(settings),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions),
insightWorker(insightWorker)
namespace Bloom::Widgets
{
this->setObjectName("hex-viewer-widget");
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
using namespace Bloom::Exceptions;
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget"
+ "/UiFiles/HexViewerWidget.ui"
)
);
using Bloom::Targets::TargetMemoryDescriptor;
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open HexViewerWidget UI file");
}
HexViewerWidget::HexViewerWidget(
const TargetMemoryDescriptor& targetMemoryDescriptor,
HexViewerWidgetSettings& settings,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
QWidget* parent
)
: QWidget(parent)
, targetMemoryDescriptor(targetMemoryDescriptor)
, settings(settings)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
, insightWorker(insightWorker)
{
this->setObjectName("hex-viewer-widget");
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget"
+ "/UiFiles/HexViewerWidget.ui"
)
);
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open HexViewerWidget UI file");
}
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>("highlight-stack-memory-btn");
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-hovered-rows-columns-btn"
);
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
"highlight-focused-memory-btn"
);
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("display-ascii-btn");
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->goToAddressInput = this->container->findChild<TextInput*>("go-to-address-input");
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
this->toolBar->setContentsMargins(0, 0, 0, 0);
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-stack-memory-btn"
);
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-hovered-rows-columns-btn"
);
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
"highlight-focused-memory-btn"
);
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("display-ascii-btn");
this->bottomBar->setContentsMargins(0, 0, 0, 0);
this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0);
this->goToAddressInput = this->container->findChild<TextInput*>("go-to-address-input");
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
this->toolBar->setContentsMargins(0, 0, 0, 0);
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("graphics-view-container");
this->byteItemGraphicsView = new ByteItemContainerGraphicsView(
this->targetMemoryDescriptor,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->insightWorker,
this->settings,
this->hoveredAddressLabel,
this->byteItemGraphicsViewContainer
);
this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene();
this->bottomBar->setContentsMargins(0, 0, 0, 0);
this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0);
this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol);
this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory);
this->setAnnotationsEnabled(this->settings.displayAnnotations);
this->setDisplayAsciiEnabled(this->settings.displayAsciiValues);
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) {
this->highlightStackMemoryButton->show();
this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory);
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("graphics-view-container");
this->byteItemGraphicsView = new ByteItemContainerGraphicsView(
this->targetMemoryDescriptor,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->insightWorker,
this->settings,
this->hoveredAddressLabel,
this->byteItemGraphicsViewContainer
);
this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene();
this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol);
this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory);
this->setAnnotationsEnabled(this->settings.displayAnnotations);
this->setDisplayAsciiEnabled(this->settings.displayAsciiValues);
if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) {
this->highlightStackMemoryButton->show();
this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory);
QObject::connect(
this->highlightStackMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory);
}
);
} else {
this->highlightStackMemoryButton->hide();
this->setStackMemoryHighlightingEnabled(false);
}
QObject::connect(
this->highlightStackMemoryButton,
this->highlightHoveredRowAndColumnButton,
&QToolButton::clicked,
this,
[this] {
this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory);
this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol);
}
);
} else {
this->highlightStackMemoryButton->hide();
this->setStackMemoryHighlightingEnabled(false);
QObject::connect(
this->highlightFocusedMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory);
}
);
QObject::connect(
this->displayAnnotationsButton,
&QToolButton::clicked,
this,
[this] {
this->setAnnotationsEnabled(!this->settings.displayAnnotations);
}
);
QObject::connect(
this->displayAsciiButton,
&QToolButton::clicked,
this,
[this] {
this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues);
}
);
QObject::connect(
this->goToAddressInput,
&QLineEdit::textEdited,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
this->goToAddressInput,
&TextInput::focusChanged,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&HexViewerWidget::onTargetStateChanged
);
this->show();
}
QObject::connect(
this->highlightHoveredRowAndColumnButton,
&QToolButton::clicked,
this,
[this] {
this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol);
}
);
QObject::connect(
this->highlightFocusedMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory);
}
);
QObject::connect(
this->displayAnnotationsButton,
&QToolButton::clicked,
this,
[this] {
this->setAnnotationsEnabled(!this->settings.displayAnnotations);
}
);
QObject::connect(
this->displayAsciiButton,
&QToolButton::clicked,
this,
[this] {
this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues);
}
);
QObject::connect(
this->goToAddressInput,
&QLineEdit::textEdited,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
this->goToAddressInput,
&TextInput::focusChanged,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&HexViewerWidget::onTargetStateChanged
);
this->show();
}
void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) {
this->byteItemGraphicsScene->updateValues(buffer);
}
void HexViewerWidget::refreshRegions() {
this->byteItemGraphicsScene->refreshRegions();
}
void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) {
this->byteItemGraphicsScene->updateStackPointer(stackPointer);
}
void HexViewerWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::showEvent(QShowEvent* event) {
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) {
this->highlightStackMemoryButton->setChecked(enabled);
this->settings.highlightStackMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) {
this->highlightHoveredRowAndColumnButton->setChecked(enabled);
this->settings.highlightHoveredRowAndCol = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) {
this->highlightFocusedMemoryButton->setChecked(enabled);
this->settings.highlightFocusedMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setAnnotationsEnabled(bool enabled) {
this->displayAnnotationsButton->setChecked(enabled);
this->settings.displayAnnotations = enabled;
this->byteItemGraphicsScene->adjustSize(true);
}
void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) {
this->displayAsciiButton->setChecked(enabled);
this->settings.displayAsciiValues = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::onGoToAddressInputChanged() {
auto addressConversionOk = false;
const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16);
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) {
this->byteItemGraphicsScene->setHighlightedAddresses({address});
this->byteItemGraphicsView->scrollToByteItemAtAddress(address);
return;
void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) {
this->byteItemGraphicsScene->updateValues(buffer);
}
this->byteItemGraphicsScene->setHighlightedAddresses({});
void HexViewerWidget::refreshRegions() {
this->byteItemGraphicsScene->refreshRegions();
}
void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) {
this->byteItemGraphicsScene->updateStackPointer(stackPointer);
}
void HexViewerWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::showEvent(QShowEvent* event) {
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) {
this->highlightStackMemoryButton->setChecked(enabled);
this->settings.highlightStackMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) {
this->highlightHoveredRowAndColumnButton->setChecked(enabled);
this->settings.highlightHoveredRowAndCol = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) {
this->highlightFocusedMemoryButton->setChecked(enabled);
this->settings.highlightFocusedMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setAnnotationsEnabled(bool enabled) {
this->displayAnnotationsButton->setChecked(enabled);
this->settings.displayAnnotations = enabled;
this->byteItemGraphicsScene->adjustSize(true);
}
void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) {
this->displayAsciiButton->setChecked(enabled);
this->settings.displayAsciiValues = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::onGoToAddressInputChanged() {
auto addressConversionOk = false;
const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16);
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) {
this->byteItemGraphicsScene->setHighlightedAddresses({address});
this->byteItemGraphicsView->scrollToByteItemAtAddress(address);
return;
}
this->byteItemGraphicsScene->setHighlightedAddresses({});
}
}

View File

@@ -2,68 +2,46 @@
#include <algorithm>
using namespace Bloom::Widgets;
ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion)
: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP)
, focusedMemoryRegion(focusedMemoryRegion)
, endianness(focusedMemoryRegion.endianness)
namespace Bloom::Widgets
{
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
}
void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) {
this->value = value;
if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) {
std::reverse(this->value.begin(), this->value.end());
}
this->refreshLabelText();
this->setToolTip(this->labelText);
}
void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto font = painter->font();
font.setItalic(true);
painter->setFont(font);
AnnotationItem::paint(painter, option, widget);
}
void ValueAnnotationItem::refreshLabelText() {
this->update();
if (this->value.empty()) {
ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion)
: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP)
, focusedMemoryRegion(focusedMemoryRegion)
, endianness(focusedMemoryRegion.endianness)
{
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
return;
}
switch (this->focusedMemoryRegion.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
std::uint64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) {
this->value = value;
this->labelText = QString::number(integerValue);
break;
if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) {
std::reverse(this->value.begin(), this->value.end());
}
case MemoryRegionDataType::SIGNED_INTEGER: {
const auto valueSize = this->value.size();
if (valueSize == 1) {
this->labelText = QString::number(static_cast<int8_t>(this->value[0]));
break;
}
this->refreshLabelText();
this->setToolTip(this->labelText);
}
if (valueSize == 2) {
this->labelText = QString::number(static_cast<int16_t>((this->value[0] << 8) | this->value[1]));
break;
}
void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto font = painter->font();
font.setItalic(true);
painter->setFont(font);
if (valueSize <= 4) {
std::int32_t integerValue = 0;
AnnotationItem::paint(painter, option, widget);
}
void ValueAnnotationItem::refreshLabelText() {
this->update();
if (this->value.empty()) {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
return;
}
switch (this->focusedMemoryRegion.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
std::uint64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
@@ -71,42 +49,65 @@ void ValueAnnotationItem::refreshLabelText() {
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::SIGNED_INTEGER: {
const auto valueSize = this->value.size();
std::int64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
if (valueSize == 1) {
this->labelText = QString::number(static_cast<int8_t>(this->value[0]));
break;
}
if (valueSize == 2) {
this->labelText = QString::number(static_cast<int16_t>((this->value[0] << 8) | this->value[1]));
break;
}
if (valueSize <= 4) {
std::int32_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
this->labelText = QString::number(integerValue);
break;
}
std::int64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
// Replace non-ASCII chars with '?'
auto asciiData = this->value;
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
// Replace non-ASCII chars with '?'
auto asciiData = this->value;
std::replace_if(
asciiData.begin(),
asciiData.end(),
[] (unsigned char value) {
/*
* We only care about non-control characters (with the exception of the white space character) in
* the standard ASCII range.
*/
constexpr auto asciiRangeStart = 32;
constexpr auto asciiRangeEnd = 126;
return value < asciiRangeStart || value > asciiRangeEnd;
},
'?'
);
std::replace_if(
asciiData.begin(),
asciiData.end(),
[] (unsigned char value) {
/*
* We only care about non-control characters (with the exception of the white space character) in
* the standard ASCII range.
*/
constexpr auto asciiRangeStart = 32;
constexpr auto asciiRangeEnd = 126;
return value < asciiRangeStart || value > asciiRangeEnd;
},
'?'
);
this->labelText = "'" + QString::fromLatin1(
reinterpret_cast<const char*>(asciiData.data()),
static_cast<qsizetype>(asciiData.size())
) + "'";
break;
}
default: {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
this->labelText = "'" + QString::fromLatin1(
reinterpret_cast<const char*>(asciiData.data()),
static_cast<qsizetype>(asciiData.size())
) + "'";
break;
}
default: {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
}
}
}
}

View File

@@ -6,41 +6,42 @@
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
ExcludedRegionItem::ExcludedRegionItem(
const ExcludedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui"
)
);
using Targets::TargetMemoryAddressRange;
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file");
}
ExcludedRegionItem::ExcludedRegionItem(
const ExcludedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui"
)
);
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file");
this->initFormInputs();
}
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
void ExcludedRegionItem::applyChanges() {
using Targets::TargetMemoryAddressRange;
this->initFormInputs();
}
void ExcludedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange = this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
}
}

View File

@@ -6,99 +6,105 @@
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Targets::TargetMemoryAddressRange;
using Targets::TargetMemoryAddressRange;
FocusedRegionItem::FocusedRegionItem(
const FocusedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui"
)
);
FocusedRegionItem::FocusedRegionItem(
const FocusedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui"
)
);
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file");
}
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
this->initFormInputs();
}
void FocusedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString();
if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) {
this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(selectedDataTypeOptionName).dataType;
}
auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString();
if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) {
this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at(
selectedEndiannessOptionName
).endianness;
}
}
void FocusedRegionItem::initFormInputs() {
RegionItem::initFormInputs();
const auto& region = this->memoryRegion;
this->dataTypeInput = this->formWidget->findChild<QComboBox*>("data-type-input");
this->endiannessInput = this->formWidget->findChild<QComboBox*>("endianness-input");
for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) {
this->dataTypeInput->addItem(option.text, optionName);
}
for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) {
this->endiannessInput->addItem(option.text, optionName);
}
switch (region.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text);
break;
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file");
}
case MemoryRegionDataType::SIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text);
break;
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
this->initFormInputs();
}
void FocusedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString();
if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) {
this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(
selectedDataTypeOptionName
).dataType;
}
case MemoryRegionDataType::ASCII_STRING: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text);
break;
}
default: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text);
auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString();
if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) {
this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at(
selectedEndiannessOptionName
).endianness;
}
}
switch (region.endianness) {
case Targets::TargetMemoryEndianness::LITTLE: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text);
break;
void FocusedRegionItem::initFormInputs() {
RegionItem::initFormInputs();
const auto& region = this->memoryRegion;
this->dataTypeInput = this->formWidget->findChild<QComboBox*>("data-type-input");
this->endiannessInput = this->formWidget->findChild<QComboBox*>("endianness-input");
for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) {
this->dataTypeInput->addItem(option.text, optionName);
}
case Targets::TargetMemoryEndianness::BIG: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text);
break;
for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) {
this->endiannessInput->addItem(option.text, optionName);
}
switch (region.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(
FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text
);
break;
}
case MemoryRegionDataType::SIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(
FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text
);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text);
break;
}
default: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text);
}
}
switch (region.endianness) {
case Targets::TargetMemoryEndianness::LITTLE: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text);
break;
}
case Targets::TargetMemoryEndianness::BIG: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text);
break;
}
}
}
}

View File

@@ -10,383 +10,401 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
using Bloom::Exceptions::Exception;
MemoryRegionManagerWindow::MemoryRegionManagerWindow(
const Targets::TargetMemoryDescriptor& memoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
):
QWidget(parent),
memoryDescriptor(memoryDescriptor),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions)
namespace Bloom::Widgets
{
this->setWindowFlag(Qt::Window);
this->setObjectName("memory-region-manager-window");
this->setWindowTitle(
"Memory Regions - "
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
);
using Bloom::Exceptions::Exception;
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
)
);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file");
}
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(QSize(970, 540));
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->regionSelector = this->container->findChild<QWidget*>("region-selector");
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
"item-container-layout"
);
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
regionSelectorToolBar->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom);
QObject::connect(
this->addFocusedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewFocusedRegionTrigger
);
QObject::connect(
this->addExcludedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewExcludedRegionTrigger
);
QObject::connect(
this->removeRegionButton,
&QToolButton::clicked,
this,
&MemoryRegionManagerWindow::onDeleteRegionTrigger
);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges);
QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage);
this->refreshRegions();
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
void MemoryRegionManagerWindow::refreshRegions() {
this->clearRegions();
for (const auto& focusedRegion: this->focusedMemoryRegions) {
this->addFocusedRegion(focusedRegion);
}
for (const auto& excludedRegion: this->excludedMemoryRegions) {
this->addExcludedRegion(excludedRegion);
}
this->sortRegionItems();
}
void MemoryRegionManagerWindow::showEvent(QShowEvent* event) {
if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* firstRegionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
MemoryRegionManagerWindow::MemoryRegionManagerWindow(
const Targets::TargetMemoryDescriptor& memoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
)
: QWidget(parent)
, memoryDescriptor(memoryDescriptor)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
{
this->setWindowFlag(Qt::Window);
this->setObjectName("memory-region-manager-window");
this->setWindowTitle(
"Memory Regions - "
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
);
if (firstRegionItem != nullptr) {
firstRegionItem->setSelected(true);
}
}
}
void MemoryRegionManagerWindow::clearRegions() {
this->selectedRegion = nullptr;
for (auto* focusedRegionItem: this->focusedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem);
focusedRegionItem->getFormWidget()->deleteLater();
focusedRegionItem->deleteLater();
}
for (auto* excludedRegionItem: this->excludedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem);
excludedRegionItem->getFormWidget()->deleteLater();
excludedRegionItem->deleteLater();
}
this->focusedRegionItems.clear();
this->excludedRegionItems.clear();
}
void MemoryRegionManagerWindow::sortRegionItems() {
/*
* This isn't very pretty.
*
* Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the
* layout, and then re-inserting them in the correct order.
*/
auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) {
return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate;
};
auto sortedRegionItems = std::set<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
auto* regionItem = qobject_cast<RegionItem*>(layoutItem->widget());
if (regionItem != nullptr) {
sortedRegionItems.insert(regionItem);
}
delete layoutItem;
}
for (auto* regionItem: sortedRegionItems) {
this->regionItemScrollAreaViewportLayout->addWidget(regionItem);
}
}
FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) {
auto* focusedRegionItem = new FocusedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport);
this->focusedRegionItems.insert(focusedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem);
this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget());
QObject::connect(focusedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected);
return focusedRegionItem;
}
ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) {
auto* excludedRegionItem = new ExcludedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport);
this->excludedRegionItems.insert(excludedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem);
this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget());
QObject::connect(excludedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected);
return excludedRegionItem;
}
void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) {
if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) {
this->selectedRegion->setSelected(false);
}
this->selectedRegion = selectedRegion;
this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget());
}
void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addFocusedRegion(FocusedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addExcludedRegion(ExcludedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onDeleteRegionTrigger() {
if (this->selectedRegion == nullptr) {
return;
}
auto* regionItem = this->selectedRegion;
const auto& region = regionItem->getMemoryRegion();
if (region.type == MemoryRegionType::FOCUSED) {
auto* focusedRegionItem = qobject_cast<FocusedRegionItem*>(regionItem);
if (focusedRegionItem != nullptr) {
this->focusedRegionItems.erase(focusedRegionItem);
}
} else {
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(regionItem);
if (excludedRegionItem != nullptr) {
this->excludedRegionItems.erase(excludedRegionItem);
}
}
regionItem->getFormWidget()->deleteLater();
this->regionItemScrollAreaViewportLayout->removeWidget(regionItem);
regionItem->deleteLater();
this->selectedRegion = nullptr;
if (this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* regionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
)
);
if (regionItem != nullptr) {
regionItem->setSelected(true);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file");
}
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(QSize(970, 540));
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->regionSelector = this->container->findChild<QWidget*>("region-selector");
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
"item-container-layout"
);
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
regionSelectorToolBar->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom);
QObject::connect(
this->addFocusedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewFocusedRegionTrigger
);
QObject::connect(
this->addExcludedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewExcludedRegionTrigger
);
QObject::connect(
this->removeRegionButton,
&QToolButton::clicked,
this,
&MemoryRegionManagerWindow::onDeleteRegionTrigger
);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges);
QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage);
this->refreshRegions();
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
}
void MemoryRegionManagerWindow::applyChanges() {
auto processedFocusedMemoryRegions = std::vector<FocusedMemoryRegion>();
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
void MemoryRegionManagerWindow::refreshRegions() {
this->clearRegions();
for (auto* focusedRegionItem : this->focusedRegionItems) {
const auto validationFailures = focusedRegionItem->getValidationFailures();
for (const auto& focusedRegion: this->focusedMemoryRegions) {
this->addFocusedRegion(focusedRegion);
}
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
for (const auto& excludedRegion: this->excludedMemoryRegions) {
this->addExcludedRegion(excludedRegion);
}
this->sortRegionItems();
}
void MemoryRegionManagerWindow::showEvent(QShowEvent* event) {
if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* firstRegionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
);
errorDialogue->show();
if (firstRegionItem != nullptr) {
firstRegionItem->setSelected(true);
}
}
}
void MemoryRegionManagerWindow::clearRegions() {
this->selectedRegion = nullptr;
for (auto* focusedRegionItem: this->focusedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem);
focusedRegionItem->getFormWidget()->deleteLater();
focusedRegionItem->deleteLater();
}
for (auto* excludedRegionItem: this->excludedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem);
excludedRegionItem->getFormWidget()->deleteLater();
excludedRegionItem->deleteLater();
}
this->focusedRegionItems.clear();
this->excludedRegionItems.clear();
}
void MemoryRegionManagerWindow::sortRegionItems() {
/*
* This isn't very pretty.
*
* Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the
* layout, and then re-inserting them in the correct order.
*/
auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) {
return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate;
};
auto sortedRegionItems = std::set<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
auto* regionItem = qobject_cast<RegionItem*>(layoutItem->widget());
if (regionItem != nullptr) {
sortedRegionItems.insert(regionItem);
}
delete layoutItem;
}
for (auto* regionItem: sortedRegionItems) {
this->regionItemScrollAreaViewportLayout->addWidget(regionItem);
}
}
FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) {
auto* focusedRegionItem = new FocusedRegionItem(
region,
this->memoryDescriptor,
this->regionItemScrollAreaViewport
);
this->focusedRegionItems.insert(focusedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem);
this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget());
QObject::connect(
focusedRegionItem,
&RegionItem::selected,
this,
&MemoryRegionManagerWindow::onRegionSelected
);
return focusedRegionItem;
}
ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) {
auto* excludedRegionItem = new ExcludedRegionItem(
region,
this->memoryDescriptor,
this->regionItemScrollAreaViewport
);
this->excludedRegionItems.insert(excludedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem);
this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget());
QObject::connect(
excludedRegionItem,
&RegionItem::selected,
this,
&MemoryRegionManagerWindow::onRegionSelected
);
return excludedRegionItem;
}
void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) {
if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) {
this->selectedRegion->setSelected(false);
}
this->selectedRegion = selectedRegion;
this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget());
}
void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addFocusedRegion(FocusedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addExcludedRegion(ExcludedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onDeleteRegionTrigger() {
if (this->selectedRegion == nullptr) {
return;
}
focusedRegionItem->applyChanges();
const auto& focusedRegion = focusedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(focusedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + focusedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
auto* regionItem = this->selectedRegion;
const auto& region = regionItem->getMemoryRegion();
if (region.type == MemoryRegionType::FOCUSED) {
auto* focusedRegionItem = qobject_cast<FocusedRegionItem*>(regionItem);
if (focusedRegionItem != nullptr) {
this->focusedRegionItems.erase(focusedRegionItem);
}
} else {
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(regionItem);
if (excludedRegionItem != nullptr) {
this->excludedRegionItems.erase(excludedRegionItem);
}
}
processedFocusedMemoryRegions.emplace_back(focusedRegion);
}
regionItem->getFormWidget()->deleteLater();
this->regionItemScrollAreaViewportLayout->removeWidget(regionItem);
regionItem->deleteLater();
for (auto* excludedRegionItem : this->excludedRegionItems) {
const auto validationFailures = excludedRegionItem->getValidationFailures();
this->selectedRegion = nullptr;
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
if (this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* regionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
);
errorDialogue->show();
return;
}
excludedRegionItem->applyChanges();
auto excludedRegion = excludedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
if (regionItem != nullptr) {
regionItem->setSelected(true);
}
}
for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) {
if (processedExcludedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedExcludedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedExcludedMemoryRegions.emplace_back(excludedRegion);
}
this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions);
this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions);
this->close();
emit this->changesApplied();
}
void MemoryRegionManagerWindow::applyChanges() {
auto processedFocusedMemoryRegions = std::vector<FocusedMemoryRegion>();
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
void MemoryRegionManagerWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions"))
);
for (auto* focusedRegionItem : this->focusedRegionItems) {
const auto validationFailures = focusedRegionItem->getValidationFailures();
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
);
errorDialogue->show();
return;
}
focusedRegionItem->applyChanges();
const auto& focusedRegion = focusedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(focusedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + focusedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedFocusedMemoryRegions.emplace_back(focusedRegion);
}
for (auto* excludedRegionItem : this->excludedRegionItems) {
const auto validationFailures = excludedRegionItem->getValidationFailures();
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
);
errorDialogue->show();
return;
}
excludedRegionItem->applyChanges();
auto excludedRegion = excludedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) {
if (processedExcludedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedExcludedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedExcludedMemoryRegions.emplace_back(excludedRegion);
}
this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions);
this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions);
this->close();
emit this->changesApplied();
}
void MemoryRegionManagerWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions"))
);
}
}

View File

@@ -3,217 +3,228 @@
#include <QHBoxLayout>
#include <QString>
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Targets::TargetMemoryAddressRange;
using Targets::TargetMemoryAddressRange;
RegionItem::RegionItem(
const MemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) {
this->setObjectName("region-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 5, 5, 0);
RegionItem::RegionItem(
const MemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) {
this->setObjectName("region-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 5, 5, 0);
this->timeLabel->setText(region.createdDate.toString("hh:mm"));
this->timeLabel->setObjectName("time-label");
this->timeLabel->setText(region.createdDate.toString("hh:mm"));
this->timeLabel->setObjectName("time-label");
auto regionName = region.name;
regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(regionName);
this->nameLabel->setObjectName("name-label");
auto regionName = region.name;
regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(regionName);
this->nameLabel->setObjectName("name-label");
this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded");
this->typeLabel->setObjectName("type-label");
this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded");
this->typeLabel->setObjectName("type-label");
this->addressRangeLabel->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
this->addressRangeLabel->setObjectName("address-label");
this->addressRangeLabel->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
this->addressRangeLabel->setObjectName("address-label");
auto* topLabelLayout = new QHBoxLayout();
topLabelLayout->setSpacing(0);
topLabelLayout->setContentsMargins(0, 0, 0, 0);
topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft);
topLabelLayout->addStretch(1);
topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* topLabelLayout = new QHBoxLayout();
topLabelLayout->setSpacing(0);
topLabelLayout->setContentsMargins(0, 0, 0, 0);
topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft);
topLabelLayout->addStretch(1);
topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* bottomLabelLayout = new QHBoxLayout();
bottomLabelLayout->setSpacing(0);
bottomLabelLayout->setContentsMargins(0, 0, 0, 0);
bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft);
bottomLabelLayout->addStretch(1);
bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* bottomLabelLayout = new QHBoxLayout();
bottomLabelLayout->setSpacing(0);
bottomLabelLayout->setContentsMargins(0, 0, 0, 0);
bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft);
bottomLabelLayout->addStretch(1);
bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight);
this->layout->setSpacing(5);
this->layout->addLayout(topLabelLayout);
this->layout->addLayout(bottomLabelLayout);
this->layout->addStretch(1);
this->layout->setSpacing(5);
this->layout->addLayout(topLabelLayout);
this->layout->addLayout(bottomLabelLayout);
this->layout->addStretch(1);
auto onClick = [this] {
this->setSelected(true);
};
auto onClick = [this] {
this->setSelected(true);
};
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
void RegionItem::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
}
}
QStringList RegionItem::getValidationFailures() const {
auto validationFailures = QStringList();
if (this->nameInput->text().isEmpty()) {
validationFailures.emplace_back("Missing region name.");
this->setSelected(false);
}
bool conversionOk = false;
void RegionItem::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid start address.");
if (selected) {
emit this->selected(this);
}
}
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid end address.");
QStringList RegionItem::getValidationFailures() const {
auto validationFailures = QStringList();
if (this->nameInput->text().isEmpty()) {
validationFailures.emplace_back("Missing region name.");
}
bool conversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid start address.");
}
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid end address.");
}
if (startAddress > endAddress) {
validationFailures.emplace_back("The start address exceeds the end address.");
}
auto addressType = this->getSelectedAddressInputType();
const auto memoryAddressRange = this->memoryDescriptor.addressRange;
const auto memoryAddressRangeStr = QString(
"0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper()
);
const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(
TargetMemoryAddressRange(startAddress, endAddress)
) : TargetMemoryAddressRange(startAddress, endAddress);
if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.startAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")."
);
}
if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.endAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")."
);
}
return validationFailures;
}
if (startAddress > endAddress) {
validationFailures.emplace_back("The start address exceeds the end address.");
void RegionItem::initFormInputs() {
const auto& region = this->getMemoryRegion();
this->nameInput = this->formWidget->findChild<TextInput*>("name-input");
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
this->sizeInput = this->formWidget->findChild<TextInput*>("size-input");
this->nameInput->setText(region.name);
this->sizeInput->setText(
QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1)
);
for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) {
this->addressTypeInput->addItem(option.text, optionName);
}
if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) {
auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange);
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text);
this->startAddressInput->setText(
"0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper()
);
this->endAddressInput->setText(
"0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper()
);
} else {
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text);
this->startAddressInput->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper()
);
this->endAddressInput->setText(
"0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
}
QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange);
QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange);
}
auto addressType = this->getSelectedAddressInputType();
const auto memoryAddressRange = this->memoryDescriptor.addressRange;
MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const {
auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString();
if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) {
return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType;
}
const auto memoryAddressRangeStr = QString(
"0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper()
);
return MemoryRegionAddressInputType::ABSOLUTE;
}
const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(TargetMemoryAddressRange(startAddress, endAddress))
: TargetMemoryAddressRange(startAddress, endAddress);
if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.startAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")."
TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange(
const TargetMemoryAddressRange& absoluteAddressRange
) const {
return TargetMemoryAddressRange(
absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress,
absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress
);
}
if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.endAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")."
TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange(
const TargetMemoryAddressRange& relativeAddressRange
) const {
return TargetMemoryAddressRange(
relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress,
relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress
);
}
return validationFailures;
}
void RegionItem::onAddressRangeInputChange() {
bool startAddressConversionOk = false;
bool endAddressConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16);
void RegionItem::initFormInputs() {
const auto& region = this->getMemoryRegion();
this->nameInput = this->formWidget->findChild<TextInput*>("name-input");
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
this->sizeInput = this->formWidget->findChild<TextInput*>("size-input");
this->nameInput->setText(region.name);
this->sizeInput->setText(QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1));
for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) {
this->addressTypeInput->addItem(option.text, optionName);
if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) {
this->sizeInput->setText(QString::number((endAddress - startAddress) + 1));
}
}
if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) {
auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange);
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text);
void RegionItem::onSizeInputChange() {
bool startAddressConversionOk = false;
bool sizeConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10);
this->startAddressInput->setText("0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper());
this->endAddressInput->setText("0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper());
} else {
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text);
this->startAddressInput->setText("0x" + QString::number(region.addressRange.startAddress, 16).toUpper());
this->endAddressInput->setText("0x" + QString::number(region.addressRange.endAddress, 16).toUpper());
if (startAddressConversionOk && sizeConversionOk && size > 0) {
this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper());
}
}
QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange);
QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange);
}
MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const {
auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString();
if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) {
return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType;
}
return MemoryRegionAddressInputType::ABSOLUTE;
}
TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange(
const TargetMemoryAddressRange& absoluteAddressRange
) const {
return TargetMemoryAddressRange(
absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress,
absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress
);
}
TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange(
const TargetMemoryAddressRange& relativeAddressRange
) const {
return TargetMemoryAddressRange(
relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress,
relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress
);
}
void RegionItem::onAddressRangeInputChange() {
bool startAddressConversionOk = false;
bool endAddressConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16);
if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) {
this->sizeInput->setText(QString::number((endAddress - startAddress) + 1));
void RegionItem::onNameInputChange() {
auto newName = this->nameInput->text();
newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(newName);
}
}
void RegionItem::onSizeInputChange() {
bool startAddressConversionOk = false;
bool sizeConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10);
if (startAddressConversionOk && sizeConversionOk && size > 0) {
this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper());
}
}
void RegionItem::onNameInputChange() {
auto newName = this->nameInput->text();
newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(newName);
}

View File

@@ -4,75 +4,79 @@
#include <QScrollArea>
#include <QMargins>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
BitBodyWidget::BitBodyWidget(
int bitIndex,
std::bitset<std::numeric_limits<unsigned char>::digits>::reference bit,
bool readOnly,
QWidget* parent
): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) {
this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT);
this->setContentsMargins(0, 0, 0, 0);
}
BitBodyWidget::BitBodyWidget(
int bitIndex,
std::bitset<std::numeric_limits<unsigned char>::digits>::reference bit,
bool readOnly,
QWidget* parent
): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) {
this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT);
this->setContentsMargins(0, 0, 0, 0);
}
bool BitBodyWidget::event(QEvent* event) {
if (this->isEnabled() && !this->readOnly) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
this->update();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->update();
break;
}
default: {
break;
}
}
}
bool BitBodyWidget::event(QEvent* event) {
if (this->isEnabled() && !this->readOnly) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
return QWidget::event(event);
}
void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->isEnabled()) {
if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) {
this->bit = !this->bit;
this->update();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->update();
break;
}
default: {
break;
}
ClickableWidget::mouseReleaseEvent(event);
}
}
return QWidget::event(event);
}
void BitBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->isEnabled()) {
if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) {
this->bit = !this->bit;
this->update();
void BitBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85");
if (!this->isEnabled()) {
bodyColor.setAlpha(100);
} else if (!this->hoverActive) {
bodyColor.setAlpha(235);
}
ClickableWidget::mouseReleaseEvent(event);
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(bodyColor);
painter.drawRect(
0,
0,
BitBodyWidget::WIDTH,
BitBodyWidget::HEIGHT
);
}
}
void BitBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85");
if (!this->isEnabled()) {
bodyColor.setAlpha(100);
} else if (!this->hoverActive) {
bodyColor.setAlpha(235);
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(bodyColor);
painter.drawRect(
0,
0,
BitBodyWidget::WIDTH,
BitBodyWidget::HEIGHT
);
}

View File

@@ -6,50 +6,51 @@
#include "BitBodyWidget.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
BitWidget::BitWidget(
int bitIndex,
int bitNumber,
std::bitset<std::numeric_limits<unsigned char>::digits>& bitset,
bool readOnly,
QWidget* parent
): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) {
this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT);
BitWidget::BitWidget(
int bitIndex,
int bitNumber,
std::bitset<std::numeric_limits<unsigned char>::digits>& bitset,
bool readOnly,
QWidget* parent
): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) {
this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT);
auto* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignmentFlag::AlignTop);
auto* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignmentFlag::AlignTop);
this->body = new BitBodyWidget(
this->bitIndex,
this->bitset[static_cast<size_t>(this->bitIndex)],
this->readOnly,
this
);
this->body = new BitBodyWidget(
this->bitIndex,
this->bitset[static_cast<size_t>(this->bitIndex)],
this->readOnly,
this
);
this->bitLabel = new QLabel("Bit", this);
this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this);
this->bitLabel = new QLabel("Bit", this);
this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this);
this->bitLabel->setObjectName("register-bit-label");
this->bitNumberLabel->setObjectName("register-bit-number-label");
this->bitLabel->setObjectName("register-bit-label");
this->bitNumberLabel->setObjectName("register-bit-number-label");
this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
layout->addWidget(this->bitLabel);
layout->addWidget(this->bitNumberLabel);
layout->addSpacing(BitWidget::VERTICAL_SPACING);
layout->addWidget(this->body);
layout->addStretch(1);
layout->addWidget(this->bitLabel);
layout->addWidget(this->bitNumberLabel);
layout->addSpacing(BitWidget::VERTICAL_SPACING);
layout->addWidget(this->body);
layout->addStretch(1);
if (!this->readOnly) {
QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] {
emit this->bitChanged();
});
if (!this->readOnly) {
QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] {
emit this->bitChanged();
});
}
}
}

View File

@@ -8,101 +8,102 @@
#include "../TargetRegisterInspectorWindow.hpp"
using namespace Bloom::Widgets;
BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent):
QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) {
this->setObjectName("bitset-widget");
auto* bitLayout = new QHBoxLayout(this);
bitLayout->setSpacing(BitWidget::SPACING);
bitLayout->setContentsMargins(0, 0, 0, 0);
this->setContentsMargins(0, 0, 0, 0);
this->setFixedSize(
static_cast<int>((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING),
BitsetWidget::HEIGHT
);
for (int bitIndex = (std::numeric_limits<unsigned char>::digits - 1); bitIndex >= 0; bitIndex--) {
auto* bitWidget = new BitWidget(
bitIndex,
(this->byteNumber * 8) + bitIndex,
this->bitset,
this->readOnly,
this
namespace Bloom::Widgets
{
BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent)
: QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) {
this->setObjectName("bitset-widget");
auto* bitLayout = new QHBoxLayout(this);
bitLayout->setSpacing(BitWidget::SPACING);
bitLayout->setContentsMargins(0, 0, 0, 0);
this->setContentsMargins(0, 0, 0, 0);
this->setFixedSize(
static_cast<int>((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING),
BitsetWidget::HEIGHT
);
bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
QObject::connect(
bitWidget,
&BitWidget::bitChanged,
this,
[this] {
this->byte = static_cast<unsigned char>(this->bitset.to_ulong());
this->repaint();
emit this->byteChanged();
}
for (int bitIndex = (std::numeric_limits<unsigned char>::digits - 1); bitIndex >= 0; bitIndex--) {
auto* bitWidget = new BitWidget(
bitIndex,
(this->byteNumber * 8) + bitIndex,
this->bitset,
this->readOnly,
this
);
bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
QObject::connect(
bitWidget,
&BitWidget::bitChanged,
this,
[this] {
this->byte = static_cast<unsigned char>(this->bitset.to_ulong());
this->repaint();
emit this->byteChanged();
}
);
}
}
void BitsetWidget::updateValue() {
this->bitset = {this->byte};
this->update();
}
void BitsetWidget::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitsetWidget::drawWidget(QPainter& painter) {
auto byteHex = "0x" + QString::number(this->byte, 16).toUpper();
painter.setPen(QColor("#474747"));
constexpr int labelHeight = 11;
int containerWidth = this->width();
constexpr int charWidth = 6;
auto labelWidth = static_cast<int>(charWidth * byteHex.size()) + 13;
auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2;
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT,
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
static_cast<int>((BitWidget::WIDTH / 2) + width + labelWidth),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.setPen(QColor("#8a8a8d"));
painter.drawText(
QRect(
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2),
labelWidth,
labelHeight
),
Qt::AlignCenter,
byteHex
);
}
}
void BitsetWidget::updateValue() {
this->bitset = {this->byte};
this->update();
}
void BitsetWidget::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitsetWidget::drawWidget(QPainter& painter) {
auto byteHex = "0x" + QString::number(this->byte, 16).toUpper();
painter.setPen(QColor("#474747"));
constexpr int labelHeight = 11;
int containerWidth = this->width();
constexpr int charWidth = 6;
auto labelWidth = static_cast<int>(charWidth * byteHex.size()) + 13;
auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2;
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT,
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
static_cast<int>((BitWidget::WIDTH / 2) + width + labelWidth),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.setPen(QColor("#8a8a8d"));
painter.drawText(
QRect(
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2),
labelWidth,
labelHeight
),
Qt::AlignCenter,
byteHex
);
}

View File

@@ -3,15 +3,16 @@
#include <QStyle>
#include <QVBoxLayout>
using namespace Bloom::Widgets;
CurrentItem::CurrentItem(
const Targets::TargetMemoryBuffer& registerValue,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("current-item");
this->setFixedHeight(30);
this->titleLabel->setText("Current value");
this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft);
this->layout->setContentsMargins(5, 0, 5, 0);
namespace Bloom::Widgets
{
CurrentItem::CurrentItem(
const Targets::TargetMemoryBuffer& registerValue,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("current-item");
this->setFixedHeight(30);
this->titleLabel->setText("Current value");
this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft);
this->layout->setContentsMargins(5, 0, 5, 0);
}
}

View File

@@ -2,26 +2,27 @@
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent)
: ClickableWidget(parent), registerValue(registerValue) {
auto onClick = [this] {
this->setSelected(true);
};
Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent):
ClickableWidget(parent), registerValue(registerValue) {
auto onClick = [this] {
this->setSelected(true);
};
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
this->setSelected(false);
}
void Item::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
void Item::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
if (selected) {
emit this->selected(this);
}
}
}

View File

@@ -4,38 +4,39 @@
#include <QHBoxLayout>
#include <QByteArray>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
RegisterHistoryItem::RegisterHistoryItem(
const Targets::TargetMemoryBuffer& registerValue,
const QDateTime& changeDate,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("register-history-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 8, 5, 0);
RegisterHistoryItem::RegisterHistoryItem(
const Targets::TargetMemoryBuffer& registerValue,
const QDateTime& changeDate,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("register-history-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 8, 5, 0);
this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss"));
this->dateLabel->setObjectName("date-label");
this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss"));
this->dateLabel->setObjectName("date-label");
this->valueLabel->setText("0x" + QString(QByteArray(
reinterpret_cast<const char*>(registerValue.data()),
static_cast<qsizetype>(registerValue.size())
).toHex()).toUpper());
this->valueLabel->setObjectName("value-label");
this->valueLabel->setText("0x" + QString(QByteArray(
reinterpret_cast<const char*>(registerValue.data()),
static_cast<qsizetype>(registerValue.size())
).toHex()).toUpper());
this->valueLabel->setObjectName("value-label");
this->descriptionLabel->setText("Register Written");
this->descriptionLabel->setObjectName("description-label");
this->descriptionLabel->setText("Register Written");
this->descriptionLabel->setObjectName("description-label");
auto* subLabelLayout = new QHBoxLayout();
subLabelLayout->setSpacing(0);
subLabelLayout->setContentsMargins(0, 0, 0, 0);
subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft);
subLabelLayout->addStretch(1);
subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* subLabelLayout = new QHBoxLayout();
subLabelLayout->setSpacing(0);
subLabelLayout->setContentsMargins(0, 0, 0, 0);
subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft);
subLabelLayout->addStretch(1);
subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight);
this->layout->setSpacing(5);
this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addLayout(subLabelLayout);
this->layout->addStretch(1);
this->layout->setSpacing(5);
this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addLayout(subLabelLayout);
this->layout->addStretch(1);
}
}

View File

@@ -12,125 +12,130 @@
#include "src/Helpers/DateTime.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
RegisterHistoryWidget::RegisterHistoryWidget(
const Targets::TargetRegisterDescriptor& registerDescriptor,
const Targets::TargetMemoryBuffer& currentValue,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-register-history-widget");
this->setFixedWidth(300);
RegisterHistoryWidget::RegisterHistoryWidget(
const Targets::TargetRegisterDescriptor& registerDescriptor,
const Targets::TargetMemoryBuffer& currentValue,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-register-history-widget");
this->setFixedWidth(300);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget"
+ "/UiFiles/RegisterHistoryWidget.ui"
)
);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget"
+ "/UiFiles/RegisterHistoryWidget.ui"
)
);
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open RegisterHistoryWidget UI file");
}
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(1, 1, 1, 1);
this->itemContainer = this->container->findChild<QWidget*>("item-container");
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
auto titleBar = this->container->findChild<QWidget*>("title-bar");
auto title = titleBar->findChild<QLabel*>("title");
titleBar->setContentsMargins(0, 0, 0, 0);
title->setFixedHeight(titleBar->height());
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&RegisterHistoryWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&RegisterHistoryWidget::onRegistersWritten
);
this->currentItem = new CurrentItem(currentValue, this);
QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->addWidget(this->currentItem);
this->currentItem->setSelected(true);
auto* separatorWidget = new QWidget(this);
auto* separatorLayout = new QHBoxLayout(separatorWidget);
auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget);
separatorWidget->setFixedHeight(40);
separatorWidget->setObjectName("separator-widget");
separatorLayout->setContentsMargins(0, 10, 0, 10);
separatorLabel->setObjectName("separator-label");
separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->itemContainerLayout->addWidget(separatorWidget);
this->show();
}
void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) {
this->currentItem->registerValue = registerValue;
if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) {
this->selectCurrentItem();
}
}
void RegisterHistoryWidget::selectCurrentItem() {
this->currentItem->setSelected(true);
}
void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) {
auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer);
QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->insertWidget(2, item);
}
void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
}
void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) {
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open RegisterHistoryWidget UI file");
}
this->selectedItemWidget = newlySelectedWidget;
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(1, 1, 1, 1);
this->itemContainer = this->container->findChild<QWidget*>("item-container");
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
auto titleBar = this->container->findChild<QWidget*>("title-bar");
auto title = titleBar->findChild<QLabel*>("title");
titleBar->setContentsMargins(0, 0, 0, 0);
title->setFixedHeight(titleBar->height());
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&RegisterHistoryWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&RegisterHistoryWidget::onRegistersWritten
);
this->currentItem = new CurrentItem(currentValue, this);
QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->addWidget(this->currentItem);
this->currentItem->setSelected(true);
auto* separatorWidget = new QWidget(this);
auto* separatorLayout = new QHBoxLayout(separatorWidget);
auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget);
separatorWidget->setFixedHeight(40);
separatorWidget->setObjectName("separator-widget");
separatorLayout->setContentsMargins(0, 10, 0, 10);
separatorLabel->setObjectName("separator-label");
separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->itemContainerLayout->addWidget(separatorWidget);
this->show();
}
emit this->historyItemSelected(newlySelectedWidget->registerValue);
}
void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) {
this->currentItem->registerValue = registerValue;
void RegisterHistoryWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters, const QDateTime& changeDate) {
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->addItem(targetRegister.value, changeDate);
this->updateCurrentItemValue(targetRegister.value);
if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) {
this->selectCurrentItem();
}
}
void RegisterHistoryWidget::selectCurrentItem() {
this->currentItem->setSelected(true);
}
void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) {
auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer);
QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->insertWidget(2, item);
}
void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
}
void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) {
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
}
this->selectedItemWidget = newlySelectedWidget;
}
emit this->historyItemSelected(newlySelectedWidget->registerValue);
}
void RegisterHistoryWidget::onRegistersWritten(
Targets::TargetRegisters targetRegisters,
const QDateTime& changeDate
) {
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->addItem(targetRegister.value, changeDate);
this->updateCurrentItemValue(targetRegister.value);
}
}
}
}

View File

@@ -14,360 +14,380 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
#include "src/Insight/InsightWorker/Tasks/WriteTargetRegister.hpp"
using namespace Bloom::Widgets;
using Bloom::Exceptions::Exception;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetState;
TargetRegisterInspectorWindow::TargetRegisterInspectorWindow(
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
TargetState currentTargetState,
std::optional<Targets::TargetMemoryBuffer> registerValue,
QWidget* parent
):
QWidget(parent),
registerDescriptor(registerDescriptor),
insightWorker(insightWorker),
registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0)))
namespace Bloom::Widgets
{
this->setWindowFlag(Qt::Window);
auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper();
this->setObjectName("target-register-inspector-window");
this->setWindowTitle("Inspect Register");
using Bloom::Exceptions::Exception;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetState;
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/TargetRegisterInspectorWindow.ui"
)
);
TargetRegisterInspectorWindow::TargetRegisterInspectorWindow(
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
TargetState currentTargetState,
const std::optional<Targets::TargetMemoryBuffer>& registerValue,
QWidget* parent
)
: QWidget(parent)
, registerDescriptor(registerDescriptor)
, insightWorker(insightWorker)
, registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0)))
{
this->setWindowFlag(Qt::Window);
auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper();
this->setObjectName("target-register-inspector-window");
this->setWindowTitle("Inspect Register");
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/TargetRegisterInspectorWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file");
}
auto windowSize = QSize(
840,
static_cast<int>(
440
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(this->registerValue.size()) / 2))
+ (!this->registerDescriptor.writable ? 50 : 0)
)
);
auto containerMargins = QMargins(15, 30, 15, 10);
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(windowSize);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(containerMargins);
this->registerNameLabel = this->container->findChild<QLabel*>("register-name");
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
this->contentContainer = this->container->findChild<QWidget*>("content-container");
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>("register-value-bitset-widget-container");
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
this->registerNameLabel->setText(registerName);
if (this->registerDescriptor.description.has_value()) {
this->registerDescriptionLabel->setText(QString::fromStdString(this->registerDescriptor.description.value()));
this->registerDescriptionLabel->setVisible(true);
}
this->registerHistoryWidget = new RegisterHistoryWidget(
this->registerDescriptor,
this->registerValue,
insightWorker,
this->container
);
auto* contentLayout = this->container->findChild<QHBoxLayout*>("content-layout");
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
registerValueContainer->setContentsMargins(15, 15, 15, 15);
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-name-input");
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-size-input");
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-start-address-input"
);
registerDetailsNameInput->setText(registerName);
registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size));
registerDetailsStartAddressInput->setText(
"0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper()
);
this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2);
if (!this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(true);
this->applyButton->setVisible(false);
auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild<QLabel*>("read-only-indicator-label");
readOnlyIndicatorLabel->show();
}
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
"register-value-bitset-widget-layout"
);
/*
* Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested
* within a vertical layout.
*/
auto* bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
// The register value will be in MSB, which is OK for us as we present the bit widgets in MSB.
auto byteNumber = static_cast<int>(this->registerValue.size() - 1);
for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) {
auto* bitsetWidget = new BitsetWidget(
byteNumber,
this->registerValue.at(registerByteIndex),
!this->registerDescriptor.writable,
this
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/"
"TargetRegisterInspectorWindow.ui"
)
);
bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/"
"TargetRegisterInspectorWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file");
}
auto windowSize = QSize(
840,
static_cast<int>(
440
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(this->registerValue.size()) / 2))
+ (!this->registerDescriptor.writable ? 50 : 0)
)
);
auto containerMargins = QMargins(15, 30, 15, 10);
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(windowSize);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(containerMargins);
this->registerNameLabel = this->container->findChild<QLabel*>("register-name");
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
this->contentContainer = this->container->findChild<QWidget*>("content-container");
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>(
"register-value-bitset-widget-container"
);
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
this->registerNameLabel->setText(registerName);
if (this->registerDescriptor.description.has_value()) {
this->registerDescriptionLabel->setText(
QString::fromStdString(this->registerDescriptor.description.value())
);
this->registerDescriptionLabel->setVisible(true);
}
this->registerHistoryWidget = new RegisterHistoryWidget(
this->registerDescriptor,
this->registerValue,
insightWorker,
this->container
);
auto* contentLayout = this->container->findChild<QHBoxLayout*>("content-layout");
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
registerValueContainer->setContentsMargins(15, 15, 15, 15);
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-name-input"
);
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-size-input"
);
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-start-address-input"
);
registerDetailsNameInput->setText(registerName);
registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size));
registerDetailsStartAddressInput->setText(
"0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper()
);
this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2);
if (!this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(true);
this->applyButton->setVisible(false);
auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild<QLabel*>(
"read-only-indicator-label"
);
readOnlyIndicatorLabel->show();
}
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
"register-value-bitset-widget-layout"
);
/*
* Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested
* within a vertical layout.
*/
auto* bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
// The register value will be in MSB, which is OK for us as we present the bit widgets in MSB.
auto byteNumber = static_cast<int>(this->registerValue.size() - 1);
for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) {
auto* bitsetWidget = new BitsetWidget(
byteNumber,
this->registerValue.at(registerByteIndex),
!this->registerDescriptor.writable,
this
);
bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft);
QObject::connect(
bitsetWidget,
&BitsetWidget::byteChanged,
this,
&TargetRegisterInspectorWindow::updateRegisterValueInputField
);
this->bitsetWidgets.push_back(bitsetWidget);
if (((registerByteIndex + 1) % 2) == 0) {
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
}
byteNumber--;
}
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
registerBitsetWidgetLayout->addStretch(1);
this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height());
QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(
bitsetWidget,
&BitsetWidget::byteChanged,
this->refreshValueButton,
&QPushButton::clicked,
this,
&TargetRegisterInspectorWindow::updateRegisterValueInputField
&TargetRegisterInspectorWindow::refreshRegisterValue
);
this->bitsetWidgets.push_back(bitsetWidget);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges);
if (((registerByteIndex + 1) % 2) == 0) {
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
QObject::connect(
this->registerHistoryWidget,
&RegisterHistoryWidget::historyItemSelected,
this,
&TargetRegisterInspectorWindow::onHistoryItemSelected
);
QObject::connect(
this->registerValueTextInput,
&QLineEdit::textEdited,
this,
&TargetRegisterInspectorWindow::onValueTextInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegisterInspectorWindow::onTargetStateChanged
);
this->updateRegisterValueInputField();
this->onTargetStateChanged(currentTargetState);
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) {
return (descriptor.size > 0 && descriptor.size <= 8);
}
void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) {
this->registerValue = registerValue;
this->registerHistoryWidget->updateCurrentItemValue(this->registerValue);
this->registerHistoryWidget->selectCurrentItem();
}
void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) {
if (text.isEmpty()) {
text = "0";
}
byteNumber--;
}
bool validHexValue = false;
text.toLongLong(&validHexValue, 16);
if (!validHexValue) {
return;
}
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
registerBitsetWidgetLayout->addStretch(1);
auto registerSize = this->registerDescriptor.size;
auto newValue = QByteArray::fromHex(
text.remove("0x", Qt::CaseInsensitive).toLatin1()
).rightJustified(registerSize, 0).right(registerSize);
this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height());
assert(newValue.size() >= registerSize);
assert(registerValue.size() == registerSize);
for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) {
this->registerValue.at(byteIndex) = static_cast<unsigned char>(newValue.at(byteIndex));
}
QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(
this->refreshValueButton,
&QPushButton::clicked,
this,
&TargetRegisterInspectorWindow::refreshRegisterValue
);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges);
QObject::connect(
this->registerHistoryWidget,
&RegisterHistoryWidget::historyItemSelected,
this,
&TargetRegisterInspectorWindow::onHistoryItemSelected
);
QObject::connect(
this->registerValueTextInput,
&QLineEdit::textEdited,
this,
&TargetRegisterInspectorWindow::onValueTextInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegisterInspectorWindow::onTargetStateChanged
);
this->updateRegisterValueInputField();
this->onTargetStateChanged(currentTargetState);
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) {
return (descriptor.size > 0 && descriptor.size <= 8);
}
void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) {
this->registerValue = registerValue;
this->registerHistoryWidget->updateCurrentItemValue(this->registerValue);
this->registerHistoryWidget->selectCurrentItem();
}
void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) {
if (text.isEmpty()) {
text = "0";
}
bool validHexValue = false;
text.toLongLong(&validHexValue, 16);
if (!validHexValue) {
return;
}
auto registerSize = this->registerDescriptor.size;
auto newValue = QByteArray::fromHex(
text.remove("0x", Qt::CaseInsensitive).toLatin1()
).rightJustified(registerSize, 0).right(registerSize);
assert(newValue.size() >= registerSize);
assert(registerValue.size() == registerSize);
for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) {
this->registerValue.at(byteIndex) = static_cast<unsigned char>(newValue.at(byteIndex));
}
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) {
if (newState != TargetState::STOPPED) {
this->registerValueTextInput->setDisabled(true);
this->registerValueBitsetWidgetContainer->setDisabled(true);
this->applyButton->setDisabled(true);
this->refreshValueButton->setDisabled(true);
} else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) {
this->registerValueBitsetWidgetContainer->setDisabled(false);
this->refreshValueButton->setDisabled(false);
if (this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(false);
this->applyButton->setDisabled(false);
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
this->targetState = newState;
}
void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) {
if (newState != TargetState::STOPPED) {
this->registerValueTextInput->setDisabled(true);
this->registerValueBitsetWidgetContainer->setDisabled(true);
this->applyButton->setDisabled(true);
this->refreshValueButton->setDisabled(true);
void TargetRegisterInspectorWindow::onHistoryItemSelected(const Targets::TargetMemoryBuffer& selectedRegisterValue) {
this->registerValue = selectedRegisterValue;
this->updateValue();
} else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) {
this->registerValueBitsetWidgetContainer->setDisabled(false);
this->refreshValueButton->setDisabled(false);
if (this->registerHistoryWidget->isCurrentItemSelected()) {
this->refreshValueButton->setVisible(true);
} else {
this->refreshValueButton->setVisible(false);
}
}
void TargetRegisterInspectorWindow::updateRegisterValueInputField() {
auto value = QByteArray(
reinterpret_cast<const char*>(this->registerValue.data()),
static_cast<qsizetype>(this->registerValue.size())
);
this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper());
}
void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() {
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::updateValue() {
this->updateRegisterValueInputField();
this->updateRegisterValueBitsetWidgets();
}
void TargetRegisterInspectorWindow::refreshRegisterValue() {
this->registerValueContainer->setDisabled(true);
auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor});
QObject::connect(
readTargetRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters targetRegisters) {
this->registerValueContainer->setDisabled(false);
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->setValue(targetRegister.value);
}
if (this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(false);
this->applyButton->setDisabled(false);
}
}
);
QObject::connect(
readTargetRegisterTask,
&InsightWorkerTask::failed,
this,
[this] {
this->registerValueContainer->setDisabled(false);
this->targetState = newState;
}
void TargetRegisterInspectorWindow::onHistoryItemSelected(
const Targets::TargetMemoryBuffer& selectedRegisterValue
) {
this->registerValue = selectedRegisterValue;
this->updateValue();
if (this->registerHistoryWidget->isCurrentItemSelected()) {
this->refreshValueButton->setVisible(true);
} else {
this->refreshValueButton->setVisible(false);
}
);
}
this->insightWorker.queueTask(readTargetRegisterTask);
}
void TargetRegisterInspectorWindow::applyChanges() {
this->registerValueContainer->setDisabled(true);
const auto targetRegister = Targets::TargetRegister(this->registerDescriptor, this->registerValue);
auto* writeRegisterTask = new WriteTargetRegister(targetRegister);
QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] {
this->registerValueContainer->setDisabled(false);
emit this->insightWorker.targetRegistersWritten(
{targetRegister},
DateTime::currentDateTime()
void TargetRegisterInspectorWindow::updateRegisterValueInputField() {
auto value = QByteArray(
reinterpret_cast<const char*>(this->registerValue.data()),
static_cast<qsizetype>(this->registerValue.size())
);
this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value);
this->registerHistoryWidget->selectCurrentItem();
});
QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) {
this->registerValueContainer->setDisabled(false);
auto* errorDialogue = new ErrorDialogue(
"Error",
"Failed to update " + QString::fromStdString(
this->registerDescriptor.name.value_or("")
).toUpper() + " register value - " + errorMessage,
this
this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper());
}
void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() {
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::updateValue() {
this->updateRegisterValueInputField();
this->updateRegisterValueBitsetWidgets();
}
void TargetRegisterInspectorWindow::refreshRegisterValue() {
this->registerValueContainer->setDisabled(true);
auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor});
QObject::connect(
readTargetRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters targetRegisters) {
this->registerValueContainer->setDisabled(false);
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->setValue(targetRegister.value);
}
}
}
);
errorDialogue->show();
});
this->insightWorker.queueTask(writeRegisterTask);
}
QObject::connect(
readTargetRegisterTask,
&InsightWorkerTask::failed,
this,
[this] {
this->registerValueContainer->setDisabled(false);
}
);
void TargetRegisterInspectorWindow::openHelpPage() {
QDesktopServices::openUrl(QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection")));
this->insightWorker.queueTask(readTargetRegisterTask);
}
void TargetRegisterInspectorWindow::applyChanges() {
this->registerValueContainer->setDisabled(true);
const auto targetRegister = Targets::TargetRegister(
this->registerDescriptor,
this->registerValue
);
auto* writeRegisterTask = new WriteTargetRegister(targetRegister);
QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] {
this->registerValueContainer->setDisabled(false);
emit this->insightWorker.targetRegistersWritten(
{targetRegister},
DateTime::currentDateTime()
);
this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value);
this->registerHistoryWidget->selectCurrentItem();
});
QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) {
this->registerValueContainer->setDisabled(false);
auto* errorDialogue = new ErrorDialogue(
"Error",
"Failed to update " + QString::fromStdString(
this->registerDescriptor.name.value_or("")
).toUpper() + " register value - " + errorMessage,
this
);
errorDialogue->show();
});
this->insightWorker.queueTask(writeRegisterTask);
}
void TargetRegisterInspectorWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection"))
);
}
}

View File

@@ -29,7 +29,7 @@ namespace Bloom::Widgets
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
Targets::TargetState currentTargetState,
std::optional<Targets::TargetMemoryBuffer> registerValue = std::nullopt,
const std::optional<Targets::TargetMemoryBuffer>& registerValue = std::nullopt,
QWidget* parent = nullptr
);

View File

@@ -2,27 +2,28 @@
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) {
auto onClick = [this] {
this->setSelected(true);
};
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);
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->setSelected(false);
}
this->postSetSelected(selected);
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);
}
}

View File

@@ -8,143 +8,145 @@
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
RegisterGroupWidget::RegisterGroupWidget(
QString name,
const std::set<TargetRegisterDescriptor>& registerDescriptors,
InsightWorker& insightWorker,
TargetRegistersPaneWidget* parent
): ItemWidget(parent), name(std::move(name)) {
this->setObjectName(this->name);
RegisterGroupWidget::RegisterGroupWidget(
QString name,
const std::set<TargetRegisterDescriptor>& registerDescriptors,
InsightWorker& insightWorker,
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->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->label->setText(this->name);
this->arrowIcon->setObjectName("arrow-icon");
auto static arrowIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
);
this->arrowIcon->setSvgFilePath(arrowIconPath);
this->arrowIcon->setContainerHeight(15);
this->arrowIcon->setContainerWidth(14);
this->arrowIcon->setObjectName("arrow-icon");
auto static arrowIconPath = QString::fromStdString(
Paths::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(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
);
this->registerGroupIcon->setSvgFilePath(registerIconPath);
this->registerGroupIcon->setContainerHeight(15);
this->registerGroupIcon->setContainerWidth(15);
this->registerGroupIcon->setObjectName("register-group-icon");
auto static registerIconPath = QString::fromStdString(
Paths::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);
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);
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;
for (const auto& descriptor : registerDescriptors) {
if (!descriptor.name.has_value()) {
continue;
}
if (!descriptor.readable) {
continue;
}
auto* registerWidget = new RegisterWidget(descriptor, insightWorker, 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));
}
if (!descriptor.readable) {
continue;
}
bodyLayout->addStretch(1);
auto* registerWidget = new RegisterWidget(descriptor, insightWorker, this->bodyWidget);
bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop);
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(
registerWidget,
this->headerWidget,
&ItemWidget::selected,
parent,
&TargetRegistersPaneWidget::onItemSelectionChange
);
this->registerWidgets.insert(registerWidget);
this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget));
}
bodyLayout->addStretch(1);
void RegisterGroupWidget::collapse() {
this->arrowIcon->setAngle(0);
this->bodyWidget->setVisible(false);
this->collapsed = true;
}
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);
void RegisterGroupWidget::expand() {
this->arrowIcon->setAngle(90);
this->bodyWidget->setVisible(true);
this->collapsed = false;
}
this->collapse();
void RegisterGroupWidget::setAllRegistersVisible(bool visible) {
for (const auto& registerWidget : this->registerWidgets) {
registerWidget->setVisible(visible);
}
}
QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] {
if (this->collapsed) {
this->expand();
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 {
} else {
registerWidget->setVisible(false);
}
}
if (matchingWidgetCount == 0) {
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);
this->setVisible(false);
} else {
registerWidget->setVisible(false);
}
}
this->setVisible(true);
if (!keyword.isEmpty()) {
this->expand();
if (matchingWidgetCount == 0) {
this->collapse();
this->setVisible(false);
} else {
this->setVisible(true);
if (!keyword.isEmpty()) {
this->expand();
} else {
this->collapse();
} else {
this->collapse();
}
}
}
}

View File

@@ -9,226 +9,229 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
RegisterWidget::RegisterWidget(
TargetRegisterDescriptor descriptor,
InsightWorker& insightWorker,
QWidget *parent
)
: ItemWidget(parent)
, descriptor(std::move(descriptor))
, searchKeywords(QString::fromStdString(
this->descriptor.name.value_or("") + this->descriptor.description.value_or("")
).toLower())
, insightWorker(insightWorker)
namespace Bloom::Widgets
{
this->setObjectName("register-item");
this->setFixedHeight(25);
using namespace Bloom::Exceptions;
this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper());
this->valueLabel->setObjectName("value");
using Bloom::Targets::TargetRegisterDescriptor;
this->registerIcon->setObjectName("register-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
);
this->registerIcon->setSvgFilePath(registerIconPath);
this->registerIcon->setContainerHeight(15);
this->registerIcon->setContainerWidth(15);
RegisterWidget::RegisterWidget(
TargetRegisterDescriptor descriptor,
InsightWorker& insightWorker,
QWidget *parent
)
: ItemWidget(parent)
, descriptor(std::move(descriptor))
, searchKeywords(QString::fromStdString(
this->descriptor.name.value_or("") + this->descriptor.description.value_or("")
).toLower())
, insightWorker(insightWorker)
{
this->setObjectName("register-item");
this->setFixedHeight(25);
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);
this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper());
this->valueLabel->setObjectName("value");
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);
this->registerIcon->setObjectName("register-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
);
this->registerIcon->setSvgFilePath(registerIconPath);
this->registerIcon->setContainerHeight(15);
this->registerIcon->setContainerWidth(15);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&RegisterWidget::onTargetStateChange
);
}
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);
void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) {
const auto valueChanged = this->currentRegister.has_value()
&& this->currentRegister.value().value != targetRegister.value;
this->currentRegister = targetRegister;
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);
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) + "'";
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&RegisterWidget::onTargetStateChange
);
}
this->valueLabel->setProperty("changed", valueChanged);
this->valueLabel->style()->unpolish(this->valueLabel);
this->valueLabel->style()->polish(this->valueLabel);
void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) {
const auto valueChanged = this->currentRegister.has_value()
&& this->currentRegister.value().value != targetRegister.value;
this->currentRegister = targetRegister;
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->insightWorker,
this->targetState,
this->currentRegister.has_value() ? std::optional(this->currentRegister->value) : std::nullopt,
this
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(targetRegister.value.data()),
static_cast<qsizetype>(targetRegister.value.size())
);
} else {
if (this->currentRegister.has_value()) {
this->inspectWindow->setValue(this->currentRegister->value);
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) + "'";
}
if (!this->inspectWindow->isVisible()) {
this->inspectWindow->show();
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->insightWorker,
this->targetState,
this->currentRegister.has_value() ? std::optional(this->currentRegister->value)
: std::nullopt,
this
);
} else {
this->inspectWindow->activateWindow();
}
}
}
if (this->currentRegister.has_value()) {
this->inspectWindow->setValue(this->currentRegister->value);
}
void RegisterWidget::refreshValue() {
auto* readRegisterTask = new ReadTargetRegisters({this->descriptor});
if (!this->inspectWindow->isVisible()) {
this->inspectWindow->show();
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters registers) {
for (const auto& targetRegister : registers) {
if (targetRegister.descriptor == this->descriptor) {
this->setRegisterValue(targetRegister);
}
} else {
this->inspectWindow->activateWindow();
}
}
);
this->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::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);
}
}
}
);
this->insightWorker.queueTask(readRegisterTask);
}
}
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');
void RegisterWidget::copyName() {
if (this->nameLabel != nullptr) {
QApplication::clipboard()->setText(this->nameLabel->text());
}
}
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;
if (this->targetState == Targets::TargetState::RUNNING) {
this->clearInlineValue();
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;
if (this->targetState == Targets::TargetState::RUNNING) {
this->clearInlineValue();
}
}
}

View File

@@ -12,239 +12,244 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
TargetRegistersPaneWidget::TargetRegistersPaneWidget(
const TargetDescriptor& targetDescriptor,
InsightWorker& insightWorker,
PanelWidget* parent
): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-registers-side-pane");
TargetRegistersPaneWidget::TargetRegistersPaneWidget(
const TargetDescriptor& targetDescriptor,
InsightWorker& insightWorker,
PanelWidget* parent
): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-registers-side-pane");
auto targetRegistersPaneUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui"
)
);
auto targetRegistersPaneUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/"
"TargetRegistersSidePane.ui"
)
);
if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegistersSidePane UI file");
}
if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegistersSidePane UI file");
}
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&targetRegistersPaneUiFile, this);
this->container->setFixedSize(parent->width(), parent->maximumHeight());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&targetRegistersPaneUiFile, this);
this->container->setFixedSize(parent->width(), parent->maximumHeight());
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(this->container);
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(this->container);
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
this->searchInput = this->container->findChild<QLineEdit*>("search-input");
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
this->searchInput = this->container->findChild<QLineEdit*>("search-input");
QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] {
this->expandAllRegisterGroups();
});
QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] {
this->expandAllRegisterGroups();
});
QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] {
this->collapseAllRegisterGroups();
});
QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] {
this->collapseAllRegisterGroups();
});
QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] {
this->filterRegisters(this->searchInput->text());
});
QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] {
this->filterRegisters(this->searchInput->text());
});
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
this->itemContainer = this->container->findChild<QWidget*>("item-container");
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
this->itemContainer = this->container->findChild<QWidget*>("item-container");
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
this->renderedDescriptors = 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,
insightWorker,
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);
}
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) {
auto* registerGroupWidget = new RegisterGroupWidget(
QString::fromStdString(groupName).toUpper(),
registerDescriptors,
auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget(
"CPU General Purpose",
this->renderedDescriptors,
insightWorker,
this
);
itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(registerGroupWidget);
this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
}
itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget);
itemLayout->addStretch(1);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegistersPaneWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
}
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);
if (!keyword.isEmpty()) {
registerGroupWidget->expand();
} else {
registerGroupWidget->collapse();
}
} else {
registerGroupWidget->filterRegisters(keyword);
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);
}
}
}
void TargetRegistersPaneWidget::collapseAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->collapse();
}
}
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
void TargetRegistersPaneWidget::expandAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->expand();
}
}
for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) {
auto* registerGroupWidget = new RegisterGroupWidget(
QString::fromStdString(groupName).toUpper(),
registerDescriptors,
insightWorker,
this
);
void TargetRegistersPaneWidget::refreshRegisterValues(std::optional<std::function<void(void)>> callback) {
auto& descriptors = this->renderedDescriptors;
itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(registerGroupWidget);
this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
}
if (descriptors.empty()) {
return;
}
itemLayout->addStretch(1);
auto* readRegisterTask = new ReadTargetRegisters(descriptors);
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
if (callback.has_value()) {
QObject::connect(
readRegisterTask,
&InsightWorkerTask::completed,
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
callback.value()
&TargetRegistersPaneWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
}
this->insightWorker.queueTask(readRegisterTask);
}
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);
void TargetRegistersPaneWidget::activate() {
this->show();
this->activated = true;
this->postActivate();
}
if (!keyword.isEmpty()) {
registerGroupWidget->expand();
void TargetRegistersPaneWidget::deactivate() {
this->hide();
this->activated = false;
this->postDeactivate();
}
} else {
registerGroupWidget->collapse();
}
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);
} else {
registerGroupWidget->filterRegisters(keyword);
}
}
}
void TargetRegistersPaneWidget::collapseAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->collapse();
}
}
void TargetRegistersPaneWidget::expandAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->expand();
}
}
void TargetRegistersPaneWidget::refreshRegisterValues(std::optional<std::function<void(void)>> callback) {
auto& descriptors = this->renderedDescriptors;
if (descriptors.empty()) {
return;
}
this->selectedItemWidget = newlySelectedWidget;
auto* readRegisterTask = new ReadTargetRegisters(descriptors);
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
if (callback.has_value()) {
QObject::connect(
readRegisterTask,
&InsightWorkerTask::completed,
this,
callback.value()
);
}
this->insightWorker.queueTask(readRegisterTask);
}
}
void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) {
const auto parentSize = this->parent->size();
const auto width = parentSize.width() - 1;
this->container->setFixedWidth(width);
this->searchInput->setFixedWidth(width - 20);
/*
* 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->parent->getHandleSize());
}
void TargetRegistersPaneWidget::postActivate() {
if (this->targetState == Targets::TargetState::STOPPED) {
this->refreshRegisterValues();
void TargetRegistersPaneWidget::activate() {
this->show();
this->activated = true;
this->postActivate();
}
}
void TargetRegistersPaneWidget::postDeactivate() {
}
void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
if (newState == TargetState::STOPPED && this->activated) {
this->refreshRegisterValues();
void TargetRegistersPaneWidget::deactivate() {
this->hide();
this->activated = false;
this->postDeactivate();
}
}
void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) {
for (const auto& targetRegister : registers) {
auto& descriptor = targetRegister.descriptor;
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);
}
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) {
registerGroupWidget->registerWidgetsMappedByDescriptor.at(descriptor)->setRegisterValue(targetRegister);
break;
this->selectedItemWidget = newlySelectedWidget;
}
}
void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) {
const auto parentSize = this->parent->size();
const auto width = parentSize.width() - 1;
this->container->setFixedWidth(width);
this->searchInput->setFixedWidth(width - 20);
/*
* 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->parent->getHandleSize());
}
void TargetRegistersPaneWidget::postActivate() {
if (this->targetState == Targets::TargetState::STOPPED) {
this->refreshRegisterValues();
}
}
void TargetRegistersPaneWidget::postDeactivate() {
}
void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
if (newState == TargetState::STOPPED && this->activated) {
this->refreshRegisterValues();
}
}
void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) {
for (const auto& targetRegister : registers) {
auto& descriptor = targetRegister.descriptor;
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) {
registerGroupWidget->registerWidgetsMappedByDescriptor.at(
descriptor
)->setRegisterValue(targetRegister);
break;
}
}
}
}

View File

@@ -1,81 +1,85 @@
#include "BodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) {
this->setObjectName("target-body");
BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) {
this->setObjectName("target-body");
/*
* The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and
* indicator sizes are proportional to the pin count.
*
* The proportionality constants used below were chosen because they look the nicest. No other reason.
*/
this->setFixedHeight(
std::min(
BodyWidget::MAXIMUM_BODY_HEIGHT,
std::max(
BodyWidget::MINIMUM_BODY_HEIGHT,
static_cast<int>(std::ceil(3.6 * static_cast<double>(pinCount)))
/*
* The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and
* indicator sizes are proportional to the pin count.
*
* The proportionality constants used below were chosen because they look the nicest. No other reason.
*/
this->setFixedHeight(
std::min(
BodyWidget::MAXIMUM_BODY_HEIGHT,
std::max(
BodyWidget::MINIMUM_BODY_HEIGHT,
static_cast<int>(std::ceil(3.6 * static_cast<double>(pinCount)))
)
)
)
);
);
this->firstPinIndicatorDiameter = std::min(
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(std::floor(pinCount / 2)))
);
this->firstPinIndicatorDiameter = std::min(
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(std::floor(pinCount / 2)))
);
this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ?
this->firstPinIndicatorDiameter + 3
: this->firstPinIndicatorDiameter + 2;
}
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
const auto bodyHeight = this->height();
const auto bodyRect = QRectF(
0,
0,
this->width(),
bodyHeight
);
// The first pin indicator is just a circle positioned close to the first pin
const auto firstPinIndicatorRect = QRectF(
6,
(bodyHeight - this->firstPinIndicatorDiameter - 6),
this->firstPinIndicatorDiameter,
this->firstPinIndicatorDiameter
);
/*
* The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package closest to
* the first pin.
*/
const auto orientationIndicatorRect = QRectF(
-(this->orientationIndicatorDiameter / 2),
(bodyHeight / 2) - (this->orientationIndicatorDiameter / 2),
this->orientationIndicatorDiameter,
this->orientationIndicatorDiameter
);
static const auto backgroundColor = QColor(0x37, 0x38, 0x35);
auto targetBodyColor = this->getBodyColor();
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ?
this->firstPinIndicatorDiameter + 3
: this->firstPinIndicatorDiameter + 2;
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.drawRect(bodyRect);
painter.setBrush(backgroundColor);
painter.drawEllipse(firstPinIndicatorRect);
painter.drawEllipse(orientationIndicatorRect);
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
const auto bodyHeight = this->height();
const auto bodyRect = QRectF(
0,
0,
this->width(),
bodyHeight
);
// The first pin indicator is just a circle positioned close to the first pin
const auto firstPinIndicatorRect = QRectF(
6,
(bodyHeight - this->firstPinIndicatorDiameter - 6),
this->firstPinIndicatorDiameter,
this->firstPinIndicatorDiameter
);
/*
* The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package
* closest to the first pin.
*/
const auto orientationIndicatorRect = QRectF(
-(this->orientationIndicatorDiameter / 2),
(bodyHeight / 2) - (this->orientationIndicatorDiameter / 2),
this->orientationIndicatorDiameter,
this->orientationIndicatorDiameter
);
static const auto backgroundColor = QColor(0x37, 0x38, 0x35);
auto targetBodyColor = this->getBodyColor();
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.drawRect(bodyRect);
painter.setBrush(backgroundColor);
painter.drawEllipse(firstPinIndicatorRect);
painter.drawEllipse(orientationIndicatorRect);
}
}

View File

@@ -8,242 +8,245 @@
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetVariant;
using Bloom::Targets::TargetVariant;
DualInlinePackageWidget::DualInlinePackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/DualInlinePackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(PinWidget::WIDTH_SPACING);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
} else {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
}
}
this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size());
this->layout->addLayout(this->topPinLayout);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
const auto bodyWidgetHeight = this->bodyWidget->height();
const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING)
* static_cast<int>(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46;
this->bodyWidget->setGeometry(
0,
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING,
bodyWidgetWidth,
bodyWidgetHeight
);
const auto width = bodyWidgetWidth;
const auto height = (
(
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + (
(PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2
DualInlinePackageWidget::DualInlinePackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/"
"DualInlinePackage.qss"
)
) * 2) + bodyWidgetHeight;
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT));
this->bottomPinLayout->setGeometry(
QRect(
0,
(PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)),
width,
PinWidget::MAXIMUM_HEIGHT
)
);
this->topPinLayout->setContentsMargins(23, 0, 23, 0);
this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0);
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(PinWidget::WIDTH_SPACING);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->setFixedSize(width, height);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
void DualInlinePackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void DualInlinePackageWidget::drawWidget(QPainter& painter) {
using Targets::TargetPinState;
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y()
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
} else {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
}
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size());
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
));
this->layout->addLayout(this->topPinLayout);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
const auto bodyWidgetHeight = this->bodyWidget->height();
const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING)
* static_cast<int>(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46;
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
this->bodyWidget->setGeometry(
0,
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING,
bodyWidgetWidth,
bodyWidgetHeight
);
const auto width = bodyWidgetWidth;
const auto height = (
(
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + (
(PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2
)
) * 2) + bodyWidgetHeight;
this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT));
this->bottomPinLayout->setGeometry(
QRect(
0,
(PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)),
width,
PinWidget::MAXIMUM_HEIGHT
)
);
this->topPinLayout->setContentsMargins(23, 0, 23, 0);
this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0);
this->setFixedSize(width, height);
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
void DualInlinePackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void DualInlinePackageWidget::drawWidget(QPainter& painter) {
using Targets::TargetPinState;
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
pinGeoPosition.y()
));
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
}
}
}

View File

@@ -2,34 +2,39 @@
#include <QPainter>
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Targets;
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto pinWidth = PinBodyWidget::WIDTH;
auto pinHeight = PinBodyWidget::HEIGHT;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto pinWidth = PinBodyWidget::WIDTH;
auto pinHeight = PinBodyWidget::HEIGHT;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}
}

View File

@@ -1,47 +1,49 @@
#include "PinWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Targets;
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT);
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT);
this->layout = new QVBoxLayout();
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
this->layout = new QVBoxLayout();
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor);
this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2))
? Position::TOP : Position::BOTTOM;
this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor);
this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2))
? Position::TOP : Position::BOTTOM;
const bool isTopWidget = this->position == Position::TOP;
const bool isTopWidget = this->position == Position::TOP;
this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)
: (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop));
this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)
: (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop));
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (isTopWidget) {
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
if (isTopWidget) {
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
}
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter);
this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING);
this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter);
this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING);
this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}

View File

@@ -2,47 +2,51 @@
#include <QPainter>
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto targetBodyColor = this->getBodyColor();
auto backgroundColor = QColor("#373835");
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
const auto containerSize = this->size();
const auto targetBodyWidth = containerSize.width() - 8;
const auto targetBodyHeight = containerSize.height() - 8;
auto targetBodyColor = this->getBodyColor();
auto backgroundColor = QColor("#373835");
auto targetBodyPoint = QPoint(
(containerSize.width() / 2) - (targetBodyWidth / 2),
(containerSize.height() / 2) - (targetBodyHeight / 2)
);
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
}
painter.drawRect(
targetBodyPoint.x(),
targetBodyPoint.y(),
targetBodyWidth,
targetBodyHeight
);
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.setBrush(backgroundColor);
painter.drawEllipse(QRectF(
targetBodyPoint.x() + 13,
targetBodyPoint.y() + 13,
18,
18
));
const auto containerSize = this->size();
const auto targetBodyWidth = containerSize.width() - 8;
const auto targetBodyHeight = containerSize.height() - 8;
auto targetBodyPoint = QPoint(
(containerSize.width() / 2) - (targetBodyWidth / 2),
(containerSize.height() / 2) - (targetBodyHeight / 2)
);
painter.drawRect(
targetBodyPoint.x(),
targetBodyPoint.y(),
targetBodyWidth,
targetBodyHeight
);
painter.setBrush(backgroundColor);
painter.drawEllipse(QRectF(
targetBodyPoint.x() + 13,
targetBodyPoint.y() + 13,
18,
18
));
}
}

View File

@@ -3,39 +3,44 @@
#include <QPainter>
#include <QEvent>
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) {
if (isVertical) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) {
if (isVertical) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
} else {
this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH);
} else {
this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH);
}
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT;
auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT;
auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}

View File

@@ -7,95 +7,97 @@
#include "PinWidget.hpp"
#include "PinBodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->layout = new QBoxLayout(QBoxLayout::TopToBottom);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->layout = new QBoxLayout(QBoxLayout::TopToBottom);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4);
auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4);
if (pinDescriptor.number <= pinCountPerLayout) {
this->position = Position::LEFT;
if (pinDescriptor.number <= pinCountPerLayout) {
this->position = Position::LEFT;
} else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) {
this->position = Position::BOTTOM;
} else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) {
this->position = Position::BOTTOM;
} else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) {
this->position = Position::RIGHT;
} else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) {
this->position = Position::RIGHT;
} else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) {
this->position = Position::TOP;
}
} else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) {
this->position = Position::TOP;
}
this->bodyWidget = new PinBodyWidget(
this,
this->pinDescriptor,
(this->position == Position::TOP || this->position == Position::BOTTOM)
);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
auto pinNumberText = QString::number(pinDescriptor.number);
pinNumberText.truncate(5);
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (this->position == Position::LEFT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight));
this->layout->setDirection(QBoxLayout::Direction::RightToLeft);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::BOTTOM) {
this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
this->layout->setDirection(QBoxLayout::Direction::TopToBottom);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
} else if (this->position == Position::RIGHT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft));
this->layout->setDirection(QBoxLayout::Direction::LeftToRight);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::TOP) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom));
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
}
this->layout->addWidget(this->bodyWidget);
this->layout->addSpacing(3);
this->layout->addWidget(this->pinNumberLabel);
if (this->position == Position::LEFT || this->position == Position::RIGHT) {
this->pinNumberLabel->setFixedSize(
PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH,
PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2
this->bodyWidget = new PinBodyWidget(
this,
this->pinDescriptor,
(this->position == Position::TOP || this->position == Position::BOTTOM)
);
} else if (this->position == Position::TOP || this->position == Position::BOTTOM) {
this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
auto pinNumberText = QString::number(pinDescriptor.number);
pinNumberText.truncate(5);
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (this->position == Position::LEFT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight));
this->layout->setDirection(QBoxLayout::Direction::RightToLeft);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::BOTTOM) {
this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
this->layout->setDirection(QBoxLayout::Direction::TopToBottom);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
} else if (this->position == Position::RIGHT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft));
this->layout->setDirection(QBoxLayout::Direction::LeftToRight);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::TOP) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom));
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
}
this->layout->addWidget(this->bodyWidget);
this->layout->addSpacing(3);
this->layout->addWidget(this->pinNumberLabel);
if (this->position == Position::LEFT || this->position == Position::RIGHT) {
this->pinNumberLabel->setFixedSize(
PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH,
PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2
);
} else if (this->position == Position::TOP || this->position == Position::BOTTOM) {
this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2);
}
this->layout->addStretch(1);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
this->layout->addStretch(1);
this->setLayout(this->layout);
void PinWidget::updatePinState(const Targets::TargetPinState& pinState) {
TargetPinWidget::updatePinState(pinState);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
void PinWidget::updatePinState(const Targets::TargetPinState& pinState) {
TargetPinWidget::updatePinState(pinState);
if (this->bodyWidget != nullptr) {
this->bodyWidget->setPinState(pinState);
if (this->bodyWidget != nullptr) {
this->bodyWidget->setPinState(pinState);
}
}
}

View File

@@ -11,403 +11,407 @@
#include "PinWidget.hpp"
#include "BodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
QuadFlatPackageWidget::QuadFlatPackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0);
QuadFlatPackageWidget::QuadFlatPackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0);
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(0);
this->layout->setContentsMargins(0, 0, 0, 0);
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(0);
this->layout->setContentsMargins(0, 0, 0, 0);
this->horizontalLayout = new QHBoxLayout();
this->horizontalLayout->setSpacing(0);
this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->horizontalLayout = new QHBoxLayout();
this->horizontalLayout->setSpacing(0);
this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->rightPinLayout = new QVBoxLayout();
this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop);
this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->rightPinLayout = new QVBoxLayout();
this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop);
this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->leftPinLayout = new QVBoxLayout();
this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom);
this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->leftPinLayout = new QVBoxLayout();
this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom);
this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
const auto pinCountPerLayout = static_cast<int>(targetVariant.pinDescriptorsByNumber.size() / 4);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
const auto pinCountPerLayout = static_cast<int>(targetVariant.pinDescriptorsByNumber.size() / 4);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
if (pinWidget->position == Position::LEFT) {
this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight);
if (pinWidget->position == Position::LEFT) {
this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight);
} else if (pinWidget->position == Position::BOTTOM) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop);
} else if (pinWidget->position == Position::BOTTOM) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop);
} else if (pinWidget->position == Position::RIGHT) {
this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft);
} else if (pinWidget->position == Position::RIGHT) {
this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft);
} else if (pinWidget->position == Position::TOP) {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom);
} else if (pinWidget->position == Position::TOP) {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom);
}
}
this->bodyWidget = new BodyWidget(this);
this->layout->addLayout(this->topPinLayout);
this->horizontalLayout->addLayout(this->leftPinLayout);
this->horizontalLayout->addWidget(this->bodyWidget);
this->horizontalLayout->addLayout(this->rightPinLayout);
this->layout->addLayout(this->horizontalLayout);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
// Layout sizing, positioning and padding
const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT;
const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH;
const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT;
const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH;
/*
* Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets.
* The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets.
*/
const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
+ (PinWidget::MAXIMUM_LABEL_HEIGHT * 2)
+ (PinWidget::PIN_LABEL_SPACING * 3)
) * 2
);
const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::MAXIMUM_LABEL_WIDTH
+ PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 2)
) * 2
);
this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->topPinLayout->addSpacing(horizontalPinWidgetWidth);
this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth);
this->leftPinLayout->setGeometry(QRect(
0,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight);
this->rightPinLayout->setGeometry(QRect(
horizontalLayoutHeight + horizontalPinWidgetWidth,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2;
this->topPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->bottomPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->leftPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->rightPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->setFixedSize(width, height);
// Set the fixed size and center the widget
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
this->bodyWidget = new BodyWidget(this);
this->layout->addLayout(this->topPinLayout);
this->horizontalLayout->addLayout(this->leftPinLayout);
this->horizontalLayout->addWidget(this->bodyWidget);
this->horizontalLayout->addLayout(this->rightPinLayout);
this->layout->addLayout(this->horizontalLayout);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
// Layout sizing, positioning and padding
const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT;
const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH;
const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT;
const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH;
void QuadFlatPackageWidget::drawWidget(QPainter& painter) {
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
/*
* Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets.
* The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets.
*/
const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
+ (PinWidget::MAXIMUM_LABEL_HEIGHT * 2)
+ (PinWidget::PIN_LABEL_SPACING * 3)
) * 2
);
const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::MAXIMUM_LABEL_WIDTH
+ PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 2)
) * 2
);
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->topPinLayout->addSpacing(horizontalPinWidgetWidth);
painter.setFont(pinNameFont);
this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth);
if (pinWidget->position == Position::LEFT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2),
pinGeoPosition.x(),
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2)
));
this->leftPinLayout->setGeometry(QRect(
0,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight);
this->rightPinLayout->setGeometry(QRect(
horizontalLayoutHeight + horizontalPinWidgetWidth,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2;
this->topPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->bottomPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->leftPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->rightPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->setFixedSize(width, height);
// Set the fixed size and center the widget
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void QuadFlatPackageWidget::drawWidget(QPainter& painter) {
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::LEFT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2),
pinGeoPosition.x(),
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2)
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH
- (PinWidget::PIN_LABEL_SPACING * 2),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH
- (PinWidget::PIN_LABEL_SPACING * 3) - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH
- PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 2),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
}
} else if (pinWidget->position == Position::RIGHT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH + PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
));
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH
- PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 3)
- PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::RIGHT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8,
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
));
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 3),
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y()
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 3),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
pinGeoPosition.y()
));
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
}
}
}

View File

@@ -4,90 +4,90 @@
#include "src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp"
using namespace Bloom;
using namespace Bloom::Widgets::InsightTargetWidgets;
namespace Bloom::Widgets::InsightTargetWidgets
{
using Bloom::Targets::TargetState;
using Bloom::Targets::TargetState;
TargetPackageWidget::TargetPackageWidget(
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) {
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&TargetPackageWidget::onTargetStateChanged
);
TargetPackageWidget::TargetPackageWidget(
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) {
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&TargetPackageWidget::onTargetStateChanged
);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetRegistersWritten,
this,
&TargetPackageWidget::onRegistersWritten
);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetRegistersWritten,
this,
&TargetPackageWidget::onRegistersWritten
);
this->setDisabled(true);
}
this->setDisabled(true);
}
void TargetPackageWidget::refreshPinStates(std::optional<std::function<void(void)>> callback) {
auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id);
QObject::connect(
refreshTask,
&RefreshTargetPinStates::targetPinStatesRetrieved,
this,
&TargetPackageWidget::updatePinStates
);
if (callback.has_value()) {
void TargetPackageWidget::refreshPinStates(std::optional<std::function<void(void)>> callback) {
auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id);
QObject::connect(
refreshTask,
&InsightWorkerTask::completed,
&RefreshTargetPinStates::targetPinStatesRetrieved,
this,
callback.value()
&TargetPackageWidget::updatePinStates
);
if (callback.has_value()) {
QObject::connect(
refreshTask,
&InsightWorkerTask::completed,
this,
callback.value()
);
}
this->insightWorker.queueTask(refreshTask);
}
this->insightWorker.queueTask(refreshTask);
}
void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) {
for (auto& pinWidget : this->pinWidgets) {
auto pinNumber = pinWidget->getPinNumber();
if (pinStatesByNumber.contains(pinNumber)) {
pinWidget->updatePinState(pinStatesByNumber.at(pinNumber));
}
}
void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) {
for (auto& pinWidget : this->pinWidgets) {
auto pinNumber = pinWidget->getPinNumber();
if (pinStatesByNumber.contains(pinNumber)) {
pinWidget->updatePinState(pinStatesByNumber.at(pinNumber));
this->update();
}
void TargetPackageWidget::onTargetStateChanged(TargetState newState) {
this->targetState = newState;
if (newState == TargetState::RUNNING) {
this->setDisabled(true);
} else if (newState == TargetState::STOPPED) {
this->refreshPinStates([this] {
if (this->targetState == TargetState::STOPPED) {
this->setDisabled(false);
}
});
}
}
this->update();
}
void TargetPackageWidget::onTargetStateChanged(TargetState newState) {
this->targetState = newState;
if (newState == TargetState::RUNNING) {
this->setDisabled(true);
} else if (newState == TargetState::STOPPED) {
this->refreshPinStates([this] {
if (this->targetState == TargetState::STOPPED) {
this->setDisabled(false);
}
});
}
}
void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) {
if (this->targetState != TargetState::STOPPED) {
return;
}
// If a PORT register was just updated, refresh pin states.
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) {
this->refreshPinStates();
void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) {
if (this->targetState != TargetState::STOPPED) {
return;
}
// If a PORT register was just updated, refresh pin states.
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) {
this->refreshPinStates();
return;
}
}
}
}

View File

@@ -2,29 +2,29 @@
#include <QLayout>
using namespace Bloom;
using namespace Bloom::Widgets::InsightTargetWidgets;
namespace Bloom::Widgets::InsightTargetWidgets
{
TargetPackageWidgetContainer::TargetPackageWidgetContainer(QWidget* parent): QWidget(parent) {}
TargetPackageWidgetContainer::TargetPackageWidgetContainer(QWidget* parent): QWidget(parent) {}
void TargetPackageWidgetContainer::setPackageWidget(TargetPackageWidget* packageWidget) {
this->packageWidget = packageWidget;
void TargetPackageWidgetContainer::setPackageWidget(TargetPackageWidget* packageWidget) {
this->packageWidget = packageWidget;
if (packageWidget != nullptr) {
this->layout()->addWidget(packageWidget);
}
}
void TargetPackageWidgetContainer::resizeEvent(QResizeEvent* event) {
if (this->packageWidget == nullptr) {
return;
if (packageWidget != nullptr) {
this->layout()->addWidget(packageWidget);
}
}
const auto packageSize = this->packageWidget->size();
this->packageWidget->setGeometry(
(this->width() / 2) - (packageSize.width() / 2),
(this->height() / 2) - (packageSize.height() / 2),
packageSize.width(),
packageSize.height()
);
void TargetPackageWidgetContainer::resizeEvent(QResizeEvent* event) {
if (this->packageWidget == nullptr) {
return;
}
const auto packageSize = this->packageWidget->size();
this->packageWidget->setGeometry(
(this->width() / 2) - (packageSize.width() / 2),
(this->height() / 2) - (packageSize.height() / 2),
packageSize.width(),
packageSize.height()
);
}
}

View File

@@ -2,72 +2,74 @@
#include "TargetPinBodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets
{
using namespace Bloom::Targets;
TargetPinBodyWidget::TargetPinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: QWidget(parent), pinDescriptor(std::move(pinDescriptor)) {
this->setObjectName("target-pin-body");
this->setToolTip(QString::fromStdString(this->pinDescriptor.name).toUpper());
}
TargetPinBodyWidget::TargetPinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: QWidget(parent), pinDescriptor(std::move(pinDescriptor)) {
this->setObjectName("target-pin-body");
this->setToolTip(QString::fromStdString(this->pinDescriptor.name).toUpper());
}
QColor TargetPinBodyWidget::getBodyColor() {
auto pinColor = this->defaultBodyColor;
QColor TargetPinBodyWidget::getBodyColor() {
auto pinColor = this->defaultBodyColor;
if (this->pinDescriptor.type == TargetPinType::VCC) {
pinColor = this->vccBodyColor;
if (this->pinDescriptor.type == TargetPinType::VCC) {
pinColor = this->vccBodyColor;
} else if (this->pinDescriptor.type == TargetPinType::GND) {
pinColor = this->gndBodyColor;
} else if (this->pinDescriptor.type == TargetPinType::GND) {
pinColor = this->gndBodyColor;
} else if (this->pinDescriptor.type == TargetPinType::GPIO) {
if (this->pinState.has_value()
&& this->pinState->ioState.has_value()
&& this->pinState->ioDirection.has_value()
) {
const auto ioDirection = this->pinState->ioDirection.value();
const auto ioState = this->pinState->ioState.value();
if (this->pinState->ioState.value() == TargetPinState::IoState::HIGH) {
pinColor = ioDirection == TargetPinState::IoDirection::OUTPUT ?
this->outputHighBodyColor : this->inputHighBodyColor;
}
if ((
ioDirection == TargetPinState::IoDirection::OUTPUT
|| (ioDirection == TargetPinState::IoDirection::INPUT && ioState == TargetPinState::IoState::LOW)
) && !this->hoverActive
} else if (this->pinDescriptor.type == TargetPinType::GPIO) {
if (this->pinState.has_value()
&& this->pinState->ioState.has_value()
&& this->pinState->ioDirection.has_value()
) {
pinColor.setAlpha(220);
const auto ioDirection = this->pinState->ioDirection.value();
const auto ioState = this->pinState->ioState.value();
if (this->pinState->ioState.value() == TargetPinState::IoState::HIGH) {
pinColor = ioDirection == TargetPinState::IoDirection::OUTPUT ?
this->outputHighBodyColor : this->inputHighBodyColor;
}
if ((
ioDirection == TargetPinState::IoDirection::OUTPUT
|| (ioDirection == TargetPinState::IoDirection::INPUT && ioState == TargetPinState::IoState::LOW)
) && !this->hoverActive
) {
pinColor.setAlpha(220);
}
}
}
if (!this->isEnabled()) {
pinColor.setAlpha(this->disableAlphaLevel);
}
return pinColor;
}
if (!this->isEnabled()) {
pinColor.setAlpha(this->disableAlphaLevel);
}
return pinColor;
}
bool TargetPinBodyWidget::event(QEvent* event) {
if (this->pinState.has_value() && this->pinState->ioDirection == TargetPinState::IoDirection::OUTPUT) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
this->repaint();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->repaint();
break;
}
default: {
break;
bool TargetPinBodyWidget::event(QEvent* event) {
if (this->pinState.has_value() && this->pinState->ioDirection == TargetPinState::IoDirection::OUTPUT) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
this->repaint();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->repaint();
break;
}
default: {
break;
}
}
}
}
return QWidget::event(event);
return QWidget::event(event);
}
}

View File

@@ -2,49 +2,51 @@
#include "src/Insight/InsightWorker/Tasks/SetTargetPinState.hpp"
using namespace Bloom;
using namespace Bloom::Widgets::InsightTargetWidgets;
namespace Bloom::Widgets::InsightTargetWidgets
{
using Bloom::Targets::TargetVariant;
using Bloom::Targets::TargetPinDescriptor;
using Bloom::Targets::TargetPinType;
using Bloom::Targets::TargetPinState;
using Bloom::Targets::TargetVariant;
using Bloom::Targets::TargetPinDescriptor;
using Bloom::Targets::TargetPinType;
using Bloom::Targets::TargetPinState;
TargetPinWidget::TargetPinWidget(
Targets::TargetPinDescriptor pinDescriptor,
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent),
insightWorker(insightWorker),
targetVariant(std::move(targetVariant)),
pinDescriptor(std::move(pinDescriptor)) {
if (this->pinDescriptor.type == TargetPinType::UNKNOWN) {
this->setDisabled(true);
}
}
void TargetPinWidget::onWidgetBodyClicked() {
// Currently, we only allow users to toggle the IO state of output pins
if (this->pinState.has_value()
&& this->pinState.value().ioDirection == TargetPinState::IoDirection::OUTPUT
TargetPinWidget::TargetPinWidget(
Targets::TargetPinDescriptor pinDescriptor,
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
)
: QWidget(parent)
, insightWorker(insightWorker)
, targetVariant(std::move(targetVariant))
, pinDescriptor(std::move(pinDescriptor)
) {
this->setDisabled(true);
if (this->pinDescriptor.type == TargetPinType::UNKNOWN) {
this->setDisabled(true);
}
}
auto pinState = this->pinState.value();
pinState.ioState = (pinState.ioState == TargetPinState::IoState::HIGH) ?
TargetPinState::IoState::LOW : TargetPinState::IoState::HIGH;
void TargetPinWidget::onWidgetBodyClicked() {
// Currently, we only allow users to toggle the IO state of output pins
if (this->pinState.has_value()
&& this->pinState.value().ioDirection == TargetPinState::IoDirection::OUTPUT
) {
this->setDisabled(true);
auto* setPinStateTask = new SetTargetPinState(this->pinDescriptor, pinState);
QObject::connect(setPinStateTask, &InsightWorkerTask::completed, this, [this, pinState] {
this->updatePinState(pinState);
this->setDisabled(false);
});
auto pinState = this->pinState.value();
pinState.ioState = (pinState.ioState == TargetPinState::IoState::HIGH) ?
TargetPinState::IoState::LOW : TargetPinState::IoState::HIGH;
QObject::connect(setPinStateTask, &InsightWorkerTask::failed, this, [this] {
this->setDisabled(false);
});
auto* setPinStateTask = new SetTargetPinState(this->pinDescriptor, pinState);
QObject::connect(setPinStateTask, &InsightWorkerTask::completed, this, [this, pinState] {
this->updatePinState(pinState);
this->setDisabled(false);
});
this->insightWorker.queueTask(setPinStateTask);
QObject::connect(setPinStateTask, &InsightWorkerTask::failed, this, [this] {
this->setDisabled(false);
});
this->insightWorker.queueTask(setPinStateTask);
}
}
}

View File

@@ -3,29 +3,30 @@
#include <QMenu>
#include <QAction>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
TextInput::TextInput(QWidget* parent): QLineEdit(parent) {}
TextInput::TextInput(QWidget* parent): QLineEdit(parent) {}
void TextInput::contextMenuEvent(QContextMenuEvent* event) {
if (QMenu* menu = this->createStandardContextMenu()) {
menu->setAttribute(Qt::WA_DeleteOnClose);
void TextInput::contextMenuEvent(QContextMenuEvent* event) {
if (QMenu* menu = this->createStandardContextMenu()) {
menu->setAttribute(Qt::WA_DeleteOnClose);
// Remove default icons
for (auto& action : menu->actions()) {
action->setIcon(QIcon());
}
// Remove default icons
for (auto& action : menu->actions()) {
action->setIcon(QIcon());
menu->popup(event->globalPos());
}
}
menu->popup(event->globalPos());
void TextInput::focusInEvent(QFocusEvent* event) {
QLineEdit::focusInEvent(event);
emit this->focusChanged();
}
void TextInput::focusOutEvent(QFocusEvent* event) {
QLineEdit::focusOutEvent(event);
emit this->focusChanged();
}
}
void TextInput::focusInEvent(QFocusEvent* event) {
QLineEdit::focusInEvent(event);
emit this->focusChanged();
}
void TextInput::focusOutEvent(QFocusEvent* event) {
QLineEdit::focusOutEvent(event);
emit this->focusChanged();
}