diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 6a43d5a1..419bba0e 100755 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRCS ${DolphinProcessSrc} GUI/MemViewer/MemViewer.cpp GUI/MemViewer/MemViewerWidget.cpp GUI/MainWindow.cpp + GUI/Widgets/AddressInputWidget.cpp Resources/resource.qrc ${ExeIconSrc} main.cpp) diff --git a/Source/GUI/MemCopy/DlgCopy.cpp b/Source/GUI/MemCopy/DlgCopy.cpp index 1b74cd3a..a11afac5 100644 --- a/Source/GUI/MemCopy/DlgCopy.cpp +++ b/Source/GUI/MemCopy/DlgCopy.cpp @@ -20,8 +20,7 @@ DlgCopy::DlgCopy(QWidget* parent) : QDialog(parent) QVBoxLayout* entireCopyLayout = new QVBoxLayout; QFormLayout* copySettingsLayout = new QFormLayout(); - m_spnWatcherCopyAddress = new QLineEdit(); - m_spnWatcherCopyAddress->setMaxLength(8); + m_spnWatcherCopyAddress = new AddressInputWidget(); copySettingsLayout->addRow("Base Address", m_spnWatcherCopyAddress); m_spnWatcherCopySize = new QLineEdit(); diff --git a/Source/GUI/MemCopy/DlgCopy.h b/Source/GUI/MemCopy/DlgCopy.h index 73bdf066..9e4250d6 100644 --- a/Source/GUI/MemCopy/DlgCopy.h +++ b/Source/GUI/MemCopy/DlgCopy.h @@ -10,6 +10,7 @@ #include #include "../GUICommon.h" +#include "../Widgets/AddressInputWidget.h" class DlgCopy : public QDialog { @@ -43,7 +44,7 @@ class DlgCopy : public QDialog static bool uintStringToU32(std::string_view str, u32& output); static std::string charToHexString(const char* input, size_t count, ByteStringFormats format); - QLineEdit* m_spnWatcherCopyAddress; + AddressInputWidget* m_spnWatcherCopyAddress{}; QLineEdit* m_spnWatcherCopySize; QTextEdit* m_spnWatcherCopyOutput; QComboBox* m_cmbViewerBytesSeparator; diff --git a/Source/GUI/MemScanner/MemScanWidget.cpp b/Source/GUI/MemScanner/MemScanWidget.cpp index 6b4f560a..7986d015 100644 --- a/Source/GUI/MemScanner/MemScanWidget.cpp +++ b/Source/GUI/MemScanner/MemScanWidget.cpp @@ -89,21 +89,16 @@ void MemScanWidget::initialiseWidgets() m_txbSearchTerm1 = new QLineEdit(); m_txbSearchTerm2 = new QLineEdit(); - m_txbSearchRange1 = new QLineEdit(); - m_txbSearchRange1->setMaxLength(8); - m_txbSearchRange1->setPlaceholderText("Search Begin (Optional)"); + m_searchRange = new QWidget(); + m_txbSearchRange1 = new AddressInputWidget(); m_txbSearchRange1->setToolTip("Search Range Begin (Optional)"); - m_txbSearchRange2 = new QLineEdit(); - m_txbSearchRange2->setMaxLength(8); - m_txbSearchRange2->setPlaceholderText("Search End (Optional)"); + m_txbSearchRange2 = new AddressInputWidget(); m_txbSearchRange2->setToolTip("Search Range End (Optional)"); const QFont fixedFont{QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont)}; m_txbSearchTerm1->setFont(fixedFont); m_txbSearchTerm2->setFont(fixedFont); - m_txbSearchRange1->setFont(fixedFont); - m_txbSearchRange2->setFont(fixedFont); m_searchTerm2Widget = new QWidget(); @@ -160,9 +155,16 @@ void MemScanWidget::makeLayouts() results_layout->addWidget(m_tblResulstList); results_layout->addLayout(multiAddButtons_layout); - QHBoxLayout* range_layout = new QHBoxLayout(); - range_layout->addWidget(m_txbSearchRange1); - range_layout->addWidget(m_txbSearchRange2); + QHBoxLayout* const range_sublayout{new QHBoxLayout}; + range_sublayout->setContentsMargins(0, 0, 0, 0); + range_sublayout->setSpacing(1); + range_sublayout->addWidget(m_txbSearchRange1); + range_sublayout->addWidget(new QLabel("-")); + range_sublayout->addWidget(m_txbSearchRange2); + QHBoxLayout* const range_layout{new QHBoxLayout(m_searchRange)}; + range_layout->setContentsMargins(0, 0, 0, 0); + range_layout->addWidget(new QLabel("Search Range (Optional):")); + range_layout->addLayout(range_sublayout); QHBoxLayout* buttons_layout = new QHBoxLayout(); buttons_layout->addWidget(m_btnFirstScan); @@ -193,7 +195,7 @@ void MemScanWidget::makeLayouts() layout_extraParams->addWidget(m_chkSignedScan); QVBoxLayout* scannerParams_layout = new QVBoxLayout(); - scannerParams_layout->addLayout(range_layout); + scannerParams_layout->addWidget(m_searchRange); scannerParams_layout->addLayout(buttons_layout); scannerParams_layout->addWidget(m_cmbScanType); scannerParams_layout->addWidget(m_cmbScanFilter); @@ -436,8 +438,7 @@ void MemScanWidget::onFirstScan() m_btnResetScan->show(); m_btnUndoScan->show(); m_btnUndoScan->setEnabled(m_memScanner->hasUndo()); - m_txbSearchRange1->hide(); - m_txbSearchRange2->hide(); + m_searchRange->hide(); m_cmbScanType->setDisabled(true); m_chkSignedScan->setDisabled(true); m_chkEnforceMemAlignment->setDisabled(true); @@ -513,8 +514,7 @@ void MemScanWidget::onResetScan() m_btnNextScan->hide(); m_btnResetScan->hide(); m_btnUndoScan->hide(); - m_txbSearchRange1->show(); - m_txbSearchRange2->show(); + m_searchRange->show(); m_cmbScanType->setEnabled(true); m_chkSignedScan->setEnabled(true); m_chkEnforceMemAlignment->setEnabled(true); diff --git a/Source/GUI/MemScanner/MemScanWidget.h b/Source/GUI/MemScanner/MemScanWidget.h index 49812abf..1b5fd506 100644 --- a/Source/GUI/MemScanner/MemScanWidget.h +++ b/Source/GUI/MemScanner/MemScanWidget.h @@ -14,6 +14,8 @@ #include "ResultsListModel.h" +#include "../Widgets/AddressInputWidget.h" + class MemScanWidget : public QWidget { Q_OBJECT @@ -65,8 +67,9 @@ class MemScanWidget : public QWidget MemScanner* m_memScanner{}; ResultsListModel* m_resultsListModel{}; - QLineEdit* m_txbSearchRange1{}; - QLineEdit* m_txbSearchRange2{}; + QWidget* m_searchRange{}; + AddressInputWidget* m_txbSearchRange1{}; + AddressInputWidget* m_txbSearchRange2{}; QPushButton* m_btnFirstScan{}; QPushButton* m_btnNextScan{}; QPushButton* m_btnResetScan{}; diff --git a/Source/GUI/MemViewer/MemViewerWidget.cpp b/Source/GUI/MemViewer/MemViewerWidget.cpp index 28cc0726..a4647c37 100644 --- a/Source/GUI/MemViewer/MemViewerWidget.cpp +++ b/Source/GUI/MemViewer/MemViewerWidget.cpp @@ -24,7 +24,7 @@ MemViewerWidget::~MemViewerWidget() void MemViewerWidget::initialiseWidgets() { - m_txtJumpAddress = new QLineEdit(this); + m_txtJumpAddress = new AddressInputWidget(this); connect(m_txtJumpAddress, &QLineEdit::textChanged, this, &MemViewerWidget::onJumpToAddressTextChanged); m_btnGoToMEM1Start = new QPushButton(tr("Go to MEM1")); diff --git a/Source/GUI/MemViewer/MemViewerWidget.h b/Source/GUI/MemViewer/MemViewerWidget.h index 6563efc2..dcf50013 100644 --- a/Source/GUI/MemViewer/MemViewerWidget.h +++ b/Source/GUI/MemViewer/MemViewerWidget.h @@ -6,6 +6,8 @@ #include "MemViewer.h" +#include "../Widgets/AddressInputWidget.h" + class MemViewerWidget : public QWidget { Q_OBJECT @@ -35,7 +37,7 @@ class MemViewerWidget : public QWidget void initialiseWidgets(); void makeLayouts(); - QLineEdit* m_txtJumpAddress{}; + AddressInputWidget* m_txtJumpAddress{}; QPushButton* m_btnGoToMEM1Start{}; QPushButton* m_btnGoToSecondaryRAMStart{}; QTimer* m_updateMemoryTimer{}; diff --git a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp index e5cbfd6a..e017e570 100644 --- a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp +++ b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp @@ -41,12 +41,10 @@ void DlgAddWatchEntry::initialiseWidgets() m_lblValuePreview = new QLineEdit("", this); m_lblValuePreview->setReadOnly(true); - m_txbAddress = new QLineEdit(this); - m_txbAddress->setMaxLength(10); + m_txbAddress = new AddressInputWidget(this); connect(m_txbAddress, &QLineEdit::textEdited, this, &DlgAddWatchEntry::onAddressChanged); const QFont fixedFont{QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont)}; - m_txbAddress->setFont(fixedFont); m_lblValuePreview->setFont(fixedFont); m_offsetsLayout = new QGridLayout; diff --git a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h index 42f819b6..b271acef 100644 --- a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h +++ b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h @@ -12,6 +12,7 @@ #include #include "../../../MemoryWatch/MemWatchEntry.h" +#include "../../Widgets/AddressInputWidget.h" class DlgAddWatchEntry : public QDialog { @@ -45,7 +46,7 @@ class DlgAddWatchEntry : public QDialog void removeAllPointerOffset(); MemWatchEntry* m_entry{}; - QLineEdit* m_txbAddress{}; + AddressInputWidget* m_txbAddress{}; QVector m_offsets; QVector m_addressPath; QGridLayout* m_offsetsLayout{}; diff --git a/Source/GUI/Widgets/AddressInputWidget.cpp b/Source/GUI/Widgets/AddressInputWidget.cpp new file mode 100644 index 00000000..07dab6d7 --- /dev/null +++ b/Source/GUI/Widgets/AddressInputWidget.cpp @@ -0,0 +1,86 @@ +#include "AddressInputWidget.h" + +#include +#include +#include +#include +#include + +namespace +{ +constexpr int horizontalMargin{2}; // QLineEditPrivate::horizontalMargin + +class AddressValidator : public QValidator +{ +public: + QValidator::State validate(QString& input, int& pos) const override + { + if (input.startsWith("0x")) + { + input.remove(0, 2); + pos -= 2; + } + if (input.size() > 8) + { + return QValidator::Invalid; + } + return QValidator::Acceptable; + } +}; +} // namespace + +AddressInputWidget::AddressInputWidget(QWidget* const parent) : QLineEdit(parent) +{ + setPlaceholderText("00000000"); + setValidator(new AddressValidator); + setMaxLength(10); // 8 + 2, to allow the user to paste a value that includes the "0x" prefix + + const QFont fixedFont{QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont)}; + setFont(fixedFont); + + updateStyleSheet(); +} + +bool AddressInputWidget::event(QEvent* const event) +{ + if (event->type() == QEvent::FontChange) + { + updateStyleSheet(); + } + return QLineEdit::event(event); +} + +void AddressInputWidget::paintEvent(QPaintEvent* const event) +{ + QLineEdit::paintEvent(event); + + const QColor placeholderTextColor{palette().color(QPalette::PlaceholderText)}; + const QColor textColor{palette().color(QPalette::Text)}; + const QColor prefixColor{ + (placeholderTextColor.red() + textColor.red()) / 2, + (placeholderTextColor.green() + textColor.green()) / 2, + (placeholderTextColor.blue() + textColor.blue()) / 2, + }; + + QStyleOptionFrame panel{}; + initStyleOption(&panel); + const QRect contentsRect{style()->subElementRect(QStyle::SE_LineEditContents, &panel, this)}; + const int x{contentsRect.x() + horizontalMargin - AddressInputWidget::calcPrefixWidth()}; + const int y{(rect().height() + fontMetrics().tightBoundingRect("0x").height()) / 2}; + + QPainter painter(this); + painter.setPen(prefixColor); + painter.setFont(font()); + painter.drawText(x, y, "0x"); +} + +int AddressInputWidget::calcPrefixWidth() +{ + return fontMetrics().horizontalAdvance("0x"); +} + +void AddressInputWidget::updateStyleSheet() +{ + setStyleSheet(QStringLiteral("QLineEdit { padding-left: %1px }") + .arg(AddressInputWidget::calcPrefixWidth())); +} diff --git a/Source/GUI/Widgets/AddressInputWidget.h b/Source/GUI/Widgets/AddressInputWidget.h new file mode 100644 index 00000000..6f8b13ad --- /dev/null +++ b/Source/GUI/Widgets/AddressInputWidget.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class AddressInputWidget final : public QLineEdit +{ +public: + explicit AddressInputWidget(QWidget* parent = nullptr); + ~AddressInputWidget() override = default; + + AddressInputWidget(const AddressInputWidget&) = delete; + AddressInputWidget(AddressInputWidget&&) = delete; + AddressInputWidget& operator=(const AddressInputWidget&) = delete; + AddressInputWidget& operator=(AddressInputWidget&&) = delete; + + bool event(QEvent* event) override; + void paintEvent(QPaintEvent* event) override; + +private: + int calcPrefixWidth(); + void updateStyleSheet(); +}; diff --git a/Source/dolphin-memory-engine.vcxproj b/Source/dolphin-memory-engine.vcxproj index 41e71bc2..4d7f6372 100755 --- a/Source/dolphin-memory-engine.vcxproj +++ b/Source/dolphin-memory-engine.vcxproj @@ -126,6 +126,7 @@ + @@ -138,6 +139,7 @@ +