2023-03-05 23:29:03 +00:00
|
|
|
#include "RegisterGroupItem.hpp"
|
|
|
|
|
|
|
|
|
|
#include <QGraphicsScene>
|
|
|
|
|
#include <QGraphicsView>
|
|
|
|
|
#include <QSvgRenderer>
|
|
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QString>
|
|
|
|
|
#include <QRect>
|
2024-12-24 18:27:59 +00:00
|
|
|
#include <algorithm>
|
2023-03-05 23:29:03 +00:00
|
|
|
|
|
|
|
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListScene.hpp"
|
|
|
|
|
#include "src/Services/PathService.hpp"
|
2024-12-24 18:27:59 +00:00
|
|
|
#include "src/Logger/Logger.hpp"
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2023-08-13 15:47:51 +01:00
|
|
|
namespace Widgets
|
2023-03-05 23:29:03 +00:00
|
|
|
{
|
|
|
|
|
RegisterGroupItem::RegisterGroupItem(
|
2024-12-24 18:27:59 +00:00
|
|
|
const Targets::TargetRegisterGroupDescriptor& registerGroupDescriptor,
|
|
|
|
|
std::size_t nestedLevel,
|
|
|
|
|
std::unordered_map<Targets::TargetRegisterId, RegisterItem*>& flattenedRegisterItemsByRegisterIds,
|
|
|
|
|
Targets::TargetRegisterDescriptors& flattenedRegisterDescriptors,
|
|
|
|
|
QGraphicsItem* parent
|
2023-03-05 23:29:03 +00:00
|
|
|
)
|
2024-12-24 18:27:59 +00:00
|
|
|
: ListItem(parent)
|
|
|
|
|
, registerGroupDescriptor(registerGroupDescriptor)
|
|
|
|
|
, nestedLevel(nestedLevel)
|
|
|
|
|
, startAddress(this->registerGroupDescriptor.startAddress())
|
|
|
|
|
, name(QString::fromStdString(this->registerGroupDescriptor.name))
|
|
|
|
|
, searchKeywords(
|
|
|
|
|
this->name + " " + QString::fromStdString(this->registerGroupDescriptor.description.value_or(""))
|
|
|
|
|
)
|
2023-03-05 23:29:03 +00:00
|
|
|
{
|
2024-12-24 18:27:59 +00:00
|
|
|
for (const auto& [groupKey, groupDescriptor] : this->registerGroupDescriptor.subgroupDescriptorsByKey) {
|
|
|
|
|
auto* subgroupItem = new RegisterGroupItem{
|
|
|
|
|
groupDescriptor,
|
|
|
|
|
this->nestedLevel + 1,
|
|
|
|
|
flattenedRegisterItemsByRegisterIds,
|
|
|
|
|
flattenedRegisterDescriptors,
|
|
|
|
|
this
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-22 21:56:05 +00:00
|
|
|
if (subgroupItem->isEmpty()) {
|
|
|
|
|
delete subgroupItem;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subgroupItem->setVisible(this->expanded);
|
2024-12-24 18:27:59 +00:00
|
|
|
this->childItems.emplace_back(subgroupItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& [registerKey, registerDescriptor] : this->registerGroupDescriptor.registerDescriptorsByKey) {
|
2025-02-22 21:56:05 +00:00
|
|
|
if (!registerDescriptor.access.readable) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
auto* registerItem = new RegisterItem{registerDescriptor, this->nestedLevel + 1, this};
|
|
|
|
|
registerItem->setVisible(this->expanded);
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
this->childItems.emplace_back(registerItem);
|
|
|
|
|
flattenedRegisterItemsByRegisterIds.emplace(registerDescriptor.id, registerItem);
|
|
|
|
|
flattenedRegisterDescriptors.emplace_back(®isterDescriptor);
|
2023-03-05 23:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
std::sort(
|
|
|
|
|
this->childItems.begin(),
|
|
|
|
|
this->childItems.end(),
|
|
|
|
|
[] (const ListItem* itemA, const ListItem* itemB) {
|
|
|
|
|
const auto* registerItemA = dynamic_cast<const RegisterItem*>(itemA);
|
|
|
|
|
const auto startAddressA = registerItemA != nullptr
|
|
|
|
|
? registerItemA->registerDescriptor.startAddress
|
|
|
|
|
: dynamic_cast<const RegisterGroupItem*>(itemA)->startAddress;
|
|
|
|
|
|
|
|
|
|
const auto* registerItemB = dynamic_cast<const RegisterItem*>(itemB);
|
|
|
|
|
const auto startAddressB = registerItemB != nullptr
|
|
|
|
|
? registerItemB->registerDescriptor.startAddress
|
|
|
|
|
: dynamic_cast<const RegisterGroupItem*>(itemB)->startAddress;
|
|
|
|
|
|
|
|
|
|
return startAddressA < startAddressB;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2023-03-05 23:29:03 +00:00
|
|
|
if (!RegisterGroupItem::registerGroupIconPixmap.has_value()) {
|
|
|
|
|
this->generatePixmaps();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-22 21:56:05 +00:00
|
|
|
bool RegisterGroupItem::isEmpty() const {
|
|
|
|
|
for (const auto* childItem : this->childItems) {
|
|
|
|
|
const auto* subgroupItem = dynamic_cast<const RegisterGroupItem*>(childItem);
|
|
|
|
|
if (subgroupItem != nullptr) {
|
|
|
|
|
if (!subgroupItem->isEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto* registerItem = dynamic_cast<const RegisterItem*>(childItem);
|
|
|
|
|
if (registerItem != nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:29:03 +00:00
|
|
|
void RegisterGroupItem::setExpanded(bool expanded) {
|
|
|
|
|
if (expanded == this->expanded) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->expanded = expanded;
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
for (auto* childItem : this->childItems) {
|
|
|
|
|
childItem->setVisible(expanded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RegisterGroupItem::setAllExpanded(bool expanded) {
|
|
|
|
|
this->expanded = expanded;
|
|
|
|
|
|
|
|
|
|
for (auto* childItem : this->childItems) {
|
|
|
|
|
childItem->setVisible(expanded);
|
|
|
|
|
|
|
|
|
|
auto* groupItem = dynamic_cast<RegisterGroupItem*>(childItem);
|
|
|
|
|
if (groupItem != nullptr) {
|
|
|
|
|
groupItem->setAllExpanded(expanded);
|
|
|
|
|
}
|
2023-03-05 23:29:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RegisterGroupItem::refreshGeometry() {
|
|
|
|
|
const auto groupWidth = this->size.width();
|
|
|
|
|
|
|
|
|
|
const auto startXPosition = 0;
|
|
|
|
|
auto startYPosition = RegisterGroupItem::GROUP_LIST_ITEM_HEIGHT;
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
if (this->expanded) {
|
|
|
|
|
for (auto& childItem : this->childItems) {
|
|
|
|
|
if (childItem->excluded || !childItem->isVisible()) {
|
2023-03-05 23:29:03 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
auto* groupItem = dynamic_cast<RegisterGroupItem*>(childItem);
|
|
|
|
|
if (groupItem != nullptr) {
|
|
|
|
|
groupItem->refreshGeometry();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
childItem->size.setWidth(groupWidth);
|
|
|
|
|
childItem->setPos(startXPosition, startYPosition);
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
childItem->onGeometryChanged();
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
startYPosition += childItem->size.height();
|
2023-03-05 23:29:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->size.setHeight(startYPosition);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
void RegisterGroupItem::applyFilter(const QString& keyword, bool displayEntirePeripheral) {
|
|
|
|
|
const auto displayEntireGroup = displayEntirePeripheral || keyword.isEmpty()
|
|
|
|
|
|| this->searchKeywords.contains(keyword, Qt::CaseInsensitive);
|
|
|
|
|
|
|
|
|
|
auto visibleChildCount = std::size_t{0};
|
|
|
|
|
for (auto* childItem : this->childItems) {
|
|
|
|
|
auto* groupItem = dynamic_cast<RegisterGroupItem*>(childItem);
|
|
|
|
|
if (groupItem != nullptr) {
|
|
|
|
|
groupItem->applyFilter(keyword, displayEntirePeripheral);
|
|
|
|
|
|
|
|
|
|
if (!groupItem->excluded) {
|
|
|
|
|
++visibleChildCount;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* registerItem = dynamic_cast<RegisterItem*>(childItem);
|
|
|
|
|
if (registerItem != nullptr) {
|
|
|
|
|
registerItem->excluded = !displayEntireGroup
|
|
|
|
|
&& !registerItem->searchKeywords.contains(keyword, Qt::CaseInsensitive);
|
|
|
|
|
|
|
|
|
|
if (!registerItem->excluded) {
|
|
|
|
|
++visibleChildCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->excluded = !displayEntireGroup && visibleChildCount == 0 && !keyword.isEmpty();
|
|
|
|
|
this->setExpanded((displayEntireGroup || visibleChildCount > 0) && !keyword.isEmpty());
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 23:29:03 +00:00
|
|
|
void RegisterGroupItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
2024-12-24 18:27:59 +00:00
|
|
|
if (this->excluded) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto& collapsedArrowIconPixmap = *(RegisterGroupItem::collapsedArrowIconPixmap);
|
|
|
|
|
const auto& expandedArrowIconPixmap = *(RegisterGroupItem::expandedArrowIconPixmap);
|
|
|
|
|
const auto& registerGroupIconPixmap = *(RegisterGroupItem::registerGroupIconPixmap);
|
|
|
|
|
|
|
|
|
|
static constexpr auto selectedBackgroundColor = QColor{0x3C, 0x59, 0x5C};
|
2023-03-05 23:29:03 +00:00
|
|
|
|
|
|
|
|
static constexpr auto itemTopPadding = 4;
|
|
|
|
|
static constexpr auto iconLeftMargin = 5;
|
|
|
|
|
static constexpr auto labelLeftMargin = 6;
|
2024-12-24 18:27:59 +00:00
|
|
|
const auto itemLeftPadding = (
|
|
|
|
|
(expandedArrowIconPixmap.width() + iconLeftMargin) * static_cast<int>(this->nestedLevel)
|
|
|
|
|
) + 3;
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
static const auto nameLabelFont = QFont{"'Ubuntu', sans-serif", 11};
|
|
|
|
|
static constexpr auto nameLabelFontColor = QColor{0xAF, 0xB1, 0xB3};
|
2023-03-05 23:29:03 +00:00
|
|
|
|
|
|
|
|
if (this->selected) {
|
|
|
|
|
painter->setBrush(selectedBackgroundColor);
|
|
|
|
|
painter->setPen(Qt::PenStyle::NoPen);
|
|
|
|
|
painter->drawRect(0, 0, this->size.width(), RegisterGroupItem::GROUP_LIST_ITEM_HEIGHT);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
const auto& arrowPixmap = this->isExpanded() ? expandedArrowIconPixmap : collapsedArrowIconPixmap;
|
2023-03-05 23:29:03 +00:00
|
|
|
|
|
|
|
|
painter->drawPixmap(itemLeftPadding, itemTopPadding + 2, arrowPixmap);
|
|
|
|
|
painter->drawPixmap(
|
2024-12-24 18:27:59 +00:00
|
|
|
itemLeftPadding + arrowPixmap.width() + iconLeftMargin,
|
2023-03-05 23:29:03 +00:00
|
|
|
itemTopPadding + 1,
|
|
|
|
|
registerGroupIconPixmap
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
painter->setFont(nameLabelFont);
|
|
|
|
|
painter->setPen(nameLabelFontColor);
|
|
|
|
|
|
|
|
|
|
const auto fontMetrics = painter->fontMetrics();
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
const auto nameLabelSize = fontMetrics.size(Qt::TextSingleLine, this->name);
|
|
|
|
|
const auto nameLabelRect = QRect{
|
|
|
|
|
itemLeftPadding + arrowPixmap.width() + iconLeftMargin + registerGroupIconPixmap.width() + labelLeftMargin,
|
2023-03-05 23:29:03 +00:00
|
|
|
itemTopPadding,
|
|
|
|
|
nameLabelSize.width(),
|
|
|
|
|
nameLabelSize.height()
|
2024-12-24 18:27:59 +00:00
|
|
|
};
|
2023-03-05 23:29:03 +00:00
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
painter->drawText(nameLabelRect, Qt::AlignLeft, this->name);
|
2023-03-05 23:29:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RegisterGroupItem::generatePixmaps() const {
|
2024-12-24 18:27:59 +00:00
|
|
|
auto svgRenderer = QSvgRenderer{};
|
2023-03-05 23:29:03 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
svgRenderer.load(QString::fromStdString(
|
|
|
|
|
Services::PathService::compiledResourcesPath()
|
|
|
|
|
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
|
|
|
|
|
));
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
auto registerGroupIconPixmap = QPixmap{svgRenderer.defaultSize()};
|
2023-03-05 23:29:03 +00:00
|
|
|
registerGroupIconPixmap.fill(Qt::GlobalColor::transparent);
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
auto painter = QPainter{®isterGroupIconPixmap};
|
2023-03-05 23:29:03 +00:00
|
|
|
svgRenderer.render(&painter);
|
|
|
|
|
|
|
|
|
|
RegisterGroupItem::registerGroupIconPixmap = registerGroupIconPixmap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
svgRenderer.load(QString::fromStdString(
|
|
|
|
|
Services::PathService::compiledResourcesPath()
|
|
|
|
|
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
const auto iconSize = svgRenderer.defaultSize();
|
2024-12-24 18:27:59 +00:00
|
|
|
auto arrowIconPixmap = QPixmap{iconSize};
|
2023-03-05 23:29:03 +00:00
|
|
|
arrowIconPixmap.fill(Qt::GlobalColor::transparent);
|
|
|
|
|
|
2024-12-24 18:27:59 +00:00
|
|
|
auto painter = QPainter{&arrowIconPixmap};
|
2023-03-05 23:29:03 +00:00
|
|
|
svgRenderer.render(&painter);
|
|
|
|
|
|
|
|
|
|
RegisterGroupItem::collapsedArrowIconPixmap = arrowIconPixmap;
|
|
|
|
|
|
|
|
|
|
painter.translate(
|
|
|
|
|
std::ceil(static_cast<float>(iconSize.width() / 2)),
|
|
|
|
|
std::ceil(static_cast<float>(iconSize.height() / 2))
|
|
|
|
|
);
|
|
|
|
|
painter.rotate(90);
|
|
|
|
|
painter.translate(
|
|
|
|
|
-std::ceil(static_cast<float>(iconSize.width() / 2)),
|
|
|
|
|
-std::ceil(static_cast<float>(iconSize.height() / 2))
|
|
|
|
|
);
|
|
|
|
|
arrowIconPixmap.fill(Qt::GlobalColor::transparent);
|
|
|
|
|
svgRenderer.render(&painter);
|
|
|
|
|
|
|
|
|
|
RegisterGroupItem::expandedArrowIconPixmap = arrowIconPixmap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|