123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "QtViewPaneManager.h"
- #include "Controls/ConsoleSCB.h"
- #include <AzQtComponents/Components/FancyDocking.h>
- #include <AzQtComponents/Components/Titlebar.h>
- #include <AzQtComponents/Components/Widgets/Card.h>
- #include <QDockWidget>
- #include <QMainWindow>
- #include <QDataStream>
- #include <QDebug>
- #include <QCloseEvent>
- #include <QLayout>
- #include <QApplication>
- #include <QRect>
- #include <QDesktopWidget>
- #include <QMessageBox>
- #include <QRubberBand>
- #include <QCursor>
- #include <QTimer>
- #include <QGraphicsOpacityEffect>
- #include "MainWindow.h"
- #include <algorithm>
- #include <QScopedValueRollback>
- #include <AzFramework/API/ApplicationAPI.h>
- #include <AzAssetBrowser/AzAssetBrowserWindow.h>
- #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
- #include <AzQtComponents/Utilities/AutoSettingsGroup.h>
- #include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
- #include <AzToolsFramework/UI/Docking/DockWidgetUtils.h>
- #include <AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx>
- #include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
- #include <AzToolsFramework/Viewport/ViewportMessages.h>
- #include <AzQtComponents/Buses/ShortcutDispatch.h>
- #include <AzQtComponents/Utilities/QtViewPaneEffects.h>
- #include <AzQtComponents/Components/StyleManager.h>
- #include <AzCore/UserSettings/UserSettingsComponent.h>
- // Helper for EditorComponentModeNotifications to be used
- // as a member instead of inheriting from EBus directly.
- class ViewportEditorModeNotificationsBusImpl
- : public AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
- {
- public:
- // Set the function to be called when entering ComponentMode.
- void SetEnteredComponentModeFunc(
- const AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)>& enteredComponentModeFunc)
- {
- m_enteredComponentModeFunc = enteredComponentModeFunc;
- }
- // Set the function to be called when leaving ComponentMode.
- void SetLeftComponentModeFunc(
- const AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)>& leftComponentModeFunc)
- {
- m_leftComponentModeFunc = leftComponentModeFunc;
- }
- private:
- // ViewportEditorModeNotificationsBus overrides ...
- void OnEditorModeActivated(
- const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
- {
- if (mode == AzToolsFramework::ViewportEditorMode::Component)
- {
- m_enteredComponentModeFunc(editorModeState);
- }
- }
- void OnEditorModeDeactivated(
- const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override
- {
- if (mode == AzToolsFramework::ViewportEditorMode::Component)
- {
- m_leftComponentModeFunc(editorModeState);
- }
- }
- AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)> m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode.
- AZStd::function<void(const AzToolsFramework::ViewportEditorModesInterface&)> m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode.
- };
- struct ViewLayoutState
- {
- QVector<QString> viewPanes;
- QByteArray mainWindowState;
- QMap<QString, QRect> fakeDockWidgetGeometries;
- };
- Q_DECLARE_METATYPE(ViewLayoutState)
- static QDataStream &operator<<(QDataStream & out, const ViewLayoutState&myObj)
- {
- int placeHolderVersion = 1;
- out << myObj.viewPanes << myObj.mainWindowState << placeHolderVersion << myObj.fakeDockWidgetGeometries;
- return out;
- }
- static QDataStream& operator>>(QDataStream& in, ViewLayoutState& myObj)
- {
- in >> myObj.viewPanes;
- in >> myObj.mainWindowState;
- int version = 0;
- if (!in.atEnd())
- {
- in >> version;
- in >> myObj.fakeDockWidgetGeometries;
- }
- return in;
- }
- // All settings keys for stored layouts are in the form "layouts/<name>"
- // When starting up, "layouts/last" is loaded
- static QLatin1String s_lastLayoutName = QLatin1String("last");
- static QString GetFancyViewPaneStateGroupName()
- {
- return QString("%1/%2").arg("Editor").arg("fancyWindowLayouts");
- }
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- // this event filter class eats mouse events
- // it is used in the non dockable fake dock widget
- // to make sure its inner title bar cannot be dragged
- class MouseEatingEventFilter : public QObject
- {
- public:
- MouseEatingEventFilter(QObject* parent)
- : QObject(parent)
- {
- }
- protected:
- bool eventFilter(QObject*, QEvent* event) override
- {
- switch (event->type())
- {
- case QEvent::MouseButtonPress:
- case QEvent::MouseButtonRelease:
- case QEvent::MouseButtonDblClick:
- case QEvent::MouseMove:
- return true;
- default:
- return false;
- }
- }
- };
- #endif
- Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance)
- QWidget* QtViewPane::CreateWidget()
- {
- QWidget* w = nullptr;
- if (m_factoryFunc)
- {
- // Although all the factory lambdas do have a default nullptr argument, this information
- // doesn't get retained when they are converted to std::function<QWidget*(QWidget*)>,
- // thus we need to set the parent explicitly.
- // At the same time, adding a default argument to the lambdas will allow for them to be
- // called exactly as before in all other places where they are not converted, so we get
- // to explicitly pass an argument only if strictly necessary.
- w = m_factoryFunc(nullptr);
- }
- else
- {
- // If a view pane was registered using RegisterCustomViewPane, then instead of a factory function, we rely
- // on ViewPaneCallbackBus::CreateViewPaneWidget to create the widget for us and then pass back the Qt windowId
- // so that we can retrieve it.
- AZ::u64 createdWidgetWinId;
- AzToolsFramework::ViewPaneCallbackBus::EventResult(createdWidgetWinId, m_name.toUtf8().constData(), &AzToolsFramework::ViewPaneCallbacks::CreateViewPaneWidget);
- w = QWidget::find(createdWidgetWinId);
- }
- return w;
- }
- bool QtViewPane::Close(QtViewPane::CloseModes closeModes)
- {
- if (!IsConstructed())
- {
- return true;
- }
- return CloseInstance(m_dockWidget, closeModes);
- }
- bool QtViewPane::CloseInstance(QDockWidget* dockWidget, CloseModes closeModes)
- {
- if (!dockWidget)
- {
- return false;
- }
- bool canClose = true;
- bool destroy = closeModes & CloseMode::Destroy;
- // Console is not deletable, so always hide it instead of destroying
- if (!m_options.isDeletable)
- {
- destroy = false;
- }
- if (!(closeModes & CloseMode::Force))
- {
- QCloseEvent closeEvent;
- // Prevent closing view pane if it has modal dialog open, as modal dialogs
- // are often constructed on stack and will not finish properly when the view
- // pane is destroyed.
- QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
- const int numTopLevel = topLevelWidgets.size();
- for (size_t i = 0; i < numTopLevel; ++i)
- {
- QWidget* widget = topLevelWidgets[static_cast<int>(i)];
- if (widget->isModal() && widget->isVisible())
- {
- widget->activateWindow();
- return false;
- }
- }
- // Check if embedded QWidget allows view pane to be closed.
- QCoreApplication::sendEvent(dockWidget->widget(), &closeEvent);
- // If widget accepted the close event, we delete the dockwidget, which will also delete the child widget in case it doesn't have Qt::WA_DeleteOnClose
- if (!closeEvent.isAccepted())
- {
- // Widget doesn't want to close
- canClose = false;
- }
- }
- if (canClose)
- {
- if (destroy)
- {
- //important to set parent to null otherwise docking code will still find it while restoring since that happens before the delete.
- dockWidget->setParent(nullptr);
- dockWidget->deleteLater();
- if (dockWidget == m_dockWidget)
- {
- //clear dockwidget pointer otherwise if we open this pane before the delete happens we'll think it's already there, then it gets deleted on us.
- m_dockWidget.clear();
- }
- }
- else
- {
- // If the dock widget is tabbed, then just remove it from the tab widget
- AzQtComponents::DockTabWidget* tabWidget = AzQtComponents::DockTabWidget::ParentTabWidget(dockWidget);
- if (tabWidget)
- {
- tabWidget->removeTab(dockWidget);
- }
- // Otherwise just hide the widget
- else
- {
- dockWidget->hide();
- }
- }
- AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEventsBus::Handler::OnViewPaneClosed, m_name.toUtf8().data());
- }
- return canClose;
- }
- static bool SkipTitleBarOverdraw(QtViewPane* pane)
- {
- return !pane->m_options.isDockable;
- }
- DockWidget::DockWidget(QWidget* widget, QtViewPane* pane, [[maybe_unused]] QSettings* settings, QMainWindow* parent, AzQtComponents::FancyDocking* advancedDockManager)
- : AzQtComponents::StyledDockWidget(pane->m_name, SkipTitleBarOverdraw(pane),
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- pane->m_options.detachedWindow ? nullptr : parent)
- #else
- parent)
- #endif
- , m_mainWindow(parent)
- , m_pane(pane)
- , m_advancedDockManager(advancedDockManager)
- {
- // keyboard shortcuts from any other context shouldn't trigger actions under this dock widget
- AzQtComponents::MarkAsShortcutSearchBreak(this);
- if (pane->m_options.isDeletable)
- {
- setAttribute(Qt::WA_DeleteOnClose);
- }
- QString objectNameForSave = pane->m_options.saveKeyName.length() > 0 ? pane->m_options.saveKeyName : pane->m_name;
- setObjectName(objectNameForSave);
- setWidget(widget);
- setFocusPolicy(Qt::StrongFocus);
- setAttribute(Qt::WA_Hover, true);
- setMouseTracking(true);
- }
- bool DockWidget::event(QEvent* qtEvent)
- {
- // this accounts for a difference in behavior where we want all floating windows to be always parented to the main window instead of to each other, so that
- // they don't overlap in odd ways - for example, if you tear off a floating window from another floating window, under Qt's system its technically still a child of that window
- // so that window can't ever be placed on top of it. This is not what we want. We want you to be able to then take that window and drag it into this new one.
- // (Qt's original behavior is like that so if you double click on a floating widget it docks back into the parent which it came from - we don't use this functionality)
- if (qtEvent->type() == QEvent::WindowActivate
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- && !m_pane->m_options.detachedWindow
- #endif
- )
- {
- reparentToMainWindowFix();
- }
- if (qtEvent->type() == QEvent::Close)
- {
- // Wait one frame so that the pane's state is propagated correctly.
- QTimer::singleShot(0, this, [pane = m_pane]()
- {
- AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEventsBus::Handler::OnViewPaneClosed, pane->m_name.toUtf8().data());
- }
- );
- }
- return AzQtComponents::StyledDockWidget::event(qtEvent);
- }
- void DockWidget::reparentToMainWindowFix()
- {
- if (!isFloating() || !AzToolsFramework::DockWidgetUtils::isDockWidgetWindowGroup(parentWidget()))
- {
- return;
- }
- if (qApp->mouseButtons() & Qt::LeftButton)
- {
- // We're still dragging, lets try later
- QTimer::singleShot(200, this, &DockWidget::reparentToMainWindowFix);
- return;
- }
- // bump it up and to the left by the size of its frame, to account for the reparenting operation;
- QPoint framePos = pos();
- QPoint contentPos = mapToGlobal(QPoint(0, 0));
- move(framePos.x() - (contentPos.x() - framePos.x()), framePos.y() - (contentPos.y() - framePos.y()));
- // we have to dock this to the mainwindow, even if we're floating, so that the mainwindow knows about it.
- // if the preferred area is valid, use that. Otherwise, arbitrarily toss it in the left.
- // This is relevant because it will determine where the widget goes if the title bar is double clicked
- // after it's been detached from a QDockWidgetGroupWindow
- auto dockArea = (m_pane->m_options.preferedDockingArea != Qt::DockWidgetArea::NoDockWidgetArea) ? m_pane->m_options.preferedDockingArea : Qt::LeftDockWidgetArea;
- setParent(m_mainWindow);
- m_mainWindow->addDockWidget(dockArea, this);
- setFloating(true);
- }
- QString DockWidget::PaneName() const
- {
- return m_pane->m_name;
- }
- void DockWidget::RestoreState(bool forceDefault)
- {
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- if (m_pane->m_options.detachedWindow)
- {
- if (forceDefault)
- {
- window()->setGeometry(m_pane->m_options.paneRect);
- }
- else
- {
- QRect geometry = QtViewPaneManager::instance()->GetLayout().fakeDockWidgetGeometries[objectName()];
- if (!geometry.isValid())
- {
- geometry = m_pane->m_options.paneRect;
- }
- window()->setGeometry(geometry);
- }
- return;
- }
- #endif
- // check if we can get the main window to do all the work for us first
- // (which is also the proper way to do this)
- if (!forceDefault)
- {
- // If the advanced docking is enabled, let it try to restore the dock widget
- bool restored = false;
- if (m_advancedDockManager)
- {
- restored = m_advancedDockManager->restoreDockWidget(this);
- }
- // Otherwise, let our main window do it directly
- else
- {
- restored = m_mainWindow->restoreDockWidget(this);
- }
- if (restored)
- {
- AzToolsFramework::DockWidgetUtils::correctVisibility(this);
- return;
- }
- }
- // can't rely on the main window; fall back to our preferences
- auto dockingArea = m_pane->m_options.preferedDockingArea;
- auto paneRect = m_pane->m_options.paneRect;
- // If we are floating and have multiple instances, try to calculate an appropriate rect from the most recently created, non-docked instance
- // make sure the new location would be reasonable on screen - otherwise use default paneRect for new widget positioning
- if (dockingArea == Qt::NoDockWidgetArea && m_pane->m_dockWidgetInstances.size() > 1)
- {
- static const int horizontalCascadeAmount = 20;
- static const int verticalCascadeAmount = 20;
- static const int lowerScreenEdgeBuffer = 50;
- QRect screenRect = QApplication::primaryScreen()->geometry();
- int screenHeight = screenRect.height();
- int screenWidth = screenRect.width();
- for (QList<DockWidget*>::reverse_iterator it = m_pane->m_dockWidgetInstances.rbegin(); it != m_pane->m_dockWidgetInstances.rend(); ++it)
- {
- DockWidget* dock = *it;
- if (dock != this)
- {
- QMainWindow* mainWindow = qobject_cast<QMainWindow*>(dock->parentWidget());
- if (mainWindow && mainWindow->parentWidget())
- {
- QPoint windowLocation = mainWindow->parentWidget()->mapToGlobal(QPoint(0, 0));
- // Only nudge it to the right if we have room to do so
- if (windowLocation.x() + horizontalCascadeAmount < screenWidth - paneRect.width())
- {
- paneRect.moveLeft(windowLocation.x() + horizontalCascadeAmount);
- // Keep it from getting too low on the screen
- if (windowLocation.y() + verticalCascadeAmount < screenHeight - lowerScreenEdgeBuffer)
- {
- paneRect.moveTop(windowLocation.y() + verticalCascadeAmount);
- }
- }
- // We found an undocked window, just go ahead and break, if we couldn't adjust because of positioning,
- // it will be placed at the default location
- break;
- }
- }
- }
- }
- // make sure we're sized properly before we dock
- if (paneRect.isValid())
- {
- resize(paneRect.size());
- }
- // check if we should force floating
- bool floatWidget = (dockingArea == Qt::NoDockWidgetArea);
- // if we're floating, we need to move and resize again, because the act of docking may have moved us
- if (floatWidget)
- {
- // in order for saving and restoring state to work properly in Qt,
- // along with docking widgets within other floating widgets, the widget
- // must be added at least once to the main window, with a VALID area,
- // before we set it to floating.
- auto arbitraryDockingArea = Qt::LeftDockWidgetArea;
- m_mainWindow->addDockWidget(arbitraryDockingArea, this);
- // If we are using the fancy docking, let it handle making the dock
- // widget floating, or else the titlebar will be missing, since
- // floating widgets are actually contained in a floating main
- // window container
- if (m_advancedDockManager)
- {
- m_advancedDockManager->makeDockWidgetFloating(this, paneRect);
- }
- // Otherwise, we can make the dock widget floating directly and move it
- else
- {
- setFloating(true);
- // Not using setGeometry() since it excludes the frame when positioning
- if (paneRect.isValid())
- {
- resize(paneRect.size());
- move(paneRect.topLeft());
- }
- }
- }
- else
- {
- m_mainWindow->addDockWidget(dockingArea, this);
- }
- }
- QRect DockWidget::ProperGeometry() const
- {
- QRect myGeom(pos(), size());
- // we need this state in global coordinates, but if we're parented to one of those group dock windows, there is a problem, it will be local coords.
- if (!isFloating())
- {
- if (parentWidget() && (strcmp(parentWidget()->metaObject()->className(), "QDockWidgetGroupWindow") == 0))
- {
- myGeom = QRect(parentWidget()->pos(), parentWidget()->size());
- }
- }
- return myGeom;
- }
- QString DockWidget::settingsKey() const
- {
- return settingsKey(m_pane->m_name);
- }
- QString DockWidget::settingsKey(const QString& paneName)
- {
- return QStringLiteral("ViewPane-") + paneName;
- }
- void EnableAllWidgetInstances(QList<DockWidget*>& widgetInstances, bool enable)
- {
- for (auto& dockWidget : widgetInstances)
- {
- AzQtComponents::SetWidgetInteractEnabled(dockWidget->widget(), enable);
- }
- }
- QtViewPaneManager::QtViewPaneManager(QObject* parent)
- : QObject(parent)
- , m_mainWindow(nullptr)
- , m_settings(nullptr)
- , m_restoreInProgress(false)
- , m_advancedDockManager(nullptr)
- , m_componentModeNotifications(AZStd::make_unique<ViewportEditorModeNotificationsBusImpl>())
- {
- qRegisterMetaTypeStreamOperators<ViewLayoutState>("ViewLayoutState");
- qRegisterMetaTypeStreamOperators<QVector<QString> >("QVector<QString>");
- // view pane manager is interested when we enter/exit ComponentMode
- m_componentModeNotifications->BusConnect(AzToolsFramework::GetEntityContextId());
- m_windowRequest.BusConnect();
- m_componentModeNotifications->SetEnteredComponentModeFunc(
- [this]([[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModes)
- {
- for (QtViewPane& p : m_registeredPanes)
- {
- if (p.m_options.isDisabledInComponentMode)
- {
- // By default, disable all widgets when entering Component Mode
- EnableAllWidgetInstances(p.m_dockWidgetInstances, false);
- }
- }
- });
- m_componentModeNotifications->SetLeftComponentModeFunc(
- [this]([[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModes)
- {
- for (QtViewPane& p : m_registeredPanes)
- {
- if (p.m_options.isDisabledInComponentMode)
- {
- // By default, enable all widgets again when leaving Component Mode
- EnableAllWidgetInstances(p.m_dockWidgetInstances, true);
- }
- }
- });
- m_windowRequest.SetEnableEditorUiFunc(
- [this](bool enable)
- {
- for (QtViewPane& p : m_registeredPanes)
- {
- if (p.m_options.isDisabledInImGuiMode)
- {
- // By default, disable/enable all widgets when entering/exiting IMGUI
- EnableAllWidgetInstances(p.m_dockWidgetInstances, enable);
- }
- }
- });
- }
- QtViewPaneManager::~QtViewPaneManager()
- {
- m_windowRequest.BusDisconnect();
- m_componentModeNotifications->BusDisconnect();
- }
- static bool lessThan(const QtViewPane& v1, const QtViewPane& v2)
- {
- if (v1.IsViewportPane() && v2.IsViewportPane())
- {
- // Registration order (Top, Front, Left ...)
- return v1.m_id < v2.m_id;
- }
- else if (!v1.IsViewportPane() && !v2.IsViewportPane())
- {
- // Sort by name
- return v1.m_name.compare(v2.m_name, Qt::CaseInsensitive) < 0;
- }
- else
- {
- // viewports on top of non-viewports
- return v1.IsViewportPane();
- }
- }
- void QtViewPaneManager::RegisterPane(const QString& name, const QString& category, ViewPaneFactory factory, const AzToolsFramework::ViewPaneOptions& options)
- {
- if (IsPaneRegistered(name))
- {
- return;
- }
- QtViewPane view = { NextAvailableId(), name, category, factory, nullptr, options };
- // Sorted insert
- auto it = std::upper_bound(m_registeredPanes.begin(), m_registeredPanes.end(), view, lessThan);
- m_registeredPanes.insert(it, view);
- emit registeredPanesChanged();
- }
- void QtViewPaneManager::UnregisterPane(const QString& name)
- {
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [name](const QtViewPane& pane) { return name == pane.m_name; });
- if (it != m_registeredPanes.end())
- {
- QtViewPane& pane = *it;
- ClosePane(&pane);
- m_knownIdsSet.removeOne(pane.m_id);
- m_registeredPanes.erase(it);
- emit registeredPanesChanged();
- }
- }
- QtViewPaneManager* QtViewPaneManager::instance()
- {
- return s_viewPaneManagerInstance();
- }
- bool QtViewPaneManager::exists()
- {
- return s_viewPaneManagerInstance.exists();
- }
- void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState)
- {
- Q_ASSERT(mainWindow && !m_mainWindow && settings && !m_settings);
- m_mainWindow = mainWindow;
- m_settings = settings;
- m_advancedDockManager = new AzQtComponents::FancyDocking(mainWindow);
- m_defaultMainWindowState = mainWindow->saveState();
- m_loadedMainWindowState = lastMainWindowState;
- }
- const QtViewPane* QtViewPaneManager::OpenPane(const QString& name, QtViewPane::OpenModes modes)
- {
- QtViewPane* pane = GetPane(name);
- if (!pane || !pane->IsValid())
- {
- qWarning() << Q_FUNC_INFO << "Could not find pane with name" << name;
- return nullptr;
- }
- // this multi-pane code is a bit of an hack to support more than one view of the same class
- // All views are single pane, except for one in Maglev Control plugin
- // Save/Restore support of the duplicates will only be implemented if required.
- const bool isMultiPane = modes & QtViewPane::OpenMode::MultiplePanes;
- DockWidget* newDockWidget = pane->m_dockWidget;
- if (!pane->IsVisible() || isMultiPane)
- {
- if (!pane->IsConstructed() || isMultiPane)
- {
- QWidget* w = pane->CreateWidget();
- if (!w)
- {
- qWarning() << Q_FUNC_INFO << "Unable to create widget for pane with name" << name;
- return nullptr;
- }
- w->setProperty("restored", (modes & QtViewPane::OpenMode::RestoreLayout) != 0);
- newDockWidget = new DockWidget(w, pane, m_settings, m_mainWindow, m_advancedDockManager);
- AzQtComponents::StyleManager::repolishStyleSheet(newDockWidget);
- // track every new dock widget instance that we created
- pane->m_dockWidgetInstances.push_back(newDockWidget);
- connect(newDockWidget, &QObject::destroyed, this, [this, name, newDockWidget]() {
- QtViewPane* pane = GetPane(name);
- if (pane && pane->IsValid())
- {
- pane->m_dockWidgetInstances.removeAll(newDockWidget);
- }
- AzToolsFramework::EditorEventsBus::Broadcast(
- &AzToolsFramework::EditorEventsBus::Handler::OnViewPaneClosed, pane->m_name.toUtf8().data());
- });
- // only set the single instance of the dock widget on the pane if this
- // isn't a special multi-pane instance
- if (!isMultiPane)
- {
- pane->m_dockWidget = newDockWidget;
- }
- else
- {
- m_advancedDockManager->disableAutoSaveLayout(newDockWidget);
- }
- newDockWidget->setVisible(true);
- // If this pane isn't dockable, set the allowed areas to none on the
- // dock widget so the fancy docking knows to prevent it from docking
- if (!pane->m_options.isDockable)
- {
- newDockWidget->setAllowedAreas(Qt::NoDockWidgetArea);
- }
- // only emit this signal if we're not creating a non-saving instance of the view pane
- if (!isMultiPane)
- {
- emit viewPaneCreated(pane);
- }
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- // handle showing fake dock widgets
- if (pane->m_options.detachedWindow)
- {
- ShowFakeNonDockableDockWidget(newDockWidget, pane);
- }
- #endif
- }
- else if (!AzQtComponents::DockTabWidget::IsTabbed(newDockWidget))
- {
- newDockWidget->setVisible(true);
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- if (pane->m_options.detachedWindow)
- {
- newDockWidget->window()->show();
- }
- #endif
- }
- if ((modes & QtViewPane::OpenMode::UseDefaultState) || isMultiPane)
- {
- const bool forceToDefault = true;
- newDockWidget->RestoreState(forceToDefault);
- }
- else if (!AzQtComponents::DockTabWidget::IsTabbed(newDockWidget) && !(modes & QtViewPane::OpenMode::OnlyOpen))
- {
- newDockWidget->RestoreState();
- }
- }
- // If the dock widget is off screen (e.g. second monitor was disconnected),
- // restore its default state
- if (QApplication::desktop()->screenNumber(newDockWidget) == -1)
- {
- const bool forceToDefault = true;
- newDockWidget->RestoreState(forceToDefault);
- }
- // If the widget's window is minimized, show it.
- QWidget* window = newDockWidget->window();
- if (window->isMinimized())
- {
- window->setWindowState(window->windowState() & ~Qt::WindowMinimized | Qt::WindowActive);
- }
- if (pane->IsVisible())
- {
- if (!modes.testFlag(QtViewPane::OpenMode::RestoreLayout))
- {
- newDockWidget->setFocus();
- }
- }
- else
- {
- // If the dock widget is tabbed, then set it as the active tab
- AzQtComponents::DockTabWidget* tabWidget = AzQtComponents::DockTabWidget::ParentTabWidget(newDockWidget);
- if (tabWidget)
- {
- int index = tabWidget->indexOf(newDockWidget);
- tabWidget->setCurrentIndex(index);
- }
- // Otherwise just show the widget
- else
- {
- newDockWidget->show();
- }
- }
- // When a user opens a pane, if it is docked in a floating window, make sure
- // it isn't hidden behind other floating windows or the Editor main window
- if (modes.testFlag(QtViewPane::OpenMode::None))
- {
- QMainWindow* mainWindow = qobject_cast<QMainWindow*>(newDockWidget->parentWidget());
- if (!mainWindow)
- {
- // If the parent of our dock widgets isn't a QMainWindow, then it
- // might be tabbed, so try to find the tab container dock widget
- // and then get the QMainWindow from that.
- AzQtComponents::DockTabWidget* tabWidget = AzQtComponents::DockTabWidget::ParentTabWidget(newDockWidget);
- if (tabWidget)
- {
- QDockWidget* tabDockContainer = qobject_cast<QDockWidget*>(tabWidget->parentWidget());
- if (tabDockContainer)
- {
- mainWindow = qobject_cast<QMainWindow*>(tabDockContainer->parentWidget());
- }
- }
- }
- if (mainWindow)
- {
- // If our pane is part of a floating window, then the parent of its
- // QMainWindow will be another dock widget container that is floating.
- // If this is the case, then raise it to the front so it won't be
- // hidden behind other floating windows (or the Editor main window)
- QDockWidget* parentDockWidget = qobject_cast<QDockWidget*>(mainWindow->parentWidget());
- if (parentDockWidget && parentDockWidget->isFloating())
- {
- parentDockWidget->raise();
- }
- }
- }
- AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEventsBus::Handler::OnViewPaneOpened, pane->m_name.toUtf8().data());
- return pane;
- }
- QDockWidget* QtViewPaneManager::InstancePane(const QString& name)
- {
- const QtViewPane* pane = OpenPane(name, QtViewPane::OpenMode::UseDefaultState | QtViewPane::OpenMode::MultiplePanes);
- QDockWidget* newPaneWidget = nullptr;
- if (pane != nullptr)
- {
- newPaneWidget = pane->m_dockWidgetInstances.last();
- }
- return newPaneWidget;
- }
- bool QtViewPaneManager::ClosePane(const QString& name, QtViewPane::CloseModes closeModes)
- {
- if (QtViewPane* p = GetPane(name))
- {
- return ClosePane(p, closeModes);
- }
- return false;
- }
- bool QtViewPaneManager::ClosePaneInstance(const QString& name, QDockWidget* dockWidget, QtViewPane::CloseModes closeModes)
- {
- if (QtViewPane* p = GetPane(name))
- {
- return p->CloseInstance(dockWidget, closeModes);
- }
- return false;
- }
- bool QtViewPaneManager::ClosePane(QtViewPane* pane, QtViewPane::CloseModes closeModes)
- {
- if (pane)
- {
- // Don't allow a dock widget to be closed if it is being dragged for docking
- if (m_advancedDockManager && m_advancedDockManager->IsDockWidgetBeingDragged(pane->m_dockWidget))
- {
- return false;
- }
- return pane->Close(closeModes | QtViewPane::CloseMode::Force);
- }
- return false;
- }
- bool QtViewPaneManager::CloseAllPanes()
- {
- for (QtViewPane& p : m_registeredPanes)
- {
- if (!p.Close())
- {
- return false; // Abort closing
- }
- }
- return true;
- }
- void QtViewPaneManager::CloseAllNonStandardPanes()
- {
- for (QtViewPane& p : m_registeredPanes)
- {
- if (!p.m_options.isStandard)
- {
- p.Close(QtViewPane::CloseMode::Force);
- }
- }
- }
- void QtViewPaneManager::TogglePane(const QString& name)
- {
- // Find the first pane with this name, or an enumerated instance.
- QtViewPane* pane = GetFirstVisiblePaneMatching(name);
- if (pane)
- {
- ClosePane(pane->m_name);
- return;
- }
- pane = GetPane(name);
- if (!pane)
- {
- Q_ASSERT_X(false, "QtViewPaneManager", ("Failed to open pane " + name).toUtf8().data());
- return;
- }
- OpenPane(name);
- }
- QWidget* QtViewPaneManager::CreateWidget(const QString& paneName)
- {
- QtViewPane* pane = GetPane(paneName);
- if (!pane)
- {
- qWarning() << Q_FUNC_INFO << "Couldn't find pane" << paneName << "; paneCount=" << m_registeredPanes.size();
- return nullptr;
- }
- QWidget* w = pane->CreateWidget();
- if (w)
- {
- w->setWindowTitle(paneName);
- return w;
- }
- return nullptr;
- }
- void QtViewPaneManager::SaveLayout()
- {
- SaveLayout(s_lastLayoutName);
- }
- void QtViewPaneManager::RestoreLayout(bool restoreDefaults)
- {
- if (!restoreDefaults)
- {
- restoreDefaults = !RestoreLayout(s_lastLayoutName);
- }
- if (restoreDefaults)
- {
- // Nothing is saved in settings, restore default layout
- RestoreDefaultLayout();
- }
- }
- bool QtViewPaneManager::ClosePanesWithRollback(const QVector<QString>& panesToKeepOpen)
- {
- QVector<QString> closedPanes;
- // try to close all panes that aren't remaining open after relayout
- bool rollback = false;
- for (QtViewPane& p : m_registeredPanes)
- {
- // Only close the panes that aren't remaining open and are currently
- // visible (which has to include a check if the pane is tabbed since
- // it could be hidden if its not the active tab)
- if (panesToKeepOpen.contains(p.m_name) || (!p.IsVisible() && !AzQtComponents::DockTabWidget::IsTabbed(p.m_dockWidget)))
- {
- continue;
- }
- // attempt to close this pane; if Close returns false, then the close event
- // was intercepted and the pane doesn't want to close, so we should cancel the whole thing
- // and rollback
- if (!p.Close())
- {
- rollback = true;
- break;
- }
- // keep track of the panes that we closed, so we can rollback later and reopen them
- closedPanes.push_back(p.m_name);
- }
- // check if we cancelled and need to roll everything back
- if (rollback)
- {
- for (const QString& paneName : closedPanes)
- {
- // append this to the end of the event loop with a zero length timer, so that
- // all of the close/hide events above are entirely processed
- QTimer::singleShot(0, this, [paneName, this]()
- {
- OpenPane(paneName, QtViewPane::OpenMode::RestoreLayout);
- });
- }
- return false;
- }
- return true;
- }
- /**
- * Restore the default layout (also known as component entity layout)
- */
- void QtViewPaneManager::RestoreDefaultLayout(bool resetSettings)
- {
- // Get whether the prefab system is enabled
- bool isPrefabSystemEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- if (resetSettings)
- {
- // We're going to do something destructive (removing all of the viewpane settings). Better confirm with the user
- auto buttonPressed = QMessageBox::warning(m_mainWindow, tr("Restore Default Layout"), tr("Are you sure you'd like to restore to the default layout? This will reset all of your view related settings."), QMessageBox::Cancel | QMessageBox::RestoreDefaults, QMessageBox::RestoreDefaults);
- if (buttonPressed != QMessageBox::RestoreDefaults)
- {
- return;
- }
- }
- // First, close all the open panes
- if (!ClosePanesWithRollback(QVector<QString>()))
- {
- return;
- }
- // Disable updates while we restore the layout to avoid temporary glitches
- // as the panes are moved around
- m_mainWindow->setUpdatesEnabled(false);
- AzToolsFramework::EntityIdList selectedEntityIds;
- // Reset all of the settings, or windows opened outside of RestoreDefaultLayout won't be reset at all.
- // Also ensure that this is done after CloseAllPanes, because settings will be saved in CloseAllPanes
- if (resetSettings)
- {
- // Store off currently selected entities
- AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
- // Clear any selection
- AzToolsFramework::EntityIdList noEntities;
- AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, noEntities);
- ViewLayoutState state;
- state.viewPanes.push_back(LyViewPane::EntityOutliner);
- state.viewPanes.push_back(LyViewPane::Inspector);
- state.viewPanes.push_back(LyViewPane::AssetBrowser);
- state.viewPanes.push_back(LyViewPane::Console);
- if (!isPrefabSystemEnabled)
- {
- state.viewPanes.push_back(LyViewPane::LevelInspector);
- }
- state.mainWindowState = m_defaultMainWindowState;
- {
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- m_settings->setValue(s_lastLayoutName, QVariant::fromValue<ViewLayoutState>(state));
- }
- m_settings->sync();
- // Let anything listening know to reset as well (*cough*CLayoutWnd*cough*)
- emit layoutReset();
- // Ensure that the main window knows it's new state
- // otherwise when we load view panes that haven't been loaded,
- // the main window will attempt to position them where they were last, not in their default spot
- m_mainWindow->restoreState(m_defaultMainWindowState);
- }
- // Reset the default view panes to be opened. Used for restoring default layout and component entity layout.
- const QtViewPane* entityOutlinerViewPane = OpenPane(LyViewPane::EntityOutliner, QtViewPane::OpenMode::UseDefaultState);
- const QtViewPane* assetBrowserViewPane = OpenPane(LyViewPane::AssetBrowser, QtViewPane::OpenMode::UseDefaultState);
- const QtViewPane* InspectorViewPane = OpenPane(LyViewPane::Inspector, QtViewPane::OpenMode::UseDefaultState);
- const QtViewPane* consoleViewPane = OpenPane(LyViewPane::Console, QtViewPane::OpenMode::UseDefaultState);
- const QtViewPane* levelInspectorPane = nullptr;
- if (!isPrefabSystemEnabled)
- {
- levelInspectorPane = OpenPane(LyViewPane::LevelInspector, QtViewPane::OpenMode::UseDefaultState);
- }
- // This class does all kinds of behind the scenes magic to make docking / restore work, especially with groups
- // so instead of doing our special default layout attach / docking right now, we want to make it happen
- // after all of the other events have been processed.
- QTimer::singleShot(0, [=]
- {
- // If we are using the new docking, set the right dock area to be absolute
- // so that the inspector will be to the right of the viewport and console
- m_advancedDockManager->setAbsoluteCornersForDockArea(m_mainWindow, Qt::RightDockWidgetArea);
- // Retrieve the width and height of the screen that our main window is on so we can
- // use it later for resizing our panes. The main window ends up being maximized
- // when we restore the default layout, but even if we maximize the main window
- // before doing anything else, its height and width won't update until after this has all
- // been processed, so we need to resize the panes based on what the main window
- // height and width WILL be after maximized
- int screenWidth = QApplication::desktop()->screenGeometry(m_mainWindow).width();
- int screenHeight = QApplication::desktop()->screenGeometry(m_mainWindow).height();
- // Add the console view pane first
- m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, consoleViewPane->m_dockWidget);
- consoleViewPane->m_dockWidget->setFloating(false);
- if (assetBrowserViewPane)
- {
- m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, assetBrowserViewPane->m_dockWidget);
- assetBrowserViewPane->m_dockWidget->setFloating(false);
- static const float bottomTabWidgetPercentage = 0.25f;
- int newHeight = static_cast<int>((float)screenHeight * bottomTabWidgetPercentage);
- AzQtComponents::DockTabWidget* bottomTabWidget = m_advancedDockManager->tabifyDockWidget(assetBrowserViewPane->m_dockWidget, consoleViewPane->m_dockWidget, m_mainWindow);
- if (bottomTabWidget)
- {
- bottomTabWidget->setCurrentWidget(assetBrowserViewPane->m_dockWidget);
- QDockWidget* bottomTabWidgetParent = qobject_cast<QDockWidget*>(bottomTabWidget->parentWidget());
- m_mainWindow->resizeDocks({ bottomTabWidgetParent }, { newHeight }, Qt::Vertical);
- }
- else
- {
- m_mainWindow->resizeDocks({ assetBrowserViewPane->m_dockWidget }, { newHeight }, Qt::Vertical);
- }
- }
- if (InspectorViewPane)
- {
- m_mainWindow->addDockWidget(Qt::RightDockWidgetArea, InspectorViewPane->m_dockWidget);
- InspectorViewPane->m_dockWidget->setFloating(false);
- static const float tabWidgetWidthPercentage = 0.2f;
- int newWidth = static_cast<int>((float)screenWidth * tabWidgetWidthPercentage);
- if (levelInspectorPane)
- {
- // Tab the entity inspector with the level Inspector so that when they are
- // tabbed they will be given the default width, and move the entity inspector
- // to be the first tab on the left and active
- AzQtComponents::DockTabWidget* tabWidget = m_advancedDockManager->tabifyDockWidget(levelInspectorPane->m_dockWidget, InspectorViewPane->m_dockWidget, m_mainWindow);
- if (tabWidget)
- {
- tabWidget->moveTab(1, 0);
- tabWidget->setCurrentWidget(InspectorViewPane->m_dockWidget);
- QDockWidget* tabWidgetParent = qobject_cast<QDockWidget*>(tabWidget->parentWidget());
- m_mainWindow->resizeDocks({ tabWidgetParent }, { newWidth }, Qt::Horizontal);
- }
- }
- else
- {
- m_mainWindow->resizeDocks({ InspectorViewPane->m_dockWidget }, { newWidth }, Qt::Horizontal);
- }
- }
- if (entityOutlinerViewPane)
- {
- m_mainWindow->addDockWidget(Qt::LeftDockWidgetArea, entityOutlinerViewPane->m_dockWidget);
- entityOutlinerViewPane->m_dockWidget->setFloating(false);
- // Resize our entity outliner so that it gets an appropriate default width
- // since the minimum sizes been removed from this widget
- static const float entityOutlinerWidthPercentage = 0.15f;
- int newWidth = static_cast<int>((float)screenWidth * entityOutlinerWidthPercentage);
- m_mainWindow->resizeDocks({ entityOutlinerViewPane->m_dockWidget }, { newWidth }, Qt::Horizontal);
- }
- // Re-enable updates now that we've finished restoring the layout
- m_mainWindow->setUpdatesEnabled(true);
- // Default layout should always be maximized
- // (use window() because the MainWindow may be wrapped in another window
- // like a WindowDecoratorWrapper or another QMainWindow for various layout reasons)
- m_mainWindow->window()->showMaximized();
- if (resetSettings)
- {
- // Restore selection
- AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
- }
- });
- }
- void QtViewPaneManager::SaveLayout(QString layoutName)
- {
- if (!m_mainWindow || m_restoreInProgress)
- {
- return;
- }
- layoutName = layoutName.trimmed();
- ViewLayoutState state;
- foreach(const QtViewPane &pane, m_registeredPanes)
- {
- // Include all visible and tabbed panes in our layout, since tabbed panes
- // won't be visible if they aren't the active tab, but still need to be
- // retained in the layout
- if (pane.IsVisible() || AzQtComponents::DockTabWidget::IsTabbed(pane.m_dockWidget))
- {
- state.viewPanes.push_back(pane.m_dockWidget->PaneName());
- }
- }
- state.mainWindowState = m_advancedDockManager->saveState();
- state.fakeDockWidgetGeometries = m_fakeDockWidgetGeometries;
- SaveStateToLayout(state, layoutName);
- AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequestBus::Events::Save);
- }
- void QtViewPaneManager::SaveStateToLayout(const ViewLayoutState& state, const QString& layoutName)
- {
- const bool isNew = !HasLayout(layoutName);
- {
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- m_settings->setValue(layoutName, QVariant::fromValue<ViewLayoutState>(state));
- }
- m_settings->sync();
- if (isNew)
- {
- emit savedLayoutsChanged();
- }
- }
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- /*
- * This methods creates a fake wrapper dock widget around the passed dock widget. The returned dock widget has
- * no parent and can therefore be used for a contained QOpenGLWidget on macOS, since this doesn't work as expected
- * when the QOpenGLWidget has the application's main window as (grand)parent.
- * The return dock widget looks like a normal dock widget. It cannot be docked and no other dock widget can be
- * docked into it.
- */
- QDockWidget* QtViewPaneManager::ShowFakeNonDockableDockWidget(AzQtComponents::StyledDockWidget* dockWidget, QtViewPane* pane)
- {
- dockWidget->customTitleBar()->setButtons({});
- dockWidget->customTitleBar()->setContextMenuPolicy(Qt::NoContextMenu);
- dockWidget->customTitleBar()->installEventFilter(new MouseEatingEventFilter(dockWidget));
- auto fakeDockWidget = new AzQtComponents::StyledDockWidget(QString(), false, nullptr);
- connect(dockWidget, &QObject::destroyed, fakeDockWidget, &QObject::deleteLater);
- fakeDockWidget->setAllowedAreas(Qt::NoDockWidgetArea);
- auto titleBar = fakeDockWidget->customTitleBar();
- titleBar->setDragEnabled(true);
- titleBar->setDrawSimple(true);
- titleBar->setButtons({AzQtComponents::DockBarButton::MaximizeButton, AzQtComponents::DockBarButton::CloseButton});
- fakeDockWidget->setWidget(dockWidget);
- fakeDockWidget->show();
- fakeDockWidget->setObjectName(dockWidget->objectName());
- connect(fakeDockWidget, &AzQtComponents::StyledDockWidget::aboutToClose, this, [this, fakeDockWidget]() {
- m_fakeDockWidgetGeometries[fakeDockWidget->objectName()] = fakeDockWidget->geometry();
- });
- if (pane->m_options.isDeletable)
- {
- fakeDockWidget->setAttribute(Qt::WA_DeleteOnClose);
- }
- return fakeDockWidget;
- }
- #endif
- void QtViewPaneManager::SerializeLayout(XmlNodeRef& parentNode) const
- {
- ViewLayoutState state = GetLayout();
- XmlNodeRef paneListNode = XmlHelpers::CreateXmlNode("ViewPanes");
- parentNode->addChild(paneListNode);
- for (const QString& paneName : state.viewPanes)
- {
- XmlNodeRef paneNode = XmlHelpers::CreateXmlNode("ViewPane");
- paneNode->setContent(paneName.toUtf8().data());
- paneListNode->addChild(paneNode);
- }
- XmlNodeRef windowStateNode = XmlHelpers::CreateXmlNode("WindowState");
- windowStateNode->setContent(state.mainWindowState.toHex().data());
- parentNode->addChild(windowStateNode);
- }
- bool QtViewPaneManager::DeserializeLayout(const XmlNodeRef& parentNode)
- {
- ViewLayoutState state;
- XmlNodeRef paneListNode = parentNode->findChild("ViewPanes");
- if (!paneListNode)
- return false;
- for (int i = 0; i < paneListNode->getChildCount(); ++i)
- {
- XmlNodeRef paneNode = paneListNode->getChild(i);
- state.viewPanes.push_back(QString(paneNode->getContent()));
- }
- XmlNodeRef windowStateNode = parentNode->findChild("WindowState");
- if (!windowStateNode)
- return false;
- state.mainWindowState = QByteArray::fromHex(windowStateNode->getContent());
- return RestoreLayout(state);
- }
- ViewLayoutState QtViewPaneManager::GetLayout() const
- {
- ViewLayoutState state;
- foreach(const QtViewPane &pane, m_registeredPanes)
- {
- // Include all visible and tabbed panes in our layout, since tabbed panes
- // won't be visible if they aren't the active tab, but still need to be
- // retained in the layout
- if (pane.IsVisible() || AzQtComponents::DockTabWidget::IsTabbed(pane.m_dockWidget))
- {
- state.viewPanes.push_back(pane.m_dockWidget->PaneName());
- }
- }
- state.mainWindowState = m_advancedDockManager->saveState();
- state.fakeDockWidgetGeometries = m_fakeDockWidgetGeometries;
- return state;
- }
- bool QtViewPaneManager::RestoreLayout(QString layoutName)
- {
- if (m_restoreInProgress) // Against re-entrancy
- {
- return true;
- }
- QScopedValueRollback<bool> recursionGuard(m_restoreInProgress);
- m_restoreInProgress = true;
- layoutName = layoutName.trimmed();
- if (layoutName.isEmpty())
- {
- return false;
- }
- ViewLayoutState state;
- {
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- if (!m_settings->contains(layoutName))
- {
- return false;
- }
- state = m_settings->value(layoutName).value<ViewLayoutState>();
- }
- // If we have the legacy UI disabled and are restoring the last user layout,
- // if it doesn't contain the Entity Inspector and Outliner then we need to
- // save their previous layout for them and switch them to the new default
- // layout because they won't be able to do much without them
- static const QString userLegacyLayout = "User Legacy Layout";
- if (layoutName == s_lastLayoutName && !HasLayout(userLegacyLayout))
- {
- bool layoutHasInspector = false;
- bool layoutHasEntityOutliner = false;
- for (const QString& paneName : state.viewPanes)
- {
- if (paneName == LyViewPane::Inspector)
- {
- layoutHasInspector = true;
- }
- else if (paneName == LyViewPane::EntityOutliner)
- {
- layoutHasEntityOutliner = true;
- }
- }
- if (!layoutHasInspector || !layoutHasEntityOutliner)
- {
- SaveStateToLayout(state, userLegacyLayout);
- QMessageBox box(AzToolsFramework::GetActiveWindow());
- box.addButton(QMessageBox::Ok);
- box.setWindowTitle(tr("Layout Saved"));
- box.setText(tr("Your layout has been automatically updated for the new Component-Entity workflows. Your old layout has been saved as \"%1\" and can be restored from the View -> Layouts menu.").arg(userLegacyLayout));
- box.exec();
- return false;
- }
- }
- if (!ClosePanesWithRollback(state.viewPanes))
- {
- return false;
- }
- // Store off currently selected entities
- AzToolsFramework::EntityIdList selectedEntityIds;
- AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
- // Clear any selection
- AzToolsFramework::EntityIdList noEntities;
- AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, noEntities);
- m_fakeDockWidgetGeometries = state.fakeDockWidgetGeometries;
- for (const QString& paneName : state.viewPanes)
- {
- OpenPane(paneName, QtViewPane::OpenMode::OnlyOpen);
- }
- // must do this after opening all of the panes!
- m_advancedDockManager->restoreState(state.mainWindowState);
- AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
- // In case of a crash it might happen that the QMainWindow state gets out of sync with the
- // QtViewPaneManager state, which would result in we opening dock widgets that QMainWindow
- // didn't know how to restore.
- // Check if that happened and return false indicating the restore failed and giving caller
- // a chance to restore the default layout.
- if (AzToolsFramework::DockWidgetUtils::hasInvalidDockWidgets(m_mainWindow))
- {
- return false;
- }
- return true;
- }
- bool QtViewPaneManager::RestoreLayout(const ViewLayoutState& state)
- {
- m_fakeDockWidgetGeometries = state.fakeDockWidgetGeometries;
- if (!ClosePanesWithRollback(state.viewPanes))
- {
- return false;
- }
- for (const QString& paneName : state.viewPanes)
- {
- OpenPane(paneName, QtViewPane::OpenMode::OnlyOpen);
- }
- // must do this after opening all of the panes!
- m_advancedDockManager->restoreState(state.mainWindowState);
- return true;
- }
- void QtViewPaneManager::RenameLayout(QString name, QString newName)
- {
- name = name.trimmed();
- newName = newName.trimmed();
- if (name == newName || newName.isEmpty() || name.isEmpty())
- {
- return;
- }
- {
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- m_settings->setValue(newName, m_settings->value(name));
- m_settings->remove(name);
- }
- m_settings->sync();
- emit savedLayoutsChanged();
- }
- void QtViewPaneManager::RemoveLayout(QString layoutName)
- {
- layoutName = layoutName.trimmed();
- if (layoutName.isEmpty())
- {
- return;
- }
- {
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- m_settings->remove(layoutName.trimmed());
- }
- m_settings->sync();
- emit savedLayoutsChanged();
- }
- bool QtViewPaneManager::HasLayout(const QString& name) const
- {
- return LayoutNames().contains(name.trimmed(), Qt::CaseInsensitive);
- }
- QStringList QtViewPaneManager::LayoutNames(bool userLayoutsOnly) const
- {
- QStringList layouts;
- AzQtComponents::AutoSettingsGroup settingsGroupGuard(m_settings, GetFancyViewPaneStateGroupName());
- layouts = m_settings->childKeys();
- if (userLayoutsOnly)
- {
- layouts.removeOne(s_lastLayoutName); // "last" is internal
- }
- return layouts;
- }
- QtViewPanes QtViewPaneManager::GetRegisteredPanes(bool viewPaneMenuOnly) const
- {
- if (!viewPaneMenuOnly)
- {
- return m_registeredPanes;
- }
- QtViewPanes panes;
- panes.reserve(30); // approximate
- std::copy_if(m_registeredPanes.cbegin(), m_registeredPanes.cend(), std::back_inserter(panes), [](QtViewPane pane)
- {
- return pane.m_options.showInMenu;
- });
- return panes;
- }
- QtViewPanes QtViewPaneManager::GetRegisteredMultiInstancePanes(bool viewPaneMenuOnly) const
- {
- QtViewPanes panes;
- panes.reserve(30); // approximate
- if (viewPaneMenuOnly)
- {
- std::copy_if(m_registeredPanes.cbegin(), m_registeredPanes.cend(), std::back_inserter(panes), [](QtViewPane pane)
- {
- return pane.m_options.showInMenu && pane.m_options.canHaveMultipleInstances;
- });
- }
- else
- {
- std::copy_if(m_registeredPanes.cbegin(), m_registeredPanes.cend(), std::back_inserter(panes), [](QtViewPane pane)
- {
- return pane.m_options.canHaveMultipleInstances;
- });
- }
- return panes;
- }
- QtViewPanes QtViewPaneManager::GetRegisteredViewportPanes() const
- {
- QtViewPanes viewportPanes;
- viewportPanes.reserve(5); // approximate
- std::copy_if(m_registeredPanes.cbegin(), m_registeredPanes.cend(), std::back_inserter(viewportPanes), [](QtViewPane pane)
- {
- return pane.IsViewportPane();
- });
- return viewportPanes;
- }
- int QtViewPaneManager::NextAvailableId()
- {
- for (int candidate = ID_VIEW_OPENPANE_FIRST; candidate <= ID_VIEW_OPENPANE_LAST; ++candidate)
- {
- if (!m_knownIdsSet.contains(candidate))
- {
- m_knownIdsSet.push_back(candidate);
- return candidate;
- }
- }
- return -1;
- }
- QtViewPane* QtViewPaneManager::GetPane(int id)
- {
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [id](const QtViewPane& pane) { return id == pane.m_id; });
- return it == m_registeredPanes.end() ? nullptr : it;
- }
- QtViewPane* QtViewPaneManager::GetPane(const QString& name)
- {
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [name](const QtViewPane& pane) { return name == pane.m_name; });
- QtViewPane* foundPane = ((it == m_registeredPanes.end()) ? nullptr : it);
- if (foundPane == nullptr)
- {
- // if we couldn't find the pane based on the name (which will be the title), look it up by saveKeyName next
- it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [name](const QtViewPane& pane) { return name == pane.m_options.saveKeyName; });
- foundPane = ((it == m_registeredPanes.end()) ? nullptr : it);
- }
- return foundPane;
- }
- QtViewPane* QtViewPaneManager::GetFirstVisiblePaneMatching(const QString& name)
- {
- QString baseName = name;
- // Strip away any enumeration.
- baseName = baseName.remove(QRegExp("\\([0-9]+\\)$"));
- // Build a regexp which will match just the name, or the name followed by a number in parentheses.
- QRegExp pattern(name + "([ ]*\\([0-9]+\\))*");
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [pattern](const QtViewPane& pane)
- {
- return pattern.exactMatch(pane.m_name) && pane.IsVisible();
- });
- QtViewPane* foundPane = ((it == m_registeredPanes.end()) ? nullptr : it);
- if (foundPane == nullptr)
- {
- // if we couldn't find the pane based on the name (which will be the title), look it up by saveKeyName next
- auto optionsIt = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [pattern](const QtViewPane& pane)
- {
- return pattern.exactMatch(pane.m_options.saveKeyName) && pane.IsVisible();
- });
- foundPane = ((optionsIt == m_registeredPanes.end()) ? nullptr : optionsIt);
- }
- return foundPane;
- }
- QtViewPane* QtViewPaneManager::GetViewportPane(int viewportType)
- {
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [viewportType](const QtViewPane& pane) { return viewportType == pane.m_options.viewportType; });
- return it == m_registeredPanes.end() ? nullptr : it;
- }
- QDockWidget* QtViewPaneManager::GetView(const QString& name)
- {
- QtViewPane* pane = GetPane(name);
- return pane ? pane->m_dockWidget : nullptr;
- }
- bool QtViewPaneManager::IsVisible(const QString& name)
- {
- QtViewPane* view = GetPane(name);
- return view && view->IsVisible();
- }
- bool QtViewPaneManager::IsEnumeratedInstanceVisible(const QString& name)
- {
- // Returns true is panel "name" is visible or "name (1)" etc.
- return GetFirstVisiblePaneMatching(name);
- }
- bool QtViewPaneManager::IsPaneRegistered(const QString& name) const
- {
- auto it = std::find_if(m_registeredPanes.begin(), m_registeredPanes.end(),
- [name](const QtViewPane& pane) { return name == pane.m_name; });
- return it != m_registeredPanes.end();
- }
- #include <moc_QtViewPaneManager.cpp>
|