// Copyright Epic Games, Inc. All Rights Reserved. #include "STransformViewportToolbar.h" #include "EditorInteractiveGizmoManager.h" #include "EngineDefines.h" #include "Modules/ModuleManager.h" #include "Widgets/SBoxPanel.h" #include "Styling/SlateTypes.h" #include "Framework/Commands/UIAction.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Images/SImage.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SUniformGridPanel.h" #include "Framework/MultiBox/MultiBoxDefs.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SSlider.h" #include "Styling/AppStyle.h" #include "Editor/UnrealEdEngine.h" #include "EditorViewportClient.h" #include "UnrealEdGlobals.h" #include "SEditorViewport.h" #include "EditorViewportCommands.h" #include "SViewportToolBarIconMenu.h" #include "SViewportToolBarComboMenu.h" #include "ISettingsModule.h" #include "Widgets/Input/SNumericEntryBox.h" #include "Settings/EditorProjectSettings.h" #include "LevelEditor.h" #include "LevelEditorActions.h" #include "Styling/ToolBarStyle.h" #include "SEditorViewportToolBarMenu.h" #include "ViewportToolbar/UnrealEdViewportToolbar.h" #define LOCTEXT_NAMESPACE "TransformToolBar" void STransformViewportToolBar::Construct( const FArguments& InArgs ) { Viewport = InArgs._Viewport; CommandList = InArgs._CommandList; OnCamSpeedChanged = InArgs._OnCamSpeedChanged; OnCamSpeedScalarChanged = InArgs._OnCamSpeedScalarChanged; ChildSlot [ MakeTransformToolBar(InArgs._Extenders) ]; SViewportToolBar::Construct(SViewportToolBar::FArguments()); } TSharedRef< SWidget > STransformViewportToolBar::MakeSurfaceSnappingButton() { check(!SurfaceSnappingMenu.IsValid()); SurfaceSnappingMenu = SNew(SEditorViewportToolbarMenu) .ParentToolBar(SharedThis(this)) .Image("EditorViewport.ToggleSurfaceSnapping") .ToolTipText(LOCTEXT("SnapToSurfaceMenu_ToolTip", "Control how objects snap to surfaces")) .OnGetMenuContent(this, &STransformViewportToolBar::GenerateSurfaceSnappingMenu) .ForegroundColor(this, &STransformViewportToolBar::GetSurfaceSnappingForegroundColor); return SurfaceSnappingMenu.ToSharedRef(); } TSharedRef STransformViewportToolBar::GenerateSurfaceSnappingMenu() { auto IsSnappingEnabled = [] { return GetDefault()->SnapToSurface.bEnabled; }; const bool bCloseAfterSelection = true; FMenuBuilder MenuBuilder(bCloseAfterSelection, CommandList); MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().SurfaceSnapping); MenuBuilder.BeginSection("SurfaceSnappingSettings", LOCTEXT("SnapToSurfaceSettings", "Settings")); { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().RotateToSurfaceNormal); MenuBuilder.AddWidget( SNew( SBox ) .Padding( FMargin(8.0f, 0.0f, 0.0f, 0.0f) ) .MinDesiredWidth( 100.0f ) [ SNew ( SBorder ) .BorderImage(FAppStyle::Get().GetBrush("Menu.WidgetBorder")) .Padding(FMargin(1.0f)) [ SNew(SNumericEntryBox) .IsEnabled(TAttribute::Create(TAttribute::FGetter::CreateStatic(IsSnappingEnabled))) .Value( TAttribute>::Create(TAttribute>::FGetter::CreateStatic([] { const auto& Settings = GetDefault()->SnapToSurface; return TOptional(Settings.SnapOffsetExtent); })) ) .OnValueChanged(SNumericEntryBox::FOnValueChanged::CreateStatic([](float Val) { GetMutableDefault()->SnapToSurface.SnapOffsetExtent = Val; })) .MinValue(0.f) .MaxValue(static_cast(HALF_WORLD_MAX)) .MaxSliderValue(1000.f) // 'Sensible' range for the slider (10m) .AllowSpin(true) ] ], LOCTEXT("SnapToSurfaceSettings_Offset", "Surface Offset") ); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } FSlateColor STransformViewportToolBar::GetSurfaceSnappingForegroundColor() const { static const FCheckBoxStyle& ViewportToolbarCheckStyle = FAppStyle::Get().GetWidgetStyle("EditorViewportToolBar").ToggleButton; const bool bSurfaceSnappingEnabled = GetDefault()->SnapToSurface.bEnabled; bool bShouldAppearHovered = SurfaceSnappingMenu->IsHovered() || SurfaceSnappingMenu->IsMenuOpen(); // Hovered and checked if (bShouldAppearHovered && bSurfaceSnappingEnabled) { return ViewportToolbarCheckStyle.CheckedHoveredForeground; } // Not hovered and checked else if (bSurfaceSnappingEnabled) { return ViewportToolbarCheckStyle.CheckedForeground; } // Hovered not checked else if (bShouldAppearHovered) { return ViewportToolbarCheckStyle.HoveredForeground; } // Not hovered not checked else { return ViewportToolbarCheckStyle.ForegroundColor; } } TSharedRef< SWidget > STransformViewportToolBar::MakeTransformToolBar( const TSharedPtr< FExtender > InExtenders ) { FSlimHorizontalToolBarBuilder ToolbarBuilder( CommandList, FMultiBoxCustomization::None, InExtenders ); // Use a custom style FName ToolBarStyle = "EditorViewportToolBar"; ToolbarBuilder.SetStyle(&FAppStyle::Get(), ToolBarStyle); ToolbarBuilder.SetLabelVisibility(EVisibility::Collapsed); // Transform controls cannot be focusable as it fights with the press space to change transform mode feature ToolbarBuilder.SetIsFocusable( false ); ToolbarBuilder.BeginSection("Transform"); { ToolbarBuilder.BeginBlockGroup(); // Select Mode static FName SelectModeName = FName(TEXT("SelectMode")); ToolbarBuilder.AddToolBarButton(FEditorViewportCommands::Get().SelectMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), SelectModeName); // Translate Mode static FName TranslateModeName = FName(TEXT("TranslateMode")); ToolbarBuilder.AddToolBarButton( FEditorViewportCommands::Get().TranslateMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), TranslateModeName ); // TranslateRotate Mode static FName TranslateRotateModeName = FName(TEXT("TranslateRotateMode")); ToolbarBuilder.AddToolBarButton( FEditorViewportCommands::Get().TranslateRotateMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), TranslateRotateModeName ); // 2D Mode static FName TranslateRotate2DModeName = FName(TEXT("TranslateRotate2DMode")); ToolbarBuilder.AddToolBarButton(FEditorViewportCommands::Get().TranslateRotate2DMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), TranslateRotate2DModeName); // Rotate Mode static FName RotateModeName = FName(TEXT("RotateMode")); ToolbarBuilder.AddToolBarButton( FEditorViewportCommands::Get().RotateMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), RotateModeName ); // Scale Mode static FName ScaleModeName = FName(TEXT("ScaleMode")); ToolbarBuilder.AddToolBarButton( FEditorViewportCommands::Get().ScaleMode, NAME_None, TAttribute(), TAttribute(), TAttribute(), ScaleModeName ); ToolbarBuilder.EndBlockGroup(); ToolbarBuilder.AddSeparator(); ToolbarBuilder.SetIsFocusable( true ); TAttribute CoordSystemToolTip = TAttribute::CreateLambda([] { if (UEditorInteractiveGizmoManager::UsesNewTRSGizmos()) { return LOCTEXT( "CycleTransformGizmoCoordSystemWithParent_ToolTip", "Cycles the transform gizmo coordinate systems between world, local, parent and explicit space"); } return FEditorViewportCommands::Get().CycleTransformGizmoCoordSystem->GetDescription(); }); ToolbarBuilder.AddToolBarButton( FEditorViewportCommands::Get().CycleTransformGizmoCoordSystem, NAME_None, TAttribute(), CoordSystemToolTip, TAttribute(this, &STransformViewportToolBar::GetLocalToWorldIcon), FName(TEXT("CycleTransformGizmoCoordSystem")), // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( []( FMenuBuilder& InMenuBuilder ) { InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().RelativeCoordinateSystem_World); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().RelativeCoordinateSystem_Local); } )); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("LocationGridSnap"); { static FName SurfaceSnapName = FName(TEXT("SurfaceSnap")); ToolbarBuilder.AddWidget( MakeSurfaceSnappingButton(), SurfaceSnapName, false, HAlign_Fill, FNewMenuDelegate::CreateLambda( [this]( FMenuBuilder& InMenuBuilder ) { InMenuBuilder.AddWrapperSubMenu( LOCTEXT("SnapToSurfaceMenuSettings", "Surface Snap Settings"), LOCTEXT("SnapToSurfaceMenuSettings_Tooltip", "Snap To Surface Settings"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::GenerateSurfaceSnappingMenu), FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.ToggleSurfaceSnapping") ); } )); ToolbarBuilder.AddSeparator(); // Grab the existing UICommand TSharedPtr Command = FEditorViewportCommands::Get().LocationGridSnap; static FName PositionSnapName = FName(TEXT("PositionSnap")); // Setup a GridSnapSetting with the UICommand ToolbarBuilder.AddWidget( SNew(SViewportToolBarComboMenu) .IsChecked(this, &STransformViewportToolBar::IsLocationGridSnapChecked) .OnCheckStateChanged(this, &STransformViewportToolBar::HandleToggleLocationGridSnap) .Label(TAttribute::Create(&UE::UnrealEd::GetLocationGridLabel)) .OnGetMenuContent(this, &STransformViewportToolBar::FillLocationGridSnapMenu) .ToggleButtonToolTip(Command->GetDescription()) .MenuButtonToolTip(LOCTEXT("LocationGridSnap_ToolTip", "Set the Position Grid Snap value")) .Icon(Command->GetIcon()) .MinDesiredButtonWidth(24.0f) .ParentToolBar(SharedThis(this)), PositionSnapName, false, HAlign_Fill, // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( [this, Command](FMenuBuilder& InMenuBuilder) { FUIAction Action; Action.ExecuteAction = FExecuteAction::CreateLambda( [this]() { constexpr ECheckBoxState UnusedCheckedState = ECheckBoxState::Unchecked; this->HandleToggleLocationGridSnap(UnusedCheckedState); } ); Action.GetActionCheckState = FGetActionCheckState::CreateRaw(this, &STransformViewportToolBar::IsLocationGridSnapChecked); const FName UnusedExtensionHook = NAME_None; InMenuBuilder.AddMenuEntry( Command->GetLabel(), Command->GetDescription(), Command->GetIcon(), Action, UnusedExtensionHook, EUserInterfaceActionType::ToggleButton ); InMenuBuilder.AddWrapperSubMenu( LOCTEXT("GridSnapMenuSettings", "Grid Snap Settings"), LOCTEXT("GridSnapMenuSettings_ToolTip", "Set the Position Grid Snap value"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::FillLocationGridSnapMenu), FSlateIcon(Command->GetIcon()) ); } ) ); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("RotationGridSnap"); { // Grab the existing UICommand TSharedPtr Command = FEditorViewportCommands::Get().RotationGridSnap; static FName RotationSnapName = FName(TEXT("RotationSnap")); // Setup a GridSnapSetting with the UICommand ToolbarBuilder.AddWidget( SNew(SViewportToolBarComboMenu) .IsChecked(this, &STransformViewportToolBar::IsRotationGridSnapChecked) .OnCheckStateChanged(this, &STransformViewportToolBar::HandleToggleRotationGridSnap) .Label(TAttribute::Create(&UE::UnrealEd::GetRotationGridLabel)) .OnGetMenuContent(this, &STransformViewportToolBar::FillRotationGridSnapMenu) .ToggleButtonToolTip(Command->GetDescription()) .MenuButtonToolTip(LOCTEXT("RotationGridSnap_ToolTip", "Set the Rotation Grid Snap value")) .Icon(Command->GetIcon()) .ParentToolBar(SharedThis(this)), RotationSnapName, false, HAlign_Fill, // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( [this, Command](FMenuBuilder& InMenuBuilder) { FUIAction Action; Action.ExecuteAction = FExecuteAction::CreateLambda( [this]() { constexpr ECheckBoxState UnusedCheckedState = ECheckBoxState::Unchecked; this->HandleToggleRotationGridSnap(UnusedCheckedState); } ); Action.GetActionCheckState = FGetActionCheckState::CreateRaw(this, &STransformViewportToolBar::IsRotationGridSnapChecked); const FName UnusedExtensionHook = NAME_None; InMenuBuilder.AddMenuEntry( Command->GetLabel(), Command->GetDescription(), Command->GetIcon(), Action, UnusedExtensionHook, EUserInterfaceActionType::ToggleButton ); InMenuBuilder.AddWrapperSubMenu( LOCTEXT("RotationGridSnapMenuSettings", "Rotation Snap Settings"), LOCTEXT("RotationGridSnapMenuSettings_ToolTip", "Adjust the Grid Settings for Rotation Snap"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::FillRotationGridSnapMenu), FSlateIcon(Command->GetIcon()) ); } ) ); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("Layer2DSnap"); { // Grab the existing UICommand TSharedPtr Command = FEditorViewportCommands::Get().Layer2DSnap; static FName Layer2DSnapName = FName(TEXT("Layer2DSnap")); TSharedRef SnapLayerPickerWidget = SNew(SViewportToolBarComboMenu) .Visibility(this, &STransformViewportToolBar::IsLayer2DSnapVisible) .IsChecked(this, &STransformViewportToolBar::IsLayer2DSnapChecked) .OnCheckStateChanged(this, &STransformViewportToolBar::HandleToggleLayer2DSnap) .Label(this, &STransformViewportToolBar::GetLayer2DLabel) .OnGetMenuContent(this, &STransformViewportToolBar::FillLayer2DSnapMenu) .ToggleButtonToolTip(Command->GetDescription()) .MenuButtonToolTip(LOCTEXT("Layer2DSnap_ToolTip", "Set the 2d layer snap value")) .Icon(Command->GetIcon()) .ParentToolBar(SharedThis(this)) .MinDesiredButtonWidth(88.0f); ToolbarBuilder.AddWidget( SnapLayerPickerWidget, Layer2DSnapName, false, HAlign_Fill, // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( [this, Command]( FMenuBuilder& InMenuBuilder ) { if (IsLayer2DSnapVisible() == EVisibility::Visible) { FUIAction Action; Action.ExecuteAction = FExecuteAction::CreateLambda( [this]() { constexpr ECheckBoxState UnusedCheckedState = ECheckBoxState::Unchecked; this->HandleToggleLayer2DSnap(UnusedCheckedState); } ); Action.GetActionCheckState = FGetActionCheckState::CreateRaw(this, &STransformViewportToolBar::IsLayer2DSnapChecked); const FName UnusedExtensionHook = NAME_None; InMenuBuilder.AddMenuEntry( Command->GetLabel(), Command->GetDescription(), Command->GetIcon(), Action, UnusedExtensionHook, EUserInterfaceActionType::ToggleButton ); InMenuBuilder.AddWrapperSubMenu( LOCTEXT("Layer2DSnapMenuSettings", "Layer 2D Snap Settings"), LOCTEXT("Layer2DSnapMenuSettings_ToolTip", "Adjust the Grid Settings for Layer 2D Snap"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::FillLayer2DSnapMenu), FSlateIcon(Command->GetIcon()) ); } } )); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("ScaleGridSnap"); { // Grab the existing UICommand TSharedPtr Command = FEditorViewportCommands::Get().ScaleGridSnap; static FName ScaleSnapName = FName(TEXT("ScaleSnap")); // Setup a GridSnapSetting with the UICommand ToolbarBuilder.AddWidget( SNew(SViewportToolBarComboMenu) .Cursor(EMouseCursor::Default) .IsChecked(this, &STransformViewportToolBar::IsScaleGridSnapChecked) .OnCheckStateChanged(this, &STransformViewportToolBar::HandleToggleScaleGridSnap) .Label(TAttribute::Create(&UE::UnrealEd::GetScaleGridLabel)) .OnGetMenuContent(this, &STransformViewportToolBar::FillScaleGridSnapMenu) .ToggleButtonToolTip(Command->GetDescription()) .MenuButtonToolTip(LOCTEXT("ScaleGridSnap_ToolTip", "Set scaling options")) .Icon(Command->GetIcon()) .MinDesiredButtonWidth(24.0f) .ParentToolBar(SharedThis(this)), ScaleSnapName, false, HAlign_Fill, // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( [this, Command](FMenuBuilder& InMenuBuilder) { FUIAction Action; Action.ExecuteAction = FExecuteAction::CreateLambda( [this]() { constexpr ECheckBoxState UnusedCheckedState = ECheckBoxState::Unchecked; this->HandleToggleScaleGridSnap(UnusedCheckedState); } ); Action.GetActionCheckState = FGetActionCheckState::CreateRaw(this, &STransformViewportToolBar::IsScaleGridSnapChecked); const FName UnusedExtensionHook = NAME_None; InMenuBuilder.AddMenuEntry( Command->GetLabel(), Command->GetDescription(), Command->GetIcon(), Action, UnusedExtensionHook, EUserInterfaceActionType::ToggleButton ); InMenuBuilder.AddWrapperSubMenu( LOCTEXT("ScaleGridSnapMenuSettings", "Scale Snap Settings"), LOCTEXT("ScaleGridSnapMenuSettings_ToolTip", "Adjust the Grid Settings for Scale Snap"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::FillScaleGridSnapMenu), FSlateIcon(Command->GetIcon()) ); } ) ); } ToolbarBuilder.EndSection(); ToolbarBuilder.BeginSection("CameraSpeed"); { static FName CameraSpeedName = FName(TEXT("CameraSpeed")); // Camera speed ToolbarBuilder.AddWidget( SNew(SEditorViewportToolbarMenu) .ParentToolBar(SharedThis(this)) .AddMetaData(FTagMetaData(TEXT("CameraSpeedButton"))) .ToolTipText(UE::UnrealEd::GetCameraSpeedTooltip()) .LabelIcon(FAppStyle::Get().GetBrush("EditorViewport.CamSpeedSetting")) .Label(this, &STransformViewportToolBar::GetCameraSpeedLabel) // Anchor to the right, otherwise the slider in this menu will jitter when the label width changes .MenuPlacement(MenuPlacement_BelowRightAnchor) .OnGetMenuContent(this, &STransformViewportToolBar::FillCameraSpeedMenu), CameraSpeedName, false, HAlign_Fill, // explictly specify what this widget should look like as a menu item FNewMenuDelegate::CreateLambda( [this]( FMenuBuilder& InMenuBuilder ) { InMenuBuilder.AddWrapperSubMenu( LOCTEXT("CameraSpeedMenuSettings", "Camera Speed Settings"), LOCTEXT("CameraSpeedMenuSettings_ToolTip", "Adjust the camera navigation speed"), FOnGetContent::CreateSP(this, &STransformViewportToolBar::FillCameraSpeedMenu), FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.CamSpeedSetting") ); } )); } ToolbarBuilder.EndSection(); return ToolbarBuilder.MakeWidget(); } TSharedRef STransformViewportToolBar::FillCameraSpeedMenu() { TSharedRef ReturnWidget = SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("Menu.Background"))) [ SNew( SVerticalBox ) +SVerticalBox::Slot() .AutoHeight() .Padding( FMargin(8.0f, 2.0f, 60.0f, 2.0f) ) .HAlign( HAlign_Left ) [ SNew( STextBlock ) .Text( LOCTEXT("MouseSettingsCamSpeed", "Camera Speed") ) .Font( FAppStyle::GetFontStyle( TEXT( "MenuItem.Font" ) ) ) ] +SVerticalBox::Slot() .AutoHeight() .Padding( FMargin(8.0f, 4.0f) ) [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .FillWidth(1) .Padding( FMargin(0.0f, 2.0f) ) [ SNew( SBox ) .MinDesiredWidth(220) [ SAssignNew(CamSpeedSlider, SSlider) .Value(this, &STransformViewportToolBar::GetCamSpeedSliderPosition) .OnValueChanged(this, &STransformViewportToolBar::OnSetCamSpeed) ] ] +SHorizontalBox::Slot() .AutoWidth() .Padding( 8.0f, 2.0f, 0.0f, 2.0f) [ SNew( SBox ) .WidthOverride(40) [ SNew( STextBlock ) .Text(this, &STransformViewportToolBar::GetCameraSpeedLabel ) .Font( FAppStyle::GetFontStyle( TEXT( "MenuItem.Font" ) ) ) ] ] ] // Camera Speed Scalar + SVerticalBox::Slot() .AutoHeight() .Padding(FMargin(8.0f, 2.0f, 60.0f, 2.0f)) .HAlign(HAlign_Left) [ SNew(STextBlock) .Text(LOCTEXT("MouseSettingsCamSpeedScalar", "Camera Speed Scalar")) .Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font"))) ] + SVerticalBox::Slot() .AutoHeight() .Padding(FMargin(8.0f, 4.0f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1) .Padding(FMargin(0.0f, 2.0f)) [ SAssignNew(CamSpeedScalarBox, SSpinBox) .MinValue(1.0f) .MaxValue(std::numeric_limits::max()) .MinSliderValue(1.0f) .MaxSliderValue(128.0f) .Value(this, &STransformViewportToolBar::GetCamSpeedScalarBoxValue) .OnValueChanged(this, &STransformViewportToolBar::OnSetCamSpeedScalarBoxValue) .ToolTipText(LOCTEXT("CameraSpeedScalar_ToolTip", "Scalar to increase camera movement range")) ] ] ]; return ReturnWidget; } FReply STransformViewportToolBar::OnCycleCoordinateSystem() { if( Viewport.IsValid() ) { Viewport.Pin()->OnCycleCoordinateSystem(); } return FReply::Handled(); } FSlateIcon STransformViewportToolBar::GetLocalToWorldIcon() const { ECoordSystem CoordSystem = ECoordSystem::COORD_Local; if (TSharedPtr PinnedViewport = Viewport.Pin()) { CoordSystem = PinnedViewport->GetViewportClient()->GetWidgetCoordSystemSpace(); } return UE::UnrealEd::GetIconFromCoordSystem(CoordSystem); } FText STransformViewportToolBar::GetLayer2DLabel() const { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); const ULevelEditor2DSettings* Settings2D = GetDefault(); if (Settings2D->SnapLayers.IsValidIndex(ViewportSettings->ActiveSnapLayerIndex)) { return FText::FromString(Settings2D->SnapLayers[ViewportSettings->ActiveSnapLayerIndex].Name); } return FText(); } FText STransformViewportToolBar::GetCameraSpeedLabel() const { return UE::UnrealEd::GetCameraSpeedLabel(Viewport); } float STransformViewportToolBar::GetCamSpeedSliderPosition() const { float SliderPos = 0.f; auto ViewportPin = Viewport.Pin(); if (ViewportPin.IsValid() && ViewportPin->GetViewportClient().IsValid()) { SliderPos = (ViewportPin->GetViewportClient()->GetCameraSpeedSetting() - 1) / ((float)FEditorViewportClient::MaxCameraSpeeds - 1); } return SliderPos; } void STransformViewportToolBar::OnSetCamSpeed(float NewValue) { auto ViewportPin = Viewport.Pin(); if (ViewportPin.IsValid() && ViewportPin->GetViewportClient().IsValid()) { const int32 OldSpeedSetting = ViewportPin->GetViewportClient()->GetCameraSpeedSetting(); const int32 NewSpeedSetting = NewValue * ((float)FEditorViewportClient::MaxCameraSpeeds - 1) + 1; if (OldSpeedSetting != NewSpeedSetting) { ViewportPin->GetViewportClient()->SetCameraSpeedSetting(NewSpeedSetting); OnCamSpeedChanged.ExecuteIfBound(NewSpeedSetting); } } } FText STransformViewportToolBar::GetCameraSpeedScalarLabel() const { auto ViewportPin = Viewport.Pin(); if (ViewportPin.IsValid() && ViewportPin->GetViewportClient().IsValid()) { return FText::AsNumber(ViewportPin->GetViewportClient()->GetCameraSpeedScalar()); } return FText(); } float STransformViewportToolBar::GetCamSpeedScalarBoxValue() const { float CamSpeedScalar = 1.f; auto ViewportPin = Viewport.Pin(); if (ViewportPin.IsValid() && ViewportPin->GetViewportClient().IsValid()) { CamSpeedScalar = (ViewportPin->GetViewportClient()->GetCameraSpeedScalar()); } return CamSpeedScalar; } void STransformViewportToolBar::OnSetCamSpeedScalarBoxValue(float NewValue) { auto ViewportPin = Viewport.Pin(); if (ViewportPin.IsValid() && ViewportPin->GetViewportClient().IsValid()) { ViewportPin->GetViewportClient()->SetCameraSpeedScalar(NewValue); OnCamSpeedScalarChanged.ExecuteIfBound(NewValue); } } /** * Sets our grid size based on what the user selected in the UI * * @param InIndex The new index of the grid size to use */ void STransformViewportToolBar::SetGridSize( int32 InIndex ) { GEditor->SetGridSize( InIndex ); } /** * Sets the rotation grid size * * @param InIndex The new index of the rotation grid size to use * @param InGridMode Controls whether to use Preset or User selected values */ void STransformViewportToolBar::SetRotationGridSize( int32 InIndex, ERotationGridMode InGridMode ) { GEditor->SetRotGridSize( InIndex, InGridMode ); } /** * Sets the scale grid size * * @param InIndex The new index of the scale grid size to use */ void STransformViewportToolBar::SetScaleGridSize( int32 InIndex ) { GEditor->SetScaleGridSize( InIndex ); } /** * Sets the active 2d snap layer * * @param InIndex The new index of the 2d layer to use */ void STransformViewportToolBar::SetLayer2D( int32 Layer2DIndex ) { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->bEnableLayerSnap = true; ViewportSettings->ActiveSnapLayerIndex = Layer2DIndex; ViewportSettings->PostEditChange(); } /** * Checks to see if the specified grid size index is the current grid size index * * @param GridSizeIndex The grid size index to test * * @return True if the specified grid size index is the current one */ bool STransformViewportToolBar::IsGridSizeChecked( int32 GridSizeIndex ) { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); return (ViewportSettings->CurrentPosGridSize == GridSizeIndex); } /** * Checks to see if the specified rotation grid angle is the current rotation grid angle * * @param GridSizeIndex The grid size index to test * @param InGridMode Controls whether to use Preset or User selected values * * @return True if the specified rotation grid size angle is the current one */ bool STransformViewportToolBar::IsRotationGridSizeChecked( int32 GridSizeIndex, ERotationGridMode GridMode ) { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); return (ViewportSettings->CurrentRotGridSize == GridSizeIndex) && (ViewportSettings->CurrentRotGridMode == GridMode); } /** * Checks to see if the specified scale grid size is the current scale grid size * * @param GridSizeIndex The grid size index to test * * @return True if the specified scale grid size is the current one */ bool STransformViewportToolBar::IsScaleGridSizeChecked(int32 GridSizeIndex) { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); return (ViewportSettings->CurrentScalingGridSize == GridSizeIndex); } bool STransformViewportToolBar::IsLayer2DSelected(int32 LayerIndex) { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); return (ViewportSettings->ActiveSnapLayerIndex == LayerIndex); } void STransformViewportToolBar::TogglePreserveNonUniformScale() { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); ViewportSettings->PreserveNonUniformScale = !ViewportSettings->PreserveNonUniformScale; } bool STransformViewportToolBar::IsPreserveNonUniformScaleChecked() { return GetDefault()->PreserveNonUniformScale; } TSharedRef STransformViewportToolBar::FillLocationGridSnapMenu() { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); TArray GridSizes = ViewportSettings->bUsePowerOf2SnapSize ? ViewportSettings->Pow2GridSizes : ViewportSettings->DecimalGridSizes; UE::UnrealEd::FLocationGridCheckboxListExecuteActionDelegate ExecuteDelegate = UE::UnrealEd::FLocationGridCheckboxListExecuteActionDelegate::CreateLambda( [](int CurrGridSizeIndex) { STransformViewportToolBar::SetGridSize(CurrGridSizeIndex); } ); UE::UnrealEd::FLocationGridCheckboxListIsCheckedDelegate IsCheckedDelegate = UE::UnrealEd::FLocationGridCheckboxListIsCheckedDelegate::CreateLambda( [](int CurrGridSizeIndex) { return STransformViewportToolBar::IsGridSizeChecked(CurrGridSizeIndex); } ); UE::UnrealEd::FLocationGridSnapMenuOptions MenuOptions; MenuOptions.CommandList = CommandList; return UE::UnrealEd::CreateLocationGridSnapMenu(MenuOptions); } TSharedRef STransformViewportToolBar::FillRotationGridSnapMenu() { UE::UnrealEd::FRotationGridCheckboxListExecuteActionDelegate ExecuteDelegate = UE::UnrealEd::FRotationGridCheckboxListExecuteActionDelegate::CreateStatic(&STransformViewportToolBar::SetRotationGridSize ); UE::UnrealEd::FRotationGridCheckboxListIsCheckedDelegate IsCheckedDelegate = UE::UnrealEd::FRotationGridCheckboxListIsCheckedDelegate::CreateStatic( &STransformViewportToolBar::IsRotationGridSizeChecked ); return UE::UnrealEd::CreateRotationGridSnapMenu(ExecuteDelegate, IsCheckedDelegate, CommandList); } TSharedRef STransformViewportToolBar::FillLayer2DSnapMenu() { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); const ULevelEditor2DSettings* Settings2D = GetDefault(); int32 LayerCount = Settings2D->SnapLayers.Num(); const bool bInShouldCloseWindowAfterMenuSelection = true; FMenuBuilder ShowMenuBuilder(bInShouldCloseWindowAfterMenuSelection, CommandList); // rename for (int32 LayerIndex = 0; LayerIndex < LayerCount; ++LayerIndex) { FName LayerName(*Settings2D->SnapLayers[LayerIndex].Name); FUIAction Action(FExecuteAction::CreateStatic(&STransformViewportToolBar::SetLayer2D, LayerIndex), FCanExecuteAction(), FIsActionChecked::CreateStatic(&STransformViewportToolBar::IsLayer2DSelected, LayerIndex)); ShowMenuBuilder.AddMenuEntry(FText::FromName(LayerName), FText::GetEmpty(), FSlateIcon(), Action, NAME_None, EUserInterfaceActionType::RadioButton); } struct FLocalFunctions { static void ShowSettingsViewer() { if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { SettingsModule->ShowViewer("Project", "Editor", "LevelEditor2DSettings"); } } }; FUIAction ShowSettingsAction(FExecuteAction::CreateStatic(&FLocalFunctions::ShowSettingsViewer)); ShowMenuBuilder.AddMenuEntry(LOCTEXT("2DSnap_EditLayer", "Edit Layers..."), FText::GetEmpty(), FSlateIcon(), ShowSettingsAction, NAME_None, EUserInterfaceActionType::Button); // ------------------------------------------------------- ShowMenuBuilder.AddMenuSeparator(); FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().SnapTo2DLayer); ShowMenuBuilder.AddMenuSeparator(); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().MoveSelectionToTop2DLayer); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().MoveSelectionUpIn2DLayers); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().MoveSelectionDownIn2DLayers); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().MoveSelectionToBottom2DLayer); ShowMenuBuilder.AddMenuSeparator(); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().Select2DLayerAbove); ShowMenuBuilder.AddMenuEntry(LevelEditor.GetLevelEditorCommands().Select2DLayerBelow); return ShowMenuBuilder.MakeWidget(); } TSharedRef STransformViewportToolBar::FillScaleGridSnapMenu() { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); TArray GridSizes = ViewportSettings->ScalingGridSizes; UE::UnrealEd::FScaleGridCheckboxListExecuteActionDelegate ExecuteDelegate = UE::UnrealEd::FScaleGridCheckboxListExecuteActionDelegate::CreateLambda( [](int CurrGridScaleIndex) { STransformViewportToolBar::SetScaleGridSize(CurrGridScaleIndex); } ); UE::UnrealEd::FScaleGridCheckboxListIsCheckedDelegate IsCheckedDelegate = UE::UnrealEd::FScaleGridCheckboxListIsCheckedDelegate::CreateLambda( [](int CurrGridScaleIndex) { return STransformViewportToolBar::IsScaleGridSizeChecked(CurrGridScaleIndex); } ); return UE::UnrealEd::CreateScaleGridSnapMenu( ExecuteDelegate, IsCheckedDelegate, GridSizes, true, CommandList, true, FUIAction( FExecuteAction::CreateLambda( []() { ULevelEditorViewportSettings* Settings = GetMutableDefault(); Settings->PreserveNonUniformScale = !Settings->PreserveNonUniformScale; } ), FCanExecuteAction(), FIsActionChecked::CreateLambda( []() { return GetDefault()->PreserveNonUniformScale; } ) ) ); } ECheckBoxState STransformViewportToolBar::IsLocationGridSnapChecked() const { return GetDefault()->GridEnabled ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } ECheckBoxState STransformViewportToolBar::IsRotationGridSnapChecked() const { return GetDefault()->RotGridEnabled ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } ECheckBoxState STransformViewportToolBar::IsLayer2DSnapChecked() const { const ULevelEditorViewportSettings* ViewportSettings = GetDefault(); const ULevelEditor2DSettings* Settings2D = GetDefault(); const bool bChecked = ViewportSettings->bEnableLayerSnap && Settings2D->SnapLayers.IsValidIndex(ViewportSettings->ActiveSnapLayerIndex); return bChecked ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } EVisibility STransformViewportToolBar::IsLayer2DSnapVisible() const { const ULevelEditor2DSettings* Settings2D = GetDefault(); return Settings2D->bEnableSnapLayers ? EVisibility::Visible : EVisibility::Collapsed; } ECheckBoxState STransformViewportToolBar::IsScaleGridSnapChecked() const { return GetDefault()->SnapScaleEnabled ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } void STransformViewportToolBar::HandleToggleLocationGridSnap( ECheckBoxState InState ) { GUnrealEd->Exec( GEditor->GetEditorWorldContext().World(), *FString::Printf( TEXT("MODE GRID=%d"), !GetDefault()->GridEnabled ? 1 : 0 ) ); } void STransformViewportToolBar::HandleToggleRotationGridSnap(ECheckBoxState InState) { GUnrealEd->Exec(GEditor->GetEditorWorldContext().World(), *FString::Printf(TEXT("MODE ROTGRID=%d"), !GetDefault()->RotGridEnabled ? 1 : 0)); } void STransformViewportToolBar::HandleToggleLayer2DSnap(ECheckBoxState InState) { ULevelEditorViewportSettings* ViewportSettings = GetMutableDefault(); const ULevelEditor2DSettings* Settings2D = GetDefault(); if (!ViewportSettings->bEnableLayerSnap && (Settings2D->SnapLayers.Num() > 0)) { ViewportSettings->bEnableLayerSnap = true; ViewportSettings->ActiveSnapLayerIndex = FMath::Clamp(ViewportSettings->ActiveSnapLayerIndex, 0, Settings2D->SnapLayers.Num() - 1); } else { ViewportSettings->bEnableLayerSnap = false; } ViewportSettings->PostEditChange(); } void STransformViewportToolBar::HandleToggleScaleGridSnap(ECheckBoxState InState) { GUnrealEd->Exec( GEditor->GetEditorWorldContext().World(), *FString::Printf( TEXT("MODE SCALEGRID=%d"), !GetDefault()->SnapScaleEnabled ? 1 : 0 ) ); } #undef LOCTEXT_NAMESPACE