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:
Aaron Barany
2018-12-11 23:09:39 -08:00
parent 6c40d9570c
commit 21c0f5395e
6 changed files with 181 additions and 19 deletions

View File

@@ -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);

View File

@@ -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();

View File

@@ -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");

View File

@@ -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);

View File

@@ -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

View File

@@ -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