UI docking & stretching

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!

2 Likes