diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp index de73c6edaaa..9a5c84894c3 100644 --- a/src/libs/utils/fancymainwindow.cpp +++ b/src/libs/utils/fancymainwindow.cpp @@ -523,6 +523,21 @@ bool FancyMainWindow::autoHideTitleBars() const return d->m_autoHideTitleBars.isChecked(); } +void FancyMainWindow::setAutoHideTitleBars(bool on) +{ + d->m_autoHideTitleBars.setChecked(on); +} + +bool FancyMainWindow::isCentralWidgetShown() const +{ + return d->m_showCentralWidget.isChecked(); +} + +void FancyMainWindow::showCentralWidget(bool on) +{ + d->m_showCentralWidget.setChecked(on); +} + void FancyMainWindow::addDockActionsToMenu(QMenu *menu) { QList actions; diff --git a/src/libs/utils/fancymainwindow.h b/src/libs/utils/fancymainwindow.h index 623ef424086..4424b1c8bbf 100644 --- a/src/libs/utils/fancymainwindow.h +++ b/src/libs/utils/fancymainwindow.h @@ -66,6 +66,10 @@ public: void addDockActionsToMenu(QMenu *menu); bool autoHideTitleBars() const; + void setAutoHideTitleBars(bool on); + + bool isCentralWidgetShown() const; + void showCentralWidget(bool on); signals: // Emitted by resetLayoutAction(). Connect to a slot diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 5f89c07875e..b86bb1088ec 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include @@ -280,12 +281,16 @@ public: m_disassemblerAgent(engine), m_toolTipManager(engine) { - m_logWindow = new LogWindow(m_engine); // Needed before start() - m_logWindow->setObjectName(DOCKWIDGET_OUTPUT); m_debuggerName = DebuggerEngine::tr("Debugger"); - connect(action(EnableReverseDebugging), &SavedAction::valueChanged, - this, [this] { updateState(true); }); + m_logWindow = new LogWindow(m_engine); // Needed before start() + m_logWindow->setObjectName("Debugger.Dock.Output"); + + connect(action(EnableReverseDebugging), &SavedAction::valueChanged, this, [this] { + updateState(); + if (m_companionEngine) + m_companionEngine->d->updateState(); + }); static int contextCount = 0; m_context = Context(Id("Debugger.Engine.").withSuffix(++contextCount)); @@ -370,17 +375,15 @@ public: if (!m_perspective) return; - delete m_perspective; + Perspective *perspective = m_perspective; m_perspective = nullptr; EngineManager::unregisterEngine(m_engine); - // Give up ownership on claimed breakpoints. - m_breakHandler.releaseAllBreakpoints(); - m_toolTipManager.deregisterEngine(); - m_memoryAgents.handleDebuggerFinished(); - - setBusyCursor(false); + // This triggers activity in the EngineManager which + // recognizes the rampdown by the m_perpective == nullptr above. + perspective->destroy(); + delete perspective; } void updateReturnViewHeader(int section, int, int newSize) @@ -446,14 +449,14 @@ public: void setInitialActionStates(); void setBusyCursor(bool on); void cleanupViews(); - void updateState(bool alsoUpdateCompanion); + void updateState(); void updateReverseActions(); DebuggerEngine *m_engine = nullptr; // Not owned. QString m_runId; QPointer m_runConfiguration; // Not owned. QString m_debuggerName; - Perspective *m_perspective = nullptr; + QPointer m_perspective; DebuggerRunParameters m_runParameters; IDevice::ConstPtr m_device; @@ -551,16 +554,17 @@ public: void DebuggerEnginePrivate::setupViews() { const DebuggerRunParameters &rp = m_runParameters; + const QString engineId = EngineManager::registerEngine(m_engine); QTC_CHECK(!m_perspective); - m_perspective = new Perspective("Debugger.Perspective." + m_runId + '.' + m_debuggerName, + const QString perspectiveId = "Debugger.Perspective." + m_runId + '.' + m_debuggerName; + const QString settingsId = "Debugger.Perspective." + m_debuggerName; + + m_perspective = new Perspective(perspectiveId, m_engine->displayName(), Debugger::Constants::PRESET_PERSPECTIVE_ID, - m_debuggerName); - m_perspective->setShouldPersistChecker([this] { - return EngineManager::isLastOf(m_debuggerName); - }); + settingsId); m_progress.setProgressRange(0, 1000); FutureProgress *fp = ProgressManager::addTask(m_progress.future(), @@ -628,7 +632,7 @@ void DebuggerEnginePrivate::setupViews() m_engine, &DebuggerEngine::reloadModules, Qt::QueuedConnection); m_modulesWindow = addSearch(m_modulesView); - m_modulesWindow->setObjectName(DOCKWIDGET_MODULES); + m_modulesWindow->setObjectName("Debugger.Dock.Modules." + engineId); m_modulesWindow->setWindowTitle(tr("&Modules")); m_registerView = new BaseTreeView; @@ -639,7 +643,7 @@ void DebuggerEnginePrivate::setupViews() m_engine, &DebuggerEngine::reloadRegisters, Qt::QueuedConnection); m_registerWindow = addSearch(m_registerView); - m_registerWindow->setObjectName(DOCKWIDGET_REGISTER); + m_registerWindow->setObjectName("Debugger.Dock.Register." + engineId); m_registerWindow->setWindowTitle(tr("Reg&isters")); m_stackView = new StackTreeView; @@ -647,7 +651,7 @@ void DebuggerEnginePrivate::setupViews() m_stackView->setSettings(settings, "Debugger.StackView"); m_stackView->setIconSize(QSize(10, 10)); m_stackWindow = addSearch(m_stackView); - m_stackWindow->setObjectName(DOCKWIDGET_STACK); + m_stackWindow->setObjectName("Debugger.Dock.Stack." + engineId); m_stackWindow->setWindowTitle(tr("&Stack")); m_sourceFilesView = new BaseTreeView; @@ -658,7 +662,7 @@ void DebuggerEnginePrivate::setupViews() m_engine, &DebuggerEngine::reloadSourceFiles, Qt::QueuedConnection); m_sourceFilesWindow = addSearch(m_sourceFilesView); - m_sourceFilesWindow->setObjectName(DOCKWIDGET_SOURCE_FILES); + m_sourceFilesWindow->setObjectName("Debugger.Dock.SourceFiles." + engineId); m_sourceFilesWindow->setWindowTitle(tr("Source Files")); m_threadsView = new BaseTreeView; @@ -668,7 +672,7 @@ void DebuggerEnginePrivate::setupViews() m_threadsView->setIconSize(QSize(10, 10)); m_threadsView->setSpanColumn(ThreadData::FunctionColumn); m_threadsWindow = addSearch(m_threadsView); - m_threadsWindow->setObjectName(DOCKWIDGET_THREADS); + m_threadsWindow->setObjectName("Debugger.Dock.Threads." + engineId); m_threadsWindow->setWindowTitle(tr("&Threads")); m_returnView = new WatchTreeView{ReturnType}; @@ -682,26 +686,26 @@ void DebuggerEnginePrivate::setupViews() m_localsView->setModel(m_watchHandler.model()); m_localsView->setSettings(settings, "Debugger.LocalsView"); m_localsWindow = addSearch(m_localsView); - m_localsWindow->setObjectName("CppDebugLocals"); + m_localsWindow->setObjectName("Debugger.Dock.Locals." + engineId); m_localsWindow->setWindowTitle(tr("Locals")); m_inspectorView = new WatchTreeView{InspectType}; m_inspectorView->setModel(m_watchHandler.model()); m_inspectorView->setSettings(settings, "Debugger.LocalsView"); // sic! same as locals view. m_inspectorWindow = addSearch(m_inspectorView); - m_inspectorWindow->setObjectName("Inspector"); + m_inspectorWindow->setObjectName("Debugger.Dock.Inspector." + engineId); m_inspectorWindow->setWindowTitle(tr("Locals")); m_watchersView = new WatchTreeView{WatchersType}; m_watchersView->setModel(m_watchHandler.model()); m_watchersView->setSettings(settings, "Debugger.WatchersView"); m_watchersWindow = addSearch(m_watchersView); - m_watchersWindow->setObjectName("CppDebugWatchers"); + m_watchersWindow->setObjectName("Debugger.Dock.Watchers." + engineId); m_watchersWindow->setWindowTitle(tr("&Expressions")); m_localsAndInspectorWindow = new LocalsAndInspectorWindow( m_localsWindow, m_inspectorWindow, m_returnWindow); - m_localsAndInspectorWindow->setObjectName(DOCKWIDGET_LOCALS_AND_INSPECTOR); + m_localsAndInspectorWindow->setObjectName("Debugger.Dock.LocalsAndInspector." + engineId); m_localsAndInspectorWindow->setWindowTitle(m_localsWindow->windowTitle()); // Locals @@ -719,7 +723,7 @@ void DebuggerEnginePrivate::setupViews() m_breakView->setModel(m_breakHandler.model()); m_breakView->setRootIsDecorated(true); m_breakWindow = addSearch(m_breakView); - m_breakWindow->setObjectName(DOCKWIDGET_BREAK); + m_breakWindow->setObjectName("Debugger.Dock.Break." + engineId); m_breakWindow->setWindowTitle(tr("&Breakpoints")); m_perspective->useSubPerspectiveSwitcher(EngineManager::engineChooser()); @@ -846,7 +850,6 @@ void DebuggerEnginePrivate::setupViews() DebuggerEngine::DebuggerEngine() : d(new DebuggerEnginePrivate(this)) { - updateState(false); } DebuggerEngine::~DebuggerEngine() @@ -1020,7 +1023,6 @@ void DebuggerEngine::setRunTool(DebuggerRunTool *runTool) void DebuggerEngine::start() { - EngineManager::registerEngine(this); d->m_watchHandler.resetWatchers(); d->setInitialActionStates(); setState(EngineSetupRequested); @@ -1116,7 +1118,7 @@ void DebuggerEngine::abortDebugger() void DebuggerEngine::updateUi(bool isCurrentEngine) { - updateState(false); + updateState(); if (isCurrentEngine) { gotoCurrentLocation(); } else { @@ -1319,7 +1321,7 @@ void DebuggerEngine::notifyInferiorSpontaneousStop() { showMessage("NOTE: INFERIOR SPONTANEOUS STOP"); QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state()); - EngineManager::activateEngine(this); + d->m_perspective->select(); showMessage(tr("Stopped."), StatusBar); setState(InferiorStopOk); if (boolSetting(RaiseOnInterrupt)) @@ -1386,10 +1388,12 @@ void DebuggerEnginePrivate::setInitialActionStates() m_threadLabel->setEnabled(false); } -void DebuggerEnginePrivate::updateState(bool alsoUpdateCompanion) +void DebuggerEnginePrivate::updateState() { - if (!m_perspective) + // Can happen in mixed debugging. + if (!m_threadLabel) return; + QTC_ASSERT(m_threadLabel, return); const DebuggerState state = m_state; const bool companionPreventsAction = m_engine->companionPreventsActions(); @@ -1399,7 +1403,7 @@ void DebuggerEnginePrivate::updateState(bool alsoUpdateCompanion) // visible, possibly disabled. if (state == DebuggerNotReady) { // Happens when companion starts, otherwise this should not happen. - QTC_CHECK(m_companionEngine); + //QTC_CHECK(m_companionEngine); m_interruptAction.setVisible(true); m_interruptAction.setEnabled(false); m_continueAction.setVisible(false); @@ -1529,9 +1533,6 @@ void DebuggerEnginePrivate::updateState(bool alsoUpdateCompanion) || state == DebuggerFinished || state == InferiorUnrunnable; setBusyCursor(!notbusy); - - if (alsoUpdateCompanion && m_companionEngine) - m_companionEngine->updateState(false); } void DebuggerEnginePrivate::updateReverseActions() @@ -1690,9 +1691,9 @@ void DebuggerEngine::notifyInferiorExited() d->doShutdownEngine(); } -void DebuggerEngine::updateState(bool alsoUpdateCompanion) +void DebuggerEngine::updateState() { - d->updateState(alsoUpdateCompanion); + d->updateState(); } WatchTreeView *DebuggerEngine::inspectorView() @@ -1793,17 +1794,28 @@ void DebuggerEngine::setState(DebuggerState state, bool forced) if (!forced && !isAllowedTransition(oldState, state)) qDebug() << "*** UNEXPECTED STATE TRANSITION: " << this << msg; - if (state == EngineRunRequested) + if (state == EngineRunRequested) { emit engineStarted(); + d->m_perspective->select(); + } showMessage(msg, LogDebug); - d->updateState(true); + d->updateState(); + if (d->m_companionEngine) + d->m_companionEngine->d->updateState(); if (oldState != d->m_state) emit EngineManager::instance()->engineStateChanged(this); if (state == DebuggerFinished) { + d->setBusyCursor(false); + + // Give up ownership on claimed breakpoints. + d->m_breakHandler.releaseAllBreakpoints(); + d->m_toolTipManager.deregisterEngine(); + d->m_memoryAgents.handleDebuggerFinished(); + d->destroyPerspective(); emit engineFinished(); } diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 12e4356ce0d..736d2b3c03f 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -416,7 +416,7 @@ protected: void notifyInferiorStopFailed(); public: - void updateState(bool alsoUpdateCompanion); + void updateState(); QString formatStartParameters() const; WatchTreeView *inspectorView(); void updateLocalsWindow(bool showReturn); diff --git a/src/plugins/debugger/debuggerinternalconstants.h b/src/plugins/debugger/debuggerinternalconstants.h index cb9591e8ad9..8f6599b6535 100644 --- a/src/plugins/debugger/debuggerinternalconstants.h +++ b/src/plugins/debugger/debuggerinternalconstants.h @@ -28,24 +28,6 @@ #include namespace Debugger { -namespace Internal { - -// DebuggerMainWindow dock widget names -const char DOCKWIDGET_BREAKPOINTMANAGER[] = "Debugger.Docks.BreakpointManager"; -const char DOCKWIDGET_ENGINEMANAGER[] = "Debugger.Docks.Snapshots"; -const char DOCKWIDGET_GLOBALLOG[] = "Debugger.Docks.GlobalLog"; - -const char DOCKWIDGET_BREAK[] = "Debugger.Docks.Break"; -const char DOCKWIDGET_MODULES[] = "Debugger.Docks.Modules"; -const char DOCKWIDGET_REGISTER[] = "Debugger.Docks.Register"; -const char DOCKWIDGET_OUTPUT[] = "Debugger.Docks.Output"; -const char DOCKWIDGET_STACK[] = "Debugger.Docks.Stack"; -const char DOCKWIDGET_SOURCE_FILES[] = "Debugger.Docks.SourceFiles"; -const char DOCKWIDGET_THREADS[] = "Debugger.Docks.Threads"; -const char DOCKWIDGET_LOCALS_AND_INSPECTOR[] = "Debugger.Docks.LocalsAndInspector"; - -} // namespace Internal - namespace Constants { const char DEBUGGER_COMMON_SETTINGS_ID[] = "A.Debugger.General"; diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp index 7d4175fc956..9a661eee81f 100644 --- a/src/plugins/debugger/debuggermainwindow.cpp +++ b/src/plugins/debugger/debuggermainwindow.cpp @@ -49,9 +49,10 @@ #include #include -#include +#include #include #include +#include #include #include #include @@ -62,37 +63,57 @@ using namespace Debugger; using namespace Core; +Q_LOGGING_CATEGORY(perspectivesLog, "qtc.utils.perspectives", QtWarningMsg) + namespace Utils { +const int SettingsVersion = 3; + const char LAST_PERSPECTIVE_KEY[] = "LastPerspective"; -const char OWNED_BY_PERSPECTIVE[] = "OwnedByPerspective"; +const char MAINWINDOW_KEY[] = "Debugger.MainWindow"; +const char AUTOHIDE_TITLEBARS_KEY[] = "AutoHideTitleBars"; +const char SHOW_CENTRALWIDGET_KEY[] = "ShowCentralWidget"; +const char STATE_KEY[] = "State"; +const char CHANGED_DOCK_KEY[] = "ChangedDocks"; static DebuggerMainWindow *theMainWindow = nullptr; class DockOperation { public: + void setupLayout(); + QString name() const { QTC_ASSERT(widget, return QString()); return widget->objectName(); } + + Core::Id commandId; QPointer widget; - QString anchorDockId; + QPointer dock; + QPointer anchorWidget; Perspective::OperationType operationType = Perspective::Raise; bool visibleByDefault = true; + bool visibleByUser = true; Qt::DockWidgetArea area = Qt::BottomDockWidgetArea; }; class PerspectivePrivate { public: - void showToolBar(); - void hideToolBar(); + void showInnerToolBar(); + void hideInnerToolBar(); void restoreLayout(); void saveLayout(); + void resetPerspective(); + void populatePerspective(); + void depopulatePerspective(); + void saveAsLastUsedPerspective(); + Context context() const; + QString settingsId() const; QToolButton *setupToolButton(QAction *action); QString m_id; QString m_name; QString m_parentPerspectiveId; - QString m_subPerspectiveType; + QString m_settingsId; QVector m_dockOperations; QPointer m_centralWidget; Perspective::Callback m_aboutToActivateCallback; @@ -100,8 +121,6 @@ public: QHBoxLayout *m_innerToolBarLayout = nullptr; QPointer m_switcher; QString m_lastActiveSubPerspectiveId; - QHash m_nonPersistenSettings; - Perspective::ShouldPersistChecker m_shouldPersistChecker; }; class DebuggerMainWindowPrivate : public QObject @@ -118,20 +137,30 @@ public: void registerPerspective(Perspective *perspective); void resetCurrentPerspective(); int indexInChooser(Perspective *perspective) const; - void fixupLayoutIfNeeded(); void updatePerspectiveChooserWidth(); + void cleanDocks(); + void setCentralWidget(QWidget *widget); + + QDockWidget *dockForWidget(QWidget *widget) const; + + void setCurrentPerspective(Perspective *perspective) + { + m_currentPerspective = perspective; + } + DebuggerMainWindow *q = nullptr; - Perspective *m_currentPerspective = nullptr; + QPointer m_currentPerspective = nullptr; QComboBox *m_perspectiveChooser = nullptr; QStackedWidget *m_centralWidgetStack = nullptr; QHBoxLayout *m_subPerspectiveSwitcherLayout = nullptr; QHBoxLayout *m_innerToolsLayout = nullptr; - QWidget *m_editorPlaceHolder = nullptr; + QPointer m_editorPlaceHolder; Utils::StatusLabel *m_statusLabel = nullptr; QDockWidget *m_toolBarDock = nullptr; - QList m_perspectives; + QList> m_perspectives; + QSet m_persistentChangedDocks; }; DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent) @@ -211,10 +240,9 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent) m_toolBarDock = dock; q->addDockWidget(Qt::BottomDockWidgetArea, m_toolBarDock); - connect(viewButton, &QAbstractButton::clicked, [this, viewButton] { - QMenu menu; - q->addDockActionsToMenu(&menu); - menu.exec(viewButton->mapToGlobal(QPoint())); + connect(viewButton, &QAbstractButton::clicked, this, [this, viewButton] { + ActionContainer *viewsMenu = ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS); + viewsMenu->menu()->exec(viewButton->mapToGlobal(QPoint())); }); connect(closeButton, &QAbstractButton::clicked, [] { @@ -256,6 +284,19 @@ DebuggerMainWindow::DebuggerMainWindow() "Debugger.Views.ResetSimple", debugcontext); cmd->setAttribute(Command::CA_Hide); viewsMenu->addAction(cmd, Core::Constants::G_DEFAULT_THREE); + + connect(ICore::instance(), &ICore::saveSettingsRequested, this, [this] { + // There's one saveSettings triggered after plugin loading intentionally. + // We do not want to save anything at that time. + static bool firstOne = true; + if (firstOne) { + qCDebug(perspectivesLog) << "FIRST SAVE SETTINGS REQUEST IGNORED"; + firstOne = false; + } else { + qCDebug(perspectivesLog) << "SAVING SETTINGS"; + savePersistentSettings(); + } + }); } DebuggerMainWindow::~DebuggerMainWindow() @@ -263,6 +304,12 @@ DebuggerMainWindow::~DebuggerMainWindow() delete d; } +void DebuggerMainWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + ActionContainer *viewsMenu = ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS); + viewsMenu->menu()->exec(ev->globalPos()); +} + void DebuggerMainWindow::ensureMainWindowExists() { if (!theMainWindow) @@ -271,6 +318,8 @@ void DebuggerMainWindow::ensureMainWindowExists() void DebuggerMainWindow::doShutdown() { + QTC_ASSERT(theMainWindow, return); + delete theMainWindow; theMainWindow = nullptr; } @@ -286,9 +335,12 @@ void DebuggerMainWindowPrivate::registerPerspective(Perspective *perspective) void DebuggerMainWindowPrivate::destroyPerspective(Perspective *perspective) { - if (perspective == m_currentPerspective) { - depopulateCurrentPerspective(); - m_currentPerspective = nullptr; + qCDebug(perspectivesLog) << "ABOUT TO DESTROY PERSPECTIVE" << perspective->id(); + + const bool wasCurrent = perspective == m_currentPerspective; + if (wasCurrent) { + qCDebug(perspectivesLog) << "RAMPING IT DOWN FIRST AS IT WAS CURRENT" << perspective->id(); + perspective->rampDownAsCurrent(); } m_perspectives.removeAll(perspective); @@ -298,6 +350,31 @@ void DebuggerMainWindowPrivate::destroyPerspective(Perspective *perspective) const int idx = indexInChooser(perspective); if (idx != -1) m_perspectiveChooser->removeItem(idx); + + for (DockOperation &op : perspective->d->m_dockOperations) { + if (op.commandId.isValid()) + ActionManager::unregisterAction(op.dock->toggleViewAction(), op.commandId); + if (op.dock) { + theMainWindow->removeDockWidget(op.dock); + op.widget->setParent(nullptr); // Prevent deletion + op.dock->setParent(nullptr); + delete op.dock; + op.dock = nullptr; + } + } + + if (wasCurrent) { + if (Perspective *parent = Perspective::findPerspective(perspective->d->m_parentPerspectiveId)) { + qCDebug(perspectivesLog) << "RAMPING UP PARENT PERSPECTIVE" << parent->id(); + parent->rampUpAsCurrent(); + } else { + qCDebug(perspectivesLog) << "RAMPED DOWN WAS ACTION, BUT NO PARENT AVAILABLE. TAKE FIRST BEST"; + if (QTC_GUARD(!m_perspectives.isEmpty())) + m_perspectives.first()->rampUpAsCurrent(); + } + } + + qCDebug(perspectivesLog) << "DESTROYED PERSPECTIVE" << perspective->id(); } void DebuggerMainWindow::showStatusMessage(const QString &message, int timeoutMS) @@ -309,36 +386,122 @@ void DebuggerMainWindow::showStatusMessage(const QString &message, int timeoutMS void DebuggerMainWindow::enterDebugMode() { theMainWindow->setDockActionsVisible(true); - Perspective *perspective = theMainWindow->d->m_currentPerspective; - if (!perspective) { - const QSettings *settings = ICore::settings(); - const QString lastPerspectiveId = settings->value(LAST_PERSPECTIVE_KEY).toString(); - perspective = Perspective::findPerspective(lastPerspectiveId); - // If we don't find a perspective with the stored name, pick any. - // This can happen e.g. when a plugin was disabled that provided - // the stored perspective, or when the save file was modified externally. - if (!perspective && !theMainWindow->d->m_perspectives.isEmpty()) - perspective = theMainWindow->d->m_perspectives.first(); - } + theMainWindow->restorePersistentSettings(); + QTC_CHECK(theMainWindow->d->m_currentPerspective == nullptr); + + QSettings *settings = ICore::settings(); + const QString lastPerspectiveId = settings->value(LAST_PERSPECTIVE_KEY).toString(); + Perspective *perspective = Perspective::findPerspective(lastPerspectiveId); + // If we don't find a perspective with the stored name, pick any. + // This can happen e.g. when a plugin was disabled that provided + // the stored perspective, or when the save file was modified externally. + if (!perspective && !theMainWindow->d->m_perspectives.isEmpty()) + perspective = theMainWindow->d->m_perspectives.first(); + // There's at least the debugger preset perspective that should be found above. QTC_ASSERT(perspective, return); - perspective->select(); + + if (auto sub = Perspective::findPerspective(perspective->d->m_lastActiveSubPerspectiveId)) { + qCDebug(perspectivesLog) << "SWITCHING TO SUBPERSPECTIVE" << sub->d->m_id; + perspective = sub; + } + + perspective->rampUpAsCurrent(); } void DebuggerMainWindow::leaveDebugMode() { - if (Perspective *perspective = theMainWindow->d->m_currentPerspective) - perspective->d->saveLayout(); + theMainWindow->savePersistentSettings(); + + if (theMainWindow->d->m_currentPerspective) + theMainWindow->d->m_currentPerspective->rampDownAsCurrent(); + QTC_CHECK(theMainWindow->d->m_currentPerspective == nullptr); theMainWindow->setDockActionsVisible(false); // Hide dock widgets manually in case they are floating. for (QDockWidget *dockWidget : theMainWindow->dockWidgets()) { if (dockWidget->isFloating()) - dockWidget->hide(); + dockWidget->setVisible(false); } } +void DebuggerMainWindow::restorePersistentSettings() +{ + qCDebug(perspectivesLog) << "RESTORE PERSISTENT"; + QSettings *settings = ICore::settings(); + settings->beginGroup(MAINWINDOW_KEY); + const bool res = theMainWindow->restoreState(settings->value(STATE_KEY).toByteArray(), + SettingsVersion); + if (!res) + qCDebug(perspectivesLog) << "NO READABLE PERSISTENT SETTINGS FOUND, ASSUMING NEW CLEAN SETTINGS"; + + theMainWindow->setAutoHideTitleBars(settings->value(AUTOHIDE_TITLEBARS_KEY, true).toBool()); + theMainWindow->showCentralWidget(settings->value(SHOW_CENTRALWIDGET_KEY, true).toBool()); + theMainWindow->d->m_persistentChangedDocks + = QSet::fromList(settings->value(CHANGED_DOCK_KEY).toStringList()); + settings->endGroup(); + + qCDebug(perspectivesLog) << "LOADED DOCKS:" << theMainWindow->d->m_persistentChangedDocks; + + for (Perspective *perspective : theMainWindow->d->m_perspectives) { + qCDebug(perspectivesLog) << "RESTORING PERSPECTIVE" << perspective->d->m_id; + for (DockOperation &op : perspective->d->m_dockOperations) { + if (op.operationType != Perspective::Raise) { + QTC_ASSERT(op.dock, continue); + QTC_ASSERT(op.widget, continue); + if (theMainWindow->d->m_persistentChangedDocks.contains(op.name())) { + qCDebug(perspectivesLog) << "DOCK " << op.name() << "*** UNUSUAL"; + op.visibleByUser = !op.visibleByDefault; + } else { + qCDebug(perspectivesLog) << "DOCK " << op.name() << "NORMAL"; + QTC_CHECK(op.visibleByUser == op.visibleByDefault); + } + } + } + } +} + +Perspective *DebuggerMainWindow::currentPerspective() +{ + return theMainWindow->d->m_currentPerspective; +} + +void DebuggerMainWindow::savePersistentSettings() +{ + // The current one might have active, non saved changes. + if (Perspective *perspective = theMainWindow->d->m_currentPerspective) + perspective->d->saveLayout(); + + qCDebug(perspectivesLog) << "SAVE PERSISTENT"; + + QSet changedDocks = theMainWindow->d->m_persistentChangedDocks; + for (Perspective *perspective : theMainWindow->d->m_perspectives) { + qCDebug(perspectivesLog) << "SAVE PERSPECTIVE" << perspective->d->m_id; + for (const DockOperation &op : perspective->d->m_dockOperations) { + if (op.operationType != Perspective::Raise) { + QTC_ASSERT(op.dock, continue); + QTC_ASSERT(op.widget, continue); + qCDebug(perspectivesLog) << "DOCK " << op.name() << "ACTIVE: " << op.visibleByUser; + if (op.visibleByUser != op.visibleByDefault) + changedDocks.insert(op.name()); + else + changedDocks.remove(op.name()); + } + } + } + theMainWindow->d->m_persistentChangedDocks = changedDocks; + qCDebug(perspectivesLog) << "CHANGED DOCKS:" << changedDocks; + + QSettings *settings = ICore::settings(); + settings->beginGroup(MAINWINDOW_KEY); + settings->setValue(CHANGED_DOCK_KEY, QStringList(changedDocks.toList())); + settings->setValue(STATE_KEY, theMainWindow->saveState(SettingsVersion)); + settings->setValue(AUTOHIDE_TITLEBARS_KEY, theMainWindow->autoHideTitleBars()); + settings->setValue(SHOW_CENTRALWIDGET_KEY, theMainWindow->isCentralWidgetShown()); + settings->endGroup(); +} + QWidget *DebuggerMainWindow::centralWidgetStack() { return theMainWindow ? theMainWindow->d->m_centralWidgetStack : nullptr; @@ -358,21 +521,95 @@ DebuggerMainWindow *DebuggerMainWindow::instance() Perspective *Perspective::findPerspective(const QString &perspectiveId) { - return Utils::findOr(theMainWindow->d->m_perspectives, nullptr, [&](Perspective *perspective) { - return perspective->d->m_id == perspectiveId; + QTC_ASSERT(theMainWindow, return nullptr); + return Utils::findOr(theMainWindow->d->m_perspectives, nullptr, + [perspectiveId](Perspective *perspective) { + return perspective && perspective->d->m_id == perspectiveId; }); } +bool Perspective::isCurrent() const +{ + return theMainWindow->d->m_currentPerspective == this; +} + +QDockWidget *DebuggerMainWindowPrivate::dockForWidget(QWidget *widget) const +{ + QTC_ASSERT(widget, return nullptr); + + for (QDockWidget *dock : q->dockWidgets()) { + if (dock->widget() == widget) + return dock; + } + + return nullptr; +} + void DebuggerMainWindowPrivate::resetCurrentPerspective() { - if (m_currentPerspective) { - m_currentPerspective->d->m_nonPersistenSettings.clear(); - m_currentPerspective->d->hideToolBar(); + QTC_ASSERT(m_currentPerspective, return); + cleanDocks(); + m_currentPerspective->d->resetPerspective(); + setCentralWidget(m_currentPerspective->d->m_centralWidget); +} + +void DebuggerMainWindowPrivate::setCentralWidget(QWidget *widget) +{ + if (widget) { + m_centralWidgetStack->addWidget(widget); + q->showCentralWidgetAction()->setText(widget->windowTitle()); + } else { + m_centralWidgetStack->addWidget(m_editorPlaceHolder); + q->showCentralWidgetAction()->setText(DebuggerMainWindow::tr("Editor")); + } +} + +void PerspectivePrivate::resetPerspective() +{ + showInnerToolBar(); + + for (DockOperation &op : m_dockOperations) { + if (op.operationType == Perspective::Raise) { + QTC_ASSERT(op.dock, qCDebug(perspectivesLog) << op.name(); continue); + op.dock->raise(); + } else { + op.setupLayout(); + op.dock->setVisible(op.visibleByDefault); + qCDebug(perspectivesLog) << "SETTING " << op.name() + << " TO ACTIVE: " << op.visibleByDefault; + } + } +} + +void DockOperation::setupLayout() +{ + QTC_ASSERT(widget, return); + QTC_ASSERT(operationType != Perspective::Raise, return); + QTC_ASSERT(dock, return); + + QDockWidget *anchor = nullptr; + if (anchorWidget) + anchor = theMainWindow->d->dockForWidget(anchorWidget); + else if (area == Qt::BottomDockWidgetArea) + anchor = theMainWindow->d->m_toolBarDock; + + if (anchor) { + switch (operationType) { + case Perspective::AddToTab: + theMainWindow->tabifyDockWidget(anchor, dock); + break; + case Perspective::SplitHorizontal: + theMainWindow->splitDockWidget(anchor, dock, Qt::Horizontal); + break; + case Perspective::SplitVertical: + theMainWindow->splitDockWidget(anchor, dock, Qt::Vertical); + break; + default: + break; + } + } else { + theMainWindow->addDockWidget(area, dock); } - depopulateCurrentPerspective(); - populateCurrentPerspective(); - if (m_currentPerspective) - m_currentPerspective->d->saveLayout(); } int DebuggerMainWindowPrivate::indexInChooser(Perspective *perspective) const @@ -380,47 +617,6 @@ int DebuggerMainWindowPrivate::indexInChooser(Perspective *perspective) const return perspective ? m_perspectiveChooser->findData(perspective->d->m_id) : -1; } -void DebuggerMainWindowPrivate::fixupLayoutIfNeeded() -{ - // Evil workaround for QTCREATORBUG-21455: In some so far unknown situation - // the saveLayout/restoreLayout process leads to a situation where some docks - // do not end up below the perspective toolbar even though they were there - // initially, leading to an awkward dock layout. - // This here tries to detect the situation (sonmething else in the bottom - // area is at the right of the toolbar) "corrects" that by restoring the - // default layout. - - if (m_toolBarDock->width() != q->width()) { - qDebug() << "Scrambled dock layout found. Resetting it."; - resetCurrentPerspective(); - } -} - -void DebuggerMainWindowPrivate::selectPerspective(Perspective *perspective) -{ - QTC_ASSERT(perspective, return); - - if (m_currentPerspective) { - m_currentPerspective->d->saveLayout(); - m_currentPerspective->d->hideToolBar(); - } - - depopulateCurrentPerspective(); - - m_currentPerspective = perspective; - - perspective->aboutToActivate(); - - populateCurrentPerspective(); - - if (m_currentPerspective) { - m_currentPerspective->d->restoreLayout(); - fixupLayoutIfNeeded(); - } - - updatePerspectiveChooserWidth(); -} - void DebuggerMainWindowPrivate::updatePerspectiveChooserWidth() { Perspective *perspective = m_currentPerspective; @@ -444,111 +640,72 @@ void DebuggerMainWindowPrivate::updatePerspectiveChooserWidth() } } -void DebuggerMainWindowPrivate::depopulateCurrentPerspective() +void DebuggerMainWindowPrivate::cleanDocks() { - // Clean up old perspective. - for (QDockWidget *dock : q->dockWidgets()) { - if (dock->property(OWNED_BY_PERSPECTIVE).toBool()) { - // Prevent deletion of plugin-owned widgets. - if (dock->widget()) - dock->widget()->setParent(nullptr); - ActionManager::unregisterAction(dock->toggleViewAction(), - Id("Dock.").withSuffix(dock->objectName())); - delete dock; - } - } + m_statusLabel->clear(); - if (m_currentPerspective) { - ICore::removeAdditionalContext(m_currentPerspective->context()); - QWidget *central = m_currentPerspective->centralWidget(); - m_centralWidgetStack->removeWidget(central ? central : m_editorPlaceHolder); + for (QDockWidget *dock : q->dockWidgets()) { + if (dock != m_toolBarDock) + dock->setVisible(false); } } -void DebuggerMainWindowPrivate::populateCurrentPerspective() +void PerspectivePrivate::depopulatePerspective() { - // Create dock widgets wrapping ther perspective's widgets. - QHash dockForDockId; - for (const DockOperation &op : m_currentPerspective->d->m_dockOperations) { - if (op.operationType == Perspective::Raise) - continue; - QTC_ASSERT(op.widget, continue); - const QString dockId = op.widget->objectName(); - QTC_CHECK(!dockId.isEmpty()); - QTC_ASSERT(!dockForDockId.contains(dockId), continue); - QDockWidget *dock = q->addDockForWidget(op.widget); - dock->setProperty(OWNED_BY_PERSPECTIVE, true); - dockForDockId[dockId] = dock; - q->addDockWidget(op.area, dock); + ICore::removeAdditionalContext(context()); + theMainWindow->d->m_centralWidgetStack + ->removeWidget(m_centralWidget ? m_centralWidget : theMainWindow->d->m_editorPlaceHolder); - QAction *toggleViewAction = dock->toggleViewAction(); - toggleViewAction->setText(dock->windowTitle()); + theMainWindow->d->m_statusLabel->clear(); - Command *cmd = ActionManager::registerAction(toggleViewAction, - Id("Dock.").withSuffix(dock->objectName()), - m_currentPerspective->context()); - cmd->setAttribute(Command::CA_Hide); - ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS)->addAction(cmd); + for (QDockWidget *dock : theMainWindow->dockWidgets()) { + if (dock != theMainWindow->d->m_toolBarDock) + dock->setVisible(false); } - m_currentPerspective->d->showToolBar(); + hideInnerToolBar(); +} - // Pre-arrange dock widgets. - for (const DockOperation &op : m_currentPerspective->d->m_dockOperations) { - QTC_ASSERT(op.widget, continue); - const QString dockId = op.widget->objectName(); - QDockWidget *dock = dockForDockId.value(dockId); - QTC_ASSERT(dock, continue); - if (op.operationType == Perspective::Raise) { - dock->raise(); - continue; - } - QDockWidget *anchor = dockForDockId.value(op.anchorDockId); - if (!anchor && op.area == Qt::BottomDockWidgetArea) { - anchor = m_toolBarDock; - } +void PerspectivePrivate::saveAsLastUsedPerspective() +{ + if (Perspective *parent = Perspective::findPerspective(m_parentPerspectiveId)) + parent->d->m_lastActiveSubPerspectiveId = m_id; + else + m_lastActiveSubPerspectiveId.clear(); - if (anchor) { - switch (op.operationType) { - case Perspective::AddToTab: - q->tabifyDockWidget(anchor, dock); - break; - case Perspective::SplitHorizontal: - q->splitDockWidget(anchor, dock, Qt::Horizontal); - break; - case Perspective::SplitVertical: - q->splitDockWidget(anchor, dock, Qt::Vertical); - break; - default: - break; - } - } - dock->setVisible(op.visibleByDefault); + const QString &lastKey = m_parentPerspectiveId.isEmpty() ? m_id : m_parentPerspectiveId; + qCDebug(perspectivesLog) << "SAVE LAST USED PERSPECTIVE" << lastKey; + ICore::settings()->setValue(LAST_PERSPECTIVE_KEY, lastKey); +} + +void PerspectivePrivate::populatePerspective() +{ + showInnerToolBar(); + + if (m_centralWidget) { + theMainWindow->d->m_centralWidgetStack->addWidget(m_centralWidget); + theMainWindow->showCentralWidgetAction()->setText(m_centralWidget->windowTitle()); + } else { + theMainWindow->d->m_centralWidgetStack->addWidget(theMainWindow->d->m_editorPlaceHolder); + theMainWindow->showCentralWidgetAction()->setText(DebuggerMainWindow::tr("Editor")); } - QWidget *central = m_currentPerspective->centralWidget(); - m_centralWidgetStack->addWidget(central ? central : m_editorPlaceHolder); - q->showCentralWidgetAction()->setText(central ? central->windowTitle() - : DebuggerMainWindow::tr("Editor")); + ICore::addAdditionalContext(context()); - m_statusLabel->clear(); - - ICore::addAdditionalContext(m_currentPerspective->context()); + restoreLayout(); } // Perspective Perspective::Perspective(const QString &id, const QString &name, const QString &parentPerspectiveId, - const QString &subPerspectiveType) + const QString &settingsId) : d(new PerspectivePrivate) { - const bool shouldPersist = parentPerspectiveId.isEmpty(); d->m_id = id; d->m_name = name; d->m_parentPerspectiveId = parentPerspectiveId; - d->m_subPerspectiveType = subPerspectiveType; - d->m_shouldPersistChecker = [shouldPersist] { return shouldPersist; }; + d->m_settingsId = settingsId; DebuggerMainWindow::ensureMainWindowExists(); theMainWindow->d->registerPerspective(this); @@ -565,12 +722,8 @@ Perspective::Perspective(const QString &id, const QString &name, Perspective::~Perspective() { if (theMainWindow) { - d->saveLayout(); - delete d->m_innerToolBar; d->m_innerToolBar = nullptr; - - theMainWindow->d->destroyPerspective(this); } delete d; } @@ -596,12 +749,6 @@ void Perspective::setAboutToActivateCallback(const Perspective::Callback &cb) d->m_aboutToActivateCallback = cb; } -void Perspective::aboutToActivate() const -{ - if (d->m_aboutToActivateCallback) - d->m_aboutToActivateCallback(); -} - void Perspective::setEnabled(bool enabled) { QTC_ASSERT(theMainWindow, return); @@ -654,34 +801,24 @@ void Perspective::addToolbarSeparator() d->m_innerToolBarLayout->addWidget(new StyledSeparator(d->m_innerToolBar)); } -void Perspective::setShouldPersistChecker(const ShouldPersistChecker &checker) -{ - d->m_shouldPersistChecker = checker; -} - QWidget *Perspective::centralWidget() const { return d->m_centralWidget; } -Perspective *Perspective::currentPerspective() +Context PerspectivePrivate::context() const { - return theMainWindow ? theMainWindow->d->m_currentPerspective : nullptr; + return Context(Id::fromName(m_id.toUtf8())); } -Context Perspective::context() const -{ - return Context(Id::fromName(d->m_id.toUtf8())); -} - -void PerspectivePrivate::showToolBar() +void PerspectivePrivate::showInnerToolBar() { m_innerToolBar->setVisible(true); if (m_switcher) m_switcher->setVisible(true); } -void PerspectivePrivate::hideToolBar() +void PerspectivePrivate::hideInnerToolBar() { QTC_ASSERT(m_innerToolBar, return); m_innerToolBar->setVisible(false); @@ -698,63 +835,131 @@ void Perspective::addWindow(QWidget *widget, QTC_ASSERT(widget, return); DockOperation op; op.widget = widget; - if (anchorWidget) - op.anchorDockId = anchorWidget->objectName(); op.operationType = type; + op.anchorWidget = anchorWidget; op.visibleByDefault = visibleByDefault; op.area = area; + + if (op.operationType != Perspective::Raise) { + qCDebug(perspectivesLog) << "CREATING DOCK " << op.name() + << "DEFAULT: " << op.visibleByDefault; + op.dock = theMainWindow->addDockForWidget(op.widget); + op.commandId = Id("Dock.").withSuffix(op.name()); + if (theMainWindow->restoreDockWidget(op.dock)) { + qCDebug(perspectivesLog) << "RESTORED SUCCESSFULLY"; + } else { + qCDebug(perspectivesLog) << "COULD NOT RESTORE"; + op.setupLayout(); + } + op.dock->setVisible(false); + op.dock->toggleViewAction()->setText(op.dock->windowTitle()); + + QObject::connect(op.dock->toggleViewAction(), &QAction::changed, op.dock, [this, op] { + qCDebug(perspectivesLog) << "CHANGED: " << op.name() + << "ACTION CHECKED: " << op.dock->toggleViewAction()->isChecked(); + }); + + Command *cmd = ActionManager::registerAction(op.dock->toggleViewAction(), + op.commandId, d->context()); + cmd->setAttribute(Command::CA_Hide); + ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS)->addAction(cmd); + } + + op.visibleByUser = op.visibleByDefault; + if (theMainWindow->d->m_persistentChangedDocks.contains(op.name())) { + op.visibleByUser = !op.visibleByUser; + qCDebug(perspectivesLog) << "*** NON-DEFAULT USER: " << op.visibleByUser; + } else { + qCDebug(perspectivesLog) << "DEFAULT USER"; + } + d->m_dockOperations.append(op); } +void Perspective::destroy() +{ + theMainWindow->d->destroyPerspective(this); +} + +void Perspective::rampDownAsCurrent() +{ + QTC_ASSERT(this == theMainWindow->d->m_currentPerspective, return); + d->saveLayout(); + d->depopulatePerspective(); + theMainWindow->d->setCurrentPerspective(nullptr); + + Debugger::Internal::EngineManager::updatePerspectives(); +} + +void Perspective::rampUpAsCurrent() +{ + if (d->m_aboutToActivateCallback) + d->m_aboutToActivateCallback(); + + QTC_ASSERT(theMainWindow->d->m_currentPerspective == nullptr, return); + theMainWindow->d->setCurrentPerspective(this); + QTC_ASSERT(theMainWindow->d->m_currentPerspective == this, return); + + d->populatePerspective(); + + theMainWindow->d->updatePerspectiveChooserWidth(); + + d->saveAsLastUsedPerspective(); + + Debugger::Internal::EngineManager::updatePerspectives(); +} + void Perspective::select() { Debugger::Internal::EngineManager::activateDebugMode(); - if (Perspective::currentPerspective() == this) - return; - theMainWindow->d->selectPerspective(this); - if (Perspective *parent = Perspective::findPerspective(d->m_parentPerspectiveId)) - parent->d->m_lastActiveSubPerspectiveId = d->m_id; - else - d->m_lastActiveSubPerspectiveId.clear(); - const QString &lastKey = d->m_parentPerspectiveId.isEmpty() ? d->m_id : d->m_parentPerspectiveId; - ICore::settings()->setValue(LAST_PERSPECTIVE_KEY, lastKey); + if (theMainWindow->d->m_currentPerspective == this) + return; + + if (theMainWindow->d->m_currentPerspective) + theMainWindow->d->m_currentPerspective->rampDownAsCurrent(); + QTC_CHECK(theMainWindow->d->m_currentPerspective == nullptr); + + rampUpAsCurrent(); } void PerspectivePrivate::restoreLayout() { - if (m_nonPersistenSettings.isEmpty()) { - //qDebug() << "PERSPECTIVE" << m_id << "RESTORE PERSISTENT FROM " << settingsId(); - QSettings *settings = ICore::settings(); - settings->beginGroup(settingsId()); - theMainWindow->restoreSettings(settings); - settings->endGroup(); - m_nonPersistenSettings = theMainWindow->saveSettings(); - } else { - //qDebug() << "PERSPECTIVE" << m_id << "RESTORE FROM LOCAL TEMP"; - theMainWindow->restoreSettings(m_nonPersistenSettings); + qCDebug(perspectivesLog) << "PERSPECTIVE" << m_id << "RESTORING LAYOUT FROM " << settingsId(); + for (DockOperation &op : m_dockOperations) { + if (op.operationType != Perspective::Raise) { + QTC_ASSERT(op.dock, continue); + const bool active = op.visibleByUser; + op.dock->toggleViewAction()->setChecked(active); + op.dock->setVisible(active); + qCDebug(perspectivesLog) << "RESTORE DOCK " << op.name() << "ACTIVE: " << active + << (active == op.visibleByDefault ? "DEFAULT USER" : "*** NON-DEFAULT USER"); + } } } void PerspectivePrivate::saveLayout() { - //qDebug() << "PERSPECTIVE" << m_id << "SAVE LOCAL TEMP"; - m_nonPersistenSettings = theMainWindow->saveSettings(); - - if (m_shouldPersistChecker()) { - //qDebug() << "PERSPECTIVE" << m_id << "SAVE PERSISTENT TO " << settingsId(); - QSettings *settings = ICore::settings(); - settings->beginGroup(settingsId()); - theMainWindow->saveSettings(settings); - settings->endGroup(); - } else { - //qDebug() << "PERSPECTIVE" << m_id << "NOT PERSISTENT"; + qCDebug(perspectivesLog) << "PERSPECTIVE" << m_id << "SAVE LAYOUT TO " << settingsId(); + for (DockOperation &op : m_dockOperations) { + if (op.operationType != Perspective::Raise) { + QTC_ASSERT(op.dock, continue); + QTC_ASSERT(op.widget, continue); + const bool active = op.dock->toggleViewAction()->isChecked(); + op.visibleByUser = active; + if (active == op.visibleByDefault) + theMainWindow->d->m_persistentChangedDocks.remove(op.name()); + else + theMainWindow->d->m_persistentChangedDocks.insert(op.name()); + qCDebug(perspectivesLog) << "SAVE DOCK " << op.name() << "ACTIVE: " << active + << (active == op.visibleByDefault ? "DEFAULT USER" : "*** NON-DEFAULT USER"); + } } } QString PerspectivePrivate::settingsId() const { - return m_parentPerspectiveId.isEmpty() ? m_id : (m_parentPerspectiveId + '.' + m_subPerspectiveType); + return m_settingsId.isEmpty() ? m_id : m_settingsId; } // ToolbarAction diff --git a/src/plugins/debugger/debuggermainwindow.h b/src/plugins/debugger/debuggermainwindow.h index 98ff0a37d1d..116eb737174 100644 --- a/src/plugins/debugger/debuggermainwindow.h +++ b/src/plugins/debugger/debuggermainwindow.h @@ -59,12 +59,12 @@ public: QPointer m_toolButton; }; -class DEBUGGER_EXPORT Perspective +class DEBUGGER_EXPORT Perspective : public QObject { public: Perspective(const QString &id, const QString &name, const QString &parentPerspectiveId = QString(), - const QString &subPerspectiveType = QString()); + const QString &settingId = QString()); ~Perspective(); enum OperationType { SplitVertical, SplitHorizontal, AddToTab, Raise }; @@ -92,26 +92,26 @@ public: using Callback = std::function; void setAboutToActivateCallback(const Callback &cb); - void aboutToActivate() const; void setEnabled(bool enabled); void select(); + void destroy(); - static Perspective *currentPerspective(); static Perspective *findPerspective(const QString &perspectiveId); - Core::Context context() const; - - void showToolBar(); - void hideToolBar(); + bool isCurrent() const; private: + void rampDownAsCurrent(); + void rampUpAsCurrent(); + Perspective(const Perspective &) = delete; void operator=(const Perspective &) = delete; friend class DebuggerMainWindow; friend class DebuggerMainWindowPrivate; + friend class PerspectivePrivate; class PerspectivePrivate *d = nullptr; }; @@ -132,12 +132,20 @@ public: static QWidget *centralWidgetStack(); void addSubPerspectiveSwitcher(QWidget *widget); + static void savePersistentSettings(); + static void restorePersistentSettings(); + + static Perspective *currentPerspective(); + private: DebuggerMainWindow(); ~DebuggerMainWindow() override; + void contextMenuEvent(QContextMenuEvent *ev) override; + friend class Perspective; friend class PerspectivePrivate; + friend class DockOperation; class DebuggerMainWindowPrivate *d = nullptr; }; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 44ce9a03d82..419944851b5 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1045,7 +1045,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, m_breakpointManagerView->setSpanColumn(BreakpointFunctionColumn); m_breakpointManagerWindow = addSearch(m_breakpointManagerView); m_breakpointManagerWindow->setWindowTitle(tr("Breakpoint Preset")); - m_breakpointManagerWindow->setObjectName(DOCKWIDGET_BREAKPOINTMANAGER); + m_breakpointManagerWindow->setObjectName("Debugger.Docks.BreakpointManager"); addLabel(m_breakpointManagerWindow, m_breakpointManagerWindow->windowTitle()); @@ -1057,7 +1057,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, m_engineManagerView->setModel(m_engineManager.model()); m_engineManagerWindow = addSearch(m_engineManagerView); m_engineManagerWindow->setWindowTitle(tr("Debugger Perspectives")); - m_engineManagerWindow->setObjectName(DOCKWIDGET_ENGINEMANAGER); + m_engineManagerWindow->setObjectName("Debugger.Docks.Snapshots"); addLabel(m_engineManagerWindow, m_engineManagerWindow->windowTitle()); // Logging @@ -1356,7 +1356,8 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, DebuggerMainWindow::leaveDebugMode(); }); - connect(ModeManager::instance(), &ModeManager::currentModeChanged, this, [](Id mode) { + connect(ModeManager::instance(), &ModeManager::currentModeChanged, [](Id mode, Id oldMode) { + QTC_ASSERT(mode != oldMode, return); if (mode == MODE_DEBUG) { DebuggerMainWindow::enterDebugMode(); if (IEditor *editor = EditorManager::currentEditor()) @@ -1521,7 +1522,7 @@ void DebuggerPluginPrivate::updatePresetState() } else { // The startup phase should be over once we are here. // But treat it as 'undisturbable if we are here by accident. - QTC_CHECK(state != DebuggerNotReady); + //QTC_CHECK(state != DebuggerNotReady); // Everything else is "undisturbable". m_startAction.setEnabled(false); m_debugWithoutDeployAction.setEnabled(false); @@ -1559,7 +1560,7 @@ void DebuggerPluginPrivate::onStartupProjectChanged(Project *project) } for (DebuggerEngine *engine : EngineManager::engines()) { // Run controls might be deleted during exit. - engine->updateState(false); + engine->updateState(); } updatePresetState(); @@ -2014,11 +2015,9 @@ void DebuggerPluginPrivate::aboutToShutdown() m_shutdownTimer.setInterval(0); m_shutdownTimer.setSingleShot(true); connect(&m_shutdownTimer, &QTimer::timeout, this, &DebuggerPluginPrivate::doShutdown); - for (DebuggerEngine *engine : m_engineManager.engines()) { - if (engine && engine->state() != Debugger::DebuggerNotReady) { - engine->abortDebugger(); - m_shutdownTimer.setInterval(3000); - } + if (EngineManager::shutDown()) { + // If any engine is aborting we give them extra three seconds. + m_shutdownTimer.setInterval(3000); } m_shutdownTimer.start(); } diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index c72fcb6228b..ca76e8cfe1c 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -728,8 +728,6 @@ void DebuggerRunTool::stop() void DebuggerRunTool::handleEngineStarted(DebuggerEngine *engine) { - EngineManager::activateEngine(engine); - // Correct: // if (--d->engineStartsNeeded == 0) { // EngineManager::activateDebugMode(); diff --git a/src/plugins/debugger/enginemanager.cpp b/src/plugins/debugger/enginemanager.cpp index 883668e498e..ea4c67bb6e9 100644 --- a/src/plugins/debugger/enginemanager.cpp +++ b/src/plugins/debugger/enginemanager.cpp @@ -155,16 +155,18 @@ public: } EngineItem *findEngineItem(DebuggerEngine *engine); - void activateEngine(DebuggerEngine *engine); void activateEngineItem(EngineItem *engineItem); void activateEngineByIndex(int index); void selectUiForCurrentEngine(); void updateEngineChooserVisibility(); + void updatePerspectives(); TreeModel, EngineItem> m_engineModel; - QPointer m_currentItem; + QPointer m_currentItem; // The primary information is DebuggerMainWindow::d->m_currentPerspective Core::Id m_previousMode; QPointer m_engineChooser; + bool m_shuttingDown = false; + Context m_currentAdditionalContext; }; //////////////////////////////////////////////////////////////////////// @@ -192,6 +194,11 @@ QWidget *EngineManager::engineChooser() return d->m_engineChooser; } +void EngineManager::updatePerspectives() +{ + d->updatePerspectives(); +} + EngineManager::~EngineManager() { theEngineManager = nullptr; @@ -208,11 +215,6 @@ QAbstractItemModel *EngineManager::model() return &d->m_engineModel; } -void EngineManager::activateEngine(DebuggerEngine *engine) -{ - d->activateEngine(engine); -} - QVariant EngineItem::data(int column, int role) const { if (m_engine) { @@ -273,7 +275,7 @@ bool EngineItem::setData(int row, const QVariant &value, int role) if (role == BaseTreeView::ItemActivatedRole) { EngineItem *engineItem = d->findEngineItem(m_engine); - d->activateEngineItem(engineItem); + d->activateEngineByIndex(engineItem->indexInParent()); return true; } @@ -316,21 +318,27 @@ bool EngineItem::setData(int row, const QVariant &value, int role) void EngineManagerPrivate::activateEngineByIndex(int index) { - activateEngineItem(m_engineModel.rootItem()->childAt(index)); + // The actual activation is triggered indirectly via the perspective change. + Perspective *perspective = nullptr; + if (index == 0) { + perspective = Perspective::findPerspective(Debugger::Constants::PRESET_PERSPECTIVE_ID); + } else { + EngineItem *engineItem = m_engineModel.rootItem()->childAt(index); + QTC_ASSERT(engineItem, return); + QTC_ASSERT(engineItem->m_engine, return); + perspective = engineItem->m_engine->perspective(); + } + + QTC_ASSERT(perspective, return); + perspective->select(); } void EngineManagerPrivate::activateEngineItem(EngineItem *engineItem) { - Context previousContext; - if (m_currentItem) { - if (DebuggerEngine *engine = m_currentItem->m_engine) { - previousContext.add(engine->languageContext()); - previousContext.add(engine->debuggerContext()); - } else { - previousContext.add(Context(Constants::C_DEBUGGER_NOTRUNNING)); - } - } + if (m_currentItem == engineItem) + return; + QTC_ASSERT(engineItem, return); m_currentItem = engineItem; Context newContext; @@ -343,7 +351,13 @@ void EngineManagerPrivate::activateEngineItem(EngineItem *engineItem) } } - ICore::updateAdditionalContexts(previousContext, newContext); + ICore::updateAdditionalContexts(m_currentAdditionalContext, newContext); + m_currentAdditionalContext = newContext; + + // In case this was triggered externally by some Perspective::select() call. + const int idx = engineItem->indexInParent(); + m_engineChooser->setCurrentIndex(idx); + selectUiForCurrentEngine(); } @@ -352,12 +366,7 @@ void EngineManagerPrivate::selectUiForCurrentEngine() if (ModeManager::currentModeId() != Constants::MODE_DEBUG) return; - Perspective *perspective = nullptr; int row = 0; - - if (m_currentItem && m_currentItem->m_engine) - perspective = m_currentItem->m_engine->perspective(); - if (m_currentItem) row = m_engineModel.rootItem()->indexOf(m_currentItem); @@ -370,12 +379,6 @@ void EngineManagerPrivate::selectUiForCurrentEngine() QStyle::CT_ComboBox, &option, sz).width(); m_engineChooser->setFixedWidth(width); - if (!perspective) - perspective = Perspective::findPerspective(Debugger::Constants::PRESET_PERSPECTIVE_ID); - - QTC_ASSERT(perspective, return); - perspective->select(); - m_engineModel.rootItem()->forFirstLevelChildren([this](EngineItem *engineItem) { if (engineItem && engineItem->m_engine) engineItem->m_engine->updateUi(engineItem == m_currentItem); @@ -391,12 +394,6 @@ EngineItem *EngineManagerPrivate::findEngineItem(DebuggerEngine *engine) }); } -void EngineManagerPrivate::activateEngine(DebuggerEngine *engine) -{ - EngineItem *engineItem = findEngineItem(engine); - activateEngineItem(engineItem); -} - void EngineManagerPrivate::updateEngineChooserVisibility() { // Show it if there's more than one option (i.e. not the preset engine only) @@ -406,12 +403,48 @@ void EngineManagerPrivate::updateEngineChooserVisibility() } } -void EngineManager::registerEngine(DebuggerEngine *engine) +void EngineManagerPrivate::updatePerspectives() +{ + d->updateEngineChooserVisibility(); + + Perspective *current = DebuggerMainWindow::currentPerspective(); + if (!current) { + return; + } + + m_engineModel.rootItem()->forFirstLevelChildren([this, current](EngineItem *engineItem) { + if (engineItem == m_currentItem) + return; + + bool shouldBeActive = false; + if (engineItem->m_engine) { + // Normal engine. + shouldBeActive = engineItem->m_engine->perspective()->isCurrent(); + } else { + // Preset. + shouldBeActive = current->id() == Debugger::Constants::PRESET_PERSPECTIVE_ID; + } + + if (shouldBeActive && engineItem != m_currentItem) + activateEngineItem(engineItem); + }); +} + +QString EngineManager::registerEngine(DebuggerEngine *engine) { auto engineItem = new EngineItem; engineItem->m_engine = engine; d->m_engineModel.rootItem()->appendChild(engineItem); d->updateEngineChooserVisibility(); + return QString::number(d->m_engineModel.rootItem()->childCount()); +} + +void EngineManager::unregisterEngine(DebuggerEngine *engine) +{ + EngineItem *engineItem = d->findEngineItem(engine); + QTC_ASSERT(engineItem, return); + d->m_engineModel.destroyItem(engineItem); + d->updateEngineChooserVisibility(); } void EngineManager::activateDebugMode() @@ -434,33 +467,6 @@ void EngineManager::deactivateDebugMode() } } -bool EngineManager::isLastOf(const QString &type) -{ - int count = 0; - d->m_engineModel.rootItem()->forFirstLevelChildren([&](EngineItem *engineItem) { - if (engineItem && engineItem->m_engine) - count += (engineItem->m_engine->debuggerName() == type); - }); - return count == 1; -} - -void EngineManager::unregisterEngine(DebuggerEngine *engine) -{ - if (ModeManager::currentModeId() == Constants::MODE_DEBUG) { - if (Perspective *parent = Perspective::findPerspective(Constants::PRESET_PERSPECTIVE_ID)) - parent->select(); - } - - d->activateEngineItem(d->m_engineModel.rootItem()->childAt(0)); // Preset. - - // Could be that the run controls died before it was appended. - if (auto engineItem = d->findEngineItem(engine)) - d->m_engineModel.destroyItem(engineItem); - - d->updateEngineChooserVisibility(); - emit theEngineManager->currentEngineChanged(); -} - QList> EngineManager::engines() { QList> result; @@ -476,5 +482,18 @@ QPointer EngineManager::currentEngine() return d->m_currentItem ? d->m_currentItem->m_engine : nullptr; } +bool EngineManager::shutDown() +{ + d->m_shuttingDown = true; + bool anyEngineAborting = false; + for (DebuggerEngine *engine : EngineManager::engines()) { + if (engine && engine->state() != Debugger::DebuggerNotReady) { + engine->abortDebugger(); + anyEngineAborting = true; + } + } + return anyEngineAborting; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/enginemanager.h b/src/plugins/debugger/enginemanager.h index a1ee76c60b9..08549ba711e 100644 --- a/src/plugins/debugger/enginemanager.h +++ b/src/plugins/debugger/enginemanager.h @@ -45,17 +45,19 @@ public: static EngineManager *instance(); static QAbstractItemModel *model(); - static void registerEngine(DebuggerEngine *engine); + static QString registerEngine(DebuggerEngine *engine); static void unregisterEngine(DebuggerEngine *engine); - static void activateEngine(DebuggerEngine *engine); + static void activateDebugMode(); static void deactivateDebugMode(); - static bool isLastOf(const QString &type); static QList > engines(); static QPointer currentEngine(); static QWidget *engineChooser(); + static void updatePerspectives(); + + static bool shutDown(); // Return true if some engine is being forced to shut down. signals: void engineStateChanged(DebuggerEngine *engine); diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 8d2af5008a7..796416374ae 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -2953,7 +2953,7 @@ void GdbEngine::handleThreadInfo(const DebuggerResponse &response) if (response.resultClass == ResultDone) { ThreadsHandler *handler = threadsHandler(); handler->setThreads(response.data); - updateState(false); // Adjust Threads combobox. + updateState(); // Adjust Threads combobox. if (boolSetting(ShowThreadNames)) { runCommand({"threadnames " + action(MaximalStackDepth)->value().toString(), Discardable, CB(handleThreadNames)}); @@ -2993,7 +2993,7 @@ void GdbEngine::handleThreadNames(const DebuggerResponse &response) thread.name = decodeData(name["value"].data(), name["valueencoded"].data()); handler->updateThread(thread); } - updateState(false); + updateState(); } }