I made a small UI docking & stretching program. Here is an example of an inventory:
Here is the code that formatted that output:
local description: GuiObject = ...
local render: GuiObject = ...
local grid: GuiObject = ...
root(
free({vector.create(0.2, 0.2)}, {vector.create(0.6, 0.6)}, {
xlist({0.5, 0.5}, {
view(description),
ylist({0.5, 0.5}, {
view(render),
view(grid)
})
})
})
)
reflow()
rearrange()
repaint()
Code Explanation
local description: GuiObject = ...
local render: GuiObject = ...
local grid: GuiObject = ...
-- root resizes the given cell to be the size of the screen, and makes it
-- the "start" of the hierarchy
root(
-- creates a free at (0.2, 0.2) with size (0.6, 0.6)
-- a "free" allows their children to be placed/resized freely
free({vector.create(0.2, 0.2)}, {vector.create(0.6, 0.6)}, {
-- creates a horizontal list where each child takes 0.5 the space
-- a "list" places their children one after another
xlist({0.5, 0.5}, {
-- a "view" mirrors their position changes to their instance
view(description),
ylist({0.5, 0.5}, {
view(render),
view(grid)
})
})
})
)
reflow()
-- flattens the layout such that a parent is earlier than their children.
-- Therefore, while docking & dropping need this, operations that don't
-- change the layout (stretching) can skip it.
rearrange()
-- converts relative bounds to absolute bounds
repaint()
-- applies absolute bounds to view's instances
Features & Implementation Details
Here are a few implementation details/features:
The program arranges cell positions by itself, it is not left up to Roblox UI layouts. As such, there is no complex parent-child changing, keeping natural siblings (like Inventory parts, the render, the grid, and the description) together). This is beneficial for both doing operations (like enabling/disabling their shared ScreenGui), or for minimizing ScreenGui redraw calls by strategically positioning UIs.
Also I implemented saving & loading user’s layouts to keep their arrangement for next time, like when they reopen an inventory after closing it or leaving.
Being data-oriented, it is lightweight with no GC slowdowns & few cache misses. The rearrange & repaint operations are branchless, making them super fast. Reflow, while “slower” by that logic, is still very fast (spending ~1μs in the above example) & is called far less often.
Let me know if you have any suggestions for it/critiques of it!