Listview - getting started in BP

I am creating this guide since I had trouble grasping the listview at first, seeing many have the same struggle and there not being any good information about them in a blueprint context.
The listview should be pretty straight forward to use once you got the basic grasp of it.

Note!:If a widget is generated for one instance more than once, the engine will crash. So any instance should only be in the listview once.
Note! If the node “Is List Item Selected”, part of the entrywidget class, is used, the engine will crash.
Note! Multi Select Mode seems to be bugged, clicking an item clears the selection so it is not possible to multi-select by click. (implementing custom logic would work).

Following bug reports has been made:

For versions: 4.21, 4.22
For changes to 4.23 and 4.24, see Reply #13

For using the Listview or Tileview for “inventory”, see Reply #14, after learning the basics.

Contents: (Reply#, Subject index. subject - description)
Listview
1,1. Basic Setup - Setting up the EntryWidget Interface and adding it to the ListView.
1,2. **L****istview overview - **Examples of some useful functions.
1,3. **The Pitfall **- Common mistake adding members to the Listview.
1,4. Listview generation - Explaining how EntryWidget works and events firing.
1,5. **Creating a proper list **- Proper method for setting up a very simple list.
1,6. Going advanced - Actor representation - Displaying Actors in the list.
1,7. **Going advanced - Selection **- How selection is displayed in the list.
1,8. **On Item Selection Changed - ListView Or EntryWidget? **- Differences between the selection changed event between the Listview and Entrywidget.
1,9. Going advanced - World selection - World selection and Listview syncing.
1,10. **Going Advanced - Drag & Drop **- Basic Drag and Drop example.
1,11. **Multiselect - **Making a custom multiselect solution.
1,12. **Node Information **- Additional information about specific nodes.

Treeview
2,1. Treeview - Getting Started - Basic Treeview setup, how to set hierarchy/get children
2,2. **Treeview - Spacing [Backtracking through parent] **- Adding some spacing to children depending on [Actor parenting] hierarchy depth.
2,3. Treeview - Expandable symbol - Changing or hiding a symbol, based on the expanded state and if Item has children.
2,4. **Treeview - Spacing [When getting the items children] **- More universal way of tracking depth, does not rely on Actor parenting.
2,5. **Treeview - Expand only if Selected [revert expansion method] - **Workaround to default expansion behaviour (not recommended)
2,6. Treeview - Don’t change expansion [Override click method] - Workaround to default expansion behaviour (recommended)

12,1. Changes in 4.23

13.1 Listview & Inventory Items - Solution specifically for “inventory Items” often found in rpg games. Display.
13.2 **Drag & Drop solution with mediators **- Solution to have Drag & Drop behaviour with inventory using mediator objects.

1. Basic Setup
Create a widget to hold the listview. For this guide, this is the widget that is added to viewport. Add a listview. You can’t compile now because it wants an entry widget.
Create a widget to be the EntryWidget. Delete the canvas panel and add a Border and a Text block. Then, follow the image steps.

  • Note that the function in step 3 called “Get List item Object” has been removed in later versions.

  1. Go to the Entrywidgets class settings.
  2. Add the interface “UserobjectListEntry”.
  3. Note how the Entrywidget now has an interface function. This may be very useful but we need to return a value.
  4. Add the event “Event On List Item Object Set” which is also part of the interface we added. Right click on the List Item Object and promote it to variable. I renamed it to Item.
  5. Go back to the function in 3. and return the Item.
  6. Go to the listview widget, select the listview and the entrywidget you created can now be selected as the entry widget class.

That’s the basic setup done and you are now ready to use the listview.

This is what the EntryWidget looks like:​​​​​


But from now on, basic umg knowledge is presumed and we will just focus on the listview.

A disclaimer for new users to UE is that this tutorial does not follow best practices and can be inconsistent. My goal is to have as few nodes on as little screen space as possible for screenshot purposes. Some parts of this tutorial uses variable bindings on Text Block text or the Border brush, which is generally not good. Other parts Sets the text for the Text Block or the colour on the Border, which is generally better. Bindings is like a Tick, always checking its value. If you make a big game using this a lot in your UI, you’ll run into performance issues. Setting the value only happens during that event.
There are some other things like how the Item is set during the On List Item Object Set. Maybe, cast it here so you don’t have to cast at multiple other places? Either way, the result is the same, just slightly different execution. The goal here is to teach the overall methodology which once learned, you can use your own solution for.

2. Listview overview
First of all, the listview works with instances. You create any object (Uobject, Actor etc) to add as an item to the listview and the listview will, if needed, create an entrywidget to represent that item.
Here’s a selection of functions that the listview has. Only one for the generated widgets. It is designed to work with the items, the widgets are just there to represent the item.


3. The Pitfall
Now, you might be tempted to use the entrywidget you created to use as an item, changing some properties and expecting the changes to be displayed.
Something like this:


(How to expose variables):
[SPOILER]
Select the variable and check the options “Instance editable” and “expose on spawn”.
ExposeVarOnSpawn.png
If the node does not display the exposed variables - because you had already added the create node and afterwards checked the boxes - Right-click the node and select “Refresh Nodes”, or create a new node and see if that has it.
[/SPOILER]
You might expect that this will create a listing of numbers from 0 to 10 as the displayed text. But instead the following is the actual result:

Listview32.png

In this example a widget was created and added as an Item. The listview then generates widgets to represent those items.The **Item **has some properties changed (the text), the entrywidget - the widget that is displayed - remains default.
Listview-ItemWidget.png
While widgets can be used as items, I think it’s mostly confusing for learning. Lets start with something else. (the entrywidget could be used as a slot to which the widget item is added. But that’s a little more advanced as of now).

4. Listview generation
The listview generates a widget for each item that needs to be displayed. When an entrywidget is generated, two events are fired. One in the listview called On Entry Generated. The other in the entrywidget which we saw earlier - “Event On List Item Object Set”.
When scrolling, entrywidgets are dynamically On Entry **Released, **On Entry **Generated, **triggering **On List Item Object Set. **Also **On Item Selection Changed **- IF that item is selected.

5. Creating a proper list
Here the item will be an UObject with a single text property which is set during a loop.
To update the entrywidget, go to the entrywidget and on the event “Event On List Item Object Set” which is currently storing the item, cast the item to the Objecttype added as item and get its text.


Here, an UObject is used to just hold some data. But the item doesn’t have to “just hold data”. The ltem can be any instance and even different classes. So I could have an item list of UObjects, Actors, characters and vehicles. How you format the entrywidget visually is really up to you, with what kind of elements to display.
Now that the basics are out of the way, lets do something more interesting. An Item can be any object, so what if we added some actors and these actors to be listed in the view?

6. Going advanced - Actor representation
For me the actor is just named “Ball” and has a static mesh sphere. These are dragged into the scene, about 10, and in the listviews construct event “get all actors(ball)”. I wouldn’t recommend this for practical use but it is good enough for an example.
Now, we could loop through the array, adding each instance with the “add item”. Or, we can just use the listviews “Set list items”.
The entrywidget will need to cast the item to ball class. For now, the entrywidget only has a textblock and I’m going to set its text to the objects “get display name”.

ListView5.png

We could make it more interesting, like randomizing the ball size and adding another textblock to the entrywidget, displaying the instance size. But that’s just more data pulling which we have already learned.
What if, instead, we were able to click on the listview and the corresponding ball is selected? Or Vice verse?

7. Going advanced - Selection
The listview has some inbuilt selection functionality. You can even choose between single and multiple selection in its properties. Single selection for now.
The following image shows a simple setup. When “Event On Item Selection Changed” is called because you clicked on one of the items in the list, one entrywidget will return false and another will return true. One was deselected, the other selected. When debugging, if we only printed the boolean, how would we know which was selected and which was deselected? The impulse might be to include the widget reference as debug data, but that really provides poor information. Instead, try to work with the Items, which is done in the image.

ListView6.png

Running the above will show that it works but we really need better representation.
The Entrywidget gets a new variable - “Border Color”, a linear Color which the border brush color is bound to.
The ball has also gotten an event that can be called.
The idea is that when the Item is clicked, change the scale of the Ball to show that it is selected in the world.


Running the above will change the size of the ball actor when a list item is clicked.

-Problem 1-
But there is a problem. If you scroll down you will eventually see a widget appear, marked as if it is selected.

That is because the widgets are not destroyed when they are scrolled out of view, they are On Entry **Released **and then On Entry **Generated **when needed again. So they still have their old properties. The display name text changed, because we do that “Event On List Item Object Set”, but the border color remains untouched.
So how do we fix that? Well, there are a many ways. There could be a “Reset Properties”-function that is run when Event On List Item Object Set? If we think about this execution logic, it would look like this:

  1. widget**(s)** generated, setting Item, reseting border color.
  2. Item selected, changing border color to green. and changing ball size.
  3. widget scrolled out of view, released, possibly generated again for some other item but that’s fine.
  4. Selected Item is scrolled back into view - widget generated, setting Item, reseting border color!
  5. But there’s another event that happens after that. The listview knows that the item is selected so it calls Event On Item Selection Changed on the widget. (only for those items that are selected, not for the ones which are not).

-Solution 1-
A reset function could work in this case, but would still be an issue for other types of executions. Imagine if, say a tree, would **grow **each time it was **selected. **But then it would also grow each time i was scrolled back into view…

The reason for pointing all of this out, is that it is important to **test **just what all the functions do and that different solutions is needed depending on the situation.

Update: Use the “Event On Item Released” to reset the entrywidget to its default visual.

-Problem 2-
If an item is selected - changing the ball size - and you scroll down and select another item, the ball size of the previous selected should reset, but it doesn’t!

Because we have implemented behaviour into the EntryWidget, and the entrywidget points at different items - items which might not be represented at the moment - the widgets may not be able to communicate with the relevant item.
This case example should show us that the EntryWidget may not be a proper place to implement item changes to.
So the border color stays, the ball size change goes.
To where? To the listview to handle.

-Solution 2-
The listview has the event “On Item Selection Changed”, which provides the item and if it is selected or not. But we also want to reset the previously selected ball size. Well, the listview only provides so many functionalities. This is a behaviour we need to implement ourselves. Current setup:


Remember that this is just for single selection. If we wanted multiple selected items the node setup would have to be modified.

8. On Item Selection Changed - ListView Or EntryWidget?
At first glance the Selection Changed events of the treeview and the entry widget may appear very similar, but they are in fact very different.


The EntryWidgets event should be used for setting up visual representation of the entrywidget.
The ListViews event should be used for logic such as informing the Item about its selection state. If the deselected item is relevant, you need to track it manually using a variable, which we have done in a previous example.

9. Going advanced - World selection
So we can select an item in the list to show that it is selected, and store the current selection in a variable for any class to use. But what about clicking on the actor itself?
Well, how do you want to handle that? should it be with the spheres on clicked event? Or a linetrace from the camera?
If we do it with the spheres On Clicked event, then the sphere needs to either send information to -some class- that it was clicked, or have -some class- bind to the ball being clicked.
I’m going to go with a linetrace from the third person character, in the third person template, because that’s just the easy way to do it. The third person character in my case is also the class that creates the listview widget.

Now there is a slight issue.
We know that by clicking on an entrywidget, that item will be selected, telling that actor to change size. To demonstrate the issue, the ball will now print the value of an int, which is incremented if selected and decremented when deselected. So deselected =0, selected =1.
Say I do the following:

  1. Detect ball through a linetrace.
  2. Tell the ball that it has been selected.
  3. Inform the listview to mark the ball as selected.
    What now happens is that the listview tells the ball to be selected, again. As you can see a 2 int.
    When it comes to selection - if the item needs to know that it is selected - in my experience it is better to go through the listview to have IT perform the selection.

Let’s follow some execution flow with some requirements.

  • The character needs to be updated about the selection, say saving the reference in a variable.
  • The Item needs to be informed that it has been selected, say becoming highlighted.
  • The List needs to be updated about the selection.

First is a selection in the listview.
ListView-SelectListView.png

Second, the item selected though the world. For example, a linetrace from the character. If the Item and character are informed of the selection after the trace, and then the Listview is told to select the item it creates unnecessary logic, which might be problematic.
ListView-SelectCircular.png

Third, the item selected though the world. For example, a linetrace from the character. The character informs the Listview to select the Item which triggers the execution of the flow in the first example.
ListView-SelectusingList.png
But in the end the setup depends on what you require the selection to do.

10. Going Advanced - Drag & Drop
Drag & drop is as easy to implement for a listview as anything else. If we want to be able to reorder the items then, instead of having the listview handle the items, we need to take more control in the form of an array that we can remove and insert items into.
The entrywidget now has a “Item dropped on” dispatcher which the listview binds to when the entry is generated

Bindings in the Entry Widget
If your Entry Widget has a event dispatcher that the widget holding the listview binds to during the “on entry generated” event, you do not have to unbind them when the widgets are released. Seems like the entrywidget internally unbinds any bindings during the released event.
However, if the Entry Widget binds to event dispatchers - say the item they represent - those have to be unbound manually.

11. Multiselect

So what about multi select? Well, it’s not looking good: Unreal Engine Issues and Bug Tracker (UE-72610)
I’d expect that holding shift while clicking items would be the default behavior but such a feature is backlogged.
So I have to do it myself. This is going to be somewhat of a brute force and will not be pretty.
There are several methods I have considered but many have met one issue or another. So first of all I’ll list the issues.

  1. If the function “Set Item Selection” is used, the “On item selection changed” of the ListView will usually not provide the item that was changed, but the first one in the list or the item most recently clicked on.
  2. When the EntryWidget is pressed ( Mouse button down), it will clear the selection of other items - silently. When mouse button is released, all the items will be marked correctly. That means every time I click, the list flickers/blinks.
    there’s also some other annoyances that would take too much space to write about. But to circumvent these problems, first the entrywidgets default clicks can not be used. We need a Button to consume the click. We also have to provide some additional logic to the listview so that we don’t get the wrong Item when “On Item Selection Changed” event fires.

Entrywidget is standard setup.

[ATTACH=JSON]{“data-align”:“none”,“data-size”:“full”,“data-tempid”:“temp_180463_1578269811040_365”,“title”:“1AAMultiSelectEntryWidget.png”}[/ATTACH]

The following THREE pictures for the setup for the listview.

  • A List of Objects to easily track selected Items.
  • You have a choice between two methods.
    Either you use a boolean to know if the “On Item Selection Changed” might provide the wrong item, which it may when “Set Item Selection” is used.
    Or you make your own function to know when Item selection is changed. Both examples are provided but you only need one.
    Know that when “On Item Selection Changed” provides the wrong Item, it provides the wrong Items selected state, so another boolean for if the clicked item is selected is needed.
    Explanation after the images.

[ATTACH=JSON]{“data-align”:“none”,“data-size”:“full”,“data-tempid”:“temp_180464_1578269910133_103”,“title”:“1AAMultiSelectB1.png”}[/ATTACH]
[ATTACH=JSON]{“data-align”:“none”,“data-size”:“full”,“data-tempid”:“temp_180465_1578269890120_970”,“title”:“1AAMultiSelectB1.png”}[/ATTACH]
[ATTACH=JSON]{“data-align”:“none”,“data-size”:“full”,“data-tempid”:“temp_180466_1578269953326_165”,“title”:“1AAMultiSelectB3.png”}[/ATTACH]

On Entry generated -> Bind to the buttons click event when the entry is generated.
On Clicked ->The Clicked dispatcher provides the Item clicked and we save it as a variable, so that we know what item to use, when we use “Set Item Selection”.
Then Query if shift [or whatever method to enable multi select] is used. If it is, change the selected state. If it isn’t, set the clicked item to be the only selected item.
Change Item Selection -> Selected becomes deselected, deselected becomes selected.

Deselect Item -> Remove the Item from the Selected Items list. Check that the item “On Item Selection Changed” provides may be wrong. Set the clicked items selection boolean to false. Then call “Set Item Selection”, followed by the custom “On Item Selection Changed”.
Select Item -> pretty much same as above.
Select Item (Single) -> an Item was clicked on without holding shift. That should select only that item. So first clear the selection. Know here that using “Clear Selection” for the ListView will make the “On Item Selection Changed” event fire with a null item, therefore the isvalid check later. Then, add the clicked item to the selection and once again prepare for the “On Item Selection Changed” event to fire with a wrong item.

My Custom On Item Selection Changed -> If used, the event that is used to perform whatever logic is needed with the correct Item.
On Item Selection Changed -> If used, Query if the Item is valid. It will not be when calling “Clear Selection” on the Listview. Then query of this event was called internally, or manually using “Set Item Selection”. If it is a valid item, proceed. If not, the Item was set Manually and it was the last clicked item.

Well, that was messy. Hope it makes sense. I’ve played around with it a bit and can’t see any obvious issues. But if you really want MultiSelect, this is probably the easiest solution. By the way, the Listviews selection mode between Single and Multi doesn’t seem to matter. Same behaviour in both cases but change it to Multi anyway.

12. Node information

Ignore attachments

21 Likes

[Reserved for treeview and Tileview]

The treeview and gridview are very similar to listview.

Note! The function “is List item Expanded” crashes the editor.

1. Treeview - Getting Started
You setup a treeview the same way as a listview.
The point of a treeview is to have Items be “children” of other Items, like a tree!
You add children to an item in the function “On BP on Get Item Children 0”. This is a binding you find on the treeview events in the designer graph.
Once that binding is created, you will get a function with an in Item, and an array of items as children.
Again, if a entrywidget is created more than once for an instance, the engine will crash. This is very easily done when learning the treeview.
Using the ball actor in previous examples, I have added a bunch of mesh components which will be the children.
This is the basic setup I have created:


**Addendum: On BP Get Item Children needs to return an empty array if the cast fails, or this setup will not work.
Here, Ball 12 has been selected and the treeview expands it by getting all the children - children components in my case - and generating entrywidgets for them. Do note that the “On BP on Get Item Children 0” is also executed for each item - each child - that appears.
While there’s some basic formatting happening in the entrywidget - a textblock either has a > or V symbol, it could be done better.

  • Some spacing for each child to better indicate what item they are children of.
  • If an item does not have children, it really should not have either > or V symbol.

2. Treeview - Spacing [Backtracking through parent]
The spacing is set by adjusting the border left padding. The value depends on how deep in the hierarchy the item is. In this case we can find out the depth by calling a recursive function which gets the parent of each item.

3. Treeview - Expandable symbol
There are three possibilities with the expand symbol, in my case a simple > or V.
> means it can be expanded
V means it is expanded.
But if it can not be expanded - if it has no children - no symbol should be displayed.
So how do we determine that? Sure, you can cast the item to actor or scene component and get its children. But there’s already a similar example of that above, and also maybe you use some other method to determine if an item has children for the treeview. It might be better to let the treeview determine this.
Besides, while “On Item Selection Changed” updates for the entrywidget when scrolled into view, the event “On Item Expansion Changed” does not so we can not *rely *on that though it is needed.
The “On BP on Get Item Children 0” can be called manually to determine if the item is expandable or not.
The function “is List item Expanded” would have been useful but any use of it crashes the engine.
So we need to implement a custom way of remember the expanded items. I use a TSet of UObjects.

4. Treeview - Spacing [When getting the items children]

But not all treeviews will have items that actually inherit from one another. In that case you need to track the depth of the item. The tracking can be done in the “On BP on Get Item Children” function. By using a Tmap to store the item and its depth, we can query the depth of the parent and set the depth of its children as we enter those into the map. Then, as items are generated we can get the Item depth and set it for the widget.

Treeview - I don’t want the expansion changed!
Currenty, if an item has children and is clicked on, its expansion state will change. Following are a couple of solutions so that does not happen

5. Treeview - Expand only if Selected [revert expansion method]

Currently when an item is clicked, it will expand or collapse. What if we don’t want it to change its expanded state when we select it, but if we click it when it is selected, then it will change expansion state? Again, this is possible to achieve but requires our own implementation logic.
First, we must track which item is selected but can not use the treeview’s get selected item(s) function.
For the events that we will be using, the first thing that happens is that On Item Expansion Changed is called and after that On Item Selection Changed is called. However, printing the item that the expansion event provides, shows that the clicked item has already changed.

Treeview-ExpansionDebug.png

This means we can not use the treeview’s get selected item(s) function but can track the selected item and query the previous selected item during the expansion event.
The clicked item will change its expansion state, but we can revert the change by using the treeview function “Set Item Expansion”. However, that is going to trigger “On Item Expansion Changed” event again, so we need to track the revert logic so that the second event does not call the “Set Item Expansion” again.


** After some more experience, you may want to use an Item variable **Exclusively **to track the expanded state, instead of the “selected Item” reference which would be used for other things.

6. Treeview - Don’t change expansion [Override click method]

The entrywidget will internally tell the treeview that it was clicked on. Whether it was a border, an image or another user widget, it will detect the click.
Setting the entrywidget to hit test invisible is no good - we need to be able to click on the widget.
There is one widget that does not let the userwidget know it was clicked on: the button.
So below is an image of the entrywidget hierarchy. Logic will be that when the
-“Expand Button” is clicked, the expansion of the item is changed- collapses if expanded, expands if collapsed.
-“Select Button” is clicked, the item is selected.
-The border inside the “Select Button” is there for double clicks. That is done by selecting the border and under its events, create binding.
These click events calls event dispatchers.

Inside the widget that holds the treeview, we simply bind to the dispatchers during the “on entry generated” event.


The result is that the entry widget do not receive the clicked events, thus does not change the expanded state.
We manually change the expanded state depending on our needs.
In this case, the border with the text (which is held by a button) can be clicked on to only select the item, no change in expanded state.
The visible button can be clicked to change the expanded state only, without selecting the item.

4 Likes

Wow, great job writing this up!

I struggled when working with the TreeView about a month ago. I eventually figured it out, but it became clear to me that there was insufficient documentation out there to make things easy for first-time users of these widgets.

If I had only waited a month, I would have had a much easier time, I think, thanks to your excellent tutorial here.

So, thanks for this, even if I missed the chance to maximally benefit from it!

Hey, thank you.

While this is about getting introduced to working with the listview, if anyone wonders how to do anything specific, feel free to ask. For example, if the listview listed actors based on distance. Sure that can be done, but it is not a listview thing to handle, rather you would implement your own algorithm to do the sorting of items and then update the listview.

Feedback is also welcome, if some parts were unclear or if something specific should be mentioned. A large part of this guide tries to explain why some things -don’t- work rather than just showing a setup of a listview that works. I feel that that wouldn’t actually teach anything.
I’d be especially interested to hear from someone who had never used a listview prior.

@ste1nar

This tutorial was really great. It helped me figure out the last bit of opacity on the side of how-do-list-views-work.

I have a specific problem in a EditorUtilityWidget I am making.

I create a list view of certain actors in the level. Everything is great about it unless I delete one of the list-represented actors from the level the manual way-- selecting it and hitting the DEL key.

This causes the whole editor to barf with an error about :

LogSlate: Warning: WidgetMapToItem length (6) does not match ItemsWithGeneratedWidgets length (5). This is often because the same item is in the list more than once in ListViewT<ItemType> [ListViewBase.h(219)]. Diagnostics follow.

The diagnostics show what would be expected from the error description… all the Items and Widget Map entries match up, but there is that one less object in the scene.

I can’t figure out what Unreal wants me to do to intercept this event.

I tried clearing, setting and regenerating the list bound to the various item release events on the listview and listitem widgets, and tried to bind to OnDestroy and OnEndPlay on the object. But I can’t figure out what event, at design time, will prevent slate from discovering my listview is out of sync.

This particular functionality is a sanity check deliberately meant to detect when the user tries deleting an object that shouldn’t be manually deleted from a set of objects.

Any idea on that?

Thanks!

@JDStrawesome

I haven’t used EditorWidgets yet, I created one just now to see if I could get it working. unfortunately, I could not. No matter what, when the listview needs updating (whether that is by: clear list items, Set List Items (with an updated array), regenerating, scrolling with the deleted item represented), it crashes. The only thing I noticed was that if the Item was not a displayed item, it could be deleted and when scrolling to it, there would be an “empty” entrywidget (without telling the listview to update).
I now also noticed that if the Item is NOT currently displayed, it can be deleted and the listview updated. But the real issue is of course the displayed items.

I did have better luck with the treeview though, if that could be a temporary solution while the listview is not working.
The idea behind the treeview is that the widget itself is the only manually added Item. When the Treeview runs the “On Get Item Children”, you just provide the array of the items you are manually tracking.
I haven’t noticed any glaring problems using the treeview, but there certainly could be.

@ste1nar

I solved this.

I combed through source code, and it just seems like when you delete an actor from the level, it’s presence is still referenced… probably for the sake of the undo stack.

Because of that, actors-as-items in a utility widget running at design time will crash the editor every time because you end up with a duplicate item entry in ItemToWidgetMap but not in the WidgetToItemMap and GeneratedWidgetsblahblah list.

To solve this, I made a data object that only holds a reference of the actor it represents… I instantiate and pass these objects to the listview as items. During EventOnListItemObjectSet, the object passes its reference of the actor to the list item widget so that it can populate all the informational widgets.

The actor handles all its own original data.

The proxy object simply acts as a stand-in or handshake.

Works like a charm… and exposes that the listview widget seems to work in utility widgets as intended if it weren’t for this special behavior in level actors.

Interesting, well done. I did submit a bug report about the issue, Unreal Engine Issues and Bug Tracker (UE-73014) so perhaps next update it will just work.

Great tutorial, it would be better if there was a small example!

2 Likes

Hi Ste1ner
I’ve been trying to follow your tutorial but am stuck at the “creating a proper list” section. You use Construct Object from Class (Item Object) but I can’t work out how to define the “Item Object” class. I’ve tried “Add New … C++ class” based on UObject but this never appears in the content browser or in any lists.
Are you able to include the definition of Item Object or some instructions.

Regards

Hi ,I try drag&drog in entrywidget,but OnMouseButtonDown event is never called,why?

Hello. The “Item Object” (which is really poorly named), is just an object class, which you can create by right-clicking the content browser to bring up the menu, click “blueprint class” to create a new blueprint. Then instead of clicking one of the common classes, expand “all classes” and select “Object”.
But by this time you have probably moved on.

Hello.If your Entry Widget has elements with the Visibility option set to “Not Hit-testable”, then those elements can not interact with the cursor.
Or, if you have a Button widget, then that will override the OnMouseButtonDown, so the Entry Widget is not automatically informed that there was a click, or button down.

Changes in 4.23
The nodes “Is List Item Selected”, “is List Item Expanded” and “Get List Item Object” has been removed. A new event has been added to the List View: “On Entry Initialized”. This event provides both the Item and the Widget references and practically renders “Get List Item Object” redundant, since it would be used every time with “On Entry Generated” event.
Now, instead of using “On Entry Generated”, use “On Entry Initialized”.

1AListRemovedNodes.png
1AListAddedNodes.png

On Entry Initialized” is called every time an Entry Widget is generated, not only the first time. ie scrolled into view, appearing in the list for a new item.
Initialized event fires first, then generated. In fact, in the following screenshot we can see that all the initialized event fires for all items before the generated event

1AListInitializedGeneratedOrder.png

In 4.23 The Entry Widget seems bugged. Adding the “User Object List Entry” interface does not create any functions that the Entry Widget needs. This is fixed in 4.24.

1. Listview & Inventory Items

This serves exclusively for using the listview to display “inventory items”.

The first question is, what are those items? Here’s some common item types:
-Structure holding the data
-Name, pointing at a data table that holds the data
-Object or Actor.

The Listview needs an Object to handle it as one of its Items, so if your inventory is data only, then you may have to create objects just to pass them to the Listview just to act as mediators.
In the case of using a data table, the EntryWidget could pull the data from the Data Table.
If your items are Objects, they can just be passed to the listview. But still there may be another more practical option.

And that option is to have Mediator objects for each item slot. This should work for constant-sized inventories and would account for empty slots. Think Minecraft or Diablo (though this will not be about having one Item occupy several slots).

So here’s an example of mediator objects acting as pointers to the inventory slot they represent.

2. Drag & Drop solution with mediators
Here in the entry widget I am creating a drag drop operation.


The result is this:
Tileview-DD-sizediff.png
The dragged element is smaller. That is because the TileView has a size setting for its tiles, default to 128x128. Then the behavior for widgets with a border and text is to be as small as possible. To have the dragged element be the same size, unfortunately we have to use a SizeBox wrapping the border.
The SizeBox Height and width should be = Entry height and width - Entry Spacing.

For the On Drop function, the idea is to get the payload index and the index it is dropped on. At first I was setting up this logic in the On Drop function of the EntryWidget. But really, isn’t it better to have the character [or whatever class holds your inventory] always be informed about a drop and let it decide what should happen?

The entry widget does not know about the character, but the mediator does. So you have two options here. You can either tell the mediator to tell the character to inform about the drop, so that the character can swap the items. If you do this, don’t forget to then tell the Listview to regenerate entries, ie to update that the widgets display the correct item. The upside to this is that you can make a function with a return value, so that you can do fancy things with the Entry Widget depending on the outcome of the drop.
OR
Have a dispatcher in the Entrywidget that the Inventory Widget is bound to, which is what is shown here.
The Inventory Widget knows about the character and can tell it that a drop happened, or it can have a dispatcher of its own that the character listens to.


Above is really just a long winded way of getting the payload index and the index of the dropped on mediator.
With a dispatcher, you can still inform the Entry Widget about the outcome of the drop, but then the dispatcher has to provide the entry widget reference and you need to implement some functions to call.

If the Entry Widget is going to have a binding, we have to bind to it when it is generated.

Now for the actual item swap query and logic.

  • If the item is dropped on itself, nothing should happen.
  • If the item is dropped on an empty slot, set that slot to the item and empty the payload (where it came from)
  • if the item is dropped on an occupied slot, swap them by having a helper variable hold item A, as item B is set to occupy A, then set the origin of Item B to equal Item A.


The reason for the return boolean, is that if there’s a change, the listview need to regenerate its entries. Try not regenerating and you’ll see that visually nothing happens.

One note is that while we can drag items around, we can also drag empty slots around. To fix that, you can’t add an additional query in the character function - Inventory Drag Drop, to see if Inventory[PayloadIndex] == None. Because then you’d still be able to drag around an empty slot. Haven’t seen any games do that. So that has to be queried before we create the Drag Drop Operation. The Mediator Object gets a new function returning if its index has an item or not.

Do note that with this type of inventory, you never delete members of the inventory array. You empty the array members that should be empty. If you delete members, you’ll start getting errors since the mediators can’t access index members that does not exist.

– End of drag and drop with mediators–

An alternative option to having mediators for your inventory items is to have the inventory slots be Objects. So instead of an array of Items or structs, you’d have an array of Inventory Slot Objects. These Objects holds the items or structs, and are the ones passed to the listview.

Here’s the concept:


In a game like Diablo, you can have a single item on a slot, or a stack of pots on a slot.
Or in a game like Kenshi, food can be stacked despite them having various amount of consumed. By having a single slot being able to hold several items, you can have item stacks where each item of the stack is an instance.
You just have to query when using drag and drop that the items are of the same type.

3 Likes

Thanks for the reply Steiner. Yes, I did figure it out in the end but took me a couple of days to get it sorted. Now I’ll try implementing the tree view, fingers crossed.
Very good tutorial sir, you certainly know your stuff.

I hate to resurrect the thread but,
Haven’t worked in UE since about 4.14, Picking it back up and rebuilding some things in 4.23
Decided to try out tile view for my inventory with this tut i got it all mostly working in no time. So thanks for that

I can pick stuff up, it displays what i have in my inventory
But i do have an issue
when setting up click events in the tile view it doesn’t recognize the clicks
but if i set them up under the entry widget for the button itself it will pick them up
any ideas on what i missed to allow the tile view to pick up the button clicks?
without being able to pick them up there IDK how i would update the list without closing the inventory and bringing it back up

Thanks,

Okay, I think the issue is that you’re using a Button.
If you have a border that has visibility: visible, then the EntryWidget detects clicks by default, and informs the Listview about clicks, and so those events fires too.
With a Button however it kind of consumes the click event [probably using wrong terminology here] so the EntryWidget is never informed about the click.
This is true for any UserWidget, not just EntryWidgets. If you have a widget with a border and override the widgets OnMouseButtonDown function, you can see that it fires when you press the widget. If you then but a Button on the border, then OnMouseButtonDown no longer fires when you press the button.

Either you keep the button but need to implement your own logic, binding to dispatchers helps in this aspect. See the last parts of the TreeView titled Treeview - Don’t change expansion [Override click method].
Or get rid of the button. I ofc don’t know exactly what your individual tile is supposed to do, maybe there’s a good reason to have a button, but for a simple inventory the button just complicates things.

working fine now, don’t know why i didn’t put that together and figure it out
Thanks for taking the time

I am having some issues with Construct Object from Class and setting up variables in the text objects
This is mine:
COfC1.PNG

And this is the one in the guide:

In the guide, an extra input pin for the text exists. How?

Edit: it seems different classes have different inputs.
Is there a way to set strings to generate specifically based on a datatable row info?

Well I figured it out on how to get those extra pins but I cannot for the life of me understand why the text will not change.
I tried to use a binding, did not work - returns blanks.
I tried to use the text as a variable and the text field as a variable, did not work - returns blanks.

The problem is that the value accessed is always null for some reason.
When creating the object and adding it to the listview, I also debugged it and the object does contain the values needed to change the text in question.

So I am at a loss here…

I am literally only trying to change the values based on a datatable.
So far accessing the save file to populate the correct number of entries has worked and binding the text also worked but the values are always null.