// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Containers/ArrayView.h" #include "Containers/ObservableArray.h" #include "Widgets/Views/STableViewBase.h" namespace UE::Slate::ItemsSource { template struct ForwardedSlateItemsSourceArgument { const TArray* ArrayPointer; ::UE::Slate::Containers::TObservableArray* ObservableArrayPointer; TSharedPtr<::UE::Slate::Containers::TObservableArray> SharedObservableArray; }; #define SLATE_ITEMS_SOURCE_ARGUMENT( ArgType, ArgName ) \ private: \ const TArray* _##ArgName##_ArrayPointer = nullptr; \ ::UE::Slate::Containers::TObservableArray* _##ArgName##_ObservableArrayPointer = nullptr; \ TSharedPtr<::UE::Slate::Containers::TObservableArray> _##ArgName##_SharedObservableArray; \ void _Reset##ArgName() \ { \ _##ArgName##_ArrayPointer = nullptr; \ _##ArgName##_ObservableArrayPointer = nullptr; \ _##ArgName##_SharedObservableArray.Reset(); \ } \ public: \ WidgetArgsType& ArgName(const TArray* InArg) \ { \ _Reset##ArgName(); \ _##ArgName##_ArrayPointer = InArg; \ return static_cast(this)->Me(); \ } \ WidgetArgsType& ArgName(::UE::Slate::Containers::TObservableArray* InArg) \ { \ _Reset##ArgName(); \ _##ArgName##_ObservableArrayPointer = InArg; \ return static_cast(this)->Me(); \ } \ WidgetArgsType& ArgName(TSharedPtr<::UE::Slate::Containers::TObservableArray> InArg) \ { \ _Reset##ArgName(); \ _##ArgName##_SharedObservableArray = InArg; \ return static_cast(this)->Me(); \ } \ WidgetArgsType& ArgName(TSharedRef<::UE::Slate::Containers::TObservableArray> InArg) \ { \ _Reset##ArgName(); \ _##ArgName##_SharedObservableArray = InArg; \ return static_cast(this)->Me(); \ } \ WidgetArgsType& ArgName(::UE::Slate::ItemsSource::ForwardedSlateItemsSourceArgument InArg) \ { \ _Reset##ArgName(); \ _##ArgName##_ArrayPointer = InArg.ArrayPointer; \ _##ArgName##_ObservableArrayPointer = InArg.ObservableArrayPointer; \ _##ArgName##_SharedObservableArray = InArg.SharedObservableArray; \ return static_cast(this)->Me(); \ } \ ::UE::Slate::ItemsSource::ForwardedSlateItemsSourceArgument Get##ArgName() const \ { \ return {_##ArgName##_ArrayPointer, _##ArgName##_ObservableArrayPointer, _##ArgName##_SharedObservableArray}; \ } \ TUniquePtr<::UE::Slate::ItemsSource::IItemsSource> Make##ArgName(TSharedRef InWidget) const \ { \ if (_##ArgName##_ArrayPointer) \ { \ return MakeUnique<::UE::Slate::ItemsSource::FArrayPointer>(_##ArgName##_ArrayPointer); \ } \ else if (_##ArgName##_ObservableArrayPointer) \ { \ return MakeUnique<::UE::Slate::ItemsSource::FObservableArrayPointer>(InWidget, _##ArgName##_ObservableArrayPointer); \ } \ else if (_##ArgName##_SharedObservableArray) \ { \ return MakeUnique<::UE::Slate::ItemsSource::FSharedObservableArray>(InWidget, _##ArgName##_SharedObservableArray.ToSharedRef()); \ } \ return TUniquePtr<::UE::Slate::ItemsSource::IItemsSource>(); \ } /* * A generic container for TableView items. */ template class IItemsSource { public: virtual ~IItemsSource() = default; /** Returns all the items in the source. */ virtual const TArrayView GetItems() const = 0; /** Test if the source has the same origin. */ virtual bool IsSame(const void* RawPointer) const = 0; }; /* * */ template class FArrayPointer : public IItemsSource { public: explicit FArrayPointer(const TArray* InItemsSource) : ItemsSource(InItemsSource) { } virtual const TArrayView GetItems() const override { return *ItemsSource; } virtual bool IsSame(const void* RawPointer) const override { return RawPointer == reinterpret_cast(ItemsSource); } private: const TArray* ItemsSource; }; /* * */ template class FObservableArrayPointer : public IItemsSource { public: using WidgetType = STableViewBase; using ItemType = InItemType; explicit FObservableArrayPointer(TSharedRef InListView, ::UE::Slate::Containers::TObservableArray* InItemsSource) : ItemsSource(InItemsSource) , ListViewOwner(InListView) { ArrayChangedHandle = InItemsSource->OnArrayChanged().AddRaw(this, &FObservableArrayPointer::HandleArrayChanged); } virtual ~FObservableArrayPointer() { /** * This is likely due to * class SMyWidget : SCoumpoundWidget * { * TObservableArray> MyArray; * SListView> MyList; * void Construct(const FArguments&) * { * MyList = SNew(SListView>) * .ItemsSource(&MyArray); * } * virtual ~SMyWidget() * { * // Remove the source to clear the binding before the ~SListView is called on MyList. * MyList->SetItemsSource(nullptr); * } * }; * If you can do this pattern, uses a TSharedPtr> instead. */ checkf(ListViewOwner.IsValid(), TEXT("The View widget has a source needed to be released to prevent bad memory access.")); ItemsSource->OnArrayChanged().Remove(ArrayChangedHandle); } virtual const TArrayView GetItems() const override { return TArrayView(ItemsSource->GetData(), ItemsSource->Num()); } virtual bool IsSame(const void* RawPointer) const override { return RawPointer == reinterpret_cast(&ItemsSource); } private: void HandleArrayChanged(typename ::UE::Slate::Containers::TObservableArray::ObservableArrayChangedArgsType Args) { if (TSharedPtr ListViewOwnerPin = ListViewOwner.Pin()) { ListViewOwnerPin->RequestListRefresh(); } } private: UE::Slate::Containers::TObservableArray* ItemsSource; TWeakPtr ListViewOwner; FDelegateHandle ArrayChangedHandle; }; /* * */ template class FSharedObservableArray : public IItemsSource { public: using WidgetType = STableViewBase; using ItemType = InItemType; explicit FSharedObservableArray(TSharedRef InListView, TSharedRef<::UE::Slate::Containers::TObservableArray> InItemsSource) : ItemsSource(InItemsSource) , ListViewOwner(InListView) { ArrayChangedHandle = InItemsSource->OnArrayChanged().AddRaw(this, &FSharedObservableArray::HandleArrayChanged); } virtual ~FSharedObservableArray() { ItemsSource->OnArrayChanged().Remove(ArrayChangedHandle); } virtual const TArrayView GetItems() const override { return TArrayView(ItemsSource->GetData(), ItemsSource->Num()); } virtual bool IsSame(const void* RawPointer) const override { UE::Slate::Containers::TObservableArray* ValueToTest = &(ItemsSource.Get()); return RawPointer == reinterpret_cast(ValueToTest); } private: void HandleArrayChanged(typename ::UE::Slate::Containers::TObservableArray::ObservableArrayChangedArgsType Args) { if (TSharedPtr ListViewOwnerPin = ListViewOwner.Pin()) { ListViewOwnerPin->RequestListRefresh(); } } private: TSharedRef> ItemsSource; TWeakPtr ListViewOwner; FDelegateHandle ArrayChangedHandle; }; } //UE::Slate::ItemsSource