When I was a kid programming was magical and RealBASIC made designing GUIs easy. Today we have so many options, all annoyingly tedious; what's a kid at heart to do?
The guys at subformapp.com are tackling the difficulty of GUI design with a new layout system (subform). HN discussion here. Actually trying the app involves a $25/mo subscription... nope. The engine itself is provided free for non-commercial use:
https://github.com/lynaghk/subform-layoutWhat's missing from the freely available engine is documentation so here's what I've got after playing with it:
Subform is concerned with boxes and where to place them. Boxes can either be:
Boxes have children. The ones that are parent-directed can be layed out either as a stack or a grid.
Boxes contain other boxes. The root of the tree of boxes is called the "artboard".
Subform uses a generalized dimension format everywhere:
Layout properties are split between the parent box and child boxes.
Absolutely positioned / self-directed items are relative to the parent box but do not follow their parent's layout mode. They have before/size/after dimensions.
Stacked layouts have a direction (horizontal or vertical). This is the main direction. The opposite direction is the cross direction.
| Parent specifies | Child specifies |
|
|
Example with all properties set to defaults. Properties are optional.
{
layout: {
mode: "self-directed",
horizontal: {before: "1s", size: "1s", after: "1s"},
vertical: {before: "1s", size: "1s", after: "1s"},
},
childrenLayout: {
mode: "stack-vertical",
mainBeforeFirst: "1s",
mainBetween: 0,
mainAfterLast: "1s",
crossBefore: "1s",
crossAfter: "1s"
}
children: [
{
layout: {
mode: "parent-directed",
main: {before: null, size: "1s", after: null},
cross: {before: null, size: "1s", after: null}
}
}
]
}
Grid layouts have both a horizontal and vertical direction (rows and cols).
| Parent specifies | Child specifies |
|
|
Example with all properties set to defaults. Properties are optional.
{
layout: {
mode: "self-directed",
horizontal: {before: "1s", size: "1s", after: "1s"},
vertical: {before: "1s", size: "1s", after: "1s"},
},
childrenLayout: {
mode: "grid",
rows: {beforeFirst: 0, between: 0, afterLast: 0, sizes: ["1s", "1s"]},
cols: {beforeFirst: 0, between: 0, afterLast: 0, sizes: ["1s", "1s"]}
}
children: [
{
layout: {
mode: "parent-directed",
rowIdx: 0,
rowSpan: 1,
colIdx: 0,
colSpan: 1
}
}
]
}
Overall, it reminds me of Java's GroupLayout and the benefits of html table layouts.
Play with layout below: (ctrl-mousedown to change the bounds of the art area)
Specification for layout as far as I can tell:
<dimension> := <int> | <int>s | <int>% | empty
- so dimensions can be Absolute, Stretch, Percentage, or Default
<node (can be empty)>
layout:
- mode: ("self-directed" | "parent-directed"(default))
(self-directed)
- horizontal:
- before: <dimension> (default 1s)
- size: <dimension> (default 1s)
- after: <dimension> (default 1s)
- vertical: (same as horizontal)
(parent-directed, parent is stack-horizontal or stack-vertical)
- main:
- before: <dimension> (default Default)
- size: <dimension> (default 1s)
- after: <dimension> (default Default)
- cross:
- before: <dimension> (default Default)
- size: <dimension> (default 1s)
- after: <dimension> (default Default)
(parent-directed, parent is grid)
- rowSpan: <int> (default 1)
- colSpan: <int> (default 1)
- rowIdx: <int> (default 0)
- colIdx: <int> (default 0)
childrenLayout:
- mode: ("grid" | "stack-horizontal" | "stack-vertical"(default))
(stack-horizontal or stack-vertical)
- mainBeforeFirst: <dimension> (default 1s)
- mainBetween: <dimension> (default 0)
- mainAfterLast: <dimension> (default 1s)
- crossBefore: <dimension> (default 1s)
- crossAfter: <dimension> (default 1s)
(grid)
- rows:
- beforeFirst: <dimension> (default 0)
- between: <dimension> (default 0)
- afterLast: <dimension> (default 0)
- sizes: [<dimension>, <dimension>, ...] (default [1s, 1s])
- cols: (same as rows)
children: [<node>, <node>, ...]