Determine what shows up first in UIListLayout

So I have a leaderboard using UIListLayout, sorting by SortOrder. SortOrder is every player’s score *-1. I need to find the person whose name shows up FIRST on the leaderboard. It should be simple: whoever has the lowest SortOrder should be in first. The only problem is sometimes two people can have the same score, and the the leaderboard can display someone in first and my other sign can think it’s someone else.

How does UIListLayout determine which item goes to the beginning of its list?

1 Like

You can use AbsolutePosition to determine which UI component is physically “above” another

1 Like

Ugh, I originally tried just using Position and was dismayed to find it was always 0,0,0,0. This is perfect for my use case though, wish I thought of it. I’ll keep the thread open in case anyone needs to know the order for some reason.

1 Like

Definitely do keep the thread open, it’s always helpful for future users. You could also mark my reply as the solution if this solved your problem!

Based on minimal testing, it seems like gui objects are sorted based on the last time their parent property was changed after LayoutOrder for UIListLayouts.
You can figure out which item was first placed in the frame by using Instance:GetChildren, which “sorts them in the order that their .Parent property was set to the object,” which is perfect for our use case.
ipairs iterates through children in order from first to last index, so this would find the first gui object parented to the LeaderboardFrame and return it:

local function GetOldestGuiObject(LeaderboardFrame)
	for _,child in ipairs(LeaderboardFrame) do
		if child:IsA("GuiObject") and child.LayoutOrder == -the_highest_score then 
			return child
		end
	end
end

Actually, with further testing, simply indexing the leaderboard frame for the label will return the oldest child under it:

LeaderboardFrame.LeaderboardLabel

LeaderboardFrame:FindFirstChild("LeaderboardLabel")

In conclusion, I don’t think you would have to worry about finding the wrong person in 1st place as long as you index the labels the same way the UIListLayout sorts it.

4 Likes

I really wouldn’t rely on the behaviour of children receiving indexes based on the order they were placed into a container as that may produce unintended results or may get messy depending on implementation. Personally, I would look towards checking the actual elements themselves and sorting by their LayoutOrders.

local function GetHighScoreElement(LeaderboardFrame)
    local Children = {} do -- Need to filter results
        for _, Child in pairs(LeaderboardFrame:GetChildren()) do
            if Child:IsA("GuiObject") then
                table.insert(Children, Child)
            end
        end
    end

    table.sort(Children, function(ElementA, ElementB) -- If only table.sort returned a table...
        return (ElementA.LayoutOrder * -1) < (ElementB.LayoutOrder * -1)
    end)

    return Children[1]
end

The function above gets all GuiObject children and places them into a table. It then inverses their LayoutOrder (since the original is -1, we flip it back to positive) and it sorts them from highest to lowest. After the children are sorted, it selects the first one in the table and returns it.

Note - I am unsure if this will cause a memory leak. You may have to experiment a little regarding that. It may also be more computationally expensive than a quick FindFirstChild operation or checking if one value equals another. This is just an extra suggestion.

4 Likes