Created
August 24, 2017 14:59
-
-
Save alexgeek/aee4e3f45a32de1f2a2d0bdc4a121d2a to your computer and use it in GitHub Desktop.
UE4 Slate: Generic wrapper to show list of structs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include "CoreMinimal.h" | |
| #include "Layout/Margin.h" | |
| #include "Widgets/DeclarativeSyntaxSupport.h" | |
| #include "Widgets/SCompoundWidget.h" | |
| #include "Widgets/SNullWidget.h" | |
| #include "Widgets/SWidget.h" | |
| #include "Widgets/Layout/SBox.h" | |
| #include "Widgets/Text/STextBlock.h" | |
| #include "Widgets/Views/STableRow.h" | |
| #include "Widgets/Views/STableViewBase.h" | |
| #include "Widgets/Views/SListView.h" | |
| /** | |
| * Generic row for a generic list. Pass in structure type. | |
| */ | |
| template<typename StructType> | |
| class SGenericStructListRow : public SMultiColumnTableRow<TSharedPtr<FString>> | |
| { | |
| public: | |
| SLATE_BEGIN_ARGS(SGenericStructListRow) {} | |
| SLATE_ARGUMENT(TSharedPtr<StructType>, Item) | |
| SLATE_END_ARGS() | |
| public: | |
| void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView) | |
| { | |
| Item = InArgs._Item; | |
| SMultiColumnTableRow<TSharedPtr<FString> >::Construct(FSuperRowType::FArguments(), InOwnerTableView); | |
| } | |
| /** | |
| * Convert a property pointer use the item to conver the property to a string. | |
| */ | |
| virtual FText GetPropertyValueText(const UProperty* Property) const | |
| { | |
| FString PropertyValueStr; | |
| void* Data = (void*)Item.Get(); | |
| const uint8* PropertyAddr = Property->ContainerPtrToValuePtr<uint8>(Data); | |
| Property->ExportTextItem(PropertyValueStr, PropertyAddr, NULL, NULL, PPF_None); | |
| return FText::FromString(PropertyValueStr); | |
| } | |
| /** | |
| * Generate a widget for the column name. | |
| * Will try to convert a property to a string and return STextBlock within an SBox. | |
| * Override if you want to special case some columns or overhaul the widgets returned. | |
| */ | |
| virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override | |
| { | |
| // iterate struct properties and generate a widget for it | |
| for (TFieldIterator<UProperty> It(StructType::StaticStruct()); It; ++It) | |
| { | |
| const UProperty* Property = *It; | |
| const FName PropertyName = Property->GetFName(); | |
| if (ColumnName == PropertyName) | |
| { | |
| return SNew(SBox) | |
| .Padding(FMargin(4.0f, 0.0f)) | |
| .VAlign(VAlign_Center) | |
| [ | |
| SNew(STextBlock) | |
| .Text(this, &SGenericStructListRow::GetPropertyValueText, Property) | |
| ]; | |
| } | |
| } | |
| // default to null widget if property cannot be found | |
| return SNullWidget::NullWidget; | |
| } | |
| protected: | |
| TSharedPtr<StructType> Item; | |
| }; | |
| /** | |
| * Generic list templated on a structure to view. | |
| * Second template parameter is the row class to use which default to the above. | |
| */ | |
| template<typename StructType, class RowClass = SGenericStructListRow<StructType>> | |
| class SGenericStructList : public SCompoundWidget | |
| { | |
| public: | |
| SLATE_BEGIN_ARGS(SGenericStructList<StructType>) | |
| : _ListSource(nullptr) | |
| , _ColumnsSource(nullptr) | |
| , _ItemHeight(20.0f) | |
| {} | |
| SLATE_DEFAULT_SLOT(FArguments, Content) | |
| SLATE_ARGUMENT(const TArray<TSharedPtr<StructType>>*, ListSource) | |
| SLATE_ARGUMENT(const TArray<SHeaderRow::FColumn>*, ColumnsSource) | |
| SLATE_ARGUMENT(float, ItemHeight) | |
| SLATE_END_ARGS() | |
| SGenericStructList() | |
| : List() | |
| , ListView(nullptr) | |
| {} | |
| void Construct(const FArguments& InArgs) | |
| { | |
| if (InArgs._ListSource != nullptr) | |
| { | |
| List = *(InArgs._ListSource); | |
| } | |
| // create header | |
| TSharedPtr<SHeaderRow> HeaderRow = SNew(SHeaderRow); | |
| // if pointer to array of columns is provided then copy the columns | |
| if (InArgs._ColumnsSource != nullptr) | |
| { | |
| for (auto It = InArgs._ColumnsSource->CreateConstIterator(); It; ++It) | |
| { | |
| SHeaderRow::FColumn Column = *It; | |
| HeaderRow->AddColumn(Column); | |
| } | |
| } | |
| else | |
| { | |
| // if no column array is provided then generate from property names | |
| for (TFieldIterator<UProperty> It(StructType::StaticStruct()); It; ++It) | |
| { | |
| const UProperty* Property = *It; | |
| const FName PropertyName = Property->GetFName(); | |
| SHeaderRow::FColumn Column = SHeaderRow::Column(PropertyName); | |
| HeaderRow->AddColumn(Column); | |
| } | |
| } | |
| // construct the list view | |
| ChildSlot | |
| [ | |
| SAssignNew(ListView, SListView<TSharedPtr<StructType>>) | |
| .ItemHeight(20.0f) | |
| .ListItemsSource(&List) | |
| .OnGenerateRow(this, &SGenericStructList<StructType, RowClass>::OnGenerateRow) | |
| .OnSelectionChanged(this, &SGenericStructList<StructType, RowClass>::HandleSelectionChanged) | |
| .OnMouseButtonDoubleClick(this, &SGenericStructList<StructType, RowClass>::HandleMouseButtonDoubleClick) | |
| .OnContextMenuOpening(this, &SGenericStructList<StructType, RowClass>::HandleContextMenuOpening) | |
| .SelectionMode(ESelectionMode::Single) | |
| .HeaderRow(HeaderRow) | |
| ]; | |
| } | |
| virtual TSharedRef<class ITableRow> OnGenerateRow(TSharedPtr<StructType> Item, const TSharedRef<STableViewBase >& OwnerTable) | |
| { | |
| return SNew(RowClass, OwnerTable).Item(Item); | |
| } | |
| virtual void HandleSelectionChanged(TSharedPtr<StructType> Selection, ESelectInfo::Type SelectInfo) | |
| { | |
| // NO OP | |
| } | |
| virtual void HandleMouseButtonDoubleClick(TSharedPtr<StructType> Item) | |
| { | |
| // NO OP | |
| } | |
| virtual TSharedPtr<SWidget> HandleContextMenuOpening() | |
| { | |
| // NO OP | |
| return SNullWidget::NullWidget; | |
| } | |
| protected: | |
| TArray<TSharedPtr<StructType>> List; | |
| TSharedPtr<SListView<TSharedPtr<StructType>>> ListView; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment