Skip to content

Commit bef0b8f

Browse files
Copilotxusheng6
andcommitted
Fix TTD Memory Widget UI issues: implement expandable Query Parameters, fix + button positioning, and add reset columns option
Co-authored-by: xusheng6 <[email protected]>
1 parent 893eb0c commit bef0b8f

File tree

2 files changed

+205
-10
lines changed

2 files changed

+205
-10
lines changed

ui/ttdmemorywidget.cpp

Lines changed: 174 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,92 @@ limitations under the License.
2424
#include <QMenu>
2525
#include <QClipboard>
2626
#include <QCheckBox>
27+
#include <QToolButton>
28+
#include <QPropertyAnimation>
29+
#include <QResizeEvent>
30+
#include <QFrame>
31+
32+
// ExpandableGroupBox implementation
33+
ExpandableGroupBox::ExpandableGroupBox(const QString& title, QWidget* parent)
34+
: QWidget(parent), m_contentWidget(nullptr), m_expanded(true)
35+
{
36+
QVBoxLayout* layout = new QVBoxLayout(this);
37+
layout->setContentsMargins(0, 0, 0, 0);
38+
layout->setSpacing(0);
39+
40+
// Create header with toggle button
41+
QHBoxLayout* headerLayout = new QHBoxLayout();
42+
headerLayout->setContentsMargins(5, 5, 5, 5);
43+
44+
m_toggleButton = new QToolButton();
45+
m_toggleButton->setArrowType(Qt::DownArrow);
46+
m_toggleButton->setCheckable(true);
47+
m_toggleButton->setChecked(true);
48+
m_toggleButton->setText(title);
49+
m_toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
50+
m_toggleButton->setStyleSheet("QToolButton { border: none; font-weight: bold; text-align: left; }");
51+
52+
connect(m_toggleButton, &QToolButton::clicked, this, &ExpandableGroupBox::toggleExpanded);
53+
54+
headerLayout->addWidget(m_toggleButton);
55+
headerLayout->addStretch();
56+
57+
layout->addLayout(headerLayout);
58+
59+
// Add a line separator
60+
QFrame* line = new QFrame();
61+
line->setFrameShape(QFrame::HLine);
62+
line->setFrameShadow(QFrame::Sunken);
63+
layout->addWidget(line);
64+
65+
setLayout(layout);
66+
}
67+
68+
void ExpandableGroupBox::setContentWidget(QWidget* widget)
69+
{
70+
if (m_contentWidget)
71+
{
72+
layout()->removeWidget(m_contentWidget);
73+
m_contentWidget->deleteLater();
74+
}
75+
76+
m_contentWidget = widget;
77+
if (m_contentWidget)
78+
{
79+
layout()->addWidget(m_contentWidget);
80+
setupAnimation();
81+
}
82+
}
83+
84+
void ExpandableGroupBox::setExpanded(bool expanded)
85+
{
86+
if (m_expanded == expanded)
87+
return;
88+
89+
m_expanded = expanded;
90+
m_toggleButton->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow);
91+
m_toggleButton->setChecked(expanded);
92+
93+
if (m_contentWidget)
94+
{
95+
m_contentWidget->setVisible(expanded);
96+
}
97+
}
98+
99+
void ExpandableGroupBox::toggleExpanded()
100+
{
101+
setExpanded(!m_expanded);
102+
}
103+
104+
void ExpandableGroupBox::setupAnimation()
105+
{
106+
if (!m_contentWidget)
107+
return;
108+
109+
m_contentAnimation = new QPropertyAnimation(m_contentWidget, "maximumHeight");
110+
m_contentAnimation->setDuration(200);
111+
m_contentAnimation->setEasingCurve(QEasingCurve::InOutQuad);
112+
}
27113

28114
// ColumnVisibilityDialog implementation
29115
ColumnVisibilityDialog::ColumnVisibilityDialog(QWidget* parent, const QStringList& columnNames, const QList<bool>& visibility)
@@ -50,9 +136,33 @@ ColumnVisibilityDialog::ColumnVisibilityDialog(QWidget* parent, const QStringLis
50136

51137
layout->addWidget(m_columnList);
52138

53-
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
139+
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
54140
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
55141
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
142+
143+
// Handle restore defaults
144+
connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, [this]() {
145+
// Reset to default visibility (hide Event Type, Time End, Unique Thread ID)
146+
QList<bool> defaultVisibility;
147+
defaultVisibility << true // Index
148+
<< false // Event Type (hidden by default)
149+
<< true // Time Start
150+
<< false // Time End (hidden by default)
151+
<< true // Access Type
152+
<< true // Address
153+
<< true // Size
154+
<< true // Value
155+
<< true // Thread ID
156+
<< false // Unique Thread ID (hidden by default)
157+
<< true; // IP
158+
159+
for (int i = 0; i < m_columnList->count() && i < defaultVisibility.size(); ++i)
160+
{
161+
QListWidgetItem* item = m_columnList->item(i);
162+
item->setCheckState(defaultVisibility[i] ? Qt::Checked : Qt::Unchecked);
163+
}
164+
});
165+
56166
layout->addWidget(buttons);
57167
}
58168

@@ -103,9 +213,12 @@ void TTDMemoryQueryWidget::setupUI()
103213

104214
QVBoxLayout* mainLayout = new QVBoxLayout(this);
105215

106-
// Input controls group
107-
QGroupBox* inputGroup = new QGroupBox("Query Parameters");
108-
QFormLayout* inputLayout = new QFormLayout(inputGroup);
216+
// Create expandable group for query parameters
217+
ExpandableGroupBox* expandableGroup = new ExpandableGroupBox("Query Parameters");
218+
219+
// Create content widget for the expandable group
220+
QWidget* inputWidget = new QWidget();
221+
QFormLayout* inputLayout = new QFormLayout(inputWidget);
109222

110223
// Address range inputs
111224
m_startAddressEdit = new QLineEdit();
@@ -155,7 +268,10 @@ void TTDMemoryQueryWidget::setupUI()
155268

156269
inputLayout->addRow("", buttonLayout);
157270

158-
mainLayout->addWidget(inputGroup);
271+
// Set the input widget as the content of the expandable group
272+
expandableGroup->setContentWidget(inputWidget);
273+
274+
mainLayout->addWidget(expandableGroup);
159275

160276
// Results table
161277
setupTable();
@@ -446,11 +562,13 @@ void TTDMemoryQueryWidget::showContextMenu(const QPoint& position)
446562
QAction* copyTableAction = menu.addAction("Copy Table");
447563
menu.addSeparator();
448564
QAction* columnsAction = menu.addAction("Columns...");
565+
QAction* resetColumnsAction = menu.addAction("Reset Columns to Default");
449566

450567
connect(copyCellAction, &QAction::triggered, this, &TTDMemoryQueryWidget::copySelectedCell);
451568
connect(copyRowAction, &QAction::triggered, this, &TTDMemoryQueryWidget::copySelectedRow);
452569
connect(copyTableAction, &QAction::triggered, this, &TTDMemoryQueryWidget::copyEntireTable);
453570
connect(columnsAction, &QAction::triggered, this, &TTDMemoryQueryWidget::showColumnVisibilityDialog);
571+
connect(resetColumnsAction, &QAction::triggered, this, &TTDMemoryQueryWidget::resetColumnsToDefault);
454572

455573
// Enable/disable actions based on selection
456574
QTableWidgetItem* item = m_resultsTable->itemAt(position);
@@ -525,6 +643,25 @@ void TTDMemoryQueryWidget::copyEntireTable()
525643
clipboard->setText(tableData.join("\n"));
526644
}
527645

646+
void TTDMemoryQueryWidget::resetColumnsToDefault()
647+
{
648+
// Reset to default visibility (hide Event Type, Time End, Unique Thread ID)
649+
m_columnVisibility.clear();
650+
m_columnVisibility << true // Index
651+
<< false // Event Type (hidden by default)
652+
<< true // Time Start
653+
<< false // Time End (hidden by default)
654+
<< true // Access Type
655+
<< true // Address
656+
<< true // Size
657+
<< true // Value
658+
<< true // Thread ID
659+
<< false // Unique Thread ID (hidden by default)
660+
<< true; // IP
661+
662+
updateColumnVisibility();
663+
}
664+
528665
void TTDMemoryQueryWidget::updateStatus(const QString& message)
529666
{
530667
m_statusLabel->setText(message);
@@ -578,32 +715,46 @@ void TTDMemoryWidget::setupUI()
578715

579716
QVBoxLayout* mainLayout = new QVBoxLayout(this);
580717

718+
// Create a horizontal layout to hold the tab widget and + button
719+
QWidget* tabContainer = new QWidget();
720+
m_tabLayout = new QHBoxLayout(tabContainer);
721+
m_tabLayout->setContentsMargins(0, 0, 0, 0);
722+
m_tabLayout->setSpacing(0);
723+
581724
// Tab widget setup
582725
m_tabWidget = new QTabWidget();
583726
m_tabWidget->setTabsClosable(true);
584727
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &TTDMemoryWidget::closeTab);
585728

586-
// Create "+" button and set it as corner widget
729+
// Create "+" button
587730
m_newTabButton = new QPushButton("+");
588-
m_newTabButton->setMaximumSize(30, 30);
731+
m_newTabButton->setMaximumSize(25, 25);
732+
m_newTabButton->setMinimumSize(25, 25);
589733
m_newTabButton->setToolTip("Create new query tab");
734+
m_newTabButton->setStyleSheet("QPushButton { border: 1px solid #555; border-radius: 3px; margin-top: 2px; }");
590735
connect(m_newTabButton, &QPushButton::clicked, this, &TTDMemoryWidget::createNewTab);
591736

592-
// Set the "+" button as a corner widget of the tab widget
593-
m_tabWidget->setCornerWidget(m_newTabButton, Qt::TopRightCorner);
737+
// Add tab widget and button to horizontal layout
738+
m_tabLayout->addWidget(m_tabWidget);
739+
m_tabLayout->addWidget(m_newTabButton, 0, Qt::AlignTop);
594740

595-
mainLayout->addWidget(m_tabWidget);
741+
mainLayout->addWidget(tabContainer);
596742
setLayout(mainLayout);
597743

598744
// Create initial tab
599745
createNewTab();
746+
747+
// Update button position when tabs change
748+
connect(m_tabWidget, &QTabWidget::currentChanged, this, &TTDMemoryWidget::updateNewTabButtonPosition);
749+
connect(m_tabWidget, &QTabWidget::tabBarClicked, this, &TTDMemoryWidget::updateNewTabButtonPosition);
600750
}
601751

602752
void TTDMemoryWidget::createNewTab()
603753
{
604754
TTDMemoryQueryWidget* queryWidget = new TTDMemoryQueryWidget(this, m_data);
605755
int tabIndex = m_tabWidget->addTab(queryWidget, QString("Query %1").arg(m_tabWidget->count() + 1));
606756
m_tabWidget->setCurrentIndex(tabIndex);
757+
updateNewTabButtonPosition();
607758
}
608759

609760
void TTDMemoryWidget::closeTab(int index)
@@ -613,9 +764,22 @@ void TTDMemoryWidget::closeTab(int index)
613764
QWidget* widget = m_tabWidget->widget(index);
614765
m_tabWidget->removeTab(index);
615766
widget->deleteLater();
767+
updateNewTabButtonPosition();
616768
}
617769
}
618770

771+
void TTDMemoryWidget::updateNewTabButtonPosition()
772+
{
773+
// This will be called to adjust the button position if needed
774+
// For now, the horizontal layout should handle this automatically
775+
}
776+
777+
void TTDMemoryWidget::resizeEvent(QResizeEvent* event)
778+
{
779+
QWidget::resizeEvent(event);
780+
updateNewTabButtonPosition();
781+
}
782+
619783

620784
// TTDMemorySidebarWidget implementation
621785
TTDMemorySidebarWidget::TTDMemorySidebarWidget(BinaryViewRef data)

ui/ttdmemorywidget.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ limitations under the License.
3434
#include <QAction>
3535
#include <QClipboard>
3636
#include <QPoint>
37+
#include <QToolButton>
38+
#include <QPropertyAnimation>
39+
#include <QParallelAnimationGroup>
3740
#include "inttypes.h"
3841
#include "binaryninjaapi.h"
3942
#include "debuggerapi.h"
@@ -42,6 +45,28 @@ limitations under the License.
4245
using namespace BinaryNinja;
4346
using namespace BinaryNinjaDebuggerAPI;
4447

48+
class ExpandableGroupBox : public QWidget
49+
{
50+
Q_OBJECT
51+
52+
public:
53+
ExpandableGroupBox(const QString& title, QWidget* parent = nullptr);
54+
void setContentWidget(QWidget* widget);
55+
void setExpanded(bool expanded);
56+
bool isExpanded() const { return m_expanded; }
57+
58+
private Q_SLOTS:
59+
void toggleExpanded();
60+
61+
private:
62+
QToolButton* m_toggleButton;
63+
QWidget* m_contentWidget;
64+
QPropertyAnimation* m_contentAnimation;
65+
bool m_expanded;
66+
67+
void setupAnimation();
68+
};
69+
4570
class ColumnVisibilityDialog : public QDialog
4671
{
4772
Q_OBJECT
@@ -98,6 +123,7 @@ private Q_SLOTS:
98123
void clearResults();
99124
void onCellDoubleClicked(int row, int column);
100125
void showColumnVisibilityDialog();
126+
void resetColumnsToDefault();
101127
void showContextMenu(const QPoint& position);
102128
void copySelectedCell();
103129
void copySelectedRow();
@@ -111,15 +137,20 @@ class TTDMemoryWidget : public QWidget
111137
private:
112138
BinaryViewRef m_data;
113139
DbgRef<DebuggerController> m_controller;
140+
QHBoxLayout* m_tabLayout;
114141
QTabWidget* m_tabWidget;
115142
QPushButton* m_newTabButton;
116143

117144
void setupUI();
145+
void updateNewTabButtonPosition();
118146

119147
public:
120148
TTDMemoryWidget(QWidget* parent, BinaryViewRef data);
121149
virtual ~TTDMemoryWidget();
122150

151+
protected:
152+
void resizeEvent(QResizeEvent* event) override;
153+
123154
private Q_SLOTS:
124155
void createNewTab();
125156
void closeTab(int index);

0 commit comments

Comments
 (0)