// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Components/ListViewBase.h" #include "Widgets/Views/SListView.h" #include "ListView.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSimpleListItemEventDynamic, UObject*, Item); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListEntryInitializedDynamic, UObject*, Item, UUserWidget*, Widget); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListItemSelectionChangedDynamic, UObject*, Item, bool, bIsSelected); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnItemIsHoveredChangedDynamic, UObject*, Item, bool, bIsHovered); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListItemScrolledIntoViewDynamic, UObject*, Item, UUserWidget*, Widget); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListViewScrolledDynamic, float, ItemOffset, float, DistanceRemaining); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnListViewFinishedScrollingDynamic); DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(bool, FOnIsItemSelectableOrNavigableDynamic, UObject*, Item); /** * A virtualized list that allows up to thousands of items to be displayed. * * An important distinction to keep in mind here is "Item" vs. "Entry" * The list itself is based on a list of n items, but only creates as many entry widgets as can fit on screen. * For example, a scrolling ListView of 200 items with 5 currently visible will only have created 5 entry widgets. * * To make a widget usable as an entry in a ListView, it must inherit from the IUserObjectListEntry interface. */ UCLASS(meta = (EntryInterface = "/Script/UMG.UserObjectListEntry"), MinimalAPI) class UListView : public UListViewBase, public ITypedUMGListView { GENERATED_BODY() IMPLEMENT_TYPED_UMG_LIST(UObject*, MyListView) public: UMG_API UListView(const FObjectInitializer& Initializer); UMG_API virtual void ReleaseSlateResources(bool bReleaseChildren) override; UMG_API virtual void PostLoad() override; /** Set the list of items to display within this listview */ template void SetListItems(const TArray& InListItems) { ClearListItems(); ListItems.Reserve(InListItems.Num()); for (const ItemObjectT ListItem : InListItems) { if (ListItem != nullptr) { ListItems.Add(ListItem); } } OnItemsChanged(ListItems, TArray()); RequestRefresh(); } ESelectionMode::Type GetSelectionMode() const { return SelectionMode; } EOrientation GetOrientation() const { return Orientation; } template RowWidgetT* GetEntryWidgetFromItem(const UObject* Item) const { return Item ? ITypedUMGListView::GetEntryWidgetFromItem(const_cast(Item)) : nullptr; } UMG_API void SetSelectedItem(const UObject* Item); /** Gets the first selected item, if any; recommended that you only use this for single selection lists. */ template ObjectT* GetSelectedItem() const { return Cast(ITypedUMGListView::GetSelectedItem()); } /** * Gets the list of all items in the list. * Note that each of these items only has a corresponding entry widget when visible. Use GetDisplayedEntryWidgets to get the currently displayed widgets. */ UFUNCTION(BlueprintCallable, Category = ListView) const TArray& GetListItems() const { return ListItems; } /** Adds an the item to the list */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void AddItem(UObject* Item); /** Removes an the item from the list */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void RemoveItem(UObject* Item); /** Returns the item at the given index */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API UObject* GetItemAt(int32 Index) const; /** Returns the total number of items */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API int32 GetNumItems() const; /** Returns the index that the specified item is at. Will return the first found, or -1 for not found */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API int32 GetIndexForItem(const UObject* Item) const; /** Removes all items from the list */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void ClearListItems(); /** Sets the new selection mode, preserving the current selection where possible. */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void SetSelectionMode(TEnumAsByte SelectionMode); /* Sets ScrollIntoViewAlignment which allows to stick the selected item to either side or center */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void SetScrollIntoViewAlignment (EScrollIntoViewAlignment NewScrollIntoViewAlignment); /** Sets padding around the scrollbar. */ UFUNCTION(Category = ListView) UMG_API void SetScrollBarPadding(const FMargin& InScrollBarPadding); /** Returns true if a refresh is pending and the list will be rebuilt on the next tick */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API bool IsRefreshPending() const; /** Requests that the item at the given index is scrolled into view */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void ScrollIndexIntoView(int32 Index); /** Sets the item at the given index as the sole selected item. */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void SetSelectedIndex(int32 Index); /** Requests that the item at the given index navigated to, scrolling it into view if needed. */ UFUNCTION(BlueprintCallable, Category = ListView) UMG_API void NavigateToIndex(int32 Index); UMG_API virtual UObject* GetListObjectFromEntry(UUserWidget& EntryWidget) override; protected: UMG_API virtual void OnItemsChanged(const TArray& AddedItems, const TArray& RemovedItems); UFUNCTION() UMG_API void OnListItemEndPlayed(AActor* Item, EEndPlayReason::Type EndPlayReason); UFUNCTION() UMG_API void OnListItemOuterEndPlayed(AActor* ItemOuter, EEndPlayReason::Type EndPlayReason); UMG_API virtual TSharedRef RebuildListWidget() override; UMG_API virtual void HandleListEntryHovered(UUserWidget& EntryWidget) override; UMG_API virtual void HandleListEntryUnhovered(UUserWidget& EntryWidget) override; #if WITH_EDITOR UMG_API virtual void OnRefreshDesignerItems() override; #endif UMG_API virtual UUserWidget& OnGenerateEntryWidgetInternal(UObject* Item, TSubclassOf DesiredEntryClass, const TSharedRef& OwnerTable) override; UMG_API virtual FMargin GetDesiredEntryPadding(UObject* Item) const override; UMG_API virtual void OnItemClickedInternal(UObject* Item) override; UMG_API virtual void OnItemDoubleClickedInternal(UObject* Item) override; UMG_API virtual void OnSelectionChangedInternal(UObject* FirstSelectedItem) override; UMG_API virtual void OnItemScrolledIntoViewInternal(UObject* Item, UUserWidget& EntryWidget) override; UMG_API virtual void OnListViewScrolledInternal(float ItemOffset, float DistanceRemaining) override; UMG_API virtual void OnListViewFinishedScrollingInternal() override; UMG_API virtual bool OnIsSelectableOrNavigableInternal(UObject* FirstSelectedItem) override; UMG_API void HandleOnEntryInitializedInternal(UObject* Item, const TSharedRef& TableRow); /** SListView construction helper - useful if using a custom STreeView subclass */ template class ListViewT = SListView> TSharedRef> ConstructListView() { FListViewConstructArgs Args; Args.bAllowFocus = bIsFocusable; Args.SelectionMode = SelectionMode; Args.bClearSelectionOnClick = bClearSelectionOnClick; Args.ConsumeMouseWheel = ConsumeMouseWheel; Args.bReturnFocusToSelection = bReturnFocusToSelection; Args.bClearScrollVelocityOnSelection = bClearScrollVelocityOnSelection; Args.Orientation = Orientation; Args.ListViewStyle = &WidgetStyle; Args.ScrollBarStyle = &ScrollBarStyle; Args.ScrollBarPadding = ScrollBarPadding; Args.ScrollIntoViewAlignment = ScrollIntoViewAlignment; MyListView = ITypedUMGListView::ConstructListView(this, ListItems, Args); MyListView->SetOnEntryInitialized(SListView::FOnEntryInitialized::CreateUObject(this, &UListView::HandleOnEntryInitializedInternal)); return StaticCastSharedRef>(MyListView.ToSharedRef()); } protected: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView, meta = (DisplayName = "Style")) FTableViewStyle WidgetStyle; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) FScrollBarStyle ScrollBarStyle; /** * The scroll & layout orientation of the list. ListView and TileView only. * Vertical will scroll vertically and arrange tiles into rows. * Horizontal will scroll horizontally and arrange tiles into columns. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) TEnumAsByte Orientation; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) TEnumAsByte SelectionMode = ESelectionMode::Single; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) EConsumeMouseWheel ConsumeMouseWheel = EConsumeMouseWheel::WhenScrollingPossible; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) bool bClearSelectionOnClick = false; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) bool bIsFocusable = true; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) bool bClearScrollVelocityOnSelection = true; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = ListView) bool bReturnFocusToSelection = false; /** Sets where to scroll a widget to when using explicit navigation */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Scrolling) EScrollIntoViewAlignment ScrollIntoViewAlignment = EScrollIntoViewAlignment::CenterAligned; UPROPERTY(Transient) TArray> ListItems; TSharedPtr> MyListView; #if WITH_EDITORONLY_DATA /** * This deprecated property was originally BlueprintReadOnly. To satisfy the compiler requirment to have a BlueprintGetter for this property, * it relies on the newly added UFunction GetHorizontalEntrySpacing() to act as its BlueprintGetter. */ UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "EntrySpacing has been deprecated. Please use HorizontalEntrySpacing and VerticalEntrySpacing."), BlueprintReadOnly, BlueprintGetter = "GetHorizontalEntrySpacing", Category = ListView, meta = (ClampMin = 0)) float EntrySpacing = 0.f; #endif // WITH_EDITOR private: UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, Category = ListView, meta = (ClampMin = 0, AllowPrivateAccess = "true")) float HorizontalEntrySpacing = 0.f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, Category = ListView, meta = (ClampMin = 0, AllowPrivateAccess = "true")) float VerticalEntrySpacing = 0.f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Getter, Setter, Category = ListView, meta = (AllowPrivateAccess = "true", DisplayAfter="ScrollBarStyle")) FMargin ScrollBarPadding; public: /** Get the horizontal spacing between entries. */ UFUNCTION(BlueprintCallable, Category = ListView) float GetHorizontalEntrySpacing() const { return HorizontalEntrySpacing; } /** Get the vertical spacing between entries. */ UFUNCTION(BlueprintCallable, Category = ListView) float GetVerticalEntrySpacing() const { return VerticalEntrySpacing; } /** Gets padding around the scrollbar. */ UFUNCTION(Category = ListView) FMargin GetScrollBarPadding() const { return ScrollBarPadding; } /** Set the vertical spacing between entries. */ UMG_API void SetVerticalEntrySpacing(float InVerticalEntrySpacing); /** Set the horizontal spacing between entries. */ UMG_API void SetHorizontalEntrySpacing(float InHorizontalEntrySpacing); protected: UE_DEPRECATED(5.6, "InitHorizontalEntrySpacing is deprecated, please use SetHorizontalEntrySpacing instead.") UMG_API void InitHorizontalEntrySpacing(float InHorizontalEntrySpacing); UE_DEPRECATED(5.6, "InitVerticalEntrySpacing is deprecated, please use SetVerticalEntrySpacing instead.") UMG_API void InitVerticalEntrySpacing(float InVerticalEntrySpacing); private: // BP exposure of ITypedUMGListView API /** Sets the given item as the sole selected item. */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Set Selected Item")) UMG_API void BP_SetSelectedItem(UObject* Item); /** Sets whether the given item is selected. */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Set Item Selection")) UMG_API void BP_SetItemSelection(UObject* Item, bool bSelected); /** Clear selection */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Clear Selection")) UMG_API void BP_ClearSelection(); /** Gets the number of items currently selected in the list */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Get Num Items Selected")) UMG_API int32 BP_GetNumItemsSelected() const; /** Gets a list of all the currently selected items */ UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "GetSelectedItems")) UMG_API bool BP_GetSelectedItems(TArray& Items) const; /** Gets whether the entry for the given object is currently visible in the list */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Is Item Visible")) UMG_API bool BP_IsItemVisible(UObject* Item) const; /** Requests that the given item is navigated to, scrolling it into view if needed. */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Navigate To Item")) UMG_API void BP_NavigateToItem(UObject* Item); /** Requests that the given item is scrolled into view */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Scroll Item Into View")) UMG_API void BP_ScrollItemIntoView(UObject* Item); /** Cancels a previous request to scroll and item into view. */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Cancel Scroll Into View")) UMG_API void BP_CancelScrollIntoView(); /** Sets the array of objects to display rows for in the list */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (AllowPrivateAccess = true, DisplayName = "Set List Items", ViewmodelBlueprintWidgetExtension = "EntryViewModel")) UMG_API void BP_SetListItems(const TArray& InListItems); /** Gets the first selected item, if any; recommended that you only use this for single selection lists. */ UFUNCTION(BlueprintCallable, Category = ListView, meta = (DisplayName = "Get Selected Item", AllowPrivateAccess = true)) UMG_API UObject* BP_GetSelectedItem() const; private: /** Called when a row widget is generated for a list item */ UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Entry Initialized")) FOnListEntryInitializedDynamic BP_OnEntryInitialized; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Item Clicked")) FSimpleListItemEventDynamic BP_OnItemClicked; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Item Double Clicked")) FSimpleListItemEventDynamic BP_OnItemDoubleClicked; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Item Is Hovered Changed")) FOnItemIsHoveredChangedDynamic BP_OnItemIsHoveredChanged; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Item Selection Changed")) FOnListItemSelectionChangedDynamic BP_OnItemSelectionChanged; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On Item Scrolled Into View")) FOnListItemScrolledIntoViewDynamic BP_OnItemScrolledIntoView; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On List View Scrolled")) FOnListViewScrolledDynamic BP_OnListViewScrolled; UPROPERTY(BlueprintAssignable, Category = Events, meta = (DisplayName = "On List View Finished Scrolling")) FOnListViewFinishedScrollingDynamic BP_OnListViewFinishedScrolling; UPROPERTY(BlueprintReadWrite, Category = Events, meta = (AllowPrivateAccess, DisplayName = "On Is Item Selectable Or Navigable")) FOnIsItemSelectableOrNavigableDynamic BP_OnIsItemSelectableOrNavigable; };