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 }