1 module walker; 2 3 import common; 4 5 struct WidgetRange 6 { 7 float x, delta, xend; 8 9 invariant 10 { 11 import std.math, std.conv; 12 13 assert(x >= 0, x.text); 14 assert(xend.isNaN || xend >= 0); 15 assert(!x.isNaN); 16 assert(!delta.isNaN); 17 } 18 19 bool empty() const 20 { 21 return x >= xend; 22 } 23 24 auto front() 25 { 26 import std.typecons : tuple; 27 return tuple(x, delta); 28 } 29 30 void popFront() 31 { 32 import std.math : isNaN; 33 x += xend.isNaN ? 0 : delta; 34 } 35 } 36 37 struct Walker 38 { 39 WorkArea area; 40 auto direction = Direction.row; 41 auto alignment = Alignment.stretch; 42 auto justification = Justification.around; 43 44 WidgetRange[] xWidgetRange, yWidgetRange; 45 46 auto wrapping = false; 47 int nestingLevel; 48 49 // for debug output in console 50 string indentPrefix; 51 // for debug rendering 52 RenderState[] renderlog; 53 54 import std.stdio; 55 import traverse; 56 57 auto render(Data, Dom)(Data data, Dom dom) 58 { 59 assert(dom); 60 import std.math : isNaN; 61 62 // defaulting 63 if (area.margin.isNaN) 64 area.margin = 0; 65 if (area.padding.isNaN) 66 area.padding = 0; 67 68 traverseImpl!(leaf, nodeEnter, nodeLeave)(this, data, dom); 69 } 70 71 static auto leaf(Data, Dom)(ref typeof(this) ctx, Data data, Dom dom) 72 { 73 with(ctx) 74 { 75 writeln(indentPrefix, data); 76 writeln(indentPrefix, ctx.area.w, "x", ctx.area.h); 77 writeln(indentPrefix, dom.attributes.direction); 78 if (!dom.attributes.direction.isNull) 79 direction = dom.attributes.direction.get; 80 if (!dom.attributes.margin.isNull) 81 area.margin = dom.attributes.margin.get; 82 if (!dom.attributes.padding.isNull) 83 area.padding = dom.attributes.padding.get; 84 85 renderlog ~= RenderState(dom.name, area, direction, 1, nestingLevel); 86 auto a = area; 87 a.x += area.margin; 88 a.y += area.margin; 89 a.w -= area.margin*2; 90 a.h -= area.margin*2; 91 renderlog ~= RenderState(dom.name, a, direction, 2, nestingLevel); 92 a.x += area.padding; 93 a.y += area.padding; 94 a.w -= area.padding*2; 95 a.h -= area.padding*2; 96 renderlog ~= RenderState(dom.name, a, direction, 3, nestingLevel); 97 } 98 } 99 100 static auto nodeEnter(Data, Dom)(ref typeof(this) ctx, Data data, Dom dom) 101 { 102 with(ctx) 103 { 104 writeln(indentPrefix, data); 105 // writeln(indentPrefix, ctx.area.x, ", ", ctx.area.y, " ", ctx.area.w, "x", ctx.area.h); 106 writeln(indentPrefix, ctx.area.margin); 107 108 if (!dom.attributes.direction.isNull) 109 direction = dom.attributes.direction.get; 110 if (!dom.attributes.margin.isNull) 111 area.margin = dom.attributes.margin.get; 112 if (!dom.attributes.padding.isNull) 113 area.padding = dom.attributes.padding.get; 114 115 renderlog ~= RenderState(dom.name, area, direction, 1, nestingLevel); 116 auto a = area; 117 a.x += area.margin; 118 a.y += area.margin; 119 a.w -= area.margin*2; 120 a.h -= area.margin*2; 121 renderlog ~= RenderState(dom.name, a, direction, 2, nestingLevel); 122 a.x += area.padding; 123 a.y += area.padding; 124 a.w -= area.padding*2; 125 a.h -= area.padding*2; 126 renderlog ~= RenderState(dom.name, a, direction, 3, nestingLevel); 127 128 import std.math : isNaN; 129 assert(!area.margin.isNaN); 130 final switch (direction) 131 { 132 case Direction.row: 133 xWidgetRange ~= WidgetRange(area.x + area.margin + area.padding, (area.w - 2*(area.margin + area.padding))/dom.child.length, area.x+area.w - 2*(area.margin + area.padding)); 134 yWidgetRange ~= WidgetRange(area.y + area.margin + area.padding, area.h - 2*(area.margin + area.padding), float.nan); 135 break; 136 case Direction.rowReverse: 137 assert(0, "not implemented"); 138 // break; 139 case Direction.column: 140 xWidgetRange ~= WidgetRange(area.x + area.margin + area.padding, area.w - 2*(area.margin + area.padding), float.nan); 141 yWidgetRange ~= WidgetRange(area.y + area.margin + area.padding, (area.h - 2*(area.margin + area.padding))/dom.child.length, area.y+area.h - 2*(area.margin + area.padding)); 142 break; 143 case Direction.columnReverse: 144 assert(0, "not implemented"); 145 // break; 146 } 147 148 indentPrefix ~= "\t"; 149 } 150 } 151 152 static auto nodeLeave(Data, Dom)(ref typeof(this) ctx, Data data, Dom dom) 153 { 154 ctx.indentPrefix = ctx.indentPrefix[0..$-1]; 155 ctx.xWidgetRange = ctx.xWidgetRange [0..$-1]; 156 ctx.yWidgetRange = ctx.yWidgetRange [0..$-1]; 157 } 158 }