Quantcast
Creating a scrollable XML "list control" and adding data via LUA - ESOUI
Thread Tools Display Modes
03/24/21, 02:11 PM   #1
scorpius2k1
 
scorpius2k1's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2019
Posts: 6
Post Creating a scrollable XML "list control" and adding data via LUA

How would I go about creating a scrollable XML list control that can have items added to it. I am trying to simply add a text entry to a list via LUA, and then display them into the XML list window when it is visible.

There is an addon which has a great looking history window that I would like to recreate in some form:
https://www.esoui.com/downloads/info...ndHistory.html

I really appreciate any help.


Last edited by scorpius2k1 : 03/24/21 at 02:22 PM.
  Reply With Quote
03/24/21, 02:28 PM   #2
votan
 
votan's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 546
Originally Posted by scorpius2k1 View Post
How would I go about creating a scrollable XML list control that can have items added to it. I am trying to simply add a text entry to a list via LUA, and then display them into the XML list window when it is visible.

There is an addon which has a great looking history window that I would like to recreate in some form:
https://www.esoui.com/downloads/info...ndHistory.html

I really appreciate any help.

I would take a look at these:
https://www.esoui.com/downloads/info1151-LibScroll.html
https://www.esoui.com/downloads/info...stExample.html
__________________
@votan73 (EU - megaserver)
  Reply With Quote
03/25/21, 03:47 AM   #3
Baertram
 
Baertram's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 3,440
My answer from my pm to you (seems I wasn't fast enough ):

ZO_SortFilterList is a base class you can reuse here but I also always have to look at what is needed.
XML is needed to define header controls for the sortheaders, a "List" control to show the lines and scrollbar, and so on.

I think there is a library by Circonian which was aiding here, but I do not know if it still works.
https://www.esoui.com/downloads/info1151-LibScroll.html

But:
Start small and try to understand the whole ZO_SortFilterList.
I think this will be better to do than using Circonian's library. Who knows what ZOs will change and if the libs till works or will work soon...
See my example below.


It basically eased the usage of the ZO class functions and provided an API to create such lists.
I've never used it. MAybe there is a wiki example how to create such lists somewhere...
Didn't find any
Else it is try and error + read ZOs code at github e.g.

Use the forum search:
https://www.esoui.com/forums/search.php

Search for ZO_SortFilterList or ZO_SortList or similar to find threads about it, getting some more info.

All I can help you with is give a short introduction. Have a look at the addon WishList -> WishList_window.lua
At the top the subclass of the ZO_SortFilterList will be defined:
Lua Code:
  1. WishListWindow = ZO_SortFilterList:Subclass()
There is a XML file where the headers are defiend as virtual controls and the list rows as virtuals and the labels of each list row as virtuals as well:
Code:
<Controls>
		<!-- Virtual labels -->
        <Label name="WishListRowLabel" font="MyFontGame16" wrapMode="ELLIPSIS" horizontalAlignment="LEFT" verticalAlignment="CENTER" virtual="true" >
		</Label>
        <!--Sort headers -->
        <Control name="WhisListSortHeader" inherits="ZO_SortHeaderBehavior" virtual="true">
            <Controls>
                <Label name="$(parent)Name" font="MyFontGame20" color="INTERFACE_COLOR_TYPE_TEXT_COLORS:INTERFACE_TEXT_COLOR_NORMAL"  horizontalAlignment="CENTER" verticalAlignment="CENTER" wrapMode="ELLIPSIS">
                        <AnchorFill />
                </Label>
            </Controls>
        </Control>
        <!-- Virtual Rows -->
		<Control name="WishListRow" mouseEnabled="true" virtual="true">
...
and the TopLevelControl of WishList is defined as well,
having some of the sort header controls (using the virtuals defined at top as template) and then the <list control is below, where the ZO_SortFilterList will be loaded into via the lua code in WishList_window.lua:
Code:
<!-- Frame/Window -->
		<TopLevelControl name="WishListFrame" inherits="ZO_RightPanelFootPrint" hidden="true">
			<Controls>
... search box here and backdrop of editbox etc.
Sort headers:
<Control name="$(parent)Headers">
					<Anchor point="TOPLEFT" offsetX="30" offsetY="51" />
					<Anchor point="TOPRIGHT" offsetY="51" />
					<Dimensions y="32" />
					<Controls>
                        <Control name="$(parent)DateTime" inherits="WhisListSortHeader">
...
List for the ZO_SortFilterList. $(parent) points to WishListFrame so the name of the control is globally defined as "WishListFrameList" and can be accessed by this, or as we "add" the ZO_SortFilterList to the global WishListWindow.frame.list it can also be accesed via this pointer (See lua code below)
<Control name="$(parent)List" inherits="ZO_ScrollList">
					<Anchor point="TOPLEFT" relativeTo="$(parent)Headers" relativePoint="BOTTOMLEFT" offsetY="3" />
					<Anchor point="BOTTOMRIGHT" offsetX="-35" offsetY="-32" />
				</Control>

The connection between the XML and the lua stuff will happen in WishList_Window.lua in function:
function WishListWindow:Setup( )
Lua Code:
  1. --Scroll UI
  2. --DataType WISHLIST_DATA is a number constant (e.g. 1) used to defined a unique dataType of the rows, which MUST be set and used for searching/filtering e.g. Having different sortfilterlist providing different row contents should also sue different datatypes
  3. --WishListRow is the temmplate from XML defined as the row type of the list row. There we have defined the columns with the virtual labels like date, name, locality etc.
  4. --30 is the height
  5. --the function defined will be the callback for EACH row as the row get's setup/created
  6. --So this will call WishListWindow:SetupItemRow on each row where the labels are hidden/unhidden (if you re-use the same row to show different columns, like I do in this addon. You could also just create multiple ZO_SortFilterList objects and assign different row types etc, and jst hide the total lists then. But I only wanted to use 1 list and re-use the rows and headers, just hiding the headers and labels/columns I do not need, depending on the selected UI buttons)
  7.     ZO_ScrollList_AddDataType(self.list, WISHLIST_DATA, "WishListRow", 30, function(control, data)
  8.         self:SetupItemRow(control, data)
  9.     end)
  10. --Adds a highlight onMouseEnter of the row
  11.     ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")
  12. --"Zebra" background for the rows
  13.     self:SetAlternateRowBackgrounds(true)
  14. --The list which hels the entries of the rowData. Each entry will have 1 table with the information needed for the row -> See function BuildMasterList
  15.     self.masterList = { }
  16.  
  17.     --Build the sortkeys depending on the settings
  18.     self.currentSortKey = "name"
  19.     self.currentSortOrder = ZO_SORT_ORDER_UP
  20.     self.sortHeaderGroup:SelectAndResetSortForKey(self.currentSortKey) -- Will call "SortScrollList" internally
  21.     --The sort function
  22.     self.sortFunction = function( listEntry1, listEntry2 )
  23.         if     self.currentSortKey == nil or self.sortKeys[self.currentSortKey] == nil
  24.             or listEntry1.data == nil or listEntry1.data[self.currentSortKey] == nil
  25.             or listEntry2.data == nil or listEntry2.data[self.currentSortKey] == nil then
  26.             return nil
  27.         end
  28.         return(ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, self.sortKeys, self.currentSortOrder))
  29.     end
  30.     self.searchDrop = ZO_ComboBox_ObjectFromContainer(self.frame:GetNamedChild("SearchDrop"))
  31.     WL.initializeSearchDropdown(self, WISHLIST_TAB_SEARCH, "set")
  32.  
  33.     WL.initializeSearchDropdown(self, WISHLIST_TAB_WISHLIST, "char")
  34.     --Search box and search functions
  35.     self.searchBox = self.frame:GetNamedChild("SearchBox")
  36.     self.searchBox:SetHandler("OnTextChanged", function() self:RefreshFilters() end)
  37.     self.searchBox:SetHandler("OnMouseUp", function(ctrl, mouseButton, upInside)
  38.         if mouseButton == MOUSE_BUTTON_INDEX_RIGHT and upInside then
  39.             self:OnSearchEditBoxContextMenu(self.searchBox)
  40.         end
  41.     end)
  42.     self.search = ZO_StringSearch:New()
  43.     self.search:AddProcessor(WL.sortType, function(stringSearch, data, searchTerm, cache)
  44.         return(self:ProcessItemEntry(stringSearch, data, searchTerm, cache))
  45.     end)
  46.  
  47. --Now we assign pointers to the XML controls of the headers
  48.     --Sort headers
  49.     self.headers = self.frame:GetNamedChild("Headers")
  50.     self.headerDate = self.headers:GetNamedChild("DateTime")
  51.     self.headerSetItemCollectionState = self.headers:GetNamedChild("SetItemCollectionState")
  52.     self.headerName = self.headers:GetNamedChild("Name")
  53.     self.headerArmorOrWeaponType = self.headers:GetNamedChild("ArmorOrWeaponType")
  54.     self.headerSlot = self.headers:GetNamedChild("Slot")
  55.     self.headerTrait = self.headers:GetNamedChild("Trait")
  56.     self.headerQuality = self.headers:GetNamedChild("Quality")
  57.     self.headerUsername = self.headers:GetNamedChild("UserName")
  58.     self.headerLocality = self.headers:GetNamedChild("Locality")
  59.  
  60. ...
  61. --Ths will call the following functions now to build the masterlist, create entries for te rows, and refresh the visible stuff:
  62.  --self:RefreshData() will run:
  63.         -->self:BuildMasterList()
  64.         -->self:FilterScrollList()
  65.         -->self:SortScrollList()
  66.         -->self:CommitScrollList()
  67. self:RefreshData()

So you can check these other functions what is being done there. It will first build the masterList, which calls
Lua Code:
  1. --Insert an entry to the masterlist, and the entry is created/defined via function WL.CreateEntryForSet which will create a { .... } data table for the row
  2. table.insert(self.masterList, WL.CreateEntryForSet(setId, setData))

self:FilterScrollList then filters the masterlist according to your filter parameters (e.g. a search editbox using string text search)
self:SortScrollList will sort the entries thena ccordingly to the selected sortheaders
self:CommitScrollList will then apply all that changed stuff, update the visuals etc.

The self:SetupItemRow is the function which reads the info from the masterlist of the current row and applies the data.locality to e.g. the locality label of the row control (defined in XML).

Just as info:
WL.CurrentTab = The currently selected tab at the WishList UI. The sets search, the WishList of each toon, or the history "who found what set item where and when"
WL.CurrentState = The current state of the tab (could be "fetching data" at the sets tab, "finished fetching data" and so on)
Depending on these values the ZO_SortFilter list headers, and rows will change.
You'd only have to define 1 rowtype and use it for your history. You do not need to differ it depending on the selected UI etc.
  Reply With Quote
03/25/21, 11:01 AM   #4
scorpius2k1
 
scorpius2k1's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2019
Posts: 6
THANK YOU both very much! With the information provided from you both, I was able to get it figured out (and learn quite a bit about creating custom controls in the process). LUA/XML has a very interesting correlation, especially from the other types of coding I am used to over the years. Thanks again, truly appreciate the help!

Originally Posted by Baertram View Post
My answer from my pm to you (seems I wasn't fast enough ):

ZO_SortFilterList is a base class you can reuse here but I also always have to look at what is needed.
XML is needed to define header controls for the sortheaders, a "List" control to show the lines and scrollbar, and so on.

I think there is a library by Circonian which was aiding here, but I do not know if it still works.
https://www.esoui.com/downloads/info1151-LibScroll.html

But:
Start small and try to understand the whole ZO_SortFilterList.
I think this will be better to do than using Circonian's library. Who knows what ZOs will change and if the libs till works or will work soon...
See my example below.


It basically eased the usage of the ZO class functions and provided an API to create such lists.
I've never used it. MAybe there is a wiki example how to create such lists somewhere...
Didn't find any
Else it is try and error + read ZOs code at github e.g.

Use the forum search:
https://www.esoui.com/forums/search.php

Search for ZO_SortFilterList or ZO_SortList or similar to find threads about it, getting some more info.

All I can help you with is give a short introduction. Have a look at the addon WishList -> WishList_window.lua
At the top the subclass of the ZO_SortFilterList will be defined:
Lua Code:
  1. WishListWindow = ZO_SortFilterList:Subclass()
There is a XML file where the headers are defiend as virtual controls and the list rows as virtuals and the labels of each list row as virtuals as well:
Code:
<Controls>
		<!-- Virtual labels -->
        <Label name="WishListRowLabel" font="MyFontGame16" wrapMode="ELLIPSIS" horizontalAlignment="LEFT" verticalAlignment="CENTER" virtual="true" >
		</Label>
        <!--Sort headers -->
        <Control name="WhisListSortHeader" inherits="ZO_SortHeaderBehavior" virtual="true">
            <Controls>
                <Label name="$(parent)Name" font="MyFontGame20" color="INTERFACE_COLOR_TYPE_TEXT_COLORS:INTERFACE_TEXT_COLOR_NORMAL"  horizontalAlignment="CENTER" verticalAlignment="CENTER" wrapMode="ELLIPSIS">
                        <AnchorFill />
                </Label>
            </Controls>
        </Control>
        <!-- Virtual Rows -->
		<Control name="WishListRow" mouseEnabled="true" virtual="true">
...
and the TopLevelControl of WishList is defined as well,
having some of the sort header controls (using the virtuals defined at top as template) and then the <list control is below, where the ZO_SortFilterList will be loaded into via the lua code in WishList_window.lua:
Code:
<!-- Frame/Window -->
		<TopLevelControl name="WishListFrame" inherits="ZO_RightPanelFootPrint" hidden="true">
			<Controls>
... search box here and backdrop of editbox etc.
Sort headers:
<Control name="$(parent)Headers">
					<Anchor point="TOPLEFT" offsetX="30" offsetY="51" />
					<Anchor point="TOPRIGHT" offsetY="51" />
					<Dimensions y="32" />
					<Controls>
                        <Control name="$(parent)DateTime" inherits="WhisListSortHeader">
...
List for the ZO_SortFilterList. $(parent) points to WishListFrame so the name of the control is globally defined as "WishListFrameList" and can be accessed by this, or as we "add" the ZO_SortFilterList to the global WishListWindow.frame.list it can also be accesed via this pointer (See lua code below)
<Control name="$(parent)List" inherits="ZO_ScrollList">
					<Anchor point="TOPLEFT" relativeTo="$(parent)Headers" relativePoint="BOTTOMLEFT" offsetY="3" />
					<Anchor point="BOTTOMRIGHT" offsetX="-35" offsetY="-32" />
				</Control>

The connection between the XML and the lua stuff will happen in WishList_Window.lua in function:
function WishListWindow:Setup( )
Lua Code:
  1. --Scroll UI
  2. --DataType WISHLIST_DATA is a number constant (e.g. 1) used to defined a unique dataType of the rows, which MUST be set and used for searching/filtering e.g. Having different sortfilterlist providing different row contents should also sue different datatypes
  3. --WishListRow is the temmplate from XML defined as the row type of the list row. There we have defined the columns with the virtual labels like date, name, locality etc.
  4. --30 is the height
  5. --the function defined will be the callback for EACH row as the row get's setup/created
  6. --So this will call WishListWindow:SetupItemRow on each row where the labels are hidden/unhidden (if you re-use the same row to show different columns, like I do in this addon. You could also just create multiple ZO_SortFilterList objects and assign different row types etc, and jst hide the total lists then. But I only wanted to use 1 list and re-use the rows and headers, just hiding the headers and labels/columns I do not need, depending on the selected UI buttons)
  7.     ZO_ScrollList_AddDataType(self.list, WISHLIST_DATA, "WishListRow", 30, function(control, data)
  8.         self:SetupItemRow(control, data)
  9.     end)
  10. --Adds a highlight onMouseEnter of the row
  11.     ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")
  12. --"Zebra" background for the rows
  13.     self:SetAlternateRowBackgrounds(true)
  14. --The list which hels the entries of the rowData. Each entry will have 1 table with the information needed for the row -> See function BuildMasterList
  15.     self.masterList = { }
  16.  
  17.     --Build the sortkeys depending on the settings
  18.     self.currentSortKey = "name"
  19.     self.currentSortOrder = ZO_SORT_ORDER_UP
  20.     self.sortHeaderGroup:SelectAndResetSortForKey(self.currentSortKey) -- Will call "SortScrollList" internally
  21.     --The sort function
  22.     self.sortFunction = function( listEntry1, listEntry2 )
  23.         if     self.currentSortKey == nil or self.sortKeys[self.currentSortKey] == nil
  24.             or listEntry1.data == nil or listEntry1.data[self.currentSortKey] == nil
  25.             or listEntry2.data == nil or listEntry2.data[self.currentSortKey] == nil then
  26.             return nil
  27.         end
  28.         return(ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, self.sortKeys, self.currentSortOrder))
  29.     end
  30.     self.searchDrop = ZO_ComboBox_ObjectFromContainer(self.frame:GetNamedChild("SearchDrop"))
  31.     WL.initializeSearchDropdown(self, WISHLIST_TAB_SEARCH, "set")
  32.  
  33.     WL.initializeSearchDropdown(self, WISHLIST_TAB_WISHLIST, "char")
  34.     --Search box and search functions
  35.     self.searchBox = self.frame:GetNamedChild("SearchBox")
  36.     self.searchBox:SetHandler("OnTextChanged", function() self:RefreshFilters() end)
  37.     self.searchBox:SetHandler("OnMouseUp", function(ctrl, mouseButton, upInside)
  38.         if mouseButton == MOUSE_BUTTON_INDEX_RIGHT and upInside then
  39.             self:OnSearchEditBoxContextMenu(self.searchBox)
  40.         end
  41.     end)
  42.     self.search = ZO_StringSearch:New()
  43.     self.search:AddProcessor(WL.sortType, function(stringSearch, data, searchTerm, cache)
  44.         return(self:ProcessItemEntry(stringSearch, data, searchTerm, cache))
  45.     end)
  46.  
  47. --Now we assign pointers to the XML controls of the headers
  48.     --Sort headers
  49.     self.headers = self.frame:GetNamedChild("Headers")
  50.     self.headerDate = self.headers:GetNamedChild("DateTime")
  51.     self.headerSetItemCollectionState = self.headers:GetNamedChild("SetItemCollectionState")
  52.     self.headerName = self.headers:GetNamedChild("Name")
  53.     self.headerArmorOrWeaponType = self.headers:GetNamedChild("ArmorOrWeaponType")
  54.     self.headerSlot = self.headers:GetNamedChild("Slot")
  55.     self.headerTrait = self.headers:GetNamedChild("Trait")
  56.     self.headerQuality = self.headers:GetNamedChild("Quality")
  57.     self.headerUsername = self.headers:GetNamedChild("UserName")
  58.     self.headerLocality = self.headers:GetNamedChild("Locality")
  59.  
  60. ...
  61. --Ths will call the following functions now to build the masterlist, create entries for te rows, and refresh the visible stuff:
  62.  --self:RefreshData() will run:
  63.         -->self:BuildMasterList()
  64.         -->self:FilterScrollList()
  65.         -->self:SortScrollList()
  66.         -->self:CommitScrollList()
  67. self:RefreshData()

So you can check these other functions what is being done there. It will first build the masterList, which calls
Lua Code:
  1. --Insert an entry to the masterlist, and the entry is created/defined via function WL.CreateEntryForSet which will create a { .... } data table for the row
  2. table.insert(self.masterList, WL.CreateEntryForSet(setId, setData))

self:FilterScrollList then filters the masterlist according to your filter parameters (e.g. a search editbox using string text search)
self:SortScrollList will sort the entries thena ccordingly to the selected sortheaders
self:CommitScrollList will then apply all that changed stuff, update the visuals etc.

The self:SetupItemRow is the function which reads the info from the masterlist of the current row and applies the data.locality to e.g. the locality label of the row control (defined in XML).

Just as info:
WL.CurrentTab = The currently selected tab at the WishList UI. The sets search, the WishList of each toon, or the history "who found what set item where and when"
WL.CurrentState = The current state of the tab (could be "fetching data" at the sets tab, "finished fetching data" and so on)
Depending on these values the ZO_SortFilter list headers, and rows will change.
You'd only have to define 1 rowtype and use it for your history. You do not need to differ it depending on the selected UI etc.
  Reply With Quote

ESOUI » Developer Discussions » Lua/XML Help » Creating a scrollable XML "list control" and adding data via LUA

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off