Debugger: Tooltips in secondary windows

Debugger tooltips are no longer unconditionally bound
to the main editor window.
They are now moved with the text editor window they appear in.

Fixes: QTCREATORBUG-24109
Change-Id: I7fd34f0717a30ec8a86c37e8dd16c1ea9512e1b5
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Andrii Semkiv
2024-10-11 11:09:10 +02:00
parent 4ecdbbd787
commit b04271108c

View File

@@ -67,10 +67,14 @@ class DebuggerToolTipManagerPrivate : public QObject
{ {
public: public:
explicit DebuggerToolTipManagerPrivate(DebuggerEngine *engine); explicit DebuggerToolTipManagerPrivate(DebuggerEngine *engine);
~DebuggerToolTipManagerPrivate() override;
void slotTooltipOverrideRequested(TextEditor::TextEditorWidget *editorWidget, void slotTooltipOverrideRequested(TextEditor::TextEditorWidget *editorWidget,
const QPoint &point, int pos, bool *handled); const QPoint &point, int pos, bool *handled);
void slotEditorOpened(Core::IEditor *e); void slotEditorOpened(Core::IEditor *e);
void slotEditorAboutToClose(Core::IEditor *e);
void slotCurrentEditorAboutToChange(Core::IEditor *e);
void slotCurrentEditorChanged(Core::IEditor *e);
void hideAllToolTips(); void hideAllToolTips();
void purgeClosedToolTips(); void purgeClosedToolTips();
@@ -99,7 +103,7 @@ public:
public: public:
DebuggerEngine *m_engine; DebuggerEngine *m_engine;
QList<QPointer<DebuggerToolTipWidget>> m_tooltips; std::map<QPointer<TextEditorWidget>, QList<QPointer<DebuggerToolTipWidget>>> m_tooltips;
bool m_debugModeActive = false; bool m_debugModeActive = false;
}; };
@@ -745,14 +749,16 @@ DebuggerToolTipManager::~DebuggerToolTipManager()
void DebuggerToolTipManagerPrivate::hideAllToolTips() void DebuggerToolTipManagerPrivate::hideAllToolTips()
{ {
purgeClosedToolTips(); purgeClosedToolTips();
for (DebuggerToolTipWidget *tooltip : std::as_const(m_tooltips)) for (const auto &[editor, tooltips] : m_tooltips) {
tooltip->hide(); for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips))
tooltip->hide();
}
} }
void DebuggerToolTipManagerPrivate::updateVisibleToolTips() void DebuggerToolTipManagerPrivate::updateVisibleToolTips()
{ {
purgeClosedToolTips(); purgeClosedToolTips();
if (m_tooltips.isEmpty()) if (m_tooltips.empty())
return; return;
if (!m_debugModeActive) { if (!m_debugModeActive) {
hideAllToolTips(); hideAllToolTips();
@@ -771,25 +777,25 @@ void DebuggerToolTipManagerPrivate::updateVisibleToolTips()
return; return;
} }
TextEditorWidget *editorWidget = toolTipEditor->editorWidget();
// Reposition and show all tooltips of that file. // Reposition and show all tooltips of that file.
for (DebuggerToolTipWidget *tooltip : std::as_const(m_tooltips)) { for (DebuggerToolTipWidget *tooltip : std::as_const(m_tooltips.at(editorWidget))) {
if (tooltip->context.fileName == filePath) tooltip->positionShow(editorWidget);
tooltip->positionShow(toolTipEditor->editorWidget());
else
tooltip->hide();
} }
} }
void DebuggerToolTipManager::updateToolTips() void DebuggerToolTipManager::updateToolTips()
{ {
d->purgeClosedToolTips(); d->purgeClosedToolTips();
if (d->m_tooltips.isEmpty()) if (d->m_tooltips.empty())
return; return;
// Stack frame changed: All tooltips of that file acquire the engine, // Stack frame changed: All tooltips of that file acquire the engine,
// all others release (arguable, this could be more precise?) // all others release (arguable, this could be more precise?)
for (DebuggerToolTipWidget *tooltip : std::as_const(d->m_tooltips)) for (const auto &[editor, tooltips] : d->m_tooltips) {
tooltip->updateTooltip(); for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips))
tooltip->updateTooltip();
}
d->updateVisibleToolTips(); // Move tooltip when stepping in same file. d->updateVisibleToolTips(); // Move tooltip when stepping in same file.
} }
@@ -799,19 +805,23 @@ void DebuggerToolTipManager::deregisterEngine()
d->purgeClosedToolTips(); d->purgeClosedToolTips();
for (DebuggerToolTipWidget *tooltip : std::as_const(d->m_tooltips)) for (const auto &[editor, tooltips] : d->m_tooltips) {
if (tooltip->context.engineType == d->m_engine->objectName()) for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips))
tooltip->releaseEngine(); if (tooltip->context.engineType == d->m_engine->objectName())
tooltip->releaseEngine();
}
// FIXME: For now remove all. // FIXME: For now remove all.
for (DebuggerToolTipWidget *tooltip : std::as_const(d->m_tooltips)) for (const auto &[window, tooltips] : d->m_tooltips) {
tooltip->destroy(); for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips))
tooltip->destroy();
}
d->purgeClosedToolTips(); d->purgeClosedToolTips();
} }
bool DebuggerToolTipManager::hasToolTips() const bool DebuggerToolTipManager::hasToolTips() const
{ {
return !d->m_tooltips.isEmpty(); return !d->m_tooltips.empty();
} }
void DebuggerToolTipManagerPrivate::sessionAboutToChange() void DebuggerToolTipManagerPrivate::sessionAboutToChange()
@@ -826,9 +836,11 @@ void DebuggerToolTipManager::closeAllToolTips()
void DebuggerToolTipManagerPrivate::closeAllToolTips() void DebuggerToolTipManagerPrivate::closeAllToolTips()
{ {
for (DebuggerToolTipWidget *tooltip : std::as_const(m_tooltips)) { for (const auto &[editor, tooltips] : m_tooltips) {
if (tooltip) for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips)) {
tooltip->destroy(); if (tooltip)
tooltip->destroy();
}
} }
m_tooltips.clear(); m_tooltips.clear();
} }
@@ -836,8 +848,10 @@ void DebuggerToolTipManagerPrivate::closeAllToolTips()
void DebuggerToolTipManager::resetLocation() void DebuggerToolTipManager::resetLocation()
{ {
d->purgeClosedToolTips(); d->purgeClosedToolTips();
for (DebuggerToolTipWidget *tooltip : std::as_const(d->m_tooltips)) for (const auto &[editor, tooltips] : d->m_tooltips) {
tooltip->pin(); for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips))
tooltip->pin();
}
} }
DebuggerToolTipManagerPrivate::DebuggerToolTipManagerPrivate(DebuggerEngine *engine) DebuggerToolTipManagerPrivate::DebuggerToolTipManagerPrivate(DebuggerEngine *engine)
@@ -850,6 +864,12 @@ DebuggerToolTipManagerPrivate::DebuggerToolTipManagerPrivate(DebuggerEngine *eng
debugModeEntered(); debugModeEntered();
} }
DebuggerToolTipManagerPrivate::~DebuggerToolTipManagerPrivate()
{
for (const auto &[editor, tooltips] : m_tooltips)
editor->window()->removeEventFilter(this);
}
void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
(TextEditorWidget *editorWidget, const QPoint &point, int pos, bool *handled) (TextEditorWidget *editorWidget, const QPoint &point, int pos, bool *handled)
{ {
@@ -876,8 +896,7 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
!= CppEditor::ProjectFile::Unsupported; != CppEditor::ProjectFile::Unsupported;
if (context.expression.isEmpty()) { if (context.expression.isEmpty()) {
ToolTip::show(point, Tr::tr("No valid expression"), ToolTip::show(point, Tr::tr("No valid expression"), editorWidget);
DebuggerMainWindow::instance());
*handled = true; *handled = true;
return; return;
} }
@@ -895,7 +914,7 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
auto reusable = [context](DebuggerToolTipWidget *tooltip) { auto reusable = [context](DebuggerToolTipWidget *tooltip) {
return tooltip->context.isSame(context); return tooltip->context.isSame(context);
}; };
DebuggerToolTipWidget *tooltip = Utils::findOrDefault(m_tooltips, reusable); DebuggerToolTipWidget *tooltip = Utils::findOrDefault(m_tooltips.at(editorWidget), reusable);
if (tooltip) { if (tooltip) {
DEBUG("REUSING LOCALS TOOLTIP"); DEBUG("REUSING LOCALS TOOLTIP");
tooltip->context.mousePosition = point; tooltip->context.mousePosition = point;
@@ -904,8 +923,8 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
DEBUG("CREATING LOCALS, WAITING..."); DEBUG("CREATING LOCALS, WAITING...");
tooltip = new DebuggerToolTipWidget(m_engine, context); tooltip = new DebuggerToolTipWidget(m_engine, context);
tooltip->setState(Acquired); tooltip->setState(Acquired);
m_tooltips.push_back(tooltip); m_tooltips.at(editorWidget).push_back(tooltip);
ToolTip::show(point, tooltip, DebuggerMainWindow::instance()); ToolTip::show(point, tooltip, editorWidget);
} }
DEBUG("SYNC IN STATE" << tooltip->state); DEBUG("SYNC IN STATE" << tooltip->state);
tooltip->updateTooltip(); tooltip->updateTooltip();
@@ -916,7 +935,7 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
auto reusable = [&context](DebuggerToolTipWidget *tooltip) { auto reusable = [&context](DebuggerToolTipWidget *tooltip) {
return tooltip->context.isSame(context); return tooltip->context.isSame(context);
}; };
DebuggerToolTipWidget *tooltip = Utils::findOrDefault(m_tooltips, reusable); DebuggerToolTipWidget *tooltip = Utils::findOrDefault(m_tooltips.at(editorWidget), reusable);
if (tooltip) { if (tooltip) {
//tooltip->destroy(); //tooltip->destroy();
@@ -927,13 +946,12 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested
DEBUG("CREATING DELAYED."); DEBUG("CREATING DELAYED.");
tooltip = new DebuggerToolTipWidget(m_engine, context); tooltip = new DebuggerToolTipWidget(m_engine, context);
tooltip->context.mousePosition = point; tooltip->context.mousePosition = point;
m_tooltips.push_back(tooltip); m_tooltips.at(editorWidget).push_back(tooltip);
tooltip->setState(PendingUnshown); tooltip->setState(PendingUnshown);
if (m_engine->canHandleToolTip(context)) { if (m_engine->canHandleToolTip(context)) {
m_engine->updateItem(context.iname); m_engine->updateItem(context.iname);
} else { } else {
ToolTip::show(point, Tr::tr("Expression too complex"), ToolTip::show(point, Tr::tr("Expression too complex"), editorWidget);
DebuggerMainWindow::instance());
tooltip->destroy(); tooltip->destroy();
} }
} }
@@ -947,6 +965,7 @@ void DebuggerToolTipManagerPrivate::slotEditorOpened(IEditor *e)
// Move tooltip along when scrolled. // Move tooltip along when scrolled.
if (auto textEditor = qobject_cast<BaseTextEditor *>(e)) { if (auto textEditor = qobject_cast<BaseTextEditor *>(e)) {
TextEditorWidget *widget = textEditor->editorWidget(); TextEditorWidget *widget = textEditor->editorWidget();
m_tooltips.insert({widget, {}});
QObject::connect(widget->verticalScrollBar(), &QScrollBar::valueChanged, QObject::connect(widget->verticalScrollBar(), &QScrollBar::valueChanged,
this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips); this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips);
QObject::connect(widget, &TextEditorWidget::tooltipOverrideRequested, QObject::connect(widget, &TextEditorWidget::tooltipOverrideRequested,
@@ -954,18 +973,49 @@ void DebuggerToolTipManagerPrivate::slotEditorOpened(IEditor *e)
} }
} }
void DebuggerToolTipManagerPrivate::slotEditorAboutToClose(IEditor *e)
{
if (auto textEditor = qobject_cast<BaseTextEditor *>(e))
m_tooltips.erase(textEditor->editorWidget());
}
void DebuggerToolTipManagerPrivate::slotCurrentEditorAboutToChange(Core::IEditor *e)
{
if (auto textEditor = qobject_cast<BaseTextEditor *>(e))
textEditor->widget()->window()->removeEventFilter(this);
}
void DebuggerToolTipManagerPrivate::slotCurrentEditorChanged(Core::IEditor *e)
{
if (auto textEditor = qobject_cast<BaseTextEditor *>(e))
textEditor->widget()->window()->installEventFilter(this);
}
void DebuggerToolTipManagerPrivate::debugModeEntered() void DebuggerToolTipManagerPrivate::debugModeEntered()
{ {
// Hook up all signals in debug mode. // Hook up all signals in debug mode.
if (!m_debugModeActive) { if (!m_debugModeActive) {
m_debugModeActive = true; m_debugModeActive = true;
QWidget *topLevel = ICore::mainWindow()->topLevelWidget();
topLevel->installEventFilter(this);
EditorManager *em = EditorManager::instance(); EditorManager *em = EditorManager::instance();
connect(em, &EditorManager::currentEditorChanged, connect(em, &EditorManager::currentEditorChanged,
this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips); this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips);
connect(em, &EditorManager::editorOpened, connect(em, &EditorManager::editorOpened,
this, &DebuggerToolTipManagerPrivate::slotEditorOpened); this, &DebuggerToolTipManagerPrivate::slotEditorOpened);
connect(
em,
&EditorManager::editorAboutToClose,
this,
&DebuggerToolTipManagerPrivate::slotEditorAboutToClose);
connect(
em,
&EditorManager::currentEditorAboutToChange,
this,
&DebuggerToolTipManagerPrivate::slotCurrentEditorAboutToChange);
connect(
em,
&EditorManager::currentEditorChanged,
this,
&DebuggerToolTipManagerPrivate::slotCurrentEditorChanged);
setupEditors(); setupEditors();
} }
@@ -976,7 +1026,7 @@ void DebuggerToolTipManagerPrivate::setupEditors()
for (IEditor *e : DocumentModel::editorsForOpenedDocuments()) for (IEditor *e : DocumentModel::editorsForOpenedDocuments())
slotEditorOpened(e); slotEditorOpened(e);
// Position tooltips delayed once all the editor placeholder layouting is done. // Position tooltips delayed once all the editor placeholder layouting is done.
if (!m_tooltips.isEmpty()) if (!m_tooltips.empty())
QTimer::singleShot(0, this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips); QTimer::singleShot(0, this, &DebuggerToolTipManagerPrivate::updateVisibleToolTips);
} }
@@ -1005,24 +1055,31 @@ DebuggerToolTipContexts DebuggerToolTipManager::pendingTooltips() const
StackFrame frame = d->m_engine->stackHandler()->currentFrame(); StackFrame frame = d->m_engine->stackHandler()->currentFrame();
DebuggerToolTipContexts rc; DebuggerToolTipContexts rc;
d->purgeClosedToolTips(); d->purgeClosedToolTips();
for (DebuggerToolTipWidget *tooltip : std::as_const(d->m_tooltips)) { for (const auto &[editor, tooltips] : d->m_tooltips) {
const DebuggerToolTipContext &context = tooltip->context; for (DebuggerToolTipWidget *tooltip : std::as_const(tooltips)) {
if (context.iname.startsWith("tooltip") && context.matchesFrame(frame)) const DebuggerToolTipContext &context = tooltip->context;
rc.push_back(context); if (context.iname.startsWith("tooltip") && context.matchesFrame(frame))
rc.push_back(context);
}
} }
return rc; return rc;
} }
bool DebuggerToolTipManagerPrivate::eventFilter(QObject *o, QEvent *e) bool DebuggerToolTipManagerPrivate::eventFilter(QObject *o, QEvent *e)
{ {
if (m_tooltips.isEmpty()) if (m_tooltips.empty())
return false; return false;
switch (e->type()) { switch (e->type()) {
case QEvent::Move: { // Move along with parent (toplevel) case QEvent::Move: { // Move along with parent (toplevel)
const auto me = static_cast<const QMoveEvent *>(e); const auto me = static_cast<const QMoveEvent *>(e);
const QPoint dist = me->pos() - me->oldPos(); const QPoint dist = me->pos() - me->oldPos();
purgeClosedToolTips(); purgeClosedToolTips();
for (const QPointer<DebuggerToolTipWidget> &tooltip : std::as_const(m_tooltips)) { QList<QPointer<DebuggerToolTipWidget>> affectedTooltips;
for (auto &[editor, tooltips] : m_tooltips) {
if (editor->window() == o)
affectedTooltips.append(tooltips);
}
for (const QPointer<DebuggerToolTipWidget> &tooltip : std::as_const(affectedTooltips)) {
if (tooltip && tooltip->isVisible()) if (tooltip && tooltip->isVisible())
tooltip->move(tooltip->pos() + dist); tooltip->move(tooltip->pos() + dist);
} }
@@ -1034,7 +1091,12 @@ bool DebuggerToolTipManagerPrivate::eventFilter(QObject *o, QEvent *e)
const bool isMinimized = static_cast<const QWidget *>(o)->windowState() & Qt::WindowMinimized; const bool isMinimized = static_cast<const QWidget *>(o)->windowState() & Qt::WindowMinimized;
if (wasMinimized != isMinimized) { if (wasMinimized != isMinimized) {
purgeClosedToolTips(); purgeClosedToolTips();
for (DebuggerToolTipWidget *tooltip : std::as_const(m_tooltips)) QList<QPointer<DebuggerToolTipWidget>> affectedTooltips;
for (auto &[editor, tooltips] : m_tooltips) {
if (editor->window() == o)
affectedTooltips.append(tooltips);
}
for (DebuggerToolTipWidget *tooltip : std::as_const(affectedTooltips))
tooltip->setVisible(!isMinimized); tooltip->setVisible(!isMinimized);
} }
break; break;
@@ -1047,11 +1109,13 @@ bool DebuggerToolTipManagerPrivate::eventFilter(QObject *o, QEvent *e)
void DebuggerToolTipManagerPrivate::purgeClosedToolTips() void DebuggerToolTipManagerPrivate::purgeClosedToolTips()
{ {
for (int i = m_tooltips.size(); --i >= 0; ) { for (auto &[editor, tooltips] : m_tooltips) {
const QPointer<DebuggerToolTipWidget> tooltip = m_tooltips.at(i); for (int i = tooltips.size(); --i >= 0;) {
if (!tooltip) { const QPointer<DebuggerToolTipWidget> tooltip = tooltips.at(i);
DEBUG("PURGE TOOLTIP, LEFT: " << m_tooltips.size()); if (!tooltip) {
m_tooltips.removeAt(i); DEBUG("PURGE TOOLTIP, LEFT: " << m_tooltips.size());
tooltips.removeAt(i);
}
} }
} }
} }