From dcd180e72886a7bbc252094c63a6f63a4af4a355 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 4 Sep 2021 18:11:52 +0100 Subject: [PATCH] New target registers side pane --- CMakeLists.txt | 9 +- .../InsightWindow/InsightWindow.cpp | 87 +++++- .../InsightWindow/InsightWindow.hpp | 9 + .../Stylesheets/InsightWindow.qss | 175 +++++++++--- .../InsightWindow/UiFiles/InsightWindow.ui | 121 +++++++-- .../TargetRegistersPane/Images/arrow.svg | 67 +++++ .../Images/collapse-all.svg | 66 +++++ .../TargetRegistersPane/Images/expand-all.svg | 66 +++++ .../Images/register-group.svg | 99 +++++++ .../Images/register-window-mockup.svg | 55 ++++ .../TargetRegistersPane/Images/register.svg | 91 +++++++ .../Images/search-registers.svg | 138 ++++++++++ .../Images/target-registers-disabled.svg | 99 +++++++ .../Images/target-registers.svg | 99 +++++++ .../TargetRegistersPane/ItemWidget.cpp | 28 ++ .../TargetRegistersPane/ItemWidget.hpp | 22 ++ .../RegisterGroupWidget.cpp | 152 +++++++++++ .../RegisterGroupWidget.hpp | 52 ++++ .../TargetRegistersPane/RegisterWidget.cpp | 192 ++++++++++++++ .../TargetRegistersPane/RegisterWidget.hpp | 68 +++++ .../TargetRegistersPaneWidget.cpp | 248 ++++++++++++++++++ .../TargetRegistersPaneWidget.hpp | 85 ++++++ .../UiFiles/TargetRegistersSidePane.ui | 134 ++++++++++ src/resources.qrc | 9 + 24 files changed, 2106 insertions(+), 65 deletions(-) create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-window-mockup.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cb745de..c768c7d6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,10 +141,11 @@ add_executable(Bloom src/Insight/InsightWorker/Tasks/InsightWorkerTask.cpp src/Insight/InsightWorker/Tasks/ReadTargetRegisters.cpp src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.cpp + src/Insight/InsightWorker/Tasks/SetTargetPinState.cpp # Target package widgets src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.cpp - src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.cpp @@ -154,6 +155,12 @@ add_executable(Bloom src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinBodyWidget.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/BodyWidget.cpp + + # Target register side pane + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp ) set_target_properties(Bloom PROPERTIES OUTPUT_NAME bloom) diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp index ba0d9805..c622cbc8 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.cpp @@ -1,9 +1,12 @@ -#include +#include "InsightWindow.hpp" + #include #include -#include "InsightWindow.hpp" -#include "AboutWindow.hpp" +#include "UiLoader.hpp" +#include "Widgets/SlidingHandleWidget.hpp" +#include "Widgets/RotatableLabel.hpp" + #include "Widgets/TargetWidgets/DIP/DualInlinePackageWidget.hpp" #include "Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.hpp" @@ -48,10 +51,13 @@ InsightWindow::InsightWindow( throw Exception("Failed to open InsightWindow stylesheet file"); } - auto uiLoader = QUiLoader(this); + auto uiLoader = UiLoader(this); this->mainWindowWidget = uiLoader.load(&mainWindowUiFile); this->mainWindowWidget->setStyleSheet(mainWindowStylesheet.readAll()); + mainWindowUiFile.close(); + mainWindowStylesheet.close(); + QApplication::setWindowIcon(QIcon( QString::fromStdString(Paths::compiledResourcesPath() + "/src/Insight/UserInterfaces/InsightWindow/Images/BloomIcon.svg" @@ -77,12 +83,45 @@ InsightWindow::InsightWindow( this->refreshIoInspectionButton = this->header->findChild("refresh-io-inspection-btn"); connect(this->refreshIoInspectionButton, &QToolButton::clicked, this, [this] { + // TODO: Move this into a member function - getting too big for a lambda if (this->targetState == TargetState::STOPPED && this->selectedVariant != nullptr) { this->toggleUi(true); - emit this->refreshTargetPinStates(this->selectedVariant->id); + if (this->targetPackageWidget != nullptr) { + this->targetPackageWidget->setDisabled(true); + this->targetPackageWidget->refreshPinStates([this] { + if (this->targetState == TargetState::STOPPED) { + this->targetPackageWidget->setDisabled(false); + + if (this->targetRegistersSidePane == nullptr || !this->targetRegistersSidePane->activated) { + this->toggleUi(false); + } + } + }); + } + + if (this->targetRegistersSidePane != nullptr && this->targetRegistersSidePane->activated) { + this->targetRegistersSidePane->refreshRegisterValues([this] { + this->toggleUi(false); + }); + } } }); + this->leftPanel = this->mainWindowWidget->findChild("left-panel"); + this->leftPanelLayoutContainer = this->leftPanel->findChild("left-panel-layout-container"); + + auto leftPanelSlider = this->mainWindowWidget->findChild("left-panel-slider"); + connect(leftPanelSlider, &SlidingHandleWidget::horizontalSlide, this, &InsightWindow::onLeftPanelHandleSlide); + + this->targetRegistersButton = this->mainWindowWidget->findChild("target-registers-btn"); + auto targetRegisterButtonLayout = this->targetRegistersButton->findChild(); + auto registersBtnLabel = new RotatableLabel(270, "Registers", this->targetRegistersButton); + registersBtnLabel->setObjectName("target-registers-btn-label"); + registersBtnLabel->setContentsMargins(5,0,9,0); + targetRegisterButtonLayout->insertWidget(0, registersBtnLabel, 0, Qt::AlignTop); + + connect(this->targetRegistersButton, &QToolButton::clicked, this, &InsightWindow::toggleTargetRegistersPane); + this->footer = this->mainWindowWidget->findChild("footer"); this->targetStatusLabel = this->footer->findChild("target-state"); this->programCounterValueLabel = this->footer->findChild("target-program-counter-value"); @@ -235,6 +274,17 @@ void InsightWindow::activate() { this->ioUnavailableWidget->show(); } + auto leftPanelLayout = this->leftPanelLayoutContainer->findChild("left-panel-layout"); + this->targetRegistersSidePane = new TargetRegistersPaneWidget( + this->targetDescriptor, + insightWorker, + this->leftPanelLayoutContainer + ); + leftPanelLayout->addWidget(this->targetRegistersSidePane); + this->targetRegistersButton->setChecked(false); + this->targetRegistersButton->setDisabled(false); + + this->toggleUi(this->targetState != TargetState::STOPPED); this->activated = true; } @@ -246,6 +296,14 @@ void InsightWindow::deactivate() { this->targetPackageWidget = nullptr; } + if (this->targetRegistersSidePane != nullptr) { + this->targetRegistersSidePane->deactivate(); + this->targetRegistersSidePane->deleteLater(); + this->leftPanel->setVisible(false); + this->targetRegistersButton->setChecked(false); + this->targetRegistersButton->setDisabled(true); + } + this->ioUnavailableWidget->setText( "Insight deactivated - Bloom has been disconnected from the target.\n\n" "Bloom will attempt to reconnect upon the start of a new debug session." @@ -365,7 +423,12 @@ void InsightWindow::toggleUi(bool disable) { this->refreshIoInspectionButton->setDisabled(disable); this->refreshIoInspectionButton->repaint(); } +} +void InsightWindow::onLeftPanelHandleSlide(int horizontalPosition) { + auto width = std::max(this->leftPanelMinWidth, this->leftPanel->width() + horizontalPosition); + this->leftPanel->setMaximumWidth(width); + this->leftPanel->setFixedWidth(width); } void InsightWindow::onTargetControllerSuspended() { @@ -446,6 +509,20 @@ void InsightWindow::onTargetIoPortsUpdate() { } } +void InsightWindow::toggleTargetRegistersPane() { + if (this->targetRegistersSidePane->activated) { + this->targetRegistersSidePane->deactivate(); + this->targetRegistersButton->setChecked(false); + /* + * Given that the target registers side pane is currently the only pane in the left panel, the panel will be + * empty so no need to leave it visible. + */ + this->leftPanel->setVisible(false); + + } else { + this->targetRegistersSidePane->activate(); + this->targetRegistersButton->setChecked(true); + this->leftPanel->setVisible(true); } } diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp index 51bdf91a..424b3890 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp @@ -14,6 +14,7 @@ #include "src/Targets/TargetVariant.hpp" #include "Widgets/TargetWidgets/TargetPackageWidget.hpp" +#include "Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp" #include "AboutWindow.hpp" namespace Bloom @@ -41,6 +42,12 @@ namespace Bloom QWidget* header = nullptr; QToolButton* refreshIoInspectionButton = nullptr; + QWidget* leftPanel = nullptr; + int leftPanelMinWidth = 300; + QWidget* leftPanelLayoutContainer = nullptr; + Widgets::TargetRegistersPaneWidget* targetRegistersSidePane = nullptr; + QToolButton* targetRegistersButton = nullptr; + QWidget* ioContainerWidget = nullptr; QLabel* ioUnavailableWidget = nullptr; Widgets::InsightTargetWidgets::TargetPackageWidget* targetPackageWidget = nullptr; @@ -78,6 +85,7 @@ namespace Bloom void show(); public slots: + void onLeftPanelHandleSlide(int horizontalPosition); void onTargetControllerSuspended(); void onTargetControllerResumed(const Bloom::Targets::TargetDescriptor& targetDescriptor); void onTargetStateUpdate(Targets::TargetState newState); @@ -87,6 +95,7 @@ namespace Bloom void openReportIssuesUrl(); static void openGettingStartedUrl(); void openAboutWindow(); + void toggleTargetRegistersPane(); signals: void refreshTargetPinStates(int variantId); diff --git a/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss b/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss index 2259cef7..857ea019 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss +++ b/src/Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss @@ -1,6 +1,7 @@ * { color: #afb1b3; font-family: 'Ubuntu', sans-serif; + font-size: 14px; } QMainWindow { @@ -11,10 +12,10 @@ QMainWindow { position: relative; background-color: transparent; max-height: 25px; + min-height: 25px; width: auto; border-top: 1px solid #41423f; - border-top: 1px solid #41423f; - border-bottom: 1px solid #2a2a2a; + border-bottom: 1px solid #2b2b2b; } QMenuBar::item { @@ -62,7 +63,6 @@ QMenu::item { } QMenu::item:selected { - background-color: #4d7bba; background-color: #335883; } @@ -70,50 +70,32 @@ QMenu::item:disabled { color: #808484; } -QMenu#help-menu::separator { +QMenu::separator { border-top: 1px solid #41423f; height: 1px; +} + +QMenu#help-menu::separator { margin: 1px 0; } -#rhSideBar { - max-width: 25px; - background-color: transparent; - height: auto; - border-left: 1px solid #515151; - text-align: right; -} - -#rhSideBar #inspectRamBtn { - position: relative; - max-width: 25px; - height: 25px; - color: #000; - border: none; - qproperty-icon: url(":/compiled/src/Insight/UserInterfaces/InsightWindow/Images/RAM.svg"); - icon-size: 25px; -} - -#rhSideBar #inspectRamBtn:hover { - background-color: #343536; - border: none; - -} - #header-tool-bar { - min-height: 25px; - min-width: 100%; + min-height: 27px; + margin-left: 10px; +} + +#header-tool-bar QHBoxLayout { + margin-left: 10px; } #header-tool-bar QToolButton { background-color: transparent; - max-width: 25px; - max-height: 21px; + /*max-width: 26px;*/ + /*max-height: 22px;*/ border: none; padding: 0; - margin-top: 3px; - margin-bottom: 3px; - margin-left: 2px; + qproperty-buttonWidth: 24; + qproperty-buttonHeight: 20; } #header-tool-bar QToolButton:hover { @@ -195,3 +177,126 @@ QToolTip { color: #838386; font-size: 14px; } + +QScrollBar:vertical { + background-color: transparent; + width: 15px; + margin: 1px 1px 1px 5px; +} + +QScrollBar:handle:vertical { + border: none; + background-color: rgba(69, 69, 66, 0.6); +} + +QScrollBar:handle:vertical:hover { + background-color: rgba(69, 69, 66, 0.91); +} + +QScrollBar::add-line:vertical, +QScrollBar::sub-line:vertical { + border: none; + background: none; +} + +/* Left-side panel */ +#left-side-menu-bar { + max-width: 22px; + background-color: transparent; + height: auto; + border-right: 1px solid #313131; + border-right: 1px solid #2b2b2b; + text-align: right; +} + +#left-side-menu-bar #target-registers-btn { + position: relative; + max-width: 22px; + min-height: 95px; + border: none; +} + +#left-side-menu-bar QToolButton:hover, +#left-side-menu-bar QToolButton:checked { + background-color: #2B2B29; + border: none; +} + +#left-panel-layout-container { + border-right: 1px solid #2b2b2b; +} + +/* Target Registers Pane */ +#target-registers-side-pane { + /*min-width: 250px;*/ + /*min-height: 200px;*/ + /*height: 200px;*/ + +} + +#target-registers-side-pane #tool-bar { + background-color: #353C41; + border-bottom: 1px solid #2b2b2b; +} + +#target-registers-side-pane #title { + color: #afb1b3; +} + +#target-registers-side-pane #tool-bar QToolButton { + background-color: transparent; + /*max-width: 26px;*/ + /*max-height: 22px;*/ + border: none; + padding: 0; + qproperty-buttonWidth: 23; + qproperty-buttonHeight: 21; +} + +#target-registers-side-pane #tool-bar QToolButton:hover { + background-color: #40464e; + border: none; + padding: 0; +} + +#target-registers-side-pane #search-bar { + background-color: #343532; + border-bottom: 1px solid #2b2b2b; +} + +#target-registers-side-pane #search-input { + background-color: transparent; + /*font-size: 13px;*/ + color: #9b9b9e; +} + +#target-registers-side-pane QScrollArea { + background-color: transparent; +} + +#target-registers-side-pane #item-container { + background-color: transparent; +} + +#target-registers-side-pane #header { + border: none; +} + +#target-registers-side-pane #register-group-header[selected=true], +#target-registers-side-pane #register-item[selected=true] { + background-color: #335883; +} + +#target-registers-side-pane #register-item #value { + font-size: 13px; + color: #8a8a8d; + font-style: italic; +} + +#target-registers-side-pane #register-item[selected=false] #value[changed=true] { + color: #547fba; +} + +#target-registers-side-pane #register-item[selected=true] #value[changed=true] { + color: #8a8a8d; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui b/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui index 1e7808a9..78e8b10c 100644 --- a/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui +++ b/src/Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui @@ -82,6 +82,103 @@ 0 + + + + Qt::LeftToRight + + + + 0 + + + 0 + + + + + + Target Registers + + + true + + + true + + + + 0 + + + 0 + + + + + 15 + + + 22 + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg + + + + + + + Qt::Vertical + + + + + + + + + + + + + false + + + 300 + + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + + + + + + 6 + + + + + + @@ -99,30 +196,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg new file mode 100644 index 00000000..0fe89749 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg @@ -0,0 +1,67 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg new file mode 100644 index 00000000..8b17db19 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg @@ -0,0 +1,66 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg new file mode 100644 index 00000000..efcb18b7 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg @@ -0,0 +1,66 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg new file mode 100644 index 00000000..5bc6c8c8 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg @@ -0,0 +1,99 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-window-mockup.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-window-mockup.svg new file mode 100644 index 00000000..4f87f6f3 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-window-mockup.svg @@ -0,0 +1,55 @@ + + + + + + + + image/svg+xml + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg new file mode 100644 index 00000000..49f043fb --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg @@ -0,0 +1,91 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg new file mode 100644 index 00000000..214b2c0f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg @@ -0,0 +1,138 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg new file mode 100644 index 00000000..f78115c5 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg @@ -0,0 +1,99 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg new file mode 100644 index 00000000..1f902095 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg @@ -0,0 +1,99 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp new file mode 100644 index 00000000..8a8483ea --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.cpp @@ -0,0 +1,28 @@ +#include "ItemWidget.hpp" + +#include + +using namespace Bloom::Widgets; + +ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) { + auto onClick = [this] { + this->setSelected(true); + }; + + this->connect(this, &ClickableWidget::clicked, this, onClick); + this->connect(this, &ClickableWidget::rightClicked, this, onClick); + + this->setSelected(false); +} + +void ItemWidget::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); + + if (selected) { + emit this->selected(this); + } + + this->postSetSelected(selected); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.hpp new file mode 100644 index 00000000..b7f90d8e --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/ItemWidget.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "../ClickableWidget.hpp" + +namespace Bloom::Widgets +{ + class ItemWidget: public ClickableWidget + { + Q_OBJECT + protected: + virtual void postSetSelected(bool selected) {}; + + public: + ItemWidget(QWidget *parent); + + public slots: + void setSelected(bool selected); + + signals: + void selected(ItemWidget*); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp new file mode 100644 index 00000000..ceb0f777 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.cpp @@ -0,0 +1,152 @@ +#include "RegisterGroupWidget.hpp" + +#include +#include +#include + +#include "RegisterWidget.hpp" + +#include "src/Helpers/Paths.hpp" + +using namespace Bloom::Widgets; +using namespace Bloom::Exceptions; + +using Bloom::Targets::TargetRegisterDescriptor; + +RegisterGroupWidget::RegisterGroupWidget( + QString name, + const std::set& 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->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->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); + + auto bodyLayout = new QVBoxLayout(this->bodyWidget); + bodyLayout->setContentsMargins(0, 0,0,0); + bodyLayout->setSpacing(0); + + for (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); + + this->connect( + registerWidget, + &ItemWidget::selected, + parent, + &TargetRegistersPaneWidget::onItemSelectionChange + ); + + this->registerWidgets.insert(registerWidget); + this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget)); + } + + bodyLayout->addStretch(1); + + this->layout->setContentsMargins(0,0,0,0); + this->layout->setSpacing(0); + this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop); + this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop); + this->layout->addStretch(1); + + this->collapse(); + + this->connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] { + if (this->collapsed) { + this->expand(); + + } else { + this->collapse(); + } + }); + + this->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 (auto& registerWidget : this->registerWidgets) { + registerWidget->setVisible(visible); + } +} + +void RegisterGroupWidget::filterRegisters(const std::string& keyword) { + int matchingWidgetCount = 0; + for (auto& registerWidget : this->registerWidgets) { + if (keyword.empty() + || (registerWidget->descriptor.name.value().find(keyword) != std::string::npos) + ) { + matchingWidgetCount++; + registerWidget->setVisible(true); + + } else { + registerWidget->setVisible(false); + } + } + + if (matchingWidgetCount == 0) { + this->collapse(); + this->setVisible(false); + + } else { + this->setVisible(true); + if (!keyword.empty()) { + this->expand(); + + } else { + this->collapse(); + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.hpp new file mode 100644 index 00000000..dbdcaa30 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterGroupWidget.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "TargetRegistersPaneWidget.hpp" +#include "ItemWidget.hpp" +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +#include "../ClickableWidget.hpp" +#include "../SvgWidget.hpp" + +namespace Bloom::Widgets +{ + class RegisterWidget; + class RegisterGroupWidget: public ItemWidget + { + Q_OBJECT + private: + QVBoxLayout* layout = new QVBoxLayout(this); + ItemWidget* headerWidget = new ItemWidget(this); + SvgWidget* arrowIcon = new SvgWidget(this->headerWidget); + SvgWidget* registerGroupIcon = new SvgWidget(this->headerWidget); + QLabel* label = new QLabel(this->headerWidget); + QWidget* bodyWidget = new QWidget(this); + + public: + QString name; + bool collapsed = true; + + std::set registerWidgets; + std::map registerWidgetsMappedByDescriptor; + + RegisterGroupWidget( + QString name, + const std::set& registerDescriptors, + InsightWorker& insightWorker, + TargetRegistersPaneWidget* parent + ); + + void collapse(); + void expand(); + void setAllRegistersVisible(bool visible); + + void filterRegisters(const std::string& keyword); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp new file mode 100644 index 00000000..56386065 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.cpp @@ -0,0 +1,192 @@ +#include "RegisterWidget.hpp" + +#include +#include +#include +#include +#include + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" + +#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(descriptor), insightWorker(insightWorker) { + this->setObjectName("register-item"); + this->setFixedHeight(25); + + this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper()); + this->valueLabel->setObjectName("value"); + + this->registerIcon->setObjectName("register-icon"); + auto static registerIconPath = QString::fromStdString( + Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg" + ); + this->registerIcon->setSvgFilePath(registerIconPath); + this->registerIcon->setContainerHeight(15); + this->registerIcon->setContainerWidth(15); + + this->layout->setContentsMargins(47, 0, 0, 0); + this->layout->setSpacing(0); + this->layout->addWidget(this->registerIcon); + this->layout->addSpacing(7); + this->layout->addWidget(this->nameLabel); + this->layout->addSpacing(5); + this->layout->addWidget(this->valueLabel); + this->layout->addStretch(1); + + this->connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue); + this->connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName); + this->connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex); + this->connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal); + this->connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary); + + this->connect( + &(this->insightWorker), + &InsightWorker::targetStateUpdated, + this, + &RegisterWidget::onTargetStateChange + ); +} + +void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) { + const auto valueChanged = this->currentRegister.has_value() + && this->currentRegister.value().value != targetRegister.value; + this->currentRegister = targetRegister; + + auto valueByteArray = QByteArray( + reinterpret_cast(targetRegister.value.data()), + static_cast(targetRegister.value.size()) + ); + + auto hexValueByteArray = valueByteArray.toHex(); + auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper() + + " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16)); + + if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) { + registerValue += " | '" + QString(valueByteArray) + "'"; + } + + this->valueLabel->setProperty("changed", valueChanged); + this->valueLabel->style()->unpolish(this->valueLabel); + this->valueLabel->style()->polish(this->valueLabel); + + this->valueLabel->setText(registerValue); +} + +void RegisterWidget::clearInlineValue() { + this->valueLabel->clear(); +} + +void RegisterWidget::refreshValue() { + auto readRegisterTask = new ReadTargetRegisters({this->descriptor}); + + this->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::copyName() { + if (this->nameLabel != nullptr) { + QApplication::clipboard()->setText(this->nameLabel->text()); + } +} + +void RegisterWidget::copyValueHex() { + if (this->currentRegister.has_value()) { + auto valueByteArray = QByteArray( + reinterpret_cast(this->currentRegister.value().value.data()), + static_cast(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(this->currentRegister.value().value.data()), + static_cast(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(this->currentRegister.value().size()); + auto valueByteArray = QByteArray( + reinterpret_cast(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::contextMenuEvent(QContextMenuEvent* event) { + this->setSelected(true); + + auto menu = new QMenu(this); + 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); + + 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::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(); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.hpp new file mode 100644 index 00000000..6c4468ff --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/RegisterWidget.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "ItemWidget.hpp" +#include "../SvgWidget.hpp" +#include "src/Insight/InsightWorker/InsightWorker.hpp" + +namespace Bloom::Widgets +{ + class RegisterWidget: public ItemWidget + { + Q_OBJECT + private: + InsightWorker& insightWorker; + QHBoxLayout* layout = new QHBoxLayout(this); + SvgWidget* registerIcon = new SvgWidget(this); + QLabel* nameLabel = new QLabel(this); + QLabel* valueLabel = new QLabel(this); + + // Context-menu actions + QAction* refreshValueAction = new QAction("Refresh Value", this); + QAction* copyValueNameAction = new QAction("Copy Register Name", this); + QAction* copyValueHexAction = new QAction("Copy Hexadecimal Value", this); + QAction* copyValueDecimalAction = new QAction("Copy Decimal Value", this); + QAction* copyValueBinaryAction = new QAction("Copy Binary Value", this); + + void postSetSelected(bool selected) override; + + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + + private slots: + void onTargetStateChange(Targets::TargetState newState); + + public: + Targets::TargetRegisterDescriptor descriptor; + std::optional currentRegister; + + RegisterWidget( + Targets::TargetRegisterDescriptor descriptor, + InsightWorker& insightWorker, + QWidget *parent + ); + + [[nodiscard]] QSize minimumSizeHint() const override { + auto size = QSize(this->parentWidget()->width(), 25); + return size; + } + + void setRegisterValue(const Targets::TargetRegister& targetRegister); + void clearInlineValue(); + + void contextMenuEvent(QContextMenuEvent* event) override; + + public slots: + void refreshValue(); + void copyName(); + void copyValueHex(); + void copyValueDecimal(); + void copyValueBinary(); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp new file mode 100644 index 00000000..e354c5ec --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.cpp @@ -0,0 +1,248 @@ +#include "TargetRegistersPaneWidget.hpp" + +#include +#include +#include + +#include "../../UiLoader.hpp" +#include "../ExpandingWidget.hpp" +#include "RegisterGroupWidget.hpp" +#include "RegisterWidget.hpp" + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" + +#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp" + +using namespace Bloom::Widgets; +using namespace Bloom::Exceptions; + +using Bloom::Targets::TargetDescriptor; +using Bloom::Targets::TargetRegisterDescriptor; +using Bloom::Targets::TargetRegisterDescriptors; +using Bloom::Targets::TargetRegisterType; + +TargetRegistersPaneWidget::TargetRegistersPaneWidget( + const TargetDescriptor& targetDescriptor, + InsightWorker& insightWorker, + QWidget* 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" + ) + ); + + 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 layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(this->container); + + this->toolBar = this->container->findChild("tool-bar"); + this->collapseAllButton = this->toolBar->findChild("collapse-all-button"); + this->expandAllButton = this->toolBar->findChild("expand-all-button"); + this->toolBar->layout()->setContentsMargins(5, 0, 5, 0); + this->searchInput = this->container->findChild("search-input"); + + this->connect(this->expandAllButton, &QToolButton::clicked, [this] { + this->expandAllRegisterGroups(); + }); + + this->connect(this->collapseAllButton, &QToolButton::clicked, [this] { + this->collapseAllRegisterGroups(); + }); + + this->connect(this->searchInput, &QLineEdit::textChanged, [this] { + this->filterRegisters(this->searchInput->text()); + }); + + this->itemContainer = this->container->findChild("item-container"); + auto itemLayout = this->itemContainer->findChild(); + + auto& registerDescriptors = targetDescriptor.registerDescriptorsByType; + this->renderedDescriptors = std::set( + 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>(); + for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) { + registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor); + auto groupName = registerDescriptor.groupName.value_or("other"); + + } + + for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) { + auto registerGroupWidget = new RegisterGroupWidget( + QString::fromStdString(groupName).toUpper(), + registerDescriptors, + insightWorker, + this + ); + + itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop); + this->registerGroupWidgets.insert(registerGroupWidget); + this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end()); + } + + itemLayout->addStretch(1); + + this->connect( + &insightWorker, + &InsightWorker::targetStateUpdated, + this, + &TargetRegistersPaneWidget::onTargetStateChanged + ); +} + +void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) { + auto parentSize = this->parent->size(); + this->container->setFixedSize( + parentSize.width() - 1, + parentSize.height() + ); + this->searchInput->setFixedWidth(parentSize.width() - 20); +} + +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::filterRegisters(const QString& keyword) { + auto stdKeyword = keyword.toLower().toStdString(); + + for (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(stdKeyword); + } + } +} + +void TargetRegistersPaneWidget::collapseAllRegisterGroups() { + for (auto& registerGroupWidget : this->registerGroupWidgets) { + registerGroupWidget->collapse(); + } +} + +void TargetRegistersPaneWidget::expandAllRegisterGroups() { + for (auto& registerGroupWidget : this->registerGroupWidgets) { + registerGroupWidget->expand(); + } +} + +void TargetRegistersPaneWidget::refreshRegisterValues(std::optional> callback) { + auto& descriptors = this->renderedDescriptors; + + if (descriptors.empty()) { + return; + } + + auto readRegisterTask = new ReadTargetRegisters(descriptors); + this->connect( + readRegisterTask, + &ReadTargetRegisters::targetRegistersRead, + this, + &TargetRegistersPaneWidget::onRegistersRead + ); + + if (callback.has_value()) { + this->connect( + readRegisterTask, + &InsightWorkerTask::completed, + this, + callback.value() + ); + } + + this->insightWorker.queueTask(readRegisterTask); +} + +void TargetRegistersPaneWidget::clearInlineValues() { + for (const auto& registerGroupWidget : this->registerGroupWidgets) { + for (auto& [registerDescriptor, registerWidget] : registerGroupWidget->registerWidgetsMappedByDescriptor) { + registerWidget->clearInlineValue(); + } + } +} + +void TargetRegistersPaneWidget::activate() { + this->show(); + this->activated = true; + this->postActivate(); +} + +void TargetRegistersPaneWidget::deactivate() { + this->hide(); + this->activated = false; + this->postDeactivate(); +} + +void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) { + // Only one item in the target registers pane can be selected at any given time. + if (this->selectedItemWidget != newlySelectedWidget) { + if (this->selectedItemWidget != nullptr) { + this->selectedItemWidget->setSelected(false); + } + + this->selectedItemWidget = newlySelectedWidget; + } +} + +void TargetRegistersPaneWidget::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; + } + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp new file mode 100644 index 00000000..99e2a7c4 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/TargetRegistersPaneWidget.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "ItemWidget.hpp" +#include "../SvgToolButton.hpp" +#include "src/Insight/InsightWorker/InsightWorker.hpp" +#include "src/Targets/TargetState.hpp" +#include "src/Targets/TargetDescriptor.hpp" + +namespace Bloom::Widgets +{ + class RegisterGroupWidget; + + class TargetRegistersPaneWidget: public QWidget + { + Q_OBJECT + private: + const Targets::TargetDescriptor& targetDescriptor; + InsightWorker& insightWorker; + + QWidget* parent = nullptr; + QWidget* container = nullptr; + + QWidget* toolBar = nullptr; + SvgToolButton* collapseAllButton = nullptr; + SvgToolButton* expandAllButton = nullptr; + + QLineEdit* searchInput = nullptr; + QWidget* itemContainer = nullptr; + + ItemWidget* selectedItemWidget = nullptr; + + std::set registerGroupWidgets; + Targets::TargetRegisterDescriptors renderedDescriptors; + + Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + + private slots: + void onTargetStateChanged(Targets::TargetState newState); + + protected: + void resizeEvent(QResizeEvent* event) override; + + virtual void postActivate(); + virtual void postDeactivate(); + + public: + bool activated = false; + + TargetRegistersPaneWidget( + const Targets::TargetDescriptor& targetDescriptor, + InsightWorker& insightWorker, + QWidget *parent + ); + + [[nodiscard]] QSize minimumSizeHint() const override { + return this->parent->size(); + } + + [[nodiscard]] QSize sizeHint() const override { + return this->minimumSizeHint(); + } + + void filterRegisters(const QString& keyword); + void collapseAllRegisterGroups(); + void expandAllRegisterGroups(); + + void refreshRegisterValues(std::optional> callback = std::nullopt); + void clearInlineValues(); + + void activate(); + void deactivate(); + + public slots: + void onItemSelectionChange(ItemWidget* newlySelectedWidget); + void onRegistersRead(const Targets::TargetRegisters& registers); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui new file mode 100644 index 00000000..4a62c59d --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui @@ -0,0 +1,134 @@ + + + + + + 0 + + + 0 + + + + + 28 + + + + 3 + + + 0 + + + + Target Registers + + + + + + Qt::Horizontal + + + + + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg + + + Expand All + + + + + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg + + + Collapse All + + + + + + + + + + + 27 + + + + 0 + + + 0 + + + + + 16 + + + 20 + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg + + + + + + + 25 + + + 240 + + + false + + + + + + + Qt::Horizontal + + + + + + + + + true + Qt::ScrollBarAsNeeded + QAbstractScrollArea::AdjustToContents + Qt::ScrollBarAlwaysOff + QSizePolicy::SetMinAndMaxSize + + + + + + 0 + + + 0 + + + + QLayout::SetMinAndMaxSize + + + + + + + + diff --git a/src/resources.qrc b/src/resources.qrc index 18947cb7..39d68d0a 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -19,6 +19,7 @@ ./Insight/UserInterfaces/InsightWindow/UiFiles/InsightWindow.ui ./Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui ./Insight/UserInterfaces/InsightWindow/Stylesheets/InsightWindow.qss @@ -31,5 +32,13 @@ ./Insight/UserInterfaces/InsightWindow/Images/RAM.svg ./Insight/UserInterfaces/InsightWindow/Images/refresh.svg ./Insight/UserInterfaces/InsightWindow/Images/refresh-disabled.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/collapse-all.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/expand-all.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/target-registers-disabled.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/search-registers.svg