View Single Post
06/14/21, 03:01 PM   #6
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,979
I often read "look in the ZO Code how they did it".. this is the way?
Yes it is, and if you learn something post it at the wiki for others.
That's the way. We are glad we even got the source and ESOUDocumentation_Pxx.txt API function files

Dont know how you guys make it.
Try and error, listen and repeat. Call it like you want

Basically you need the following as I had written above:
https://www.esoui.com/downloads/info...stExample.html
-> To understand the basic sort list of ZO_SortList

Wiki: https://wiki.esoui.com/How_to..._do_...templated_rows
-> To see what else is needed for the ZO_SortFilterList. It inherits from ZO_SortList but you need to use some other code like the XML providing the List and SortHeader control containers.
And the functions ZO_SortFilterList:RefreshData() => BuildMasterList() => FilterScrollList() => SortScrollList() => CommitScrollList()

The XML, as shown in the Wiki examples, provide the virtual and non virtual controls for the row -> contains the column labels.

In your SetupFunction of the sortFilterList, which you specify at the ZO_ScrollList_AddDataType parameters, you define what data is passed to what column (you need to get the columns via e.g. rowControl:GetNamedChild("myColumNameFromXML").

Have a look at maybe LibShifterBox and search for ZO_ScrollList_AddDataType . Check the XML file and the lua file, it's a library providing 2 ZO_SortFilterLists (left and right) with only 1 column.
Or at WishList, it provides 1 ZO_SortFilterList providing multiple columns.



Or download the ESOUI source code
https://www.esoui.com/downloads/info...ourcecode.html
and search for ZO_SortFilterList and you'll get a lot of examples.
e.g. the FriendsList Keyboard.
The function here contains the init of the ZO_SortFilterList (they use an inherited class named "ZO_SocialListKeyboard" there which inherits from ZO_SortFilterList):
ZO_KeyboardFriendsListManager:Initialize(control, rowTemplate)

ZO_ScrollList_AddDataType(self.list, FRIEND_DATA, rowTemplate, 30, function(control, data) self:SetupRow(control, data) end)
ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")

AddDataType provides the XML template via param rowTemplate.
The param is given via this function, which is called from the XML code of the keyboard stuff <OnInitialization>
Lua Code:
  1. function ZO_FriendsList_OnInitialized(self)
  2.     -- set up columns before initializing the social list
  3.     local rowTemplate
  4.     local headersControl
  5.     if IsHeronUI() then
  6.         rowTemplate = "ZO_FriendsListRow_Heron"
  7.         headersControl = CreateControlFromVirtual("$(parent)Headers", self, "ZO_FriendsListHeaders_Heron")
  8.     else
  9.         rowTemplate = "ZO_FriendsListRow"
  10.         headersControl = CreateControlFromVirtual("$(parent)Headers", self, "ZO_FriendsListHeaders")
  11.     end
  12.     local listControl = self:GetNamedChild("List")
  13.     listControl:SetAnchor(TOPLEFT, headersControl, BOTTOMLEFT, 0, 3)
  14.     listControl:SetAnchor(BOTTOMRIGHT, self, BOTTOMRIGHT, -35, -32)
  15.  
  16.     FRIENDS_LIST = ZO_KeyboardFriendsListManager:New(self, rowTemplate)
  17. end

This line here calls the :New function: FRIENDS_LIST = ZO_KeyboardFriendsListManager:New(self, rowTemplate)
The template name would be e.g. "ZO_FriendsListRow" which you are able to find in the XML file:
friendslist_keyboard.xml
Code:
<Label name="ZO_FriendsListRowLabel" font="ZoFontGame" wrapMode="ELLIPSIS" virtual="true"/>
        <Control name="ZO_FriendsListRow" mouseEnabled="true" virtual="true">
            <Dimensions y="30"/>
            <OnMouseEnter>
                ZO_FriendsListRow_OnMouseEnter(self)
            </OnMouseEnter>
            <OnMouseExit>
                ZO_FriendsListRow_OnMouseExit(self)
            </OnMouseExit>
            <OnMouseUp>
                ZO_FriendsListRow_OnMouseUp(self, button, upInside)
            </OnMouseUp>
            <Controls>
                <Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" />
                <Texture name="$(parent)StatusIcon" layer="OVERLAY" mouseEnabled="true">
                    <Anchor point="LEFT"/>
                    <Dimensions x="32" y="32"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowStatus_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowStatus_OnMouseExit(self)
                    </OnMouseExit>
                </Texture>
                <Texture name="$(parent)AllianceIcon" mouseEnabled="true" layer="OVERLAY">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)StatusIcon"/>
                    <Dimensions x="32" y="32"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowAlliance_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowAlliance_OnMouseExit(self)
                    </OnMouseExit>
                </Texture>                
                <Label name="$(parent)DisplayName" inherits="ZO_FriendsListRowLabel" verticalAlignment="CENTER" mouseEnabled="true">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)AllianceIcon" offsetX="25"/>
                    <Dimensions x="225" y="30"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowDisplayName_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowDisplayName_OnMouseExit(self)
                    </OnMouseExit>
                    <OnMouseUp>
                        ZO_FriendsListRow_OnMouseUp(self:GetParent(), button, upInside)
                    </OnMouseUp>
                </Label>
                <Label name="$(parent)Zone" inherits="ZO_FriendsListRowLabel">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)DisplayName" offsetX="18"/>
                    <Dimensions x="290" y="20"/>
                </Label>
                <Texture name="$(parent)ClassIcon" mouseEnabled="true" layer="OVERLAY">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)Zone" offsetX="28"/>
                    <Dimensions x="32" y="32"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowClass_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowClass_OnMouseExit(self)
                    </OnMouseExit>
                </Texture>
                <Texture name="$(parent)Champion" mouseEnabled="true" layer="OVERLAY">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)ClassIcon" offsetX="20" offsetY="2"/>
                    <Dimensions x="22" y="22"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowChampion_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowChampion_OnMouseExit(self)
                    </OnMouseExit>
                </Texture>
                <Label name="$(parent)Level" inherits="ZO_FriendsListRowLabel" horizontalAlignment="CENTER" verticalAlignment="CENTER">
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)ClassIcon" offsetX="39"/>
                    <Dimensions x="45" y="20"/>
                </Label>
                <Button name="$(parent)Note">
                    <Textures
                        normal="EsoUI/Art/Contacts/social_note_up.dds"
                        pressed="EsoUI/Art/Contacts/social_note_down.dds"
                        mouseOver="EsoUI/Art/Contacts/social_note_over.dds"
                    />                        
                    <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$(parent)Level" offsetX="18"/>
                    <Dimensions x="32" y="32"/>
                    <OnMouseEnter>
                        ZO_FriendsListRowNote_OnMouseEnter(self)
                    </OnMouseEnter>
                    <OnMouseExit>
                        ZO_FriendsListRowNote_OnMouseExit(self)
                    </OnMouseExit>
                    <OnClicked>
                        ZO_FriendsListRowNote_OnClicked(self)
                    </OnClicked>
                </Button>
            </Controls>
        </Control>
First 3 columns are textures to show the online state etc.
Then you got different labels in there which define the columns, and which inherit from the virtual label "ZO_FriendsListRowLabel".

Hope this makes clear how to define the XML now.
Also needed, like you can find in the same file, and mentioned in the Wiki:

The sortheader container in the XML
Code:
<Control name="ZO_FriendsListHeaders" mouseEnabled="true" virtual="true">
The list container for the ZO_SortList:
Code:
               <!-- Headers control added dynamically based on above templates -->

                <Control name="$(parent)List" inherits="ZO_ScrollList" />
As you are able to read here the sortheaders are dynamically added based on the used template of the row.
ZO_FriendsListRow -> ZO_FriendsListHeaders
ZO_FriendsListRow_Heron -> ZO_FriendsListRow_Heron
-> See function ZO_FriendsList_OnInitialized(self) above -> e.g. headersControl = CreateControlFromVirtual("$(parent)Headers", self, "ZO_FriendsListHeaders")


After that back to the lua code:
Every :New function normally calls an Initilaized function internally.
So FRIENDS_LIST = ZO_KeyboardFriendsListManager:New(self, rowTemplate)
->
ZO_KeyboardFriendsListManager:New(...)
calls
ZO_KeyboardFriendsListManager:Initialize(...)

And there we get to the functions I had named above already:
ZO_ScrollList_AddDataType(self.list, FRIEND_DATA, rowTemplate, 30, function(control, data) self:SetupRow(control, data) end)
Code:
--Adds a new control type for the list to handle. It must maintain a consistent size.
--@typeId - A unique identifier to give to CreateDataEntry when you want to add an element of this type.
--@templateName - The name of the virtual control template that will be used to hold this data
--@height - The control height
--@setupCallback - The function that will be called when a control of this type becomes visible. Signature: setupCallback(control, data)
--@dataTypeSelectSound - An optional sound to play when a row of this data type is selected.
--@resetControlCallback - An optional callback when the datatype control gets reset.
function ZO_ScrollList_AddDataType(self, typeId, templateName, height, setupCallback, hideCallback, dataTypeSelectSound, resetControlCallback)
ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")
Code:
--@highlightTemplate- The name of the virtual control template that will be used to show the highlight
--@highlightCallback- The callback function run as an entry is highlighted
--@overrideEndAlpha-I don't know but sounds like an SetAlpha override for the row. Try it (values should be 0 to 1 in 0.01 or 0.1 steps)
function ZO_ScrollList_EnableHighlight(self, highlightTemplate, highlightCallback, overrideEndAlpha)
self.list ist the ZO_SortFilterList

The sort function is used as the sort headers are used.
The search box is deinfed in teh XML and can be used to filter the list data.

self:SetupRow(control, data) is the setupCallback function for each row!
Search for ZO_KeyboardFriendsListManager:SetupRow(control, data) and you'll find it in the source.
It calls the default setuprow func of the ZO_SortFilterLists ZO_SortFilterList.SetupRow(self, control, data)
And a custom setup function of the friends list: FRIENDS_LIST_MANAGER:SetupEntry(control, data)
As FRIENDS_LIST_MANAGER = ZO_FriendsList:New() -> Search in file friendslist_shared.lua where the ZO_FriendsList is defined!
-> Search for SetupEntry and you'll find:
Lua Code:
  1. function ZO_FriendsList:SetupEntry(control, data, selected)
  2.     if not ZO_IsPlaystationPlatform() then
  3.         ZO_SocialList_SharedSocialSetup(control, data, selected)
  4.  
  5.         local noteControl = control:GetNamedChild("Note")
  6.         if noteControl then
  7.             noteControl:SetHidden(data.note == "")
  8.         end
  9.     else
  10.         local displayNameLabel = control:GetNamedChild("DisplayName")
  11.         if displayNameLabel then
  12.             displayNameLabel:SetText(ZO_FormatUserFacingDisplayName(data.displayName))
  13.         end
  14.  
  15.         local statusIconControl = control:GetNamedChild("StatusIcon")
  16.         if statusIconControl then
  17.             local textureFunctions = ZO_SocialList_GetPlatformTextureFunctions()
  18.             statusIconControl:SetTexture(textureFunctions.playerStatusIcon(data.status))
  19.         end
  20.     end
  21. end

Here you'll find, as I tried to describe above, how the columns are determined via the :GetNamedChild function of the rowControl, e.g.
local noteControl = ccontrol:GetNamedChild("Note")
-> Note is the <Button name="$(parent)Note"> from the XML row template in e.g. "ZO_FriendsListRow", see above!

data is the table that contans the data of the row.
Each row got 1 data entry, containing sub entries you need to specify in the BuildMasterList function e.g.

See same file:
Lua Code:
  1. function ZO_FriendsList:BuildMasterList()
  2.     ZO_ClearNumericallyIndexedTable(self.masterList)
  3.  
  4.     self.numOnlineFriends = 0
  5.     local numFriends = GetNumFriends()
  6.  
  7.     for i = 1, numFriends do
  8.         local displayName, note, status, secsSinceLogoff = GetFriendInfo(i)
  9.  
  10.         local online = status ~= PLAYER_STATUS_OFFLINE
  11.         if online then
  12.             self.numOnlineFriends = self.numOnlineFriends + 1
  13.         end
  14.  
  15.         local data = self:CreateFriendData(i, displayName, note, status)
  16.         ZO_SocialList_SetUpOnlineData(data, online, secsSinceLogoff)
  17.         self.masterList[i] = data
  18.     end
  19.  
  20.     self:OnNumOnlineChanged()
  21.     self:OnNumTotalFriendsChanged()
  22. end

self.masterList is the table containing your list data entries.

local data = self:CreateFriendData(i, displayName, note, status)
-> Creates the data entry table with it's entries needed, like the status, the note text and so on.
ZO_SocialList_SetUpOnlineData(data, online, secsSinceLogoff)
-> is just a local helper function to update some values in the data table

self.masterList[i] = data
-> The masterlist get's a new entry.

Remember: The default order of the functions called is:
ZO_SortFilterList:RefreshData() => BuildMasterList() => FilterScrollList() => SortScrollList() => CommitScrollList()

After the masterlist was build the FilterScrollList function would be called to filter the masterlist.
After that the SortSrollList function sorts them according to the sortheaders chosen.
And CommtiScrollList updates the visual values then in the list according to the sorted masterlist entries.

The SetupRow callback function then provides the data.note etc. values to the appropriate chosen XML label/button/texture controls.


I hope this clears it up for you now and you are able to build an example
I'd start with the example ZO_SortList at the top and "enhance" it to use a ZO_SortFilterList instead, with an own simple 2 columns XML template.
Just re-use the given collectibles/pets from the example and add the number of the collectible as plain tetx e.g. for the start.

If you got that working provide me the example addon please and I'll have alook if one can release it as another ZO_SortFilterList example here at esoui, with some comments, so that the missing ZOs documentation again will be done by us

Last edited by Baertram : 06/14/21 at 03:55 PM.
  Reply With Quote