// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Misc/Attribute.h" #include "Widgets/InvalidateWidgetReason.h" class FChildren; class SWidget; /** Slot are a container of a SWidget used by the FChildren. */ class FSlotBase { public: SLATECORE_API FSlotBase(); SLATECORE_API FSlotBase(const FChildren& InParent); SLATECORE_API FSlotBase(const TSharedRef& InWidget); FSlotBase& operator=(const FSlotBase&) = delete; FSlotBase(const FSlotBase&) = delete; SLATECORE_API virtual ~FSlotBase(); public: struct FSlotArguments {}; public: /** * Access the FChildren that own the slot. * The owner can be invalid when the slot is not attached. */ const FChildren* GetOwner() const { return Owner; } /** * Access the widget that own the slot. * The owner can be invalid when the slot is not attached. */ SLATECORE_API SWidget* GetOwnerWidget() const; /** * Set the owner of the slot. * Slots cannot be reassigned to different parents. */ SLATECORE_API void SetOwner(const FChildren& Children); /** Attach the child widget the slot now owns. */ inline void AttachWidget(TSharedRef&& InWidget) { DetatchParentFromContent(); Widget = MoveTemp(InWidget); AfterContentOrOwnerAssigned(); } inline void AttachWidget( const TSharedRef& InWidget ) { DetatchParentFromContent(); Widget = InWidget; AfterContentOrOwnerAssigned(); } /** * Access the widget in the current slot. * There will always be a widget in the slot; sometimes it is * the SNullWidget instance. */ inline const TSharedRef& GetWidget() const { return Widget; } /** * Remove the widget from its current slot. * The removed widget is returned so that operations could be performed on it. * If the null widget was being stored, an invalid shared ptr is returned instead. */ SLATECORE_API const TSharedPtr DetachWidget(); /** Invalidate the widget's owner. */ SLATECORE_API void Invalidate(EInvalidateWidgetReason InvalidateReason); protected: /** * Performs the attribute assignment and invalidates the widget minimally based on what actually changed. So if the boundness of the attribute didn't change * volatility won't need to be recalculated. Returns true if the value changed. */ template bool SetAttribute(TAttribute& TargetValue, const TAttribute& SourceValue, EInvalidateWidgetReason BaseInvalidationReason) { if (!TargetValue.IdenticalTo(SourceValue)) { const bool bWasBound = TargetValue.IsBound(); const bool bBoundnessChanged = bWasBound != SourceValue.IsBound(); TargetValue = SourceValue; EInvalidateWidgetReason InvalidateReason = BaseInvalidationReason; if (bBoundnessChanged) { InvalidateReason |= EInvalidateWidgetReason::Volatility; } Invalidate(InvalidateReason); return true; } return false; } private: SLATECORE_API void DetatchParentFromContent(); SLATECORE_API void AfterContentOrOwnerAssigned(); private: /** The children that own the slot. */ const FChildren* Owner; /** The content widget of the slot. */ TSharedRef Widget; }; /** A slot that can be used by the declarative syntax. */ template class TSlotBase : public FSlotBase { public: using FSlotBase::FSlotBase; SlotType& operator[](TSharedRef&& InChildWidget) { this->AttachWidget(MoveTemp(InChildWidget)); return static_cast(*this); } SlotType& operator[]( const TSharedRef& InChildWidget ) { this->AttachWidget(InChildWidget); return static_cast(*this); } SlotType& Expose(SlotType*& OutVarToInit) { OutVarToInit = static_cast(this); return static_cast(*this); } /** Argument to indicate the Slot is also its owner. */ enum EConstructSlotIsFChildren { ConstructSlotIsFChildren }; /** Struct to construct a slot. */ struct FSlotArguments : public FSlotBase::FSlotArguments { public: FSlotArguments(EConstructSlotIsFChildren) {} FSlotArguments(TUniquePtr InSlot) : Slot(MoveTemp(InSlot)) { check(Slot.Get()); } FSlotArguments(const FSlotArguments&) = delete; FSlotArguments& operator=(const FSlotArguments&) = delete; FSlotArguments(FSlotArguments&&) = default; FSlotArguments& operator=(FSlotArguments&&) = default; public: /** Attach the child widget the slot will own. */ typename SlotType::FSlotArguments& operator[](TSharedRef&& InChildWidget) { ChildWidget = MoveTemp(InChildWidget); return Me(); } typename SlotType::FSlotArguments& operator[](const TSharedRef& InChildWidget) { ChildWidget = InChildWidget; return Me(); } /** Initialize OutVarToInit with the slot that is being constructed. */ typename SlotType::FSlotArguments& Expose(SlotType*& OutVarToInit) { OutVarToInit = Slot.Get(); return Me(); } /** Attach the child widget the slot will own. */ void AttachWidget(const TSharedRef& InChildWidget) { ChildWidget = InChildWidget; } /** @return the child widget that will be owned by the slot. */ const TSharedPtr& GetAttachedWidget() const { return ChildWidget; } /** @return the slot that is being constructed. */ SlotType* GetSlot() const { return Slot.Get(); } /** Steal the slot that is being constructed from the FSlotArguments. */ TUniquePtr StealSlot() { return MoveTemp(Slot); } /** Used by the named argument pattern as a safe way to 'return *this' for call-chaining purposes. */ typename SlotType::FSlotArguments& Me() { return static_cast(*this); } private: TUniquePtr Slot; TSharedPtr ChildWidget; }; void Construct(const FChildren& SlotOwner, FSlotArguments&& InArgs) { if (InArgs.GetAttachedWidget()) { AttachWidget(InArgs.GetAttachedWidget().ToSharedRef()); } SetOwner(SlotOwner); } };