// 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/UMGCoreStyle.h" #include "UMGPrivate.h" #define LOCTEXT_NAMESPACE "UMG" ///////////////////////////////////////////////////// // UListView static FTableViewStyle* DefaultListViewStyle = nullptr; static FScrollBarStyle* DefaultListViewScrollBarStyle = nullptr; #if WITH_EDITOR static FTableViewStyle* EditorListViewStyle = nullptr; static FScrollBarStyle* EditorListViewScrollBarStyle = nullptr; #endif UListView::UListView(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , Orientation(EOrientation::Orient_Vertical) { if (DefaultListViewStyle == nullptr) { DefaultListViewStyle = new FTableViewStyle(FUMGCoreStyle::Get().GetWidgetStyle("ListView")); // Unlink UMG default colors. DefaultListViewStyle->UnlinkColors(); } if (DefaultListViewScrollBarStyle == nullptr) { DefaultListViewScrollBarStyle = new FScrollBarStyle(FUMGCoreStyle::Get().GetWidgetStyle("Scrollbar")); // Unlink UMG default colors. DefaultListViewScrollBarStyle->UnlinkColors(); } WidgetStyle = *DefaultListViewStyle; ScrollBarStyle = *DefaultListViewScrollBarStyle; #if WITH_EDITOR if (EditorListViewStyle == nullptr) { EditorListViewStyle = new FTableViewStyle(FAppStyle::Get().GetWidgetStyle("ListView")); // Unlink UMG default colors. EditorListViewStyle->UnlinkColors(); } if (EditorListViewScrollBarStyle == nullptr) { EditorListViewScrollBarStyle = new FScrollBarStyle(FCoreStyle::Get().GetWidgetStyle("Scrollbar")); // Unlink UMG default colors. EditorListViewScrollBarStyle->UnlinkColors(); } if (IsEditorWidget()) { WidgetStyle = *EditorListViewStyle; ScrollBarStyle = *EditorListViewScrollBarStyle; } #endif // WITH_EDITOR } void UListView::ReleaseSlateResources(bool bReleaseChildren) { Super::ReleaseSlateResources(bReleaseChildren); MyListView.Reset(); } #if WITH_EDITOR void UListView::OnRefreshDesignerItems() { RefreshDesignerItems(ListItems, [this] () {return NewObject(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 Added = { Item }; const TArray Removed; OnItemsChanged(Added, Removed); RequestRefresh(); } void UListView::RemoveItem(UObject* Item) { ListItems.Remove(Item); const TArray Added; const TArray 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 Added; const TArray Removed = MoveTemp(ListItems); ListItems.Reset(); OnItemsChanged(Added, Removed); RequestRefresh(); } void UListView::SetSelectionMode(TEnumAsByte InSelectionMode) { SelectionMode = InSelectionMode; if (MyListView) { MyListView->SetSelectionMode(InSelectionMode); } } int32 UListView::BP_GetNumItemsSelected() const { return GetNumItemsSelected(); } void UListView::BP_SetListItems(const TArray& InListItems) { SetListItems(InListItems); } UObject* UListView::BP_GetSelectedItem() const { return GetSelectedItem(); } void UListView::HandleOnEntryInitializedInternal(UObject* Item, const TSharedRef& TableRow) { BP_OnEntryInitialized.Broadcast(Item, GetEntryWidgetFromItem(Item)); } bool UListView::BP_GetSelectedItems(TArray& 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::SetSelectedItem(const_cast(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& AddedItems, const TArray& 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(AddedItem)) { AddedActor->OnEndPlay.AddDynamic(this, &UListView::OnListItemEndPlayed); } else if (AActor* AddedItemOuterActor = AddedItem->GetTypedOuter()) { // 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(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 UListView::RebuildListWidget() { return ConstructListView(); } void UListView::HandleListEntryHovered(UUserWidget& EntryWidget) { if (UObject* const* ListItem = ItemFromEntryWidget(EntryWidget)) { OnItemIsHoveredChanged().Broadcast(*ListItem, true); BP_OnItemIsHoveredChanged.Broadcast(*ListItem, true); } } void UListView::HandleListEntryUnhovered(UUserWidget& EntryWidget) { if (UObject* const* 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(EntrySpacing, 0.f, 0.0f, 0.f); } else { // For all entries after the first one, add the spacing as top padding return FMargin(0.f, EntrySpacing, 0.f, 0.f); } } return FMargin(0.f); } UUserWidget& UListView::OnGenerateEntryWidgetInternal(UObject* Item, TSubclassOf DesiredEntryClass, const TSharedRef& OwnerTable) { return GenerateTypedEntry(DesiredEntryClass, OwnerTable); } void UListView::OnItemClickedInternal(UObject* ListItem) { BP_OnItemClicked.Broadcast(ListItem); } void UListView::OnItemDoubleClickedInternal(UObject* ListItem) { BP_OnItemDoubleClicked.Broadcast(ListItem); } void UListView::OnSelectionChangedInternal(UObject* FirstSelectedItem) { BP_OnItemSelectionChanged.Broadcast(FirstSelectedItem, FirstSelectedItem != nullptr); } void UListView::OnItemScrolledIntoViewInternal(UObject* ListItem, UUserWidget& EntryWidget) { BP_OnItemScrolledIntoView.Broadcast(ListItem, &EntryWidget); } ///////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE