#include "PromptWidget.h" #include "UtilityLibrary.h" #include "PlayerControllerBase.h" #include "InputAction.h" #include "InputMappingContext.h" #include "Widgets/SOverlay.h" #include "Widgets/Images/SImage.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SScaleBox.h" // // Atlas contents: // // Row 1: Keyboard Button no Glyph, Left Mouse, Middle Mouse, Right Mouse // Row 2: Left Trigger, Right Trigger, Left Shoulder, Right Shoulder // Row 3: XBFace Down, XBFace Left, XBFace Up, XBFace Right // Row 4: PSFace Down, PSFace Left, PSFace Up, PSFace Right // Row 5: Arrow Down, Arrow Left, Arrow Up, Arrow Right // Row 6: DPad Down, DPad Left, DPad Up, DPad Right // Row 7: Space Bar, unused, unused, unused // Row 8: unused, unused, unused, unused. int32 UlxPromptWidget::ChooseIcon(bool Playstation, FKey Key) const { // Mouse buttons (row 1) if (Key == EKeys::LeftMouseButton) return 1; if (Key == EKeys::MiddleMouseButton) return 2; if (Key == EKeys::RightMouseButton) return 3; // Gamepad triggers and shoulders (row 2) if (Key == EKeys::Gamepad_LeftTrigger) return 4; if (Key == EKeys::Gamepad_RightTrigger) return 5; if (Key == EKeys::Gamepad_LeftShoulder) return 6; if (Key == EKeys::Gamepad_RightShoulder) return 7; // Gamepad Face buttons (rows 3 and 4) if (Key == EKeys::Gamepad_FaceButton_Bottom) return Playstation ? 12 : 8; if (Key == EKeys::Gamepad_FaceButton_Left) return Playstation ? 13 : 9; if (Key == EKeys::Gamepad_FaceButton_Top) return Playstation ? 14 : 10; if (Key == EKeys::Gamepad_FaceButton_Right) return Playstation ? 15 : 11; // Arrow keys (row 5) if (Key == EKeys::Down) return 16; if (Key == EKeys::Left) return 17; if (Key == EKeys::Up) return 18; if (Key == EKeys::Right) return 19; // Gamepad D-Pad (row 6) if (Key == EKeys::Gamepad_DPad_Down) return 20; if (Key == EKeys::Gamepad_DPad_Left) return 21; if (Key == EKeys::Gamepad_DPad_Up) return 22; if (Key == EKeys::Gamepad_DPad_Right) return 23; // Space bar (row 7) if (Key == EKeys::SpaceBar) return 24; // No icon for this key. Use the blank icon with a label. return 0; } void UlxPromptWidget::ChooseAppearance(int32 &OutIcon, FString &OutGlyph) { FKey Key = (ControllerType == ElxControllerType::KeyboardMouse) ? KeyboardKey : GamepadKey; OutIcon = ChooseIcon(ControllerType == ElxControllerType::PlayStationGamepad, Key); if (OutIcon == 0) OutGlyph = Key.GetDisplayName().ToString(); else OutGlyph = TEXT(""); } FBox2f UlxPromptWidget::GetIconUVs(int32 IconIndex) { const float ColWidth = 1.0f / 4.0f; const float RowHeight = 1.0f / 8.0f; int32 Col = IconIndex % 4; int32 Row = IconIndex / 4; float U = Col * ColWidth; float V = Row * RowHeight; return FBox2f(FVector2f(U, V), FVector2f(U + ColWidth, V + RowHeight)); } FMargin UlxPromptWidget::GetScaledMargins() const { return FMargin( GlyphMargins.Left * Size.X, GlyphMargins.Top * Size.Y, GlyphMargins.Right * Size.X, GlyphMargins.Bottom * Size.Y); } void UlxPromptWidget::SynchronizeProperties() { Super::SynchronizeProperties(); if (!MyImage.IsValid()) return; int32 Icon = 0; FString Glyph; ChooseAppearance(Icon, Glyph); MyBrush.SetUVRegion(GetIconUVs(Icon)); MyImage->InvalidateImage(); MyBox->SetWidthOverride(Size.X); MyBox->SetHeightOverride(Size.Y); const float Extra = Depressed ? 0.05f : 0.0f; const FMargin ExtraPadding(Extra * Size.X, Extra * Size.Y, Extra * Size.X, Extra * Size.Y); MyImageSlot->SetPadding(ExtraPadding); MyGlyphSlot->SetPadding(GetScaledMargins() + ExtraPadding); MyGlyph->SetColorAndOpacity(GlyphColor); if (!Glyph.IsEmpty()) { MyGlyph->SetText(FText::FromString(Glyph)); MyGlyph->SetVisibility(EVisibility::HitTestInvisible); } else { MyGlyph->SetVisibility(EVisibility::Collapsed); } } void UlxPromptWidget::SetSize(FVector2D InSize) { if (Size != InSize) { Size = InSize; SynchronizeProperties(); } } void UlxPromptWidget::SetGlyphMargins(FMargin InMargins) { if (GlyphMargins != InMargins) { GlyphMargins = InMargins; SynchronizeProperties(); } } void UlxPromptWidget::SetGlyphColor(FLinearColor InColor) { if (GlyphColor != InColor) { GlyphColor = InColor; SynchronizeProperties(); } } void UlxPromptWidget::SetDepressed(bool InDepressed) { if (Depressed != InDepressed) { Depressed = InDepressed; SynchronizeProperties(); } } void UlxPromptWidget::SetKeys(FKey InGamepadKey, FKey InKeyboardKey) { if ((GamepadKey != InGamepadKey) || (KeyboardKey != InKeyboardKey)) { GamepadKey = InGamepadKey; KeyboardKey = InKeyboardKey; SynchronizeProperties(); } } void UlxPromptWidget::SetKeysFromBindings(const UInputMappingContext* InputMappingContext, const UInputAction* EnhancedInputAction) { check(InputMappingContext); check(EnhancedInputAction); FKey NewFirstKey; FKey NewGamepadKey; FKey NewKeyboardKey; for (const FEnhancedActionKeyMapping& Mapping : InputMappingContext->GetMappings()) { if (Mapping.Action != EnhancedInputAction) continue; FKey Key = Mapping.Key; if (Key.IsDigital()) { if (!NewFirstKey.IsValid()) NewFirstKey = Mapping.Key; if (Key.IsTouch()) { /* not supported */ } else if (Key.IsGesture()) { /* not supported */ } else if (Key.IsGamepadKey()) { if (!NewGamepadKey.IsValid()) NewGamepadKey = Key; } else { if (!NewKeyboardKey.IsValid()) NewKeyboardKey = Key; } } } if (!NewGamepadKey.IsValid()) NewGamepadKey = NewFirstKey; if (!NewKeyboardKey.IsValid()) NewKeyboardKey = NewFirstKey; SetKeys(NewGamepadKey, NewKeyboardKey); } bool UlxPromptWidget::OnTick(float DeltaTime) { ElxControllerType Type = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); if (ControllerType != Type) { ControllerType = Type; SynchronizeProperties(); } return true; } TSharedRef UlxPromptWidget::RebuildWidget() { if (!IsDesignTime()) { ControllerType = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &UlxPromptWidget::OnTick)); } MyBrush.SetResourceObject(ButtonAtlas.Get()); SAssignNew(MyImage, SImage).Image(&MyBrush); SAssignNew(MyGlyph, STextBlock); SAssignNew(MyScaleBox, SScaleBox) .Stretch(EStretch::ScaleToFit) [ MyGlyph.ToSharedRef() ]; MyOverlay = SNew(SOverlay); MyOverlay->AddSlot().Expose(MyImageSlot) [ MyImage.ToSharedRef() ]; MyOverlay->AddSlot().HAlign(HAlign_Fill).VAlign(VAlign_Fill).Expose(MyGlyphSlot) [ MyScaleBox.ToSharedRef() ]; SAssignNew(MyBox, SBox) [ MyOverlay.ToSharedRef() ]; SynchronizeProperties(); return MyBox.ToSharedRef(); } void UlxPromptWidget::ReleaseSlateResources(bool bReleaseChildren) { Super::ReleaseSlateResources(bReleaseChildren); if (!IsDesignTime()) { FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); } MyBox.Reset(); MyOverlay.Reset(); MyImage.Reset(); MyScaleBox.Reset(); MyGlyph.Reset(); MyImageSlot = nullptr; MyGlyphSlot = nullptr; }