424 lines
10 KiB
C++
424 lines
10 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Components/ListView.h"
|
|
#include "Widgets/Views/SListView.h"
|
|
#include "Engine/World.h"
|
|
#include "TimerManager.h"
|
|
#include "Blueprint/ListViewDesignerPreviewItem.h"
|
|
#include "Styling/DefaultStyleCache.h"
|
|
#include "Styling/UMGCoreStyle.h"
|
|
#include "UMGPrivate.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(ListView)
|
|
|
|
#define LOCTEXT_NAMESPACE "UMG"
|
|
|
|
/////////////////////////////////////////////////////
|
|
// UListView
|
|
|
|
UListView::UListView(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
, Orientation(EOrientation::Orient_Vertical)
|
|
{
|
|
WidgetStyle = UE::Slate::Private::FDefaultStyleCache::GetRuntime().GetListViewStyle();
|
|
ScrollBarStyle = UE::Slate::Private::FDefaultStyleCache::GetRuntime().GetScrollBarStyle();
|
|
|
|
#if WITH_EDITOR
|
|
if (IsEditorWidget())
|
|
{
|
|
WidgetStyle = UE::Slate::Private::FDefaultStyleCache::GetEditor().GetListViewStyle();
|
|
ScrollBarStyle = UE::Slate::Private::FDefaultStyleCache::GetEditor().GetScrollBarStyle();
|
|
}
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
void UListView::ReleaseSlateResources(bool bReleaseChildren)
|
|
{
|
|
Super::ReleaseSlateResources(bReleaseChildren);
|
|
|
|
MyListView.Reset();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UListView::OnRefreshDesignerItems()
|
|
{
|
|
RefreshDesignerItems<TObjectPtr<UObject>>(ListItems, [this] () {return NewObject<UListViewDesignerPreviewItem>(this); });
|
|
}
|
|
#endif
|
|
|
|
void UListView::AddItem(UObject* Item)
|
|
{
|
|
if (Item == nullptr)
|
|
{
|
|
FFrame::KismetExecutionMessage(TEXT("Cannot add null item into ListView."), ELogVerbosity::Warning, "NullListViewItem");
|
|
return;
|
|
}
|
|
|
|
if (ListItems.Contains(Item))
|
|
{
|
|
FFrame::KismetExecutionMessage(TEXT("Cannot add duplicate item into ListView."), ELogVerbosity::Warning, "DuplicateListViewItem");
|
|
return;
|
|
}
|
|
|
|
ListItems.Add(Item);
|
|
|
|
const TArray<UObject*> Added = { Item };
|
|
const TArray<UObject*> Removed;
|
|
OnItemsChanged(Added, Removed);
|
|
|
|
RequestRefresh();
|
|
}
|
|
|
|
void UListView::RemoveItem(UObject* Item)
|
|
{
|
|
ListItems.Remove(Item);
|
|
|
|
const TArray<UObject*> Added;
|
|
const TArray<UObject*> Removed = { Item };
|
|
OnItemsChanged(Added, Removed);
|
|
|
|
RequestRefresh();
|
|
}
|
|
|
|
UObject* UListView::GetItemAt(int32 Index) const
|
|
{
|
|
return ListItems.IsValidIndex(Index) ? ListItems[Index] : nullptr;
|
|
}
|
|
|
|
int32 UListView::GetNumItems() const
|
|
{
|
|
return ListItems.Num();
|
|
}
|
|
|
|
int32 UListView::GetIndexForItem(const UObject* Item) const
|
|
{
|
|
return ListItems.IndexOfByKey(Item);
|
|
}
|
|
|
|
void UListView::ClearListItems()
|
|
{
|
|
const TArray<UObject*> Added;
|
|
const TArray<UObject*> Removed = MoveTemp(ListItems);
|
|
|
|
ListItems.Reset();
|
|
|
|
OnItemsChanged(Added, Removed);
|
|
|
|
RequestRefresh();
|
|
}
|
|
|
|
void UListView::SetSelectionMode(TEnumAsByte<ESelectionMode::Type> InSelectionMode)
|
|
{
|
|
SelectionMode = InSelectionMode;
|
|
if (MyListView)
|
|
{
|
|
MyListView->SetSelectionMode(InSelectionMode);
|
|
}
|
|
}
|
|
|
|
void UListView::SetScrollIntoViewAlignment (EScrollIntoViewAlignment NewScrollIntoViewAlignment)
|
|
{
|
|
ScrollIntoViewAlignment = NewScrollIntoViewAlignment;
|
|
if (MyListView)
|
|
{
|
|
MyListView->SetScrollIntoViewAlignment(NewScrollIntoViewAlignment);
|
|
}
|
|
}
|
|
|
|
void UListView::SetScrollBarPadding(const FMargin& InScrollBarPadding)
|
|
{
|
|
ScrollBarPadding = InScrollBarPadding;
|
|
if (MyListView.IsValid())
|
|
{
|
|
MyListView->SetScrollbarPadding(ScrollBarPadding);
|
|
}
|
|
}
|
|
|
|
int32 UListView::BP_GetNumItemsSelected() const
|
|
{
|
|
return GetNumItemsSelected();
|
|
}
|
|
|
|
void UListView::BP_SetListItems(const TArray<UObject*>& InListItems)
|
|
{
|
|
SetListItems(InListItems);
|
|
}
|
|
|
|
UObject* UListView::BP_GetSelectedItem() const
|
|
{
|
|
return GetSelectedItem();
|
|
}
|
|
|
|
void UListView::HandleOnEntryInitializedInternal(UObject* Item, const TSharedRef<ITableRow>& TableRow)
|
|
{
|
|
BP_OnEntryInitialized.Broadcast(Item, GetEntryWidgetFromItem(Item));
|
|
}
|
|
|
|
bool UListView::BP_GetSelectedItems(TArray<UObject*>& Items) const
|
|
{
|
|
return GetSelectedItems(Items) > 0;
|
|
}
|
|
|
|
bool UListView::BP_IsItemVisible(UObject* Item) const
|
|
{
|
|
return IsItemVisible(Item);
|
|
}
|
|
|
|
void UListView::BP_NavigateToItem(UObject* Item)
|
|
{
|
|
if (Item)
|
|
{
|
|
RequestNavigateToItem(Item);
|
|
}
|
|
}
|
|
|
|
void UListView::NavigateToIndex(int32 Index)
|
|
{
|
|
RequestNavigateToItem(GetItemAt(Index));
|
|
}
|
|
|
|
void UListView::BP_ScrollItemIntoView(UObject* Item)
|
|
{
|
|
if (Item)
|
|
{
|
|
RequestScrollItemIntoView(Item);
|
|
}
|
|
}
|
|
|
|
void UListView::ScrollIndexIntoView(int32 Index)
|
|
{
|
|
BP_ScrollItemIntoView(GetItemAt(Index));
|
|
}
|
|
|
|
void UListView::BP_CancelScrollIntoView()
|
|
{
|
|
if (MyListView.IsValid())
|
|
{
|
|
MyListView->CancelScrollIntoView();
|
|
}
|
|
}
|
|
|
|
bool UListView::IsRefreshPending() const
|
|
{
|
|
if (MyListView.IsValid())
|
|
{
|
|
return MyListView->IsPendingRefresh();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UListView::BP_SetSelectedItem(UObject* Item)
|
|
{
|
|
if (MyListView.IsValid())
|
|
{
|
|
MyListView->SetSelection(Item, ESelectInfo::Direct);
|
|
}
|
|
}
|
|
|
|
void UListView::SetSelectedItem(const UObject* Item)
|
|
{
|
|
ITypedUMGListView<UObject*>::SetSelectedItem(const_cast<UObject*>(Item));
|
|
}
|
|
|
|
void UListView::SetSelectedIndex(int32 Index)
|
|
{
|
|
SetSelectedItem(GetItemAt(Index));
|
|
}
|
|
|
|
void UListView::BP_SetItemSelection(UObject* Item, bool bSelected)
|
|
{
|
|
SetItemSelection(Item, bSelected);
|
|
}
|
|
|
|
void UListView::BP_ClearSelection()
|
|
{
|
|
ClearSelection();
|
|
}
|
|
|
|
void UListView::OnItemsChanged(const TArray<UObject*>& AddedItems, const TArray<UObject*>& RemovedItems)
|
|
{
|
|
// Allow subclasses to do special things when objects are added or removed from the list.
|
|
|
|
// Keep track of references to Actors and make sure to release them when Actors are about to be removed
|
|
for (UObject* AddedItem : AddedItems)
|
|
{
|
|
if (AActor* AddedActor = Cast<AActor>(AddedItem))
|
|
{
|
|
AddedActor->OnEndPlay.AddDynamic(this, &UListView::OnListItemEndPlayed);
|
|
}
|
|
else if (AActor* AddedItemOuterActor = AddedItem->GetTypedOuter<AActor>())
|
|
{
|
|
// Unique so that we don't spam events for shared actor outers but this also means we can't
|
|
// unsubscribe when processing RemovedItems
|
|
AddedItemOuterActor->OnEndPlay.AddUniqueDynamic(this, &UListView::OnListItemOuterEndPlayed);
|
|
}
|
|
}
|
|
for (UObject* RemovedItem : RemovedItems)
|
|
{
|
|
if (AActor* RemovedActor = Cast<AActor>(RemovedItem))
|
|
{
|
|
RemovedActor->OnEndPlay.RemoveDynamic(this, &UListView::OnListItemEndPlayed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UListView::OnListItemEndPlayed(AActor* Item, EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
RemoveItem(Item);
|
|
}
|
|
|
|
void UListView::OnListItemOuterEndPlayed(AActor* ItemOuter, EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
for (int32 ItemIndex = ListItems.Num() - 1; ItemIndex >= 0; --ItemIndex)
|
|
{
|
|
UObject* Item = ListItems[ItemIndex];
|
|
if (Item->IsIn(ItemOuter))
|
|
{
|
|
RemoveItem(Item);
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<STableViewBase> UListView::RebuildListWidget()
|
|
{
|
|
return ConstructListView<SListView>();
|
|
}
|
|
|
|
void UListView::HandleListEntryHovered(UUserWidget& EntryWidget)
|
|
{
|
|
if (const TObjectPtrWrapTypeOf<UObject*>* ListItem = ItemFromEntryWidget(EntryWidget))
|
|
{
|
|
OnItemIsHoveredChanged().Broadcast(*ListItem, true);
|
|
BP_OnItemIsHoveredChanged.Broadcast(*ListItem, true);
|
|
}
|
|
}
|
|
|
|
void UListView::HandleListEntryUnhovered(UUserWidget& EntryWidget)
|
|
{
|
|
if (const TObjectPtrWrapTypeOf<UObject*>* ListItem = ItemFromEntryWidget(EntryWidget))
|
|
{
|
|
OnItemIsHoveredChanged().Broadcast(*ListItem, false);
|
|
BP_OnItemIsHoveredChanged.Broadcast(*ListItem, false);
|
|
}
|
|
}
|
|
|
|
FMargin UListView::GetDesiredEntryPadding(UObject* Item) const
|
|
{
|
|
if (ListItems.Num() > 0 && ListItems[0] != Item)
|
|
{
|
|
if (Orientation == EOrientation::Orient_Horizontal)
|
|
{
|
|
// For all entries after the first one, add the spacing as left padding
|
|
return FMargin(HorizontalEntrySpacing, 0.f, 0.0f, 0.f);
|
|
}
|
|
else
|
|
{
|
|
// For all entries after the first one, add the spacing as top padding
|
|
return FMargin(0.f, VerticalEntrySpacing, 0.f, 0.f);
|
|
}
|
|
}
|
|
|
|
return FMargin(0.f);
|
|
}
|
|
|
|
UUserWidget& UListView::OnGenerateEntryWidgetInternal(UObject* Item, TSubclassOf<UUserWidget> DesiredEntryClass, const TSharedRef<STableViewBase>& OwnerTable)
|
|
{
|
|
return GenerateTypedEntry(DesiredEntryClass, OwnerTable);
|
|
}
|
|
|
|
UObject* UListView::GetListObjectFromEntry(UUserWidget& EntryWidget)
|
|
{
|
|
const TObjectPtrWrapTypeOf<UObject*>* Item = ITypedUMGListView::ItemFromEntryWidget(EntryWidget);
|
|
return Item ? *Item : nullptr;
|
|
}
|
|
|
|
void UListView::OnItemClickedInternal(UObject* ListItem)
|
|
{
|
|
ITypedUMGListView::OnItemClickedInternal(ListItem);
|
|
BP_OnItemClicked.Broadcast(ListItem);
|
|
}
|
|
|
|
void UListView::OnItemDoubleClickedInternal(UObject* ListItem)
|
|
{
|
|
ITypedUMGListView::OnItemDoubleClickedInternal(ListItem);
|
|
BP_OnItemDoubleClicked.Broadcast(ListItem);
|
|
}
|
|
|
|
void UListView::OnSelectionChangedInternal(UObject* FirstSelectedItem)
|
|
{
|
|
ITypedUMGListView::OnSelectionChangedInternal(FirstSelectedItem);
|
|
BP_OnItemSelectionChanged.Broadcast(FirstSelectedItem, FirstSelectedItem != nullptr);
|
|
}
|
|
|
|
void UListView::OnItemScrolledIntoViewInternal(UObject* ListItem, UUserWidget& EntryWidget)
|
|
{
|
|
ITypedUMGListView::OnItemScrolledIntoViewInternal(ListItem, EntryWidget);
|
|
BP_OnItemScrolledIntoView.Broadcast(ListItem, &EntryWidget);
|
|
}
|
|
|
|
void UListView::OnListViewScrolledInternal(float ItemOffset, float DistanceRemaining)
|
|
{
|
|
ITypedUMGListView::OnListViewScrolledInternal(ItemOffset, DistanceRemaining);
|
|
BP_OnListViewScrolled.Broadcast(ItemOffset, DistanceRemaining);
|
|
}
|
|
|
|
void UListView::OnListViewFinishedScrollingInternal()
|
|
{
|
|
ITypedUMGListView::OnListViewFinishedScrollingInternal();
|
|
BP_OnListViewFinishedScrolling.Broadcast();
|
|
}
|
|
|
|
bool UListView::OnIsSelectableOrNavigableInternal(UObject* FirstSelectedItem)
|
|
{
|
|
return BP_OnIsItemSelectableOrNavigable.IsBound() ? BP_OnIsItemSelectableOrNavigable.Execute(FirstSelectedItem) : ITypedUMGListView::OnIsSelectableOrNavigableInternal(FirstSelectedItem);
|
|
}
|
|
|
|
void UListView::SetVerticalEntrySpacing(float InVerticalEntrySpacing)
|
|
{
|
|
if (VerticalEntrySpacing != InVerticalEntrySpacing)
|
|
{
|
|
VerticalEntrySpacing = InVerticalEntrySpacing;
|
|
RequestRefresh();
|
|
}
|
|
}
|
|
|
|
void UListView::SetHorizontalEntrySpacing(float InHorizontalEntrySpacing)
|
|
{
|
|
if (HorizontalEntrySpacing != InHorizontalEntrySpacing)
|
|
{
|
|
HorizontalEntrySpacing = InHorizontalEntrySpacing;
|
|
RequestRefresh();
|
|
}
|
|
}
|
|
|
|
void UListView::InitHorizontalEntrySpacing(float InHorizontalEntrySpacing)
|
|
{
|
|
SetHorizontalEntrySpacing(InHorizontalEntrySpacing);
|
|
}
|
|
|
|
void UListView::InitVerticalEntrySpacing(float InVerticalEntrySpacing)
|
|
{
|
|
SetVerticalEntrySpacing(InVerticalEntrySpacing);
|
|
}
|
|
|
|
void UListView::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
#if WITH_EDITOR
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
if (EntrySpacing != 0.f)
|
|
{
|
|
HorizontalEntrySpacing = EntrySpacing;
|
|
VerticalEntrySpacing = EntrySpacing;
|
|
EntrySpacing = 0.f;
|
|
}
|
|
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
#endif // WITH_EDITOR
|
|
}
|
|
/////////////////////////////////////////////////////
|
|
|
|
#undef LOCTEXT_NAMESPACE
|