Skip to content

Instantly share code, notes, and snippets.

@stillwwater
Last active November 7, 2022 05:22
Show Gist options
  • Select an option

  • Save stillwwater/9e30d4962c455a6729cb3a12f8642e85 to your computer and use it in GitHub Desktop.

Select an option

Save stillwwater/9e30d4962c455a6729cb3a12f8642e85 to your computer and use it in GitHub Desktop.
spacing=1
id=1
nodes={}
rects={}
parent=nil
child=nil
function setpos(node)
local d=0
local p=node.p
node.abs={x=node.dst.x,
y=node.dst.y,
w=node.dst.w,
h=node.dst.h}
if p then
node.abs.x+=p.abs.x
node.abs.y+=p.abs.y
end
while p do
p=p.p -- pp
d+=1
end
rects[node.id]={node.abs.x,
node.abs.y,
node.abs.x+node.abs.w,
node.abs.y+node.abs.h,d}
end
function measure(node,dst)
local spc=spacing
node.dst={x=node.x+spc,
y=node.y+spc,
w=node.w,h=node.h}
dst=node.dst
-- fixed layout?
if node.b and not node.p.fix then
if node.p.horz then
-- horizontal stack layout
dst.x+=node.b.dst.x+node.b.dst.w+spc
else
-- vertical stack layout
dst.y+=node.b.dst.y+node.b.dst.h+spc
end
end
-- w in (0,1] size node
-- relative to parent width.
if node.w>0 and node.w<=1.0 then
-- only works if parent width
-- doesn't depend on children.
assert(node.p.w>0)
dst.w=node.p.dst.w*node.w-spc*2
-- calculate space remaining
-- in parent to make sure we fit.
local rs=node.p.dst.w-node.dst.x-spc
if rs<=dst.w and node.p.horz
and not node.p.fix then
-- node doesn't fit, measure
-- the remaining children
-- and size this node accordingly.
local a=node.a
while a do
measure(a)
rs-=a.dst.w+2*spc
a=a.a
end
end
dst.w=min(dst.w,rs)
end
-- h in (0,1] size node
-- relative to parent height
if node.h>0 and node.h<=1.0 then
-- only works if parent width
-- doesn't depend on children
assert(node.p.h>0)
-- same as before
dst.h=node.p.dst.h*node.h-spc*2
local rs=node.p.dst.h-node.dst.y-spc
if rs<=dst.h and not node.p.horz
and not node.p.fix then
local a=node.a
while a do
measure(a)
rs-=a.dst.h+2*spc
a=a.a
end
end
dst.h=min(dst.h,rs)
end
-- measure children
local c=node.c
local last=c
while c do
measure(c)
last=c
c=c.a
if node.col then
-- node is collapsed, node
-- size matches first child.
break
end
end
if last then
c=last
-- h=0 size node based on
-- its children.
-- collapsing parent only
-- works for vertical layout.
if node.h == 0 or node.col then
dst.h=c.dst.y+c.dst.h+spc
end
-- w=0 size node based on
-- its children.
if node.w == 0 then
dst.w=c.dst.x+c.dst.w+spc
end
end
-- x in (0,1] position node
-- relative to parent width
if node.x>0 and node.x<=1 then
node.dst.x=(node.p.dst.w*node.x)-(node.dst.w*node.x)-spc*flr(node.x)
end
-- y in (0,1] position node
-- relative to parent height
if node.y>0 and node.y<=1 then
node.dst.y=(node.p.dst.h*node.y)-(node.dst.h*node.y)-spc*flr(node.y)
end
end
function layout(node)
setpos(node)
local c=node.c
while c do
layout(c)
c=c.a
if node.col then
-- node is collapsed,
-- rest is not visible.
break
end
end
end
function window(node)
id+=1
node.id=id
node.p=parent
if parent.c==nil then
parent.c=node
elseif child then
node.b=child
child.a=node
end
parent=node
child=nil
add(nodes,node)
end
function pop()
assert(parent)
parent=parent.p
child=parent.c
while child.a do
child=child.a
end
end
function control(node)
assert(parent)
id+=1
node.id=id
node.p=parent
node.b=child
if child then
child.a=node
end
if parent.c==nil then
parent.c=node
end
child=node
end
function _init()
add(nodes,{id=1,
x=0,y=0,w=128,h=128,
p=nil,c=nil,b=nil,a=nil})
parent=nodes[1]
-- user code
window{x=0,y=0,w=124,h=124,fix=true}
window{x=1,y=0,w=64,h=1.0}
window{x=0,y=0,w=1.0,h=0}
control{x=0,y=0,w=1.0,h=5}
control{x=0,y=0,w=1.0,h=3}
window{x=0,y=0,w=1,h=0,horz=true}
control{x=0,y=0,w=0.5,h=3}
control{x=0,y=0,w=0.5,h=3}
pop()
window{x=0,y=0,w=1.0,h=0,horz=true}
control{x=0,y=0,w=1,h=3}
control{x=0,y=0,w=10,h=3}
control{x=0,y=0,w=8,h=3}
pop()
pop()
window{x=0,y=0,w=1.0,h=1.0}
window{x=0,y=0,w=1.0,h=0,horz=true}
control{x=0,y=0,w=6,h=6}
control{x=0,y=0,w=1.0,h=6}
control{x=0,y=0,w=6,h=6}
pop()
-- middle
window{x=0,y=0,w=1.0,h=1.0,fix=true}
control{x=0,y=0,w=6,h=6}
control{x=1,y=0,w=6,h=6}
control{x=0,y=1,w=6,h=6}
control{x=1,y=1,w=6,h=6}
control{x=0.5,y=0.5,w=6,h=6}
control{x=0,y=0.5,w=6,h=6}
control{x=1,y=0.5,w=6,h=6}
control{x=0.5,y=0,w=6,h=6}
control{x=0.5,y=1,w=6,h=6}
pop()
window{x=0,y=0,w=1.0,h=0,horz=true}
control{x=0,y=0,w=6,h=6}
control{x=0,y=0,w=1.0,h=6}
control{x=0,y=0,w=6,h=6}
pop()
pop()
pop()
-- dialog
window{x=0.5,y=0.5,w=64,h=32}
control{x=0,y=0,w=1,h=1}
window{x=0,y=0,w=1,h=0,horz=true}
control{x=0,y=0,w=1,h=5}
control{x=0,y=0,w=12,h=5}
control{x=0,y=0,w=12,h=5}
pop()
pop()
-- collapse window
window{x=0,y=0,w=50,h=0,col=true}
control{x=0,y=0,w=1,h=4}
control{x=0,y=0,w=1,h=4}
control{x=0,y=0,w=1,h=4}
control{x=0,y=0,w=1,h=4}
control{x=0,y=0,w=1,h=4}
pop()
pop()
measure(nodes[1])
layout(nodes[1])
end
-- renderer
function _draw()
cls(0)
for r in all(rects) do
rectfill(r[1],r[2],r[3],r[4],3+r[5])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment