From f336c48d08e762c1c41c15d75c85fcac996f18ea Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 4 Jan 2023 09:40:11 +0100 Subject: lua-bindings/diaphragm.lua: add current version --- lua-bindings/diaphragm.lua | 404 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 lua-bindings/diaphragm.lua (limited to 'lua-bindings') diff --git a/lua-bindings/diaphragm.lua b/lua-bindings/diaphragm.lua new file mode 100644 index 0000000..ffb3e4b --- /dev/null +++ b/lua-bindings/diaphragm.lua @@ -0,0 +1,404 @@ +local M = {} + +M.core = require("libdiaphragm") + +M.util = {} + +function M.util.eprint(s, ...) + io.stderr:write(string.format(s, ...)) + io.stderr:write("\n") +end + +function M.util.list_concat(...) + local result = {} + for i = 1, select("#", ...) do + local current = select(i, ...) + if current ~= nil then + for _, v in ipairs(current) do + table.insert(result, v) + end + end + end + return result +end + +function M.util.list_intersperse_with(func, list) + local result = {} + + for i = 1, #list - 1 do + table.insert(result, list[i]) + table.insert(result, func()) + end + table.insert(result, list[#list]) + + return result +end + +function M.util.tbl_map(func, t) + local result = {} + for k, v in pairs(t) do + result[k] = func(v) + end + return result +end + +function M.util.tbl_print(t) + io.stderr:write("{ ") + for k, v in pairs(t) do + io.stderr:write(string.format("%s = %s, ", k, v)) + end + io.stderr:write("}\n") +end + +function M.util.tbl_extend(...) + local result = {} + for i = 1, select("#", ...) do + local current = select(i, ...) + if current ~= nil then + for k, v in pairs(current) do + result[k] = v + end + end + end + return result +end + +function M.util.tbl_contains(t, value) + for _, v in pairs(t) do + if v == value then + return true + end + end + return false +end + +-- TODO: depending on objects +function M.util.is_reserved(s) + return M.util.tbl_contains({ + "top", + "left", + "width", + "height", + "right", + "bottom", + "vert_center", + "horiz_center", + + "top_left", + "top_right", + "bottom_left", + "bottom_right", + "middle_left", + "middle_right", + "top_middle", + "bottom_middle", + "center", + + "stroke_color", + "stroke_width", + "fill_color", + + -- "x", + -- "y", + -- + "size", + }, s) +end + +function M.util.tbl_filter_by_key(func, t) + local result = {} + for k, v in pairs(t) do + if func(k) then + result[k] = v + end + end + return result +end + +function M.util.tbl_filter_reserved(t) + return M.util.tbl_filter_by_key(function(k) + return not M.util.is_reserved(k) + end, t) +end + +function M.util.tbl_assign_reserved(t, rhs) + for k, v in pairs(t) do + if M.util.is_reserved(k) then + rhs[k] = v + end + end +end + +-- Return lhs, but with keys that are not in rhs +function M.util.tbl_diff(lhs, rhs) + local result = {} + for k, v in pairs(lhs) do + if rhs[k] == nil then + result[k] = v + end + end + return result +end + +-- Assign keys from lhs to rhs, useful if rhs is userdata +function M.util.tbl_assign(lhs, rhs) + for k, v in pairs(lhs) do + rhs[k] = v + end +end + +M.float = {} + +M.float.new = M.core.float +M.float.max = M.core.float_max +M.float.min = M.core.float_min + +M.text = {} +M.text.font = M.core.font + +function M.text.new(params) + params = params or {} + local result = M.core.text(params) + M.util.tbl_assign_reserved(params, result) + return result +end + +M.rectangle = {} + +function M.rectangle.new(params) + params = params or {} + local result = M.core.rectangle() + M.util.tbl_assign_reserved(params, result) + return result +end + +function M.rectangle.surrounding(content, params) + params = params or {} + local margin = params.margin or 0 + local margin_left = params.margin_left or margin + local margin_right = params.margin_right or margin + local margin_top = params.margin_top or margin + local margin_bottom = params.margin_bottom or margin + + return M.rectangle.new(M.util.tbl_extend({ + left = content.left - margin_left, + right = content.right + margin_right, + top = content.top - margin_top, + bottom = content.bottom + margin_bottom, + }, params)) +end + +M.straight_path = {} + +function M.straight_path.new(params) + params = params or {} + local result = M.core.straight_path(params) + M.util.tbl_assign_reserved(params, result) + return result +end + +function M.shape(params) + params = params or {} + local result = M.util.tbl_filter_reserved(params) + + local shape = M.core.complex_shape() + setmetatable(result, { + __index = function(_, k) + if M.util.is_reserved(k) then + return shape[k] + end + end, + }) + M.util.tbl_assign_reserved(params, shape) + + return result +end + +M.layout = {} + +function M.layout.margin(params) + local result = M.shape(params) + + result.margin_left = result.margin_left or M.float.new() + result.margin_right = result.margin_right or M.float.new() + result.margin_top = result.margin_top or M.float.new() + result.margin_bottom = result.margin_bottom or M.float.new() + + function result:draw() + M.constrain(self.left:eq(self.content.left - self.margin_left)) + M.constrain(self.right:eq(self.content.right + self.margin_right)) + M.constrain(self.top:eq(self.content.top - self.margin_top)) + M.constrain(self.bottom:eq(self.content.bottom + self.margin_bottom)) + + self.content:draw() + end + + return result +end + +function M.layout.margin_left(params) + return M.layout.margin(M.util.tbl_extend(params, { + margin_right = 0, + margin_top = 0, + margin_bottom = 0, + })) +end + +function M.layout.margin_right(params) + return M.layout.margin(M.util.tbl_extend(params, { + margin_left = 0, + margin_top = 0, + margin_bottom = 0, + })) +end + +function M.layout.margin_top(params) + return M.layout.margin(M.util.tbl_extend(params, { + margin_left = 0, + margin_right = 0, + margin_bottom = 0, + })) +end + +function M.layout.margin_bottom(params) + return M.layout.margin(M.util.tbl_extend(params, { + margin_left = 0, + margin_right = 0, + margin_top = 0, + })) +end + +-- TODO: factor with vstack +-- TODO: also as just a set of constraints +function M.layout.hstack(params) + local result = M.shape(params) + + result.spacing = result.spacing or 0 + result.align = result.align or "top" + if result.align == "middle" or result.align == "center" then + result.align = "vert_center" + end + + function result:draw() + local len = #self.elements + assert(len >= 1, "hstack must have at least 1 element in `elements`") + + local tops = {} + local bottoms = {} + + local previous_right = self.left - self.spacing + local previous_vert_anchor = self[self.align] + + for _, el in pairs(self.elements) do + local el_vert_anchor = el[self.align] + + M.constrain(el.left:eq(previous_right + self.spacing)) + M.constrain(el_vert_anchor:eq(previous_vert_anchor)) + + table.insert(tops, el.top) + table.insert(bottoms, el.bottom) + + el:draw() + + previous_right = el.right + previous_vert_anchor = el_vert_anchor + end + + M.constrain(self.top:eq(M.float.min(tops))) + M.constrain(self.bottom:eq(M.float.max(bottoms))) + M.constrain(self.right:eq(previous_right)) + end + + return result +end + +function M.layout.vstack(params) + local result = M.shape(params) + + result.spacing = result.spacing or 0 + result.align = result.align or "left" + if result.align == "middle" or result.align == "center" then + result.align = "horiz_center" + end + + function result:draw() + local len = #self.elements + assert(len >= 1, "vstack must have at least 1 element in `elements`") + + local lefts = {} + local rights = {} + + local previous_bottom = self.top - self.spacing + local previous_horiz_anchor = self[self.align] + + for _, el in pairs(self.elements) do + local el_horiz_anchor = el[self.align] + + M.constrain(el.top:eq(previous_bottom + self.spacing)) + M.constrain(el_horiz_anchor:eq(previous_horiz_anchor)) + + table.insert(lefts, el.left) + table.insert(rights, el.right) + + el:draw() + + previous_bottom = el.bottom + previous_horiz_anchor = el_horiz_anchor + end + + M.constrain(self.left:eq(M.float.min(lefts))) + M.constrain(self.right:eq(M.float.max(rights))) + M.constrain(self.bottom:eq(previous_bottom)) + end + + return result +end + +M.constraint = {} + +function M.constraint.left_of(lhs, rhs, margin) + M.constrain(lhs.right:eq(rhs.left - (margin or 0))) +end + +function M.constraint.right_of(rhs, lhs, margin) + M.constrain(lhs.right:eq(rhs.left - (margin or 0))) +end + +function M.constraint.above(above, below, margin) + M.constrain(above.bottom:eq(below.top - (margin or 0))) +end + +function M.constraint.below(below, above, margin) + M.constrain(above.bottom:eq(below.top - (margin or 0))) +end + +function M.constraint.same_size(elems) + local height = M.float.new() + local width = M.float.new() + + for _, el in pairs(elems) do + M.constrain(el.height:eq(height)) + M.constrain(el.width:eq(width)) + end +end + +function M.constraint.same_height(elems) + local height = M.float.new() + + for _, el in pairs(elems) do + M.constrain(el.height:eq(height)) + end +end + +function M.constraint.same_width(elems) + local width = M.float.new() + + for _, el in pairs(elems) do + M.constrain(el.width:eq(width)) + end +end + +M.constrain = M.core.constrain +M.draw = M.core.draw + +return M -- cgit v1.2.3