diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index d13bfccfe3..4169bf93a8 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -53,6 +53,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include // AzFramework #include @@ -356,6 +357,8 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(LPCTSTR lpszFileName, BOOL bAddToM for (int i = idStart; i <= idEnd; ++i) \ ON_COMMAND(i, method); +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + void CCryEditApp::RegisterActionHandlers() { ON_COMMAND(ID_APP_ABOUT, OnAppAbout) @@ -376,6 +379,10 @@ void CCryEditApp::RegisterActionHandlers() ON_COMMAND(ID_EDIT_FETCH, OnEditFetch) ON_COMMAND(ID_FILE_EXPORTTOGAMENOSURFACETEXTURE, OnFileExportToGameNoSurfaceTexture) ON_COMMAND(ID_VIEW_SWITCHTOGAME, OnViewSwitchToGame) + MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_VIEW_SWITCHTOGAME_FULLSCREEN, [this]() { + ed_previewGameInFullscreen_once = true; + OnViewSwitchToGame(); + }); ON_COMMAND(ID_MOVE_OBJECT, OnMoveObject) ON_COMMAND(ID_RENAME_OBJ, OnRenameObj) ON_COMMAND(ID_EDITMODE_MOVE, OnEditmodeMove) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index e0068c5d88..1a7c54967b 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include // AzFramework @@ -82,6 +83,8 @@ #include "AnimationContext.h" #include "Objects/SelectionGroup.h" #include "Core/QtEditorApplication.h" +#include "MainWindow.h" +#include "LayoutWnd.h" // ComponentEntityEditorPlugin #include @@ -704,6 +707,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) } } SetCurrentCursor(STD_CURSOR_GAME); + + if (ShouldPreviewFullscreen()) + { + StartFullscreenPreview(); + } } if (m_renderViewport) @@ -722,6 +730,11 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) m_bInOrbitMode = false; m_bInZoomMode = false; + if (m_inFullscreenPreview) + { + StopFullscreenPreview(); + } + RestoreViewportAfterGameMode(); } @@ -1378,11 +1391,35 @@ void EditorViewportWidget::SetViewportId(int id) { CViewport::SetViewportId(id); + // Clear the cached debugdisplay pointer. we're about to delete that render viewport, and deleting the render + // viewport invalidates the debugdisplay. + m_debugDisplay = nullptr; + + // First delete any existing layout + // This also deletes any existing render viewport widget (since it will be added to the layout) + // Below is the typical method of clearing a QLayout, see e.g. https://doc.qt.io/qt-5/qlayout.html#takeAt + if (QLayout* thisLayout = layout()) + { + QLayoutItem* item; + while ((item = thisLayout->takeAt(0)) != nullptr) + { + if (QWidget* widget = item->widget()) + { + delete widget; + } + thisLayout->removeItem(item); + delete item; + } + delete thisLayout; + } + // Now that we have an ID, we can initialize our viewport. m_renderViewport = new AtomToolsFramework::RenderViewportWidget(this, false); if (!m_renderViewport->InitializeViewportContext(id)) { AZ_Warning("EditorViewportWidget", false, "Failed to initialize RenderViewportWidget's ViewportContext"); + delete m_renderViewport; + m_renderViewport = nullptr; return; } auto viewportContext = m_renderViewport->GetViewportContext(); @@ -3037,4 +3074,116 @@ float EditorViewportSettings::AngleStep() const return SandboxEditor::AngleSnappingSize(); } +AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); + +bool EditorViewportWidget::ShouldPreviewFullscreen() const +{ + CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout(); + if (!layout) + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + return false; + } + + // Doesn't work with split layout + if (layout->GetLayout() != EViewLayout::ET_Layout0) + { + return false; + } + + // Not supported in VR + if (gSettings.bEnableGameModeVR) + { + return false; + } + + // If level not loaded, don't preview in fullscreen (preview shouldn't work at all without a level, but it does) + if (auto ge = GetIEditor()->GetGameEngine()) + { + if (!ge->IsLevelLoaded()) + { + return false; + } + } + + // Check 'ed_previewGameInFullscreen_once' + if (ed_previewGameInFullscreen_once) + { + ed_previewGameInFullscreen_once = true; + return true; + } + else + { + return false; + } +} + +void EditorViewportWidget::StartFullscreenPreview() +{ + AZ_Assert(!m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when already in full screen preview"); + m_inFullscreenPreview = true; + + // Pick the screen on which the main window lies to use as the screen for the full screen preview + const QScreen* screen = MainWindow::instance()->screen(); + const QRect screenGeometry = screen->geometry(); + + // Unparent this and show it, which turns it into a free floating window + // Also set style to frameless and disable resizing by user + setParent(nullptr); + setWindowFlag(Qt::FramelessWindowHint, true); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, true); + setFixedSize(screenGeometry.size()); + move(QPoint(screenGeometry.x(), screenGeometry.y())); + showMaximized(); + + // This must be done after unparenting this widget above + MainWindow::instance()->hide(); +} + +void EditorViewportWidget::StopFullscreenPreview() +{ + AZ_Assert(m_inFullscreenPreview, "EditorViewportWidget::StartFullscreenPreview called when not in full screen preview"); + m_inFullscreenPreview = false; + + // Unset frameless window flags + setWindowFlag(Qt::FramelessWindowHint, false); + setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false); + + // Unset fixed size (note that 50x50 is the minimum set in the constructor) + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + setMinimumSize(50, 50); + + // Attach this viewport to the primary view pane (whose index is 0). + if (CLayoutWnd* layout = GetIEditor()->GetViewManager()->GetLayout()) + { + if (CLayoutViewPane* viewPane = layout->GetViewPaneByIndex(0)) + { + // Force-reattach this viewport to its view pane by first detaching + viewPane->DetachViewport(); + viewPane->AttachViewport(this); + + // Set the main widget of the layout, which causes this widgets size to be bound to the layout + // and the viewport title bar to be displayed + layout->SetMainWidget(viewPane); + } + else + { + AZ_Assert(false, "CRenderViewport: No view pane with ID 0 (primary view pane)"); + } + } + else + { + AZ_Assert(false, "CRenderViewport: No View Manager layout"); + } + + // Set this as the selected viewport + GetIEditor()->GetViewManager()->SelectViewport(this); + + // Show this widget (setting flags may hide it) + showNormal(); + + // Show the main window + MainWindow::instance()->show(); +} + #include diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 6b2db65847..511a7910c6 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -385,6 +385,11 @@ protected: }; void ResetToViewSourceType(const ViewSourceType& viewSourType); + bool ShouldPreviewFullscreen() const; + void StartFullscreenPreview(); + void StopFullscreenPreview(); + + bool m_inFullscreenPreview = false; bool m_bRenderContextCreated = false; bool m_bInRotateMode = false; bool m_bInMoveMode = false; diff --git a/Code/Sandbox/Editor/LayoutWnd.h b/Code/Sandbox/Editor/LayoutWnd.h index 2af56f907c..66c9a9e889 100644 --- a/Code/Sandbox/Editor/LayoutWnd.h +++ b/Code/Sandbox/Editor/LayoutWnd.h @@ -113,6 +113,8 @@ public: //! Switch 2D viewports. void Cycle2DViewport(); + using AzQtComponents::ToolBarArea::SetMainWidget; + public slots: void ResetLayout(); diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index ddc8ff5058..1ad97f31de 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -949,6 +949,12 @@ void MainWindow::InitActions() .SetApplyHoverEffect() .SetCheckable(true) .RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdatePlayGame); + am->AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, tr("Play &Game (Maximized)")) + .SetShortcut(tr("Ctrl+Shift+G")) + .SetStatusTip(tr("Activate the game input mode (maximized)")) + .SetIcon(Style::icon("Play")) + .SetApplyHoverEffect() + .SetCheckable(true); am->AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, tr("Play Controls")) .SetText(tr("Play Controls")); am->AddAction(ID_SWITCH_PHYSICS, tr("Simulate")) @@ -1266,10 +1272,25 @@ void MainWindow::OnGameModeChanged(bool inGameMode) { menuBar()->setDisabled(inGameMode); m_toolbarManager->SetEnabled(!inGameMode); - QAction* action = m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME); - action->blockSignals(true); // avoid a loop - action->setChecked(inGameMode); - action->blockSignals(false); + + // block signals on the switch to game actions before setting the checked state, as + // setting the checked state triggers the action, which will re-enter this function + // and result in an infinite loop + AZStd::vector actions = { m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME), m_actionManager->GetAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN) }; + for (auto action : actions) + { + action->blockSignals(true); + } + + for (auto action : actions) + { + action->setChecked(inGameMode); + } + + for (auto action : actions) + { + action->blockSignals(false); + } } void MainWindow::OnEditorNotifyEvent(EEditorNotifyEvent ev) diff --git a/Code/Sandbox/Editor/Resource.h b/Code/Sandbox/Editor/Resource.h index bd6ae94184..8ef6c0b906 100644 --- a/Code/Sandbox/Editor/Resource.h +++ b/Code/Sandbox/Editor/Resource.h @@ -111,6 +111,7 @@ #define ID_EDIT_FETCH 33465 #define ID_FILE_EXPORTTOGAMENOSURFACETEXTURE 33473 #define ID_VIEW_SWITCHTOGAME 33477 +#define ID_VIEW_SWITCHTOGAME_FULLSCREEN 33478 #define ID_EDIT_DELETE 33480 #define ID_MOVE_OBJECT 33481 #define ID_RENAME_OBJ 33483 diff --git a/Code/Sandbox/Editor/Settings.cpp b/Code/Sandbox/Editor/Settings.cpp index 15d3e793a5..f805e2e84b 100644 --- a/Code/Sandbox/Editor/Settings.cpp +++ b/Code/Sandbox/Editor/Settings.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // AzFramework #include @@ -926,6 +927,8 @@ void SEditorSettings::Load() } ////////////////////////////////////////////////////////////////////////// +AZ_CVAR(bool, ed_previewGameInFullscreen_once, false, nullptr, AZ::ConsoleFunctorFlags::IsInvisible, "Preview the game (Ctrl+G, \"Play Game\", etc.) in fullscreen once"); + void SEditorSettings::PostInitApply() { if (!gEnv || !gEnv->pConsole) diff --git a/Code/Sandbox/Editor/ToolbarManager.cpp b/Code/Sandbox/Editor/ToolbarManager.cpp index 137057a4fa..0c68797b6a 100644 --- a/Code/Sandbox/Editor/ToolbarManager.cpp +++ b/Code/Sandbox/Editor/ToolbarManager.cpp @@ -603,6 +603,7 @@ AmazonToolbar ToolbarManager::GetPlayConsoleToolbar() const t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_TOOLBAR_WIDGET_PLAYCONSOLE_LABEL, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_VIEW_SWITCHTOGAME, TOOLBARS_WITH_PLAY_GAME); + t.AddAction(ID_VIEW_SWITCHTOGAME_FULLSCREEN, TOOLBARS_WITH_PLAY_GAME); t.AddAction(ID_TOOLBAR_SEPARATOR, ORIGINAL_TOOLBAR_VERSION); t.AddAction(ID_SWITCH_PHYSICS, TOOLBARS_WITH_PLAY_GAME); return t;