forked from qt-creator/qt-creator
Debugger: Fix sizing of debugger columns
Added span column property to BaseTreeView. This takes an index to a column that will span remaining space not explicitly taken by the other columns. This listens to resizing of the tree view and columns that are manually resized to keep the sizes consistent. The stack view now once again uses the StackView class, which now sets the function column as the span column. This will adjust the column size the first time stack frame contents are encountered. It will not resize automatically until it's re-created (the next debug session) so it doesn't undo custom resizing. This also restores the ability to toggle stack addresses. Some obsolete parts of this class were removed as part of retrofitting it for the current state of the debugger. Stack, breakpoint, and thread views now resize remaining space for the function column rather than empty space at the end. This is generally the most important field when debugging and can get very long, especially in C++. Task-number: QTCREATORBUG-21763 Change-Id: I821c83d1d951f3311d7fa9fcddcbdeedfeed1573 Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -55,6 +55,40 @@ public:
|
||||
m_settingsTimer.setSingleShot(true);
|
||||
connect(&m_settingsTimer, &QTimer::timeout,
|
||||
this, &BaseTreeViewPrivate::doSaveState);
|
||||
connect(q->header(), &QHeaderView::sectionResized, this, [this](int logicalIndex, int oldSize, int newSize) {
|
||||
if (m_processingSpans || m_spanColumn < 0)
|
||||
return;
|
||||
|
||||
QHeaderView *h = q->header();
|
||||
QTC_ASSERT(h, return);
|
||||
|
||||
// Last non-hidden column.
|
||||
int count = h->count();
|
||||
while (count > 0 && h->isSectionHidden(count - 1))
|
||||
--count;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
int column = logicalIndex;
|
||||
if (oldSize < newSize)
|
||||
{
|
||||
// Protect against sizing past the next section.
|
||||
while (column + 1 < count && h->sectionSize(column + 1) == h->minimumSectionSize())
|
||||
++column;
|
||||
}
|
||||
|
||||
if (logicalIndex >= m_spanColumn) {
|
||||
// Resize after the span column.
|
||||
column = column + 1;
|
||||
} else {
|
||||
// Resize the span column or before it.
|
||||
column = m_spanColumn;
|
||||
}
|
||||
|
||||
rebalanceColumns(column, false);
|
||||
});
|
||||
connect(q->header(), &QHeaderView::geometriesChanged, this, QOverload<>::of(&BaseTreeViewPrivate::rebalanceColumns));
|
||||
}
|
||||
|
||||
bool eventFilter(QObject *, QEvent *event) override
|
||||
@@ -197,6 +231,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void setSpanColumn(int column)
|
||||
{
|
||||
if (column == m_spanColumn)
|
||||
return;
|
||||
|
||||
m_spanColumn = column;
|
||||
if (m_spanColumn >= 0)
|
||||
q->header()->setStretchLastSection(false);
|
||||
rebalanceColumns();
|
||||
}
|
||||
|
||||
void toggleColumnWidth(int logicalIndex)
|
||||
{
|
||||
QHeaderView *h = q->header();
|
||||
@@ -211,11 +256,72 @@ public:
|
||||
int minSize = 10 * fm.width(QLatin1Char('x'));
|
||||
targetSize = qMax(minSize, headerSize);
|
||||
}
|
||||
|
||||
// Prevent rebalance as part of this resize.
|
||||
m_processingSpans = true;
|
||||
h->resizeSection(logicalIndex, targetSize);
|
||||
m_processingSpans = false;
|
||||
|
||||
// Now trigger a rebalance so it resizes the span column. (if set)
|
||||
rebalanceColumns();
|
||||
|
||||
m_userHandled.remove(logicalIndex); // Reset.
|
||||
saveState();
|
||||
}
|
||||
|
||||
void rebalanceColumns()
|
||||
{
|
||||
rebalanceColumns(m_spanColumn, true);
|
||||
}
|
||||
|
||||
void rebalanceColumns(int column, bool allowResizePrevious)
|
||||
{
|
||||
if (m_spanColumn < 0 || column < 0 || m_processingSpans)
|
||||
return;
|
||||
|
||||
QHeaderView *h = q->header();
|
||||
QTC_ASSERT(h, return);
|
||||
|
||||
int count = h->count();
|
||||
if (column >= count)
|
||||
return;
|
||||
|
||||
// Start with the target column, and resize other columns as necessary.
|
||||
int totalSize = q->viewport()->width();
|
||||
if (tryRebalanceColumns(column, totalSize))
|
||||
return;
|
||||
|
||||
for (int i = allowResizePrevious ? 0 : column + 1; i < count; ++i) {
|
||||
if (i != column && tryRebalanceColumns(i, totalSize))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool tryRebalanceColumns(int column, int totalSize)
|
||||
{
|
||||
QHeaderView *h = q->header();
|
||||
|
||||
int count = h->count();
|
||||
int otherColumnTotal = 0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i != column)
|
||||
otherColumnTotal += h->sectionSize(i);
|
||||
}
|
||||
|
||||
if (otherColumnTotal < totalSize) {
|
||||
m_processingSpans = true;
|
||||
h->resizeSection(column, totalSize - otherColumnTotal);
|
||||
m_processingSpans = false;
|
||||
} else
|
||||
return false;
|
||||
|
||||
// Make sure this didn't go over the total size.
|
||||
int totalColumnSize = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
totalColumnSize += h->sectionSize(i);
|
||||
return totalColumnSize == totalSize;
|
||||
}
|
||||
|
||||
public:
|
||||
BaseTreeView *q;
|
||||
QMap<int, int> m_userHandled; // column -> width, "not present" means "automatic"
|
||||
@@ -224,6 +330,8 @@ public:
|
||||
QString m_settingsKey;
|
||||
bool m_expectUserChanges = false;
|
||||
ProgressIndicator *m_progressIndicator = nullptr;
|
||||
int m_spanColumn = -1;
|
||||
bool m_processingSpans = false;
|
||||
};
|
||||
|
||||
class BaseTreeViewDelegate : public QItemDelegate
|
||||
@@ -369,6 +477,12 @@ void BaseTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
TreeView::mouseDoubleClickEvent(ev);
|
||||
}
|
||||
|
||||
void BaseTreeView::resizeEvent(QResizeEvent *ev)
|
||||
{
|
||||
TreeView::resizeEvent(ev);
|
||||
d->rebalanceColumns();
|
||||
}
|
||||
|
||||
void BaseTreeView::showEvent(QShowEvent *ev)
|
||||
{
|
||||
emit aboutToShow();
|
||||
@@ -416,6 +530,21 @@ void BaseTreeView::resizeColumns()
|
||||
d->resizeColumns();
|
||||
}
|
||||
|
||||
int BaseTreeView::spanColumn() const
|
||||
{
|
||||
return d->m_spanColumn;
|
||||
}
|
||||
|
||||
void BaseTreeView::setSpanColumn(int column)
|
||||
{
|
||||
d->setSpanColumn(column);
|
||||
}
|
||||
|
||||
void BaseTreeView::refreshSpanColumn()
|
||||
{
|
||||
d->rebalanceColumns();
|
||||
}
|
||||
|
||||
void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
||||
{
|
||||
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
|
||||
|
@@ -68,11 +68,19 @@ public:
|
||||
void dropEvent(QDropEvent *ev) override;
|
||||
void dragMoveEvent(QDragMoveEvent *ev) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *ev) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
void showProgressIndicator();
|
||||
void hideProgressIndicator();
|
||||
void resizeColumns();
|
||||
|
||||
int spanColumn() const;
|
||||
void setSpanColumn(int column);
|
||||
|
||||
// In some situations this needs to be called when manually resizing columns when the span
|
||||
// column is set.
|
||||
void refreshSpanColumn();
|
||||
|
||||
signals:
|
||||
void aboutToShow();
|
||||
|
||||
|
@@ -642,7 +642,7 @@ void DebuggerEnginePrivate::setupViews()
|
||||
m_registerWindow->setObjectName(DOCKWIDGET_REGISTER);
|
||||
m_registerWindow->setWindowTitle(tr("Reg&isters"));
|
||||
|
||||
m_stackView = new BaseTreeView;
|
||||
m_stackView = new StackTreeView;
|
||||
m_stackView->setModel(m_stackHandler.model());
|
||||
m_stackView->setSettings(settings, "Debugger.StackView");
|
||||
m_stackView->setIconSize(QSize(10, 10));
|
||||
@@ -666,6 +666,7 @@ void DebuggerEnginePrivate::setupViews()
|
||||
m_threadsView->setSortingEnabled(true);
|
||||
m_threadsView->setSettings(settings, "Debugger.ThreadsView");
|
||||
m_threadsView->setIconSize(QSize(10, 10));
|
||||
m_threadsView->setSpanColumn(ThreadData::FunctionColumn);
|
||||
m_threadsWindow = addSearch(m_threadsView);
|
||||
m_threadsWindow->setObjectName(DOCKWIDGET_THREADS);
|
||||
m_threadsWindow->setWindowTitle(tr("&Threads"));
|
||||
@@ -711,6 +712,7 @@ void DebuggerEnginePrivate::setupViews()
|
||||
m_breakView->setIconSize(QSize(10, 10));
|
||||
m_breakView->setWindowIcon(Icons::BREAKPOINTS.icon());
|
||||
m_breakView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_breakView->setSpanColumn(BreakpointFunctionColumn);
|
||||
connect(action(UseAddressInBreakpointsView), &QAction::toggled,
|
||||
this, [this](bool on) { m_breakView->setColumnHidden(BreakpointAddressColumn, !on); });
|
||||
m_breakView->setSettings(settings, "Debugger.BreakWindow");
|
||||
|
@@ -1038,6 +1038,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
||||
m_breakpointManagerView->setSettings(settings, "Debugger.BreakWindow");
|
||||
m_breakpointManagerView->setRootIsDecorated(true);
|
||||
m_breakpointManagerView->setModel(BreakpointManager::model());
|
||||
m_breakpointManagerView->setSpanColumn(BreakpointFunctionColumn);
|
||||
m_breakpointManagerWindow = addSearch(m_breakpointManagerView);
|
||||
m_breakpointManagerWindow->setWindowTitle(tr("Breakpoint Preset"));
|
||||
m_breakpointManagerWindow->setObjectName(DOCKWIDGET_BREAKPOINTMANAGER);
|
||||
|
@@ -32,34 +32,57 @@
|
||||
#include <utils/savedaction.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QHeaderView>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
StackTreeView::StackTreeView()
|
||||
StackTreeView::StackTreeView(QWidget *parent)
|
||||
: BaseTreeView(parent)
|
||||
{
|
||||
setWindowTitle(tr("Stack"));
|
||||
|
||||
connect(action(UseAddressInStackView), &QAction::toggled,
|
||||
this, &StackTreeView::showAddressColumn);
|
||||
setSpanColumn(StackFunctionNameColumn);
|
||||
showAddressColumn(false);
|
||||
}
|
||||
|
||||
void StackTreeView::showAddressColumn(bool on)
|
||||
{
|
||||
setColumnHidden(StackAddressColumn, !on);
|
||||
resizeColumnToContents(StackLevelColumn);
|
||||
resizeColumnToContents(StackLineNumberColumn);
|
||||
resizeColumnToContents(StackAddressColumn);
|
||||
}
|
||||
|
||||
void StackTreeView::setModel(QAbstractItemModel *model)
|
||||
{
|
||||
BaseTreeView::setModel(model);
|
||||
resizeColumnToContents(StackLevelColumn);
|
||||
resizeColumnToContents(StackLineNumberColumn);
|
||||
connect(static_cast<StackHandler*>(model), &StackHandler::stackChanged,
|
||||
this, [this]() {
|
||||
if (!m_contentsAdjusted)
|
||||
adjustForContents();
|
||||
});
|
||||
|
||||
// Resize for the current contents if any are available.
|
||||
showAddressColumn(action(UseAddressInStackView)->isChecked());
|
||||
}
|
||||
|
||||
void StackTreeView::showAddressColumn(bool on)
|
||||
{
|
||||
setColumnHidden(StackAddressColumn, !on);
|
||||
adjustForContents(true);
|
||||
}
|
||||
|
||||
void StackTreeView::adjustForContents(bool refreshSpan)
|
||||
{
|
||||
// Skip resizing if no contents. This will be called again once contents are available.
|
||||
if (!model() || model()->rowCount() == 0) {
|
||||
if (refreshSpan)
|
||||
refreshSpanColumn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Resize without attempting to fix up the columns.
|
||||
setSpanColumn(-1);
|
||||
resizeColumnToContents(StackLevelColumn);
|
||||
resizeColumnToContents(StackFileNameColumn);
|
||||
resizeColumnToContents(StackLineNumberColumn);
|
||||
resizeColumnToContents(StackAddressColumn);
|
||||
setSpanColumn(StackFunctionNameColumn);
|
||||
m_contentsAdjusted = true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
@@ -34,17 +34,16 @@ namespace Internal {
|
||||
|
||||
class StackTreeView : public Utils::BaseTreeView
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackTreeView)
|
||||
|
||||
public:
|
||||
StackTreeView();
|
||||
explicit StackTreeView(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setModel(QAbstractItemModel *model) override;
|
||||
|
||||
void showAddressColumn(bool on);
|
||||
void reloadFullStack();
|
||||
void copyContentsToClipboard();
|
||||
void adjustForContents(bool refreshSpan = false);
|
||||
|
||||
bool m_contentsAdjusted = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
Reference in New Issue
Block a user