Compare commits

..

15 Commits

Author SHA1 Message Date
Ernie Pasveer 62596d3234 First pass at ParallelStacks settings. 2026-08-24 19:37:19 -05:00
Ernie Pasveer afa4d70072 Merge branch 'main' into 508-parallelstacks-long-function-names-can-make-stack-nodes-too-wide 2026-08-24 18:33:08 -05:00
Ernie Pasveer 5183339a2b Merge pull request #533 from lvntky/fix-arrayviz-stack
Fix: Array Visualizer handleText stack usage
2026-08-24 17:24:06 -05:00
Levent Kaya fe6fa48306 Fix: Array Visualizer handleText stack usage 2026-08-24 21:40:56 +03:00
Ernie Pasveer 41f78b68da Change editor minimap width to 80 pixels. 2026-08-22 11:07:55 -05:00
Ernie Pasveer 0c876d0c92 Merge pull request #530 from epasveer/511-parallelstacks-handle-when-there-is-no-function-name-in-the-debug-info
If the function name is "??", use the function address.
2026-08-17 18:00:53 -05:00
Ernie Pasveer 42bcc76aa4 If the function name is "??", use the function address. 2026-08-17 17:46:05 -05:00
Ernie Pasveer 93b569191c Merge pull request #529 from epasveer/525-parallelstacks-node-frames-popup-shows-too-quickly
Show popup after 1 second. Keep it shown if mouse moves inside popup …
2026-08-16 13:45:53 -05:00
Ernie Pasveer aa74c16edf Show popup after 1 second. Keep it shown if mouse moves inside popup or stayes in node. 2026-08-16 13:39:46 -05:00
Ernie Pasveer 5d030042d5 Merge pull request #528 from epasveer/524-parallelstacks-node-frames-in-popup-are-in-wrong-order
Fix frame presentation to base function at the bottom of the view.
2026-08-16 12:43:36 -05:00
Ernie Pasveer afc8c188c5 Fix frame presentation to base function at the bottom of the view. 2026-08-16 12:18:20 -05:00
Ernie Pasveer ddc7245125 Merge pull request #521 from tiresiasfromthebai/imageviewer-wheel-zoom
Add Ctrl+wheel zoom to the Image Visualizer
2026-08-14 17:12:48 -05:00
tiresiasfromthebai bd04ef3199 Add Ctrl+wheel zoom to the Image Visualizer 2026-08-14 15:34:05 +02:00
Ernie Pasveer 23df8207d8 Merge pull request #520 from tiresiasfromthebai/fix-imageviewer-zoom
Fix the Image Visualizer zoom keys under Qt6
2026-08-14 07:23:52 -05:00
tiresiasfromthebai d54df0b1b3 Fix the Image Visualizer zoom keys under Qt6 2026-08-14 10:43:10 +02:00
17 changed files with 548 additions and 140 deletions
+2
View File
@@ -115,6 +115,7 @@ set(HEADER_FILES
SeerParallelStacksVisualizerWidget.h
SeerParallelStacksGraphicsView.h
SeerParallelStacksCommon.h
SeerParallelStacksSettingsDialog.h
SeerGdbMonitorWidget.h
SeerRegisterValuesBrowserWidget.h
SeerRegisterEditValueDialog.h
@@ -233,6 +234,7 @@ set(SOURCE_FILES
SeerParallelStacksVisualizerWidget.cpp
SeerParallelStacksGraphicsView.cpp
SeerParallelStacksCommon.cpp
SeerParallelStacksSettingsDialog.cpp
SeerGdbMonitorWidget.cpp
SeerRegisterValuesBrowserWidget.cpp
SeerRegisterEditValueDialog.cpp
+31 -1
View File
@@ -9,6 +9,7 @@
#include <QtGui/QImageReader>
#include <QtGui/QImageWriter>
#include <QtGui/QPainter>
#include <QtGui/QWheelEvent>
#include <QtCore/QDebug>
#include <QtPrintSupport/QPrintDialog>
@@ -19,6 +20,8 @@
QImageViewer::QImageViewer (QWidget* parent) : QWidget(parent) {
setFocusPolicy(Qt::StrongFocus); // So the widget can take focus and receive the +/-/ESC zoom keys.
_zoomFactor = 1.0;
// Setup the widgets
@@ -34,6 +37,10 @@ QImageViewer::QImageViewer (QWidget* parent) : QWidget(parent) {
_scrollArea->setWidget(_imageLabel);
_scrollArea->setWidgetResizable(false);
// The scroll area consumes wheel events for scrolling, so watch its
// viewport to catch Ctrl+wheel for zooming.
_scrollArea->viewport()->installEventFilter(this);
layout->addWidget(_scrollArea);
clearImage();
@@ -174,7 +181,30 @@ void QImageViewer::keyPressEvent (QKeyEvent* event) {
}
}
void QImageViewer::enterEvent (QEvent* event) {
bool QImageViewer::eventFilter (QObject* object, QEvent* event) {
// Ctrl+wheel over the image zooms in or out. A plain wheel keeps
// scrolling the image as usual.
if (object == _scrollArea->viewport() && event->type() == QEvent::Wheel) {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
if (wheelEvent->modifiers() & Qt::ControlModifier) {
if (wheelEvent->angleDelta().y() > 0) {
zoomIn();
}else if (wheelEvent->angleDelta().y() < 0) {
zoomOut();
}
return true;
}
}
return QWidget::eventFilter(object, event);
}
void QImageViewer::enterEvent (QEnterEvent* event) {
Q_UNUSED(event);
+3 -2
View File
@@ -38,8 +38,9 @@ class QImageViewer : public QWidget {
protected slots:
void keyPressEvent (QKeyEvent* event);
void enterEvent (QEvent* event);
void leaveEvent (QEvent* event);
bool eventFilter (QObject* object, QEvent* event) override;
void enterEvent (QEnterEvent* event) override;
void leaveEvent (QEvent* event) override;
private:
QImage _image;
+6 -3
View File
@@ -398,7 +398,10 @@ void SeerArrayVisualizerWidget::handleText (const QString& text) {
//qDebug() << text;
if (text.contains(QRegularExpression("^([0-9]+)\\^done,value="))) {
static const QRegularExpression valueResponse = QRegularExpression("^([0-9]+)\\^done,value=");
static const QRegularExpression memoryResponse = QRegularExpression("^([0-9]+)\\^done,memory=");
static const QRegularExpression errorResponse = QRegularExpression("^([0-9]+)\\^error,msg=");
if (text.contains(valueResponse)) {
// 11^done,value="1"
// 11^done,value="0x7fffffffd538"
@@ -527,7 +530,7 @@ void SeerArrayVisualizerWidget::handleText (const QString& text) {
handlebRefreshButton();
}
}else if (text.contains(QRegularExpression("^([0-9]+)\\^done,memory="))) {
}else if (text.contains(memoryResponse)) {
// 3^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613e71",contents="00"}]
// 4^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613ed4",contents="000000000000000000000000"}]
@@ -632,7 +635,7 @@ void SeerArrayVisualizerWidget::handleText (const QString& text) {
}
}
}else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg="))) {
}else if (text.contains(errorResponse)) {
// 12^error,msg="No symbol \"return\" in current context."
// 13^error,msg="No symbol \"cout\" in current context."
+1 -1
View File
@@ -439,7 +439,7 @@ int SeerEditorWidgetAssemblyArea::miniMapAreaWidth () {
return 0;
}
return 120;
return 80;
}
int SeerEditorWidgetAssemblyArea::offsetAreaWidth () {
+1 -1
View File
@@ -192,7 +192,7 @@ int SeerEditorWidgetSourceArea::miniMapAreaWidth () {
return 0;
}
return 120;
return 80;
}
void SeerEditorWidgetSourceArea::updateLineNumberArea (const QRect& rect, int dy) {
+24 -8
View File
@@ -43,6 +43,15 @@ const QString& SeerParallelStacksFrame::function () const {
return _function;
}
QString SeerParallelStacksFrame::functionOrAddr () const {
if (_function == "??") {
return _addr;
}
return _function;
}
const QString& SeerParallelStacksFrame::arch () const {
return _arch;
}
@@ -206,19 +215,26 @@ SeerParallelStacksStack SeerParallelStacksFillStack(const SeerParallelStacksNode
stack.threadIds.append(t.id());
}
if (node.function.function().isEmpty() == false) {
stack.frames.append(node.function);
}
if (node.children.size() == 1) {
// Merge single child into this stack (chain of frames).
// Keep the IDs from the leaf (most specific) node.
// Children are deeper (more toward the top of the call stack) than
// this node, so their frames go first — the resulting list reads
// top of stack (innermost) to bottom of stack (outermost).
auto child = SeerParallelStacksFillStack(node.children[0]);
stack.frames += child.frames;
stack.stacks = child.stacks;
stack.threadCount = child.threadCount;
stack.threadIds = child.threadIds;
stack.frames = child.frames;
stack.stacks = child.stacks;
stack.threadCount = child.threadCount;
stack.threadIds = child.threadIds;
if (node.function.function().isEmpty() == false) {
stack.frames.append(node.function);
}
} else {
if (node.function.function().isEmpty() == false) {
stack.frames.append(node.function);
}
for (const auto& childNode : node.children) {
stack.stacks.append(SeerParallelStacksFillStack(childNode));
}
+1
View File
@@ -19,6 +19,7 @@ class SeerParallelStacksFrame {
int level () const;
const QString& addr () const;
const QString& function () const;
QString functionOrAddr () const;
const QString& arch () const;
const QString& from () const;
const QString& file () const;
+93 -100
View File
@@ -87,6 +87,12 @@ SeerParallelStacksStackBoxItem::SeerParallelStacksStackBoxItem(const SeerParalle
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setAcceptHoverEvents(true);
_hoverTimer = new QTimer(this);
_hoverTimer->setSingleShot(true);
_hoverTimer->setInterval(_kHoverDelayMs);
QObject::connect(_hoverTimer, &QTimer::timeout, this, &SeerParallelStacksStackBoxItem::handleShowPopup);
_headerLeft = QString("%1 Thread%2") .arg(stack.threadCount) .arg(stack.threadCount == 1 ? "" : "s");
_threadIds.resize(0);
@@ -118,15 +124,15 @@ SeerParallelStacksStackBoxItem::SeerParallelStacksStackBoxItem(const SeerParalle
QFont normFont;
QFontMetrics normFm(normFont);
qreal headerW = boldFm.horizontalAdvance(_headerLeft) + boldFm.horizontalAdvance(_headerRight) + kHeaderGap;
qreal headerW = boldFm.horizontalAdvance(_headerLeft) + boldFm.horizontalAdvance(_headerRight) + _kHeaderGap;
qreal maxTextW = headerW;
for (const auto& frame : _stack.frames) {
maxTextW = std::max(maxTextW, (qreal)normFm.horizontalAdvance(frame.function()));
maxTextW = std::max(maxTextW, (qreal)normFm.horizontalAdvance(frame.functionOrAddr()));
}
_width = maxTextW + 2 * kPadX;
_height = kPadY + kRowH * (1 + (int)_stack.frames.size()) + kPadY;
_width = maxTextW + 2 * _kPadX;
_height = _kPadY + _kRowH * (1 + (int)_stack.frames.size()) + _kPadY;
qDebug() << _stack.stacks.size();
}
@@ -163,19 +169,19 @@ void SeerParallelStacksStackBoxItem::paint(QPainter* painter, const QStyleOption
QFont boldFont; boldFont.setBold(true);
QFont normFont;
const qreal innerW = _width - 2 * kPadX;
qreal y = kPadY;
const qreal innerW = _width - 2 * _kPadX;
qreal y = _kPadY;
painter->setFont(boldFont);
painter->setPen(colors.headerText);
painter->drawText(QRectF(kPadX, y, innerW, kRowH), Qt::AlignLeft | Qt::AlignVCenter, _headerLeft);
painter->drawText(QRectF(_kPadX, y, innerW, _kRowH), Qt::AlignLeft | Qt::AlignVCenter, _headerLeft);
if (!_headerRight.isEmpty()) {
painter->setPen(colors.threadIdsText);
painter->drawText(QRectF(kPadX, y, innerW, kRowH), Qt::AlignRight | Qt::AlignVCenter, _headerRight);
painter->drawText(QRectF(_kPadX, y, innerW, _kRowH), Qt::AlignRight | Qt::AlignVCenter, _headerRight);
}
y += kRowH;
y += _kRowH;
painter->setPen(QPen(colors.divider, 1));
painter->drawLine(QPointF(0, y), QPointF(_width, y));
@@ -184,8 +190,8 @@ void SeerParallelStacksStackBoxItem::paint(QPainter* painter, const QStyleOption
painter->setPen(colors.frameText);
for (const auto& frame : _stack.frames) {
painter->drawText(QRectF(kPadX, y, innerW, kRowH), Qt::AlignLeft | Qt::AlignVCenter, frame.function());
y += kRowH;
painter->drawText(QRectF(_kPadX, y, innerW, _kRowH), Qt::AlignLeft | Qt::AlignVCenter, frame.functionOrAddr());
y += _kRowH;
}
if (_dragging) {
@@ -269,37 +275,82 @@ void SeerParallelStacksStackBoxItem::hoverEnterEvent(QGraphicsSceneHoverEvent* e
QGraphicsItem::hoverEnterEvent(event);
if (_popup == nullptr) {
QString frame;
if (_stack.frames.size() > 0) {
frame = _stack.frames[0].function();
}
_popup = new SeerParallelStacksPopupTableWidget();
for (const auto& id : _threadIds) {
_popup->addRow(id, frame);
}
QGraphicsView* view = scene()->views().first(); // scene() is always valid here
// Position below the item, aligned to its left edge
QRectF sceneRect = mapToScene(boundingRect()).boundingRect();
QPoint viewportPt = view->mapFromScene(sceneRect.bottomLeft());
QPoint globalPt = view->viewport()->mapToGlobal(viewportPt);
_popup->move(globalPt);
_popup->show();
// Connect things.
QObject::connect(_popup, &SeerParallelStacksPopupTableWidget::mouseLeftPopup, this, &SeerParallelStacksStackBoxItem::handleDeletePopup);
_hoverTimer->start();
}
}
void SeerParallelStacksStackBoxItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) {
QGraphicsItem::hoverLeaveEvent(event);
// Cancel a pending popup if the mouse left before the delay elapsed.
_hoverTimer->stop();
// A popup that's already showing stays up if the mouse just moved onto
// it — give handleMaybeClosePopup() a moment to check for that.
handleMaybeClosePopup();
}
QRect SeerParallelStacksStackBoxItem::globalRect() const {
QGraphicsView* view = scene()->views().first(); // scene() is always valid here
QRectF sceneRect = mapToScene(boundingRect()).boundingRect();
QPoint topLeft = view->viewport()->mapToGlobal(view->mapFromScene(sceneRect.topLeft()));
QPoint bottomRight = view->viewport()->mapToGlobal(view->mapFromScene(sceneRect.bottomRight()));
return QRect(topLeft, bottomRight);
}
void SeerParallelStacksStackBoxItem::handleShowPopup() {
if (_popup != nullptr) {
return;
}
QString frame;
if (_stack.frames.size() > 0) {
frame = _stack.frames[0].functionOrAddr();
}
_popup = new SeerParallelStacksPopupTableWidget();
for (const auto& id : _threadIds) {
_popup->addRow(id, frame);
}
// Position below the item, aligned to its left edge
_popup->move(globalRect().bottomLeft());
_popup->show();
QObject::connect(_popup, &SeerParallelStacksPopupTableWidget::mouseLeftPopup, this, &SeerParallelStacksStackBoxItem::handleMaybeClosePopup);
}
void SeerParallelStacksStackBoxItem::handleMaybeClosePopup() {
if (_popup == nullptr) {
return;
}
// The mouse may have simply crossed from the node onto the popup (or
// vice versa) — give that a moment to register before deciding the
// mouse has truly left both, then re-check with the cursor's live
// position rather than relying on possibly-stale hover state.
QTimer::singleShot(_kCloseGraceMs, this, [this]() {
if (_popup == nullptr) {
return;
}
bool overNode = globalRect().contains(QCursor::pos());
bool overPopup = _popup->frameGeometry().contains(QCursor::pos());
if (overNode == false && overPopup == false) {
handleDeletePopup();
}
});
}
void SeerParallelStacksStackBoxItem::handleDeletePopup() {
@@ -358,7 +409,7 @@ QRectF SeerParallelStacksLiveEdge::boundingRect() const {
QPointF f = _from->sceneBottom();
QPointF t = _to->sceneTop();
qreal pad = kArrow + kVCtrl + 4;
qreal pad = _kArrow + _kVCtrl + 4;
return QRectF(f, t).normalized().adjusted(-pad, -pad, pad, pad);
}
@@ -382,39 +433,8 @@ void SeerParallelStacksLiveEdge::paint(QPainter* painter, const QStyleOptionGrap
QPointF from = _from->sceneBottom();
QPointF to = _to->sceneTop();
/*
// Bezier: control points pull vertically toward each other
QPainterPath path;
path.moveTo(from);
path.cubicTo(from + QPointF(0, kVCtrl), to + QPointF(0, -kVCtrl), to);
painter->setPen(QPen(QColor(0x55, 0x55, 0x55), 1.5));
painter->setBrush(Qt::NoBrush);
painter->drawPath(path);
// Arrowhead at `to` pointing downward (into the parent box)
// The tangent direction at the end of the cubic is (to - cp2)
QPointF cp2 = to + QPointF(0, -kVCtrl);
QPointF dir = to - cp2;
double len = std::hypot(dir.x(), dir.y());
if (len < 1e-6) return;
dir /= len; // normalise
// Perpendicular
QPointF perp(-dir.y(), dir.x());
QPointF a1 = to - dir * kArrow + perp * (kArrow * 0.5);
QPointF a2 = to - dir * kArrow - perp * (kArrow * 0.5);
QPolygonF arrowHead;
arrowHead << to << a1 << a2;
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0x55, 0x55, 0x55));
painter->drawPolygon(arrowHead);
*/
// --- Arrowhead: tip touches the child box bottom (`from`),
// base is kArrow further along toward `to`.
// base is _kArrow further along toward `to`.
// The bezier starts at the base so the line never overlaps the head.
QPointF delta = to - from;
double len = std::hypot(delta.x(), delta.y());
@@ -425,16 +445,16 @@ void SeerParallelStacksLiveEdge::paint(QPainter* painter, const QStyleOptionGrap
QPointF perp(-dir.y(), dir.x()); // perpendicular
QPointF tip = from; // touches child box
QPointF base = from + dir * kArrow; // where the line begins
QPointF base = from + dir * _kArrow; // where the line begins
QPointF a1 = base + perp * (kArrow * 0.5);
QPointF a2 = base - perp * (kArrow * 0.5);
QPointF a1 = base + perp * (_kArrow * 0.5);
QPointF a2 = base - perp * (_kArrow * 0.5);
// Bezier runs from the arrowhead base to the parent box top.
QPainterPath path;
path.moveTo(base);
path.cubicTo(base + QPointF(0, kVCtrl), to + QPointF(0, -kVCtrl), to);
path.cubicTo(base + QPointF(0, _kVCtrl), to + QPointF(0, -_kVCtrl), to);
const QColor color = edgeColor();
@@ -641,15 +661,6 @@ SeerParallelStacksPopupTableWidget::SeerParallelStacksPopupTableWidget(QWidget*
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(6, 6, 6, 6);
layout->addWidget(_table);
_closeTimer = new QTimer(this);
_closeTimer->setSingleShot(true); // Ensures it only fires once
_closeTimer->setInterval(1500); // Delay
// Connect things.
QObject::connect(_closeTimer, &QTimer::timeout, this, &SeerParallelStacksPopupTableWidget::handleCloseTimer);
_closeTimer->start();
}
void SeerParallelStacksPopupTableWidget::addRow (int threadid, const QString& frame) {
@@ -673,31 +684,13 @@ void SeerParallelStacksPopupTableWidget::addRow (int threadid, const QString& fr
_table->resizeColumnToContents(1);
}
void SeerParallelStacksPopupTableWidget::enterEvent(QEnterEvent* event) {
// If the user moves the cursor into the table, cancel the timer.
if (_closeTimer->isActive()) {
_closeTimer->stop();
}
QFrame::enterEvent(event);
}
void SeerParallelStacksPopupTableWidget::leaveEvent(QEvent* event) {
// If the user leaves the table, ask for the table to be closed.
QFrame::leaveEvent(event);
emit mouseLeftPopup();
}
void SeerParallelStacksPopupTableWidget::handleCloseTimer() {
// If the user doesn't move the cursor into the table in the default time,
// ask the table to be closed.
emit mouseLeftPopup();
}
// ================================================================
// SeerParallelStacksPopupTableWidget
// ================================================================
+26 -24
View File
@@ -52,26 +52,32 @@ class SeerParallelStacksStackBoxItem : public QObject, public QGraphicsItem {
void hoverLeaveEvent (QGraphicsSceneHoverEvent* event) override;
private slots:
void handleDeletePopup ();
void handleDeletePopup ();
void handleShowPopup ();
void handleMaybeClosePopup ();
private:
// Global (screen) rect this box occupies, used to test whether the
// cursor is still over the node.
QRect globalRect () const;
QVector<SeerParallelStacksLiveEdge*> _edges; // non-owning
QVector<int> _threadIds;
SeerParallelStacksStack _stack;
QString _headerLeft;
QString _headerRight;
qreal _width = 0;
qreal _height = 0;
SeerParallelStacksPopupTableWidget* _popup = 0;
bool _dragging = false;
qreal _width = 0;
qreal _height = 0;
SeerParallelStacksPopupTableWidget* _popup = 0;
QTimer* _hoverTimer = 0;
static constexpr int _kHoverDelayMs = 1000;
static constexpr int _kCloseGraceMs = 150;
bool _dragging = false;
QPointF _dragOffset;
QVector<SeerParallelStacksLiveEdge*> _edges; // non-owning
static constexpr qreal kPadX = 12;
static constexpr qreal kPadY = 8;
static constexpr qreal kRowH = 20;
static constexpr qreal kHeaderGap = 16;
static constexpr qreal _kPadX = 12;
static constexpr qreal _kPadY = 8;
static constexpr qreal _kRowH = 20;
static constexpr qreal _kHeaderGap = 16;
};
// ---------------------------------------------------------------
@@ -82,7 +88,7 @@ class SeerParallelStacksLiveEdge : public QGraphicsItem {
public:
SeerParallelStacksLiveEdge(SeerParallelStacksStackBoxItem* from, SeerParallelStacksStackBoxItem* to, QGraphicsItem* parent = nullptr);
~SeerParallelStacksLiveEdge() override;
~SeerParallelStacksLiveEdge() override;
QRectF boundingRect () const override;
QPainterPath shape () const override;
@@ -95,11 +101,11 @@ class SeerParallelStacksLiveEdge : public QGraphicsItem {
private:
SeerParallelStacksStackBoxItem* _from; // child (bottom anchor)
SeerParallelStacksStackBoxItem* _to; // parent (top anchor)
SeerParallelStacksStackBoxItem* _from; // child (bottom anchor)
SeerParallelStacksStackBoxItem* _to; // parent (top anchor)
static constexpr qreal kArrow = 8.0;
static constexpr qreal kVCtrl = 60.0 * 0.4; // bezier control-point stretch
static constexpr qreal _kArrow = 8.0;
static constexpr qreal _kVCtrl = 60.0 * 0.4; // bezier control-point stretch
};
@@ -142,7 +148,8 @@ class SeerParallelStacksMiniMapWidget : public QWidget {
};
// A frameless popup window that wraps a QTableWidget inside a small
// bordered frame, and closes/deletes itself when the mouse leaves it.
// bordered frame. Its lifetime is owned by the SeerParallelStacksStackBoxItem
// that created it — it stays up for as long as the mouse is in that node.
class SeerParallelStacksPopupTableWidget : public QFrame {
Q_OBJECT
@@ -153,18 +160,13 @@ class SeerParallelStacksPopupTableWidget : public QFrame {
void addRow (int threadid, const QString& function);
protected:
void enterEvent (QEnterEvent* event) override;
void leaveEvent (QEvent* event) override;
protected slots:
void handleCloseTimer ();
signals:
void mouseLeftPopup ();
private:
QTableWidget* _table;
QTimer* _closeTimer;
};
class SeerParallelStacksGraphicsView : public QGraphicsView {
+83
View File
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2026 Ernie Pasveer <epasveer@att.net>
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "SeerParallelStacksSettingsDialog.h"
#include <QtWidgets/QMessageBox>
#include <QtCore/QSettings>
#include <QtCore/QDebug>
SeerParallelStacksSettingsDialog::SeerParallelStacksSettingsDialog (QWidget* parent) : QDialog(parent) {
// Set up the UI.
setupUi(this);
// Setup the widgets
// Connect things.
// Restore window settings.
readSettings();
}
SeerParallelStacksSettingsDialog::~SeerParallelStacksSettingsDialog () {
}
void SeerParallelStacksSettingsDialog::setShowMinimap (const QString& when) {
}
QString SeerParallelStacksSettingsDialog::showMinimap () const {
}
void SeerParallelStacksSettingsDialog::setShowFullFunctionName (bool flag) {
}
bool SeerParallelStacksSettingsDialog::showFullFunctionName () const {
}
void SeerParallelStacksSettingsDialog::setFunctionNameLenght (int length) {
}
int SeerParallelStacksSettingsDialog::functionNameLenght () const {
}
void SeerParallelStacksSettingsDialog::setShowFullStackSize (bool flag) {
}
bool SeerParallelStacksSettingsDialog::showFullStackSize () const {
}
void SeerParallelStacksSettingsDialog::setStackSize (int size) {
}
int SeerParallelStacksSettingsDialog::stackSize () const {
}
void SeerParallelStacksSettingsDialog::writeSettings() {
QSettings settings;
settings.beginGroup("parallelstackssettingsdialog"); {
settings.setValue("size", size());
}settings.endGroup();
}
void SeerParallelStacksSettingsDialog::readSettings() {
QSettings settings;
settings.beginGroup("parallelstackssettingsdialog"); {
if (settings.contains("size")) {
resize(settings.value("size", QSize(400, 225)).toSize());
}
} settings.endGroup();
}
void SeerParallelStacksSettingsDialog::resizeEvent (QResizeEvent* event) {
// Write window settings.
writeSettings();
QWidget::resizeEvent(event);
}
+45
View File
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2026 Ernie Pasveer <epasveer@att.net>
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QtWidgets/QDialog>
#include <QtCore/QString>
#include <QtCore/QVector>
#include "ui_SeerParallelStacksSettingsDialog.h"
class SeerParallelStacksSettingsDialog : public QDialog, protected Ui::SeerParallelStacksSettingsDialogForm {
Q_OBJECT
public:
explicit SeerParallelStacksSettingsDialog (QWidget* parent = 0);
~SeerParallelStacksSettingsDialog ();
void setShowMinimap (const QString& when);
QString showMinimap () const;
void setShowFullFunctionName (bool flag);
bool showFullFunctionName () const;
void setFunctionNameLenght (int length);
int functionNameLenght () const;
void setShowFullStackSize (bool flag);
bool showFullStackSize () const;
void setStackSize (int size);
int stackSize () const;
public slots:
private slots:
protected:
void writeSettings ();
void readSettings ();
void resizeEvent (QResizeEvent* event);
private:
};
+201
View File
@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SeerParallelStacksSettingsDialogForm</class>
<widget class="QDialog" name="SeerParallelStacksSettingsDialogForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>373</width>
<height>200</height>
</rect>
</property>
<property name="windowTitle">
<string>Seer - ParallelStacks Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>ParallelStacks Settings</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="showMinimapLabel">
<property name="text">
<string>Show Minimap</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="showMinimapWhenNeededRadioButton">
<property name="toolTip">
<string>Only show the minimap if the contents exceeds the display area.</string>
</property>
<property name="text">
<string>When needed</string>
</property>
<attribute name="buttonGroup">
<string notr="true">minimapbuttonGroup</string>
</attribute>
</widget>
</item>
<item row="2" column="2">
<widget class="QSpinBox" name="stackFrameSizeSpinBox">
<property name="toolTip">
<string>Keep N frames of the stack. Omit the middle frames.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="functionNameLengthLabel">
<property name="text">
<string>Function Name Length</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="functionNameLengthAllRadioButton">
<property name="toolTip">
<string>Keep the full length of function names.</string>
</property>
<property name="text">
<string>Full</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="showMiniMapAlwaysRadioButton">
<property name="toolTip">
<string>Always show the minimap.</string>
</property>
<property name="text">
<string>Always</string>
</property>
<attribute name="buttonGroup">
<string notr="true">minimapbuttonGroup</string>
</attribute>
</widget>
</item>
<item row="2" column="1">
<widget class="QRadioButton" name="stackFrameSizeAllRadioButton">
<property name="toolTip">
<string>Keep all frames of the stack.</string>
</property>
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="stackFrameSizeLabel">
<property name="text">
<string>Stack Frame Size</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="functionNameLengthSpinBox">
<property name="toolTip">
<string>Keep N characters of the function name. Omit the middle characters.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<zorder>groupBox</zorder>
<zorder>buttonBox</zorder>
<zorder>verticalSpacer</zorder>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SeerParallelStacksSettingsDialogForm</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>227</x>
<y>179</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>199</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SeerParallelStacksSettingsDialogForm</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>295</x>
<y>185</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>199</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="minimapbuttonGroup"/>
</buttongroups>
</ui>
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "SeerParallelStacksVisualizerWidget.h"
#include "SeerParallelStacksSettingsDialog.h"
#include "SeerParallelStacksCommon.h"
#include "SeerHelpPageDialog.h"
#include "SeerUtl.h"
@@ -40,6 +41,7 @@ SeerParallelStacksVisualizerWidget::SeerParallelStacksVisualizerWidget (QWidget*
QObject::connect(helpToolButton, &QToolButton::clicked, this, &SeerParallelStacksVisualizerWidget::handleHelpButton);
QObject::connect(printToolButton, &QToolButton::clicked, this, &SeerParallelStacksVisualizerWidget::handlePrintButton);
QObject::connect(saveToolButton, &QToolButton::clicked, this, &SeerParallelStacksVisualizerWidget::handleSaveButton);
QObject::connect(settingsToolButton, &QToolButton::clicked, this, &SeerParallelStacksVisualizerWidget::handleSettingsButton);
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 3)
QObject::connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &SeerParallelStacksVisualizerWidget::handleThemeChanged);
#endif
@@ -238,6 +240,19 @@ void SeerParallelStacksVisualizerWidget::handleSaveButton () {
QMessageBox::information(this, "Done", "Scene saved to:\n" + fileName);
}
void SeerParallelStacksVisualizerWidget::handleSettingsButton () {
// Bring up the settings dialog.
SeerParallelStacksSettingsDialog dlg(this);
// Set dialog things.
// Execute the dialog.
if (dlg.exec()) {
// Set things.
}
}
void SeerParallelStacksVisualizerWidget::handleThemeChanged () {
// Colorize icons for theme.
+1
View File
@@ -28,6 +28,7 @@ class SeerParallelStacksVisualizerWidget : public QWidget, protected Ui::SeerPar
void handleHelpButton ();
void handlePrintButton ();
void handleSaveButton ();
void handleSettingsButton ();
void handleThemeChanged ();
protected:
+14
View File
@@ -68,6 +68,20 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="settingsToolButton">
<property name="toolTip">
<string>Settings.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resource.qrc">
<normaloff>:/seer/resources/RelaxLightIcons/application-menu.svg</normaloff>:/seer/resources/RelaxLightIcons/application-menu.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="refreshToolButton">
<property name="toolTip">
+1
View File
@@ -78,5 +78,6 @@ Available Quick keys while in the Image Visualizer:
'+' zoom in
'-' zoom out
ESC reset to default zoom level.
Ctrl+mouse wheel zoom in or out.
```