diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp index bae4c16fef9..03139a41540 100644 --- a/src/libs/utils/basetreeview.cpp +++ b/src/libs/utils/basetreeview.cpp @@ -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 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); diff --git a/src/libs/utils/basetreeview.h b/src/libs/utils/basetreeview.h index 923c69b0421..36da6589133 100644 --- a/src/libs/utils/basetreeview.h +++ b/src/libs/utils/basetreeview.h @@ -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(); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 6594e7d00c6..1f06fef2af1 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -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"); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 04b1b8d3414..042a7f4368f 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -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); diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp index 83e6af28295..e25f6aa72b2 100644 --- a/src/plugins/debugger/stackwindow.cpp +++ b/src/plugins/debugger/stackwindow.cpp @@ -32,34 +32,57 @@ #include #include +#include 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(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 diff --git a/src/plugins/debugger/stackwindow.h b/src/plugins/debugger/stackwindow.h index 5439ead73f4..79f2974e82b 100644 --- a/src/plugins/debugger/stackwindow.h +++ b/src/plugins/debugger/stackwindow.h @@ -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