diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index b16758a07e..8e6983a9d7 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -43,10 +43,11 @@ // AzToolsFramework #include +#include +#include #include #include #include -#include // AtomToolsFramework #include @@ -1032,6 +1033,7 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus() AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); m_viewportUi.ConnectViewportUiBus(GetViewportId()); + AzFramework::ViewportBorderRequestBus::Handler::BusConnect(GetViewportId()); AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect(); } @@ -1040,6 +1042,7 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus() { AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect(); + AzFramework::ViewportBorderRequestBus::Handler::BusDisconnect(); m_viewportUi.DisconnectViewportUiBus(); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect(); @@ -2638,4 +2641,25 @@ void EditorViewportWidget::StopFullscreenPreview() // Show the main window MainWindow::instance()->show(); } + +AZStd::optional EditorViewportWidget::GetViewportBorderPadding() const +{ + if (auto viewportEditorModeTracker = AZ::Interface::Get()) + { + auto viewportEditorModes = viewportEditorModeTracker->GetViewportEditorModes({ AzToolsFramework::GetEntityContextId() }); + if (viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Focus) || + viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Component)) + { + AzFramework::ViewportBorderPadding viewportBorderPadding = {}; + viewportBorderPadding.m_top = AzToolsFramework::ViewportUi::ViewportUiTopBorderSize; + viewportBorderPadding.m_left = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + viewportBorderPadding.m_right = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + viewportBorderPadding.m_bottom = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + return viewportBorderPadding; + } + } + + return AZStd::nullopt; +} + #include diff --git a/Code/Editor/EditorViewportWidget.h b/Code/Editor/EditorViewportWidget.h index 68ea48c7f5..01f6068d56 100644 --- a/Code/Editor/EditorViewportWidget.h +++ b/Code/Editor/EditorViewportWidget.h @@ -38,6 +38,7 @@ #include #include +#include // forward declarations. class CBaseObject; @@ -86,6 +87,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING class SANDBOX_API EditorViewportWidget final : public QtViewport + , public AzFramework::ViewportBorderRequestBus::Handler , private IEditorNotifyListener , private IUndoManagerListener , private Camera::EditorCameraRequestBus::Handler @@ -120,6 +122,9 @@ public: void SetFOV(float fov) override; float GetFOV() const override; + // AzFramework::ViewportBorderRequestBus overrides ... + AZStd::optional GetViewportBorderPadding() const override; + private: //////////////////////////////////////////////////////////////////////// // Private types ... diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h index 444173f773..131ecc0c57 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h @@ -8,8 +8,9 @@ #pragma once -#include #include +#include +#include namespace AZ { @@ -20,18 +21,15 @@ namespace AZ namespace AzFramework { - class ViewportRequests - : public AZ::EBusTraits + class ViewportRequests : public AZ::EBusTraits { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; using BusIdType = ViewportId; static void Reflect(AZ::ReflectContext* context); - virtual ~ViewportRequests() {} - //! Gets the current camera's world to view matrix. virtual const AZ::Matrix4x4& GetCameraViewMatrix() const = 0; //! Sets the current camera's world to view matrix. @@ -44,8 +42,36 @@ namespace AzFramework virtual AZ::Transform GetCameraTransform() const = 0; //! Convenience method, sets the camera's world to view matrix from this AZ::Transform. virtual void SetCameraTransform(const AZ::Transform& transform) = 0; + + protected: + ~ViewportRequests() = default; }; using ViewportRequestBus = AZ::EBus; -} //namespace AzFramework + //! The additional padding around the viewport when a viewport border is active. + struct ViewportBorderPadding + { + float m_top; + float m_bottom; + float m_left; + float m_right; + }; + + //! For performing queries about the state of the viewport border. + class ViewportBorderRequests : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = ViewportId; + + //! Returns if a viewport border is in effect and what the current dimensions (padding) of the border are. + virtual AZStd::optional GetViewportBorderPadding() const = 0; + + protected: + ~ViewportBorderRequests() = default; + }; + + using ViewportBorderRequestBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 43e2a6793f..c53aa66d81 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ namespace AzToolsFramework::ViewportUi::Internal { const static int HighlightBorderSize = 5; - const static int TopHighlightBorderSize = 25; const static char* HighlightBorderColor = "#4A90E2"; static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup) @@ -61,7 +61,7 @@ namespace AzToolsFramework::ViewportUi::Internal , m_uiOverlay(parent) , m_fullScreenLayout(&m_uiOverlay) , m_uiOverlayLayout() - , m_componentModeBorderText(&m_uiOverlay) + , m_viewportBorderText(&m_uiOverlay) { } @@ -221,11 +221,11 @@ namespace AzToolsFramework::ViewportUi::Internal AZStd::shared_ptr ViewportUiDisplay::GetViewportUiElement(ViewportUiElementId elementId) { - auto element = m_viewportUiElements.find(elementId); - if (element != m_viewportUiElements.end()) + if (auto element = m_viewportUiElements.find(elementId); element != m_viewportUiElements.end()) { return element->second.m_widget; } + return nullptr; } @@ -287,27 +287,30 @@ namespace AzToolsFramework::ViewportUi::Internal { return element.IsValid() && element.m_widget->isVisible(); } + return false; } void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle) { const AZStd::string styleSheet = AZStd::string::format( - "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, TopHighlightBorderSize, + "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, ViewportUiTopBorderSize, HighlightBorderColor); m_uiOverlay.setStyleSheet(styleSheet.c_str()); m_uiOverlayLayout.setContentsMargins( - HighlightBorderSize + ViewportUiOverlayMargin, TopHighlightBorderSize + ViewportUiOverlayMargin, + HighlightBorderSize + ViewportUiOverlayMargin, ViewportUiTopBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin); - m_componentModeBorderText.setVisible(true); - m_componentModeBorderText.setText(borderTitle.c_str()); + m_viewportBorderText.setVisible(true); + m_viewportBorderText.setText(borderTitle.c_str()); } void ViewportUiDisplay::RemoveViewportBorder() { - m_componentModeBorderText.setVisible(false); + m_viewportBorderText.setVisible(false); m_uiOverlay.setStyleSheet("border: none;"); - m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin); + m_uiOverlayLayout.setContentsMargins( + ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin, + ViewportUiOverlayMargin); } void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos) @@ -359,10 +362,10 @@ namespace AzToolsFramework::ViewportUi::Internal // format the label which will appear on top of the highlight border AZStd::string styleSheet = AZStd::string::format("background-color: %s; border: none;", HighlightBorderColor); - m_componentModeBorderText.setStyleSheet(styleSheet.c_str()); - m_componentModeBorderText.setFixedHeight(TopHighlightBorderSize); - m_componentModeBorderText.setVisible(false); - m_fullScreenLayout.addWidget(&m_componentModeBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter); + m_viewportBorderText.setStyleSheet(styleSheet.c_str()); + m_viewportBorderText.setFixedHeight(ViewportUiTopBorderSize); + m_viewportBorderText.setVisible(false); + m_fullScreenLayout.addWidget(&m_viewportBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter); } void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer widget) @@ -395,14 +398,14 @@ namespace AzToolsFramework::ViewportUi::Internal void ViewportUiDisplay::UpdateUiOverlayGeometry() { - // add the component mode border region if visible + // add the viewport border region if visible QRegion region; - if (m_componentModeBorderText.isVisible()) + if (m_viewportBorderText.isVisible()) { // get the border region by taking the entire region and subtracting the non-border area region += m_uiOverlay.rect(); region -= QRect( - QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + TopHighlightBorderSize), + QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + ViewportUiTopBorderSize), QPoint(m_uiOverlay.rect().right() - HighlightBorderSize, m_uiOverlay.rect().bottom() - HighlightBorderSize)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h index 5020241815..32b746a1ac 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h @@ -113,7 +113,7 @@ namespace AzToolsFramework::ViewportUi::Internal QWidget m_uiOverlay; //!< The UI Overlay which displays Viewport UI Elements. QGridLayout m_fullScreenLayout; //!< The layout which extends across the full screen. ViewportUiDisplayLayout m_uiOverlayLayout; //!< The layout used for optionally anchoring Viewport UI Elements. - QLabel m_componentModeBorderText; //!< The text used for the Component Mode border. + QLabel m_viewportBorderText; //!< The text used for the viewport border. QWidget* m_renderOverlay; QPointer m_fullScreenWidget; //!< Reference to the widget attached to m_fullScreenLayout if any. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h index 4a44f07491..335f664094 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h @@ -14,13 +14,20 @@ #include #include -namespace AzToolsFramework::ViewportUi::Internal +namespace AzToolsFramework::ViewportUi { - // margin for the Viewport UI Overlay in pixels + //! Margin for the Viewport UI Overlay (in pixels) constexpr int ViewportUiOverlayMargin = 5; - // padding to make space for ImGui + //! Padding to make space for ImGui (in pixels) constexpr int ViewportUiOverlayTopMarginPadding = 20; + //! Size of the top viewport border (in pixels) + constexpr int ViewportUiTopBorderSize = 25; + //! Size of the left, right and bottom viewport border (in pixels) + constexpr int ViewportUiLeftRightBottomBorderSize = 5; +} // namespace AzToolsFramework::ViewportUi +namespace AzToolsFramework::ViewportUi::Internal +{ //! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets. class ViewportUiDisplayLayout : public QGridLayout { diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp index 81ec4b17c8..d2f86a65cd 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "ImGuiColorDefines.h" #include "LYImGuiUtils/ImGuiDrawHelpers.h" @@ -92,8 +93,36 @@ namespace ImGui void ImGuiLYCommonMenu::OnImGuiUpdate() { + float dpiScalingFactor = 1.0f; + ImGuiManagerBus::BroadcastResult(dpiScalingFactor, &ImGuiManagerBus::Events::GetDpiScalingFactor); + + // Utility function to calculate the size in device pixels based on the current DPI + const auto dpiAwareSizeFn = [dpiScalingFactor](float size) + { + return dpiScalingFactor * size; + }; + + AZStd::optional viewportBorderPaddingOpt; + AzFramework::ViewportBorderRequestBus::BroadcastResult( + viewportBorderPaddingOpt, &AzFramework::ViewportBorderRequestBus::Events::GetViewportBorderPadding); + + AzFramework::ViewportBorderPadding viewportBorderPadding = viewportBorderPaddingOpt.value_or(AzFramework::ViewportBorderPadding{}); + // Utility function to return the current offset (scaled by DPI) if a viewport border + // is active (otherwise 0.0) + auto dpiAwareBorderOffsetFn = [&viewportBorderPaddingOpt, &dpiAwareSizeFn](float size) + { + return viewportBorderPaddingOpt.has_value() ? dpiAwareSizeFn(size) : 0.0f; + }; + + // Shift the menu down if a viewport border is active + ImVec2 cachedSafeArea = ImGui::GetStyle().DisplaySafeAreaPadding; + ImGui::GetStyle().DisplaySafeAreaPadding = ImVec2(cachedSafeArea.x, cachedSafeArea.y + dpiAwareSizeFn(viewportBorderPadding.m_top)); + if (ImGui::BeginMainMenuBar()) { + // Constant to shift right aligned menu items by (distance to the left) when a viewport border is active + const float rightAlignedBorderOffset = dpiAwareBorderOffsetFn(36.0f); + // Get Discrete Input state now, we will use it both inside the ImGui SubMenu, and along the main task bar ( when it is on ) bool discreteInputEnabled = false; ImGuiManagerBus::BroadcastResult(discreteInputEnabled, &IImGuiManager::GetEnableDiscreteInputMode); @@ -101,7 +130,8 @@ namespace ImGui // Input Mode Display { const float prevCursorPos = ImGui::GetCursorPosX(); - ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 300.0f); + ImGui::SetCursorPosX( + ImGui::GetWindowWidth() - dpiAwareSizeFn(300.0f + viewportBorderPadding.m_right) - rightAlignedBorderOffset); AZStd::string inputTitle = "Input: "; if (!discreteInputEnabled) @@ -152,7 +182,7 @@ namespace ImGui } // Add some space before the first menu so it won't overlap with view control buttons - ImGui::SetCursorPosX(40.f); + ImGui::SetCursorPosX(dpiAwareSizeFn(40.0f + viewportBorderPadding.m_left)); // Main Open 3D Engine menu if (ImGui::BeginMenu("O3DE")) @@ -557,11 +587,12 @@ namespace ImGui // End LY Common Tools menu ImGui::EndMenu(); } - const int labelSize{ 100 }; - const int buttonSize{ 40 }; + + const float labelSize = dpiAwareSizeFn(100.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; + const float buttonSize = dpiAwareSizeFn(40.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiMainMenuUpdate); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - labelSize); - float backgroundHeight = ImGui::GetTextLineHeight() + 3; + float backgroundHeight = ImGui::GetTextLineHeight() + dpiAwareSizeFn(3.0f); ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled( cursorPos, ImVec2(cursorPos.x + labelSize, cursorPos.y + backgroundHeight), IM_COL32(0, 115, 187, 255)); @@ -580,6 +611,9 @@ namespace ImGui ImGui::EndMainMenuBar(); } + // Restore original safe area. + ImGui::GetStyle().DisplaySafeAreaPadding = cachedSafeArea; + // Update Contextual Controller Window if (m_controllerLegendWindowVisible) {