diff options
Diffstat (limited to 'lua-bindings/diaphragm.lua')
-rw-r--r-- | lua-bindings/diaphragm.lua | 404 |
1 files changed, 404 insertions, 0 deletions
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 @@ | |||
1 | local M = {} | ||
2 | |||
3 | M.core = require("libdiaphragm") | ||
4 | |||
5 | M.util = {} | ||
6 | |||
7 | function M.util.eprint(s, ...) | ||
8 | io.stderr:write(string.format(s, ...)) | ||
9 | io.stderr:write("\n") | ||
10 | end | ||
11 | |||
12 | function M.util.list_concat(...) | ||
13 | local result = {} | ||
14 | for i = 1, select("#", ...) do | ||
15 | local current = select(i, ...) | ||
16 | if current ~= nil then | ||
17 | for _, v in ipairs(current) do | ||
18 | table.insert(result, v) | ||
19 | end | ||
20 | end | ||
21 | end | ||
22 | return result | ||
23 | end | ||
24 | |||
25 | function M.util.list_intersperse_with(func, list) | ||
26 | local result = {} | ||
27 | |||
28 | for i = 1, #list - 1 do | ||
29 | table.insert(result, list[i]) | ||
30 | table.insert(result, func()) | ||
31 | end | ||
32 | table.insert(result, list[#list]) | ||
33 | |||
34 | return result | ||
35 | end | ||
36 | |||
37 | function M.util.tbl_map(func, t) | ||
38 | local result = {} | ||
39 | for k, v in pairs(t) do | ||
40 | result[k] = func(v) | ||
41 | end | ||
42 | return result | ||
43 | end | ||
44 | |||
45 | function M.util.tbl_print(t) | ||
46 | io.stderr:write("{ ") | ||
47 | for k, v in pairs(t) do | ||
48 | io.stderr:write(string.format("%s = %s, ", k, v)) | ||
49 | end | ||
50 | io.stderr:write("}\n") | ||
51 | end | ||
52 | |||
53 | function M.util.tbl_extend(...) | ||
54 | local result = {} | ||
55 | for i = 1, select("#", ...) do | ||
56 | local current = select(i, ...) | ||
57 | if current ~= nil then | ||
58 | for k, v in pairs(current) do | ||
59 | result[k] = v | ||
60 | end | ||
61 | end | ||
62 | end | ||
63 | return result | ||
64 | end | ||
65 | |||
66 | function M.util.tbl_contains(t, value) | ||
67 | for _, v in pairs(t) do | ||
68 | if v == value then | ||
69 | return true | ||
70 | end | ||
71 | end | ||
72 | return false | ||
73 | end | ||
74 | |||
75 | -- TODO: depending on objects | ||
76 | function M.util.is_reserved(s) | ||
77 | return M.util.tbl_contains({ | ||
78 | "top", | ||
79 | "left", | ||
80 | "width", | ||
81 | "height", | ||
82 | "right", | ||
83 | "bottom", | ||
84 | "vert_center", | ||
85 | "horiz_center", | ||
86 | |||
87 | "top_left", | ||
88 | "top_right", | ||
89 | "bottom_left", | ||
90 | "bottom_right", | ||
91 | "middle_left", | ||
92 | "middle_right", | ||
93 | "top_middle", | ||
94 | "bottom_middle", | ||
95 | "center", | ||
96 | |||
97 | "stroke_color", | ||
98 | "stroke_width", | ||
99 | "fill_color", | ||
100 | |||
101 | -- "x", | ||
102 | -- "y", | ||
103 | -- | ||
104 | "size", | ||
105 | }, s) | ||
106 | end | ||
107 | |||
108 | function M.util.tbl_filter_by_key(func, t) | ||
109 | local result = {} | ||
110 | for k, v in pairs(t) do | ||
111 | if func(k) then | ||
112 | result[k] = v | ||
113 | end | ||
114 | end | ||
115 | return result | ||
116 | end | ||
117 | |||
118 | function M.util.tbl_filter_reserved(t) | ||
119 | return M.util.tbl_filter_by_key(function(k) | ||
120 | return not M.util.is_reserved(k) | ||
121 | end, t) | ||
122 | end | ||
123 | |||
124 | function M.util.tbl_assign_reserved(t, rhs) | ||
125 | for k, v in pairs(t) do | ||
126 | if M.util.is_reserved(k) then | ||
127 | rhs[k] = v | ||
128 | end | ||
129 | end | ||
130 | end | ||
131 | |||
132 | -- Return lhs, but with keys that are not in rhs | ||
133 | function M.util.tbl_diff(lhs, rhs) | ||
134 | local result = {} | ||
135 | for k, v in pairs(lhs) do | ||
136 | if rhs[k] == nil then | ||
137 | result[k] = v | ||
138 | end | ||
139 | end | ||
140 | return result | ||
141 | end | ||
142 | |||
143 | -- Assign keys from lhs to rhs, useful if rhs is userdata | ||
144 | function M.util.tbl_assign(lhs, rhs) | ||
145 | for k, v in pairs(lhs) do | ||
146 | rhs[k] = v | ||
147 | end | ||
148 | end | ||
149 | |||
150 | M.float = {} | ||
151 | |||
152 | M.float.new = M.core.float | ||
153 | M.float.max = M.core.float_max | ||
154 | M.float.min = M.core.float_min | ||
155 | |||
156 | M.text = {} | ||
157 | M.text.font = M.core.font | ||
158 | |||
159 | function M.text.new(params) | ||
160 | params = params or {} | ||
161 | local result = M.core.text(params) | ||
162 | M.util.tbl_assign_reserved(params, result) | ||
163 | return result | ||
164 | end | ||
165 | |||
166 | M.rectangle = {} | ||
167 | |||
168 | function M.rectangle.new(params) | ||
169 | params = params or {} | ||
170 | local result = M.core.rectangle() | ||
171 | M.util.tbl_assign_reserved(params, result) | ||
172 | return result | ||
173 | end | ||
174 | |||
175 | function M.rectangle.surrounding(content, params) | ||
176 | params = params or {} | ||
177 | local margin = params.margin or 0 | ||
178 | local margin_left = params.margin_left or margin | ||
179 | local margin_right = params.margin_right or margin | ||
180 | local margin_top = params.margin_top or margin | ||
181 | local margin_bottom = params.margin_bottom or margin | ||
182 | |||
183 | return M.rectangle.new(M.util.tbl_extend({ | ||
184 | left = content.left - margin_left, | ||
185 | right = content.right + margin_right, | ||
186 | top = content.top - margin_top, | ||
187 | bottom = content.bottom + margin_bottom, | ||
188 | }, params)) | ||
189 | end | ||
190 | |||
191 | M.straight_path = {} | ||
192 | |||
193 | function M.straight_path.new(params) | ||
194 | params = params or {} | ||
195 | local result = M.core.straight_path(params) | ||
196 | M.util.tbl_assign_reserved(params, result) | ||
197 | return result | ||
198 | end | ||
199 | |||
200 | function M.shape(params) | ||
201 | params = params or {} | ||
202 | local result = M.util.tbl_filter_reserved(params) | ||
203 | |||
204 | local shape = M.core.complex_shape() | ||
205 | setmetatable(result, { | ||
206 | __index = function(_, k) | ||
207 | if M.util.is_reserved(k) then | ||
208 | return shape[k] | ||
209 | end | ||
210 | end, | ||
211 | }) | ||
212 | M.util.tbl_assign_reserved(params, shape) | ||
213 | |||
214 | return result | ||
215 | end | ||
216 | |||
217 | M.layout = {} | ||
218 | |||
219 | function M.layout.margin(params) | ||
220 | local result = M.shape(params) | ||
221 | |||
222 | result.margin_left = result.margin_left or M.float.new() | ||
223 | result.margin_right = result.margin_right or M.float.new() | ||
224 | result.margin_top = result.margin_top or M.float.new() | ||
225 | result.margin_bottom = result.margin_bottom or M.float.new() | ||
226 | |||
227 | function result:draw() | ||
228 | M.constrain(self.left:eq(self.content.left - self.margin_left)) | ||
229 | M.constrain(self.right:eq(self.content.right + self.margin_right)) | ||
230 | M.constrain(self.top:eq(self.content.top - self.margin_top)) | ||
231 | M.constrain(self.bottom:eq(self.content.bottom + self.margin_bottom)) | ||
232 | |||
233 | self.content:draw() | ||
234 | end | ||
235 | |||
236 | return result | ||
237 | end | ||
238 | |||
239 | function M.layout.margin_left(params) | ||
240 | return M.layout.margin(M.util.tbl_extend(params, { | ||
241 | margin_right = 0, | ||
242 | margin_top = 0, | ||
243 | margin_bottom = 0, | ||
244 | })) | ||
245 | end | ||
246 | |||
247 | function M.layout.margin_right(params) | ||
248 | return M.layout.margin(M.util.tbl_extend(params, { | ||
249 | margin_left = 0, | ||
250 | margin_top = 0, | ||
251 | margin_bottom = 0, | ||
252 | })) | ||
253 | end | ||
254 | |||
255 | function M.layout.margin_top(params) | ||
256 | return M.layout.margin(M.util.tbl_extend(params, { | ||
257 | margin_left = 0, | ||
258 | margin_right = 0, | ||
259 | margin_bottom = 0, | ||
260 | })) | ||
261 | end | ||
262 | |||
263 | function M.layout.margin_bottom(params) | ||
264 | return M.layout.margin(M.util.tbl_extend(params, { | ||
265 | margin_left = 0, | ||
266 | margin_right = 0, | ||
267 | margin_top = 0, | ||
268 | })) | ||
269 | end | ||
270 | |||
271 | -- TODO: factor with vstack | ||
272 | -- TODO: also as just a set of constraints | ||
273 | function M.layout.hstack(params) | ||
274 | local result = M.shape(params) | ||
275 | |||
276 | result.spacing = result.spacing or 0 | ||
277 | result.align = result.align or "top" | ||
278 | if result.align == "middle" or result.align == "center" then | ||
279 | result.align = "vert_center" | ||
280 | end | ||
281 | |||
282 | function result:draw() | ||
283 | local len = #self.elements | ||
284 | assert(len >= 1, "hstack must have at least 1 element in `elements`") | ||
285 | |||
286 | local tops = {} | ||
287 | local bottoms = {} | ||
288 | |||
289 | local previous_right = self.left - self.spacing | ||
290 | local previous_vert_anchor = self[self.align] | ||
291 | |||
292 | for _, el in pairs(self.elements) do | ||
293 | local el_vert_anchor = el[self.align] | ||
294 | |||
295 | M.constrain(el.left:eq(previous_right + self.spacing)) | ||
296 | M.constrain(el_vert_anchor:eq(previous_vert_anchor)) | ||
297 | |||
298 | table.insert(tops, el.top) | ||
299 | table.insert(bottoms, el.bottom) | ||
300 | |||
301 | el:draw() | ||
302 | |||
303 | previous_right = el.right | ||
304 | previous_vert_anchor = el_vert_anchor | ||
305 | end | ||
306 | |||
307 | M.constrain(self.top:eq(M.float.min(tops))) | ||
308 | M.constrain(self.bottom:eq(M.float.max(bottoms))) | ||
309 | M.constrain(self.right:eq(previous_right)) | ||
310 | end | ||
311 | |||
312 | return result | ||
313 | end | ||
314 | |||
315 | function M.layout.vstack(params) | ||
316 | local result = M.shape(params) | ||
317 | |||
318 | result.spacing = result.spacing or 0 | ||
319 | result.align = result.align or "left" | ||
320 | if result.align == "middle" or result.align == "center" then | ||
321 | result.align = "horiz_center" | ||
322 | end | ||
323 | |||
324 | function result:draw() | ||
325 | local len = #self.elements | ||
326 | assert(len >= 1, "vstack must have at least 1 element in `elements`") | ||
327 | |||
328 | local lefts = {} | ||
329 | local rights = {} | ||
330 | |||
331 | local previous_bottom = self.top - self.spacing | ||
332 | local previous_horiz_anchor = self[self.align] | ||
333 | |||
334 | for _, el in pairs(self.elements) do | ||
335 | local el_horiz_anchor = el[self.align] | ||
336 | |||
337 | M.constrain(el.top:eq(previous_bottom + self.spacing)) | ||
338 | M.constrain(el_horiz_anchor:eq(previous_horiz_anchor)) | ||
339 | |||
340 | table.insert(lefts, el.left) | ||
341 | table.insert(rights, el.right) | ||
342 | |||
343 | el:draw() | ||
344 | |||
345 | previous_bottom = el.bottom | ||
346 | previous_horiz_anchor = el_horiz_anchor | ||
347 | end | ||
348 | |||
349 | M.constrain(self.left:eq(M.float.min(lefts))) | ||
350 | M.constrain(self.right:eq(M.float.max(rights))) | ||
351 | M.constrain(self.bottom:eq(previous_bottom)) | ||
352 | end | ||
353 | |||
354 | return result | ||
355 | end | ||
356 | |||
357 | M.constraint = {} | ||
358 | |||
359 | function M.constraint.left_of(lhs, rhs, margin) | ||
360 | M.constrain(lhs.right:eq(rhs.left - (margin or 0))) | ||
361 | end | ||
362 | |||
363 | function M.constraint.right_of(rhs, lhs, margin) | ||
364 | M.constrain(lhs.right:eq(rhs.left - (margin or 0))) | ||
365 | end | ||
366 | |||
367 | function M.constraint.above(above, below, margin) | ||
368 | M.constrain(above.bottom:eq(below.top - (margin or 0))) | ||
369 | end | ||
370 | |||
371 | function M.constraint.below(below, above, margin) | ||
372 | M.constrain(above.bottom:eq(below.top - (margin or 0))) | ||
373 | end | ||
374 | |||
375 | function M.constraint.same_size(elems) | ||
376 | local height = M.float.new() | ||
377 | local width = M.float.new() | ||
378 | |||
379 | for _, el in pairs(elems) do | ||
380 | M.constrain(el.height:eq(height)) | ||
381 | M.constrain(el.width:eq(width)) | ||
382 | end | ||
383 | end | ||
384 | |||
385 | function M.constraint.same_height(elems) | ||
386 | local height = M.float.new() | ||
387 | |||
388 | for _, el in pairs(elems) do | ||
389 | M.constrain(el.height:eq(height)) | ||
390 | end | ||
391 | end | ||
392 | |||
393 | function M.constraint.same_width(elems) | ||
394 | local width = M.float.new() | ||
395 | |||
396 | for _, el in pairs(elems) do | ||
397 | M.constrain(el.width:eq(width)) | ||
398 | end | ||
399 | end | ||
400 | |||
401 | M.constrain = M.core.constrain | ||
402 | M.draw = M.core.draw | ||
403 | |||
404 | return M | ||