1 module traverse; 2 3 import std.traits : isDynamicArray, isIntegral, isFloatingPoint, isBoolean, isSomeString; 4 5 enum isLeaf(T) = is(T == enum) || isIntegral!T || isFloatingPoint!T || isBoolean!T || isSomeString!T; 6 enum isNode(T) = is(T == struct) || isDynamicArray!T; 7 8 auto traverseImpl(alias leaf, alias nodeEnter, alias nodeLeave, Ctx, Data)(ref Ctx ctx, ref Data data) 9 { 10 static if (isLeaf!(Data)) 11 { 12 leaf(ctx, data); 13 } 14 else static if (isNode!(Data)) 15 { 16 ctx.nestingLevel++; 17 nodeEnter(ctx, data); 18 19 static if (is(Data == struct)) 20 { 21 import std.traits : isType; 22 foreach(memberName; __traits(allMembers, (Data))) 23 static if (!isType!(__traits(getMember, data, memberName))) 24 traverseImpl!(leaf, nodeEnter, nodeLeave)(ctx, __traits(getMember, data, memberName)); 25 } 26 else static if (isDynamicArray!(Data)) 27 { 28 foreach(member; data) 29 traverseImpl!(leaf, nodeEnter, nodeLeave)(ctx, member); 30 } 31 32 nodeLeave(ctx, data); 33 ctx.nestingLevel--; 34 } 35 else 36 static assert(0); 37 } 38 39 auto traverseImpl(alias leaf, alias nodeEnter, alias nodeLeave, Ctx, Data, Model)(ref Ctx ctx, ref Data data, ref Model model) 40 { 41 static if (isLeaf!(Data)) 42 { 43 leaf(ctx, data, model); 44 } 45 else static if (isNode!(Data)) 46 { 47 import std.array : front, popFront; 48 49 // Put on stack the current state of context before entering the next node 50 // to restore it after leaving it 51 auto old = ctx.area; 52 ctx.nestingLevel++; 53 nodeEnter(ctx, data, model); 54 55 static if (is(Data == struct)) 56 { 57 import std.traits : isType; 58 auto childs = model.child; 59 60 foreach(memberName; __traits(allMembers, Data)) 61 { 62 ctx.area.x = ctx.xWidgetRange[$-1].front[0]; 63 ctx.area.w = ctx.xWidgetRange[$-1].front[1]; 64 ctx.area.y = ctx.yWidgetRange[$-1].front[0]; 65 ctx.area.h = ctx.yWidgetRange[$-1].front[1]; 66 static if (!isType!(__traits(getMember, data, memberName))) 67 { 68 traverseImpl!(leaf, nodeEnter, nodeLeave)(ctx, __traits(getMember, data, memberName), childs.front); 69 childs.popFront; 70 ctx.xWidgetRange[$-1].popFront; 71 ctx.yWidgetRange[$-1].popFront; 72 } 73 } 74 } 75 else static if (isDynamicArray!(Data)) 76 { 77 foreach(member; data) 78 traverseImpl!(leaf, nodeEnter, nodeLeave)(ctx, member, model); 79 } 80 ctx.area = old; 81 82 nodeLeave(ctx, data, model); 83 ctx.nestingLevel--; 84 } 85 else 86 static assert(0); 87 }