LET THERE BE SUIT!
This commit is contained in:
parent
44be6169e3
commit
b5137a4477
19 changed files with 987 additions and 1481 deletions
286
layout.lua
Normal file
286
layout.lua
Normal file
|
@ -0,0 +1,286 @@
|
|||
-- This file is part of QUI, copyright (c) 2016 Matthias Richter
|
||||
|
||||
local Layout = {}
|
||||
function Layout.new()
|
||||
return setmetatable({_stack = {}}, {__index = Layout}):reset()
|
||||
end
|
||||
|
||||
function Layout:reset(x,y, padx,pady)
|
||||
self._x = x or 0
|
||||
self._y = y or 0
|
||||
self._padx = padx or 0
|
||||
self._pady = pady or 0
|
||||
self._w = 0
|
||||
self._h = 0
|
||||
self._widths = {min=math.huge,max=-math.huge}
|
||||
self._heights = {min=math.huge,max=-math.huge}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function Layout:padding(padx,pady)
|
||||
self._padx = padx
|
||||
self._pady = pady
|
||||
end
|
||||
|
||||
function Layout:push(x,y)
|
||||
self._stack[#self._stack+1] = {
|
||||
self._x, self._y,
|
||||
self._padx, self._pady,
|
||||
self._w, self._h,
|
||||
self._widths,
|
||||
self._heights,
|
||||
}
|
||||
|
||||
return self:reset(x,y)
|
||||
end
|
||||
|
||||
function Layout:pop()
|
||||
assert(#self._stack > 0, "Nothing to pop")
|
||||
local w,h = self._w, self._h
|
||||
self._x, self._y,
|
||||
self._padx,self._pady,
|
||||
self._w, self._h,
|
||||
self._widths, self._heights = unpack(self._stack[#self._stack])
|
||||
|
||||
self._w, self._h = math.max(w, self._w), math.max(h, self._h)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- recursive binary search for position of v
|
||||
local function insert_sorted_helper(t, i0, i1, v)
|
||||
if i1 <= i0 then
|
||||
table.insert(t, i0, v)
|
||||
return
|
||||
end
|
||||
|
||||
local i = i0 + math.floor((i1-i0)/2)
|
||||
if t[i] < v then
|
||||
return insert_sorted_helper(t, i+1, i1, v)
|
||||
elseif t[i] > v then
|
||||
return insert_sorted_helper(t, i0, i-1, v)
|
||||
else
|
||||
table.insert(t, i, v)
|
||||
end
|
||||
end
|
||||
|
||||
local function insert_sorted(t, v)
|
||||
if v <= 0 then return end
|
||||
insert_sorted_helper(t, 1, #t, v)
|
||||
t.min = math.min(t.min, v)
|
||||
t.max = math.max(t.max, v)
|
||||
end
|
||||
|
||||
local function calc_width_height(self, w, h)
|
||||
if w == "" or w == nil then
|
||||
w = self._w
|
||||
elseif w == "max" then
|
||||
w = self._widths.max
|
||||
elseif w == "min" then
|
||||
w = self._widths.min
|
||||
elseif w == "median" then
|
||||
w = self._widths[math.ceil(#self._widths/2)] or 0
|
||||
elseif type(w) ~= "number" then
|
||||
error("width: invalid value (" .. tostring(w) .. ")", 2)
|
||||
end
|
||||
|
||||
if h == "" or h == nil then
|
||||
h = self._h
|
||||
elseif h == "max" then
|
||||
h = self._heights.max
|
||||
elseif h == "min" then
|
||||
h = self._heights.min
|
||||
elseif h == "median" then
|
||||
h = self._heights[math.ceil(#self._heights/2)] or 0
|
||||
elseif type(h) ~= "number" then
|
||||
error("width: invalid value (" .. tostring(w) .. ")",2)
|
||||
end
|
||||
|
||||
insert_sorted(self._widths, w)
|
||||
insert_sorted(self._heights, h)
|
||||
return w,h
|
||||
end
|
||||
|
||||
function Layout:row(w, h)
|
||||
self._y = self._y + self._pady
|
||||
w,h = calc_width_height(self, w, h)
|
||||
|
||||
local x,y = self._x, self._y + self._h
|
||||
self._y, self._w, self._h = y, w, h
|
||||
|
||||
return x,y,w,h
|
||||
end
|
||||
|
||||
function Layout:col(w, h)
|
||||
self._x = self._x + self._padx
|
||||
w,h = calc_width_height(self, w, h)
|
||||
|
||||
local x,y = self._x + self._w, self._y
|
||||
self._x, self._w, self._h = x, w, h
|
||||
|
||||
return x,y,w,h
|
||||
end
|
||||
|
||||
|
||||
local function layout_iterator(t, idx)
|
||||
idx = (idx or 1) + 1
|
||||
if t[idx] == nil then return nil end
|
||||
return idx, unpack(t[idx])
|
||||
end
|
||||
|
||||
local function layout_retained_mode(self, t, constructor, string_argument_to_table, fill_width, fill_height)
|
||||
-- sanity check
|
||||
local p = t.pos or {0,0}
|
||||
assert(type(p) == "table", "Invalid argument `pos' (table expected, got "..type(p)..")",2)
|
||||
|
||||
self:push(p[1] or 0, p[2] or 0)
|
||||
|
||||
-- first pass: get dimensions, add layout info
|
||||
local layout = {n_fill_w = 0, n_fill_h = 0}
|
||||
for i,v in ipairs(t) do
|
||||
if type(v) == "string" then
|
||||
v = string_argument_to_table(v)
|
||||
end
|
||||
local x,y,w,h = 0,0, v[1], v[2]
|
||||
if v[1] == "fill" then w = 0 end
|
||||
if v[2] == "fill" then h = 0 end
|
||||
|
||||
x,y, w,h = constructor(self, w,h)
|
||||
|
||||
if v[1] == "fill" then
|
||||
w = "fill"
|
||||
layout.n_fill_w = layout.n_fill_w + 1
|
||||
end
|
||||
if v[2] == "fill" then
|
||||
h = "fill"
|
||||
layout.n_fill_h = layout.n_fill_h + 1
|
||||
end
|
||||
layout[i] = {x,y,w,h, unpack(v,3)}
|
||||
end
|
||||
|
||||
-- second pass: extend "fill" cells and shift others accordingly
|
||||
local fill_w = fill_width(layout, t.min_width or 0, self._x + self._w - p[1])
|
||||
local fill_h = fill_height(layout, t.min_height or 0, self._y + self._h - p[2])
|
||||
local dx,dy = 0,0
|
||||
for _,v in ipairs(layout) do
|
||||
v[1], v[2] = v[1] + dx, v[2] + dy
|
||||
if v[3] == "fill" then
|
||||
v[3] = fill_w
|
||||
dx = dx + v[3]
|
||||
end
|
||||
if v[4] == "fill" then
|
||||
v[4] = fill_h
|
||||
dy = dy + v[4]
|
||||
end
|
||||
end
|
||||
|
||||
-- finally: return layout with iterator
|
||||
self:pop()
|
||||
layout.item = function(self, i)
|
||||
if self ~= layout then -- allow either colon or dot syntax
|
||||
i = self
|
||||
end
|
||||
return unpack(layout[i])
|
||||
end
|
||||
return setmetatable(layout, {__call = function()
|
||||
return layout_iterator, layout, 0
|
||||
end})
|
||||
end
|
||||
|
||||
function Layout:rows(t)
|
||||
return layout_retained_mode(self, t, self.row,
|
||||
function(v) return {nil, v} end,
|
||||
function() return self._widths.max end, -- fill width
|
||||
function(l,mh,h) return (mh - h) / l.n_fill_h end) -- fill height
|
||||
end
|
||||
|
||||
function Layout:cols(t)
|
||||
return layout_retained_mode(self, t, self.col,
|
||||
function(v) return {v} end,
|
||||
function(l,mw,w) return (mw - w) / l.n_fill_w end, -- fill width
|
||||
function() return self._heights.max end) -- fill height
|
||||
end
|
||||
|
||||
-- TODO: nesting a la rows{..., cols{...} } ?
|
||||
|
||||
local instance = Layout.new()
|
||||
return setmetatable({
|
||||
new = Layout.new,
|
||||
reset = function(...) return instance:reset(...) end,
|
||||
push = function(...) return instance:push(...) end,
|
||||
pop = function(...) return instance:pop(...) end,
|
||||
row = function(...) return instance:row(...) end,
|
||||
col = function(...) return instance:col(...) end,
|
||||
rows = function(...) return instance:rows(...) end,
|
||||
cols = function(...) return instance:cols(...) end,
|
||||
}, {__call = function(_,...) return Layout.new(...) end})
|
||||
|
||||
--[[do
|
||||
|
||||
L = Layout.new()
|
||||
|
||||
|
||||
|
||||
print("immediate mode")
|
||||
print("--------------")
|
||||
x,y,w,h = L:row(100,20) -- x,y,w,h = x0,y0, 100,20
|
||||
print(1,x,y,w,h)
|
||||
x,y,w,h = L:row() -- x,y,w,h = x0, y0+20, 100,20 (default: reuse last dimensions)
|
||||
print(2,x,y,w,h)
|
||||
x,y,w,h = L:col(20) -- x,y,w,h = x0+100, y0+20, 20, 20
|
||||
print(3,x,y,w,h)
|
||||
x,y,w,h = L:row(nil,30) -- x,y,w,h = x0+100, y0+40, 20, 30
|
||||
print(4,x,y,w,h)
|
||||
print()
|
||||
|
||||
L:reset()
|
||||
|
||||
local layout = L:rows{
|
||||
pos = {10,10}, -- optional, default {0,0}
|
||||
|
||||
{100, 10},
|
||||
{nil, 10}, -- {100, 10}
|
||||
{100, 20}, -- {100, 20}
|
||||
{}, -- {100, 20} -- default = last value
|
||||
{nil, "median"}, -- {100, 20}
|
||||
"median", -- {100, 20}
|
||||
"max", -- {100, 20}
|
||||
"min", -- {100, 10}
|
||||
"" -- {100, 10} -- default = last value
|
||||
}
|
||||
|
||||
print("rows")
|
||||
print("----")
|
||||
for i,x,y,w,h in layout() do
|
||||
print(i,x,y,w,h)
|
||||
end
|
||||
print()
|
||||
|
||||
-- +-------+-------+----------------+-------+
|
||||
-- | | | | |
|
||||
-- 70 {100, | "max" | "fill" | "min" |
|
||||
-- | 70} | | | |
|
||||
-- +--100--+--100--+------220-------+--100--+
|
||||
--
|
||||
-- `-------------------,--------------------'
|
||||
-- 520
|
||||
local layout = L:cols{
|
||||
pos = {10,10},
|
||||
min_width = 520,
|
||||
|
||||
{100, 70},
|
||||
"max", -- {100, 70}
|
||||
"fill", -- {min_width - width_of_items, 70} = {420, 70}
|
||||
"min", -- {100,70}
|
||||
}
|
||||
|
||||
print("cols")
|
||||
print("----")
|
||||
for i,x,y,w,h in layout() do
|
||||
print(i,x,y,w,h)
|
||||
end
|
||||
print()
|
||||
|
||||
end
|
||||
--]]
|
Loading…
Add table
Add a link
Reference in a new issue