diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp index 1c241b432e6..c3f79374cba 100644 --- a/src/libs/utils/fancymainwindow.cpp +++ b/src/libs/utils/fancymainwindow.cpp @@ -25,6 +25,7 @@ static const char ShowCentralWidgetKey[] = "ShowCentralWidget"; static const char StateKey[] = "State"; +static const char HiddenDockAreasKey[] = "HiddenDockAreas"; static const int settingsVersion = 2; static const char dockWidgetActiveState[] = "DockWidgetActiveState"; @@ -37,12 +38,16 @@ struct FancyMainWindowPrivate { FancyMainWindowPrivate(FancyMainWindow *parent); + QVariantHash hiddenDockAreasToHash() const; + void restoreHiddenDockAreasFromHash(const QVariantHash &hash); + FancyMainWindow *q; bool m_handleDockVisibilityChanges; QAction m_showCentralWidget; QAction m_menuSeparator1; QAction m_resetLayoutAction; + QHash> m_hiddenAreas; // Qt::DockWidgetArea -> dock widgets }; class DockWidget : public QDockWidget @@ -217,12 +222,7 @@ public: : QSize(titleMinWidth, titleInactiveHeight); } - QList docksInArea() const - { - return filtered(q->q->dockWidgets(), [this, area = q->q->dockWidgetArea(q)](QDockWidget *w) { - return w->isVisible() && q->q->dockWidgetArea(w) == area; - }); - } + QList docksInArea() const { return q->q->docksInArea(q->q->dockWidgetArea(q)); } bool supportsCollapse() const { @@ -428,11 +428,50 @@ FancyMainWindowPrivate::FancyMainWindowPrivate(FancyMainWindow *parent) }); } +QVariantHash FancyMainWindowPrivate::hiddenDockAreasToHash() const +{ + QHash hash; + for (auto it = m_hiddenAreas.constKeyValueBegin(); it != m_hiddenAreas.constKeyValueEnd(); + ++it) { + hash.insert(QString::number(it->first), + transform(it->second, [](QDockWidget *w) { return w->objectName(); })); + } + return hash; +} + +void FancyMainWindowPrivate::restoreHiddenDockAreasFromHash(const QVariantHash &hash) +{ + m_hiddenAreas.clear(); + const QList docks = q->dockWidgets(); + for (auto it = hash.constKeyValueBegin(); it != hash.constKeyValueEnd(); ++it) { + bool ok; + const int area = it->first.toInt(&ok); + if (!ok + || (area != Qt::LeftDockWidgetArea && area != Qt::TopDockWidgetArea + && area != Qt::RightDockWidgetArea && area != Qt::BottomDockWidgetArea)) { + continue; + } + QList hiddenDocks; + const QStringList names = it->second.toStringList(); + for (const QString &name : names) { + QDockWidget *dock = findOrDefault(docks, [name](QDockWidget *w) { + return w->objectName() == name; + }); + if (dock) + hiddenDocks.append(dock); + } + if (!hiddenDocks.isEmpty()) + m_hiddenAreas.insert(area, hiddenDocks); + } +} + FancyMainWindow::FancyMainWindow(QWidget *parent) : QMainWindow(parent), d(new FancyMainWindowPrivate(this)) { - connect(&d->m_resetLayoutAction, &QAction::triggered, - this, &FancyMainWindow::resetLayout); + connect(&d->m_resetLayoutAction, &QAction::triggered, this, [this] { + d->m_hiddenAreas.clear(); + emit resetLayout(); + }); } FancyMainWindow::~FancyMainWindow() @@ -462,18 +501,18 @@ QDockWidget *FancyMainWindow::addDockForWidget(QWidget *widget, bool immutable) dockWidget->setProperty(dockWidgetActiveState, true); - connect(dockWidget, - &QDockWidget::dockLocationChanged, - this, - &FancyMainWindow::dockWidgetsChanged); - connect(dockWidget, - &QDockWidget::topLevelChanged, - this, - &FancyMainWindow::dockWidgetsChanged); - connect(dockWidget, - &QDockWidget::visibilityChanged, - this, - &FancyMainWindow::dockWidgetsChanged); + const auto handleDockWidgetChanged = [this, dockWidget] { + // If the dock moved to an area that was hidden, unhide the area. + const Qt::DockWidgetArea area = dockWidgetArea(dockWidget); + if (dockWidget->isVisible() && !dockWidget->isFloating() + && d->m_hiddenAreas.contains(area)) { + setDockAreaVisible(area, true); + } + emit dockWidgetsChanged(); + }; + connect(dockWidget, &QDockWidget::dockLocationChanged, this, handleDockWidgetChanged); + connect(dockWidget, &QDockWidget::topLevelChanged, this, handleDockWidgetChanged); + connect(dockWidget, &QDockWidget::visibilityChanged, this, handleDockWidgetChanged); } return dockWidget; @@ -547,6 +586,7 @@ QHash FancyMainWindow::saveSettings() const settings.insert(keyFromString(dockWidget->objectName()), dockWidget->property(dockWidgetActiveState)); } + settings.insert(HiddenDockAreasKey, d->hiddenDockAreasToHash()); return settings; } @@ -562,6 +602,7 @@ void FancyMainWindow::restoreSettings(const QHash &settings) widget->setProperty(dockWidgetActiveState, settings.value(keyFromString(widget->objectName()), false)); } + d->restoreHiddenDockAreasFromHash(settings.value(HiddenDockAreasKey).toHash()); } bool FancyMainWindow::restoreFancyState(const QByteArray &state, int version) @@ -592,6 +633,13 @@ const QList FancyMainWindow::dockWidgets() const return result; } +QList FancyMainWindow::docksInArea(Qt::DockWidgetArea area) const +{ + return filtered(dockWidgets(), [this, area](QDockWidget *w) { + return w->isVisible() && !w->isFloating() && dockWidgetArea(w) == area; + }); +} + bool FancyMainWindow::isCentralWidgetShown() const { return d->m_showCentralWidget.isChecked(); @@ -602,6 +650,37 @@ void FancyMainWindow::showCentralWidget(bool on) d->m_showCentralWidget.setChecked(on); } +void FancyMainWindow::setDockAreaVisible(Qt::DockWidgetArea area, bool visible) +{ + if (visible) { + const QList docks = d->m_hiddenAreas.value(area); + for (QDockWidget *w : docks) + w->setVisible(true); + d->m_hiddenAreas.remove(area); + } else { + const QList docks = docksInArea(area); + if (!docks.isEmpty()) { + d->m_hiddenAreas.insert(area, docks); + for (QDockWidget *w : docks) + w->setVisible(false); + } + } +} + +bool FancyMainWindow::isDockAreaVisible(Qt::DockWidgetArea area) const +{ + if (d->m_hiddenAreas.contains(area)) + return false; + return !docksInArea(area).isEmpty(); +} + +bool FancyMainWindow::isDockAreaAvailable(Qt::DockWidgetArea area) const +{ + if (d->m_hiddenAreas.contains(area)) + return true; + return !docksInArea(area).isEmpty(); +} + void FancyMainWindow::addDockActionsToMenu(QMenu *menu) { QList actions; diff --git a/src/libs/utils/fancymainwindow.h b/src/libs/utils/fancymainwindow.h index df9f2c1281e..3762b7b6e5e 100644 --- a/src/libs/utils/fancymainwindow.h +++ b/src/libs/utils/fancymainwindow.h @@ -26,6 +26,7 @@ public: * which will then be used as key for QSettings. */ QDockWidget *addDockForWidget(QWidget *widget, bool immutable = false); const QList dockWidgets() const; + QList docksInArea(Qt::DockWidgetArea area) const; void setTrackingEnabled(bool enabled); @@ -44,6 +45,10 @@ public: bool isCentralWidgetShown() const; void showCentralWidget(bool on); + void setDockAreaVisible(Qt::DockWidgetArea area, bool visible); + bool isDockAreaVisible(Qt::DockWidgetArea area) const; + bool isDockAreaAvailable(Qt::DockWidgetArea area) const; + signals: // Emitted by resetLayoutAction(). Connect to a slot // restoring the default layout. diff --git a/src/plugins/coreplugin/designmode.cpp b/src/plugins/coreplugin/designmode.cpp index 3b5e5fa41f7..728edc25dc0 100644 --- a/src/plugins/coreplugin/designmode.cpp +++ b/src/plugins/coreplugin/designmode.cpp @@ -14,6 +14,10 @@ #include +#include + +#include + #include #include #include @@ -198,6 +202,11 @@ void DesignMode::setActiveContext(const Context &context) d->m_activeContext = context; } +Utils::FancyMainWindow *DesignMode::mainWindow() +{ + return Aggregation::query(d->m_stackWidget->currentWidget()); +} + void DesignMode::createModeIfRequired() { if (d) { diff --git a/src/plugins/coreplugin/designmode.h b/src/plugins/coreplugin/designmode.h index da9a6f70ab6..82b00abb2b6 100644 --- a/src/plugins/coreplugin/designmode.h +++ b/src/plugins/coreplugin/designmode.h @@ -44,6 +44,8 @@ private: void currentEditorChanged(IEditor *editor); void updateContext(Utils::Id newMode, Utils::Id oldMode); void setActiveContext(const Context &context); + + Utils::FancyMainWindow *mainWindow() override; }; } // namespace Core diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 354ce144e13..ca92b3b9c6c 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -1343,8 +1343,7 @@ NavigationWidget *ICorePrivate::navigationWidget(Side side) const void ICorePrivate::setSidebarVisible(bool visible, Side side) { - if (NavigationWidgetPlaceHolder::current(side)) - navigationWidget(side)->setShown(visible); + navigationWidget(side)->setShown(visible); } ICorePrivate::~ICorePrivate() diff --git a/src/plugins/coreplugin/imode.cpp b/src/plugins/coreplugin/imode.cpp index 4cd530280ca..f143459cf6b 100644 --- a/src/plugins/coreplugin/imode.cpp +++ b/src/plugins/coreplugin/imode.cpp @@ -5,6 +5,10 @@ #include "modemanager.h" +#include + +#include + namespace Core { /*! @@ -111,6 +115,11 @@ void IMode::setEnabled(bool enabled) emit enabledStateChanged(m_isEnabled); } +Utils::FancyMainWindow *IMode::mainWindow() +{ + return Aggregation::query(widget()); +} + bool IMode::isEnabled() const { return m_isEnabled; diff --git a/src/plugins/coreplugin/imode.h b/src/plugins/coreplugin/imode.h index 0cdedd9bacf..339d0ccf84a 100644 --- a/src/plugins/coreplugin/imode.h +++ b/src/plugins/coreplugin/imode.h @@ -10,6 +10,10 @@ #include #include +namespace Utils { +class FancyMainWindow; +} + namespace Core { class CORE_EXPORT IMode : public IContext @@ -39,6 +43,8 @@ public: void setId(Utils::Id id) { m_id = id; } void setMenu(QMenu *menu) { m_menu = menu; } + virtual Utils::FancyMainWindow *mainWindow(); + signals: void enabledStateChanged(bool enabled); diff --git a/src/plugins/coreplugin/navigationwidget.cpp b/src/plugins/coreplugin/navigationwidget.cpp index d2b1dafcd51..b6961f8612a 100644 --- a/src/plugins/coreplugin/navigationwidget.cpp +++ b/src/plugins/coreplugin/navigationwidget.cpp @@ -7,16 +7,22 @@ #include "coreplugintr.h" #include "icontext.h" #include "icore.h" +#include "imode.h" #include "inavigationwidgetfactory.h" #include "modemanager.h" #include "navigationsubwidget.h" +#include +#include #include #include +#include + #include #include #include +#include #include #include #include @@ -107,7 +113,7 @@ void NavigationWidgetPlaceHolder::currentModeAboutToChange(Id mode) setCurrent(m_side, nullptr); navigationWidget->setParent(nullptr); navigationWidget->hide(); - navigationWidget->placeHolderChanged(nullptr); + navigationWidget->placeHolderChanged(); } if (m_mode == mode) { @@ -118,7 +124,7 @@ void NavigationWidgetPlaceHolder::currentModeAboutToChange(Id mode) applyStoredSize(); setVisible(navigationWidget->isShown()); - navigationWidget->placeHolderChanged(this); + navigationWidget->placeHolderChanged(); } } @@ -142,6 +148,7 @@ struct NavigationWidgetPrivate QHash m_actionMap; QHash m_commandMap; QStandardItemModel *m_factoryModel; + FancyMainWindow *m_mainWindow = nullptr; bool m_shown; int m_width; @@ -185,6 +192,11 @@ NavigationWidget::NavigationWidget(QAction *toggleSideBarAction, Side side) : NavigationWidgetPrivate::s_instanceLeft = this; else NavigationWidgetPrivate::s_instanceRight = this; + + connect(ModeManager::instance(), + &ModeManager::currentModeChanged, + this, + &NavigationWidget::updateMode); } NavigationWidget::~NavigationWidget() @@ -243,7 +255,7 @@ void NavigationWidget::setFactories(const QList &fac d->m_factoryModel->appendRow(newRow); } d->m_factoryModel->sort(0); - updateToggleText(); + updateToggleAction(); } Key NavigationWidget::settingsGroup() const @@ -261,23 +273,41 @@ QAbstractItemModel *NavigationWidget::factoryModel() const return d->m_factoryModel; } -void NavigationWidget::updateToggleText() +void NavigationWidget::updateMode() { - bool haveData = d->m_factoryModel->rowCount(); - d->m_toggleSideBarAction->setVisible(haveData); - d->m_toggleSideBarAction->setEnabled(haveData && NavigationWidgetPlaceHolder::current(d->m_side)); + IMode *currentMode = ModeManager::currentMode(); + FancyMainWindow *mainWindow = currentMode ? currentMode->mainWindow() : nullptr; + if (d->m_mainWindow == mainWindow) + return; + if (d->m_mainWindow) + disconnect(d->m_mainWindow, nullptr, this, nullptr); + d->m_mainWindow = mainWindow; + if (d->m_mainWindow) + connect(d->m_mainWindow, + &FancyMainWindow::dockWidgetsChanged, + this, + &NavigationWidget::updateToggleAction); + updateToggleAction(); +} - const char *trToolTip = d->m_side == Side::Left - ? (isShown() ? Constants::TR_HIDE_LEFT_SIDEBAR : Constants::TR_SHOW_LEFT_SIDEBAR) - : (isShown() ? Constants::TR_HIDE_RIGHT_SIDEBAR : Constants::TR_SHOW_RIGHT_SIDEBAR); +void NavigationWidget::updateToggleAction() +{ + d->m_toggleSideBarAction->setVisible(toggleActionVisible()); + d->m_toggleSideBarAction->setEnabled(toggleActionEnabled()); + d->m_toggleSideBarAction->setChecked(toggleActionChecked()); + const char *trToolTip = d->m_side == Side::Left ? (d->m_toggleSideBarAction->isChecked() + ? Constants::TR_HIDE_LEFT_SIDEBAR + : Constants::TR_SHOW_LEFT_SIDEBAR) + : (d->m_toggleSideBarAction->isChecked() + ? Constants::TR_HIDE_RIGHT_SIDEBAR + : Constants::TR_SHOW_RIGHT_SIDEBAR); d->m_toggleSideBarAction->setToolTip(Tr::tr(trToolTip)); } -void NavigationWidget::placeHolderChanged(NavigationWidgetPlaceHolder *holder) +void NavigationWidget::placeHolderChanged() { - d->m_toggleSideBarAction->setChecked(holder && isShown()); - updateToggleText(); + updateToggleAction(); } void NavigationWidget::resizeEvent(QResizeEvent *re) @@ -376,6 +406,37 @@ void NavigationWidget::closeSubWidget(Internal::NavigationSubWidget *subWidget) } } +bool NavigationWidget::toggleActionVisible() const +{ + const bool haveData = d->m_factoryModel->rowCount(); + return haveData || d->m_mainWindow; +} + +static Qt::DockWidgetArea dockAreaForSide(Side side) +{ + return side == Side::Left ? Qt::LeftDockWidgetArea : Qt::RightDockWidgetArea; +} + +bool NavigationWidget::toggleActionEnabled() const +{ + const bool haveData = d->m_factoryModel->rowCount(); + if (haveData && NavigationWidgetPlaceHolder::current(d->m_side)) + return true; + if (!d->m_mainWindow) + return false; + return d->m_mainWindow->isDockAreaAvailable(dockAreaForSide(d->m_side)); +} + +bool NavigationWidget::toggleActionChecked() const +{ + const bool haveData = d->m_factoryModel->rowCount(); + if (haveData && NavigationWidgetPlaceHolder::current(d->m_side)) + return d->m_shown; + if (!d->m_mainWindow) + return false; + return d->m_mainWindow->isDockAreaVisible(dockAreaForSide(d->m_side)); +} + static QString defaultFirstView(Side side) { return side == Side::Left ? QString("Projects") : QString("Outline"); @@ -498,19 +559,22 @@ void NavigationWidget::closeSubWidgets() void NavigationWidget::setShown(bool b) { - if (d->m_shown == b) - return; - bool haveData = d->m_factoryModel->rowCount(); - d->m_shown = b; NavigationWidgetPlaceHolder *current = NavigationWidgetPlaceHolder::current(d->m_side); - if (current) { - bool visible = d->m_shown && haveData; - current->setVisible(visible); - d->m_toggleSideBarAction->setChecked(visible); + if (!current && d->m_mainWindow) { + // mode without placeholder but with main window + d->m_mainWindow->setDockAreaVisible(dockAreaForSide(d->m_side), b); } else { - d->m_toggleSideBarAction->setChecked(false); + // mode with navigation widget placeholder or e.g. during startup/settings restore + if (d->m_shown == b) + return; + const bool haveData = d->m_factoryModel->rowCount(); + d->m_shown = b; + if (current) { + const bool visible = d->m_shown && haveData; + current->setVisible(visible); + } } - updateToggleText(); + updateToggleAction(); } bool NavigationWidget::isShown() const diff --git a/src/plugins/coreplugin/navigationwidget.h b/src/plugins/coreplugin/navigationwidget.h index 40ea1a5fa0c..c043456b6ae 100644 --- a/src/plugins/coreplugin/navigationwidget.h +++ b/src/plugins/coreplugin/navigationwidget.h @@ -86,7 +86,7 @@ public: int storedWidth(); // Called from the place holders - void placeHolderChanged(NavigationWidgetPlaceHolder *holder); + void placeHolderChanged(); QHash commandMap() const; QAbstractItemModel *factoryModel() const; @@ -96,7 +96,11 @@ protected: private: void closeSubWidget(Internal::NavigationSubWidget *subWidget); - void updateToggleText(); + bool toggleActionVisible() const; + bool toggleActionEnabled() const; + bool toggleActionChecked() const; + void updateMode(); + void updateToggleAction(); Internal::NavigationSubWidget *insertSubItem(int position, int factoryIndex, bool updateActivationsMap = true); diff --git a/src/plugins/designer/formeditor.cpp b/src/plugins/designer/formeditor.cpp index 205c6120a82..57075e7ecc2 100644 --- a/src/plugins/designer/formeditor.cpp +++ b/src/plugins/designer/formeditor.cpp @@ -33,6 +33,8 @@ #include #include +#include + #include #include #include @@ -405,6 +407,10 @@ void FormEditorData::fullInit() m_modeWidget = new QWidget; m_modeWidget->setObjectName("DesignerModeWidget"); + // make the editor widget (the dockable widget) accessible via the mode widget + auto agg = new Aggregation::Aggregate; + agg->add(m_modeWidget); + agg->add(m_editorWidget); auto layout = new QVBoxLayout(m_modeWidget); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0);