// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Widgets/SWidget.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "SlotBase.h" #include "Layout/Children.h" #include "Widgets/SPanel.h" #include "GameplayTaskTypes.h" #include "UObject/WeakInterfacePtr.h" #include "IndicatorDescriptor.h" #include "AsyncMixin.h" #include "Blueprint/UserWidgetPool.h" class FArrangedChildren; class SActorCanvas; class ULyraIndicatorManagerComponent; class SActorCanvas : public SPanel, public FAsyncMixin, public FGCObject { public: /** ActorCanvas-specific slot class */ class FSlot : public TSlotBase { public: FSlot(UIndicatorDescriptor* InIndicator) : TSlotBase() , Indicator(InIndicator) , ScreenPosition(FVector2D::ZeroVector) , Depth(0) , Priority(0.f) , bIsIndicatorVisible(true) , bInFrontOfCamera(true) , bHasValidScreenPosition(false) , bDirty(true) , bWasIndicatorClamped(false) , bWasIndicatorClampedStatusChanged(false) { } SLATE_SLOT_BEGIN_ARGS(FSlot, TSlotBase) SLATE_SLOT_END_ARGS() using TSlotBase::Construct; bool GetIsIndicatorVisible() const { return bIsIndicatorVisible; } void SetIsIndicatorVisible(bool bVisible) { if (bIsIndicatorVisible != bVisible) { bIsIndicatorVisible = bVisible; bDirty = true; } RefreshVisibility(); } FVector2D GetScreenPosition() const { return ScreenPosition; } void SetScreenPosition(FVector2D InScreenPosition) { if (ScreenPosition != InScreenPosition) { ScreenPosition = InScreenPosition; bDirty = true; } } double GetDepth() const { return Depth; } void SetDepth(double InDepth) { if (Depth != InDepth) { Depth = InDepth; bDirty = true; } } int32 GetPriority() const { return Priority; } void SetPriority(int32 InPriority) { if (Priority != InPriority) { Priority = InPriority; bDirty = true; } } bool GetInFrontOfCamera() const { return bInFrontOfCamera; } void SetInFrontOfCamera(bool bInFront) { if (bInFrontOfCamera != bInFront) { bInFrontOfCamera = bInFront; bDirty = true; } RefreshVisibility(); } bool HasValidScreenPosition() const { return bHasValidScreenPosition; } void SetHasValidScreenPosition(bool bValidScreenPosition) { if (bHasValidScreenPosition != bValidScreenPosition) { bHasValidScreenPosition = bValidScreenPosition; bDirty = true; } RefreshVisibility(); } bool bIsDirty() const { return bDirty; } void ClearDirtyFlag() { bDirty = false; } bool WasIndicatorClamped() const { return bWasIndicatorClamped; } void SetWasIndicatorClamped(bool bWasClamped) const { if (bWasClamped != bWasIndicatorClamped) { bWasIndicatorClamped = bWasClamped; bWasIndicatorClampedStatusChanged = true; } } bool WasIndicatorClampedStatusChanged() const { return bWasIndicatorClampedStatusChanged; } void ClearIndicatorClampedStatusChangedFlag() { bWasIndicatorClampedStatusChanged = false; } private: void RefreshVisibility() { const bool bIsVisible = bIsIndicatorVisible && bHasValidScreenPosition; GetWidget()->SetVisibility(bIsVisible ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed); } //Kept Alive by SActorCanvas::AddReferencedObjects UIndicatorDescriptor* Indicator; FVector2D ScreenPosition; double Depth; int32 Priority; uint8 bIsIndicatorVisible : 1; uint8 bInFrontOfCamera : 1; uint8 bHasValidScreenPosition : 1; uint8 bDirty : 1; /** * Cached & frame-deferred value of whether the indicator was visually screen clamped last frame or not; * Semi-hacky mutable implementation as it is cached during a const paint operation */ mutable uint8 bWasIndicatorClamped : 1; mutable uint8 bWasIndicatorClampedStatusChanged : 1; friend class SActorCanvas; }; /** ActorCanvas-specific slot class */ class FArrowSlot : public TSlotBase { }; /** Begin the arguments for this slate widget */ SLATE_BEGIN_ARGS(SActorCanvas) { _Visibility = EVisibility::HitTestInvisible; } /** Indicates that we have a slot that this widget supports */ SLATE_SLOT_ARGUMENT(SActorCanvas::FSlot, Slots) /** This always goes at the end */ SLATE_END_ARGS() SActorCanvas() : CanvasChildren(this) , ArrowChildren(this) , AllChildren(this) { AllChildren.AddChildren(CanvasChildren); AllChildren.AddChildren(ArrowChildren); } void Construct(const FArguments& InArgs, const FLocalPlayerContext& InCtx, const FSlateBrush* ActorCanvasArrowBrush); // SWidget Interface virtual void OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const override; virtual FVector2D ComputeDesiredSize(float) const override { return FVector2D::ZeroVector; } virtual FChildren* GetChildren() override { return &AllChildren; } virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const; // End SWidget void SetDrawElementsInOrder(bool bInDrawElementsInOrder) { bDrawElementsInOrder = bInDrawElementsInOrder; } virtual FString GetReferencerName() const override; virtual void AddReferencedObjects( FReferenceCollector& Collector ) override; private: void OnIndicatorAdded(UIndicatorDescriptor* Indicator); void OnIndicatorRemoved(UIndicatorDescriptor* Indicator); void AddIndicatorForEntry(UIndicatorDescriptor* Indicator); void RemoveIndicatorForEntry(UIndicatorDescriptor* Indicator); using FScopedWidgetSlotArguments = TPanelChildren::FScopedWidgetSlotArguments; FScopedWidgetSlotArguments AddActorSlot(UIndicatorDescriptor* Indicator); int32 RemoveActorSlot(const TSharedRef& SlotWidget); void SetShowAnyIndicators(bool bIndicators); EActiveTimerReturnType UpdateCanvas(double InCurrentTime, float InDeltaTime); /** Helper function for calculating the offset */ void GetOffsetAndSize(const UIndicatorDescriptor* Indicator, FVector2D& OutSize, FVector2D& OutOffset, FVector2D& OutPaddingMin, FVector2D& OutPaddingMax) const; void UpdateActiveTimer(); private: TArray AllIndicators; TArray InactiveIndicators; FLocalPlayerContext LocalPlayerContext; TWeakObjectPtr IndicatorComponentPtr; /** All the slots in this canvas */ TPanelChildren CanvasChildren; mutable TPanelChildren ArrowChildren; FCombinedChildren AllChildren; FUserWidgetPool IndicatorPool; const FSlateBrush* ActorCanvasArrowBrush = nullptr; mutable int32 NextArrowIndex = 0; mutable int32 ArrowIndexLastUpdate = 0; /** Whether to draw elements in the order they were added to canvas. Note: Enabling this will disable batching and will cause a greater number of drawcalls */ bool bDrawElementsInOrder = false; bool bShowAnyIndicators = false; mutable TOptional OptionalPaintGeometry; TSharedPtr TickHandle; };