1 module nanogui.experimental.utils;
2 
3 import nanogui.common : NanoContext;
4 import nanogui.common : Vector2f, Vector2i;
5 
6 public import auxil.sizeindex;
7 public import auxil.model;
8 public import auxil.traits;
9 public import auxil.treepath : TreePath;
10 public import auxil.default_visitor : MeasuringVisitor, TreePathVisitorImpl;
11 
12 private string enumToString(E)(E e) @nogc @safe nothrow
13 	if (is(E == enum))
14 {
15 	import std.traits : Unqual;
16 
17 	// using `if` instead of `final switch` is simple
18 	// workaround of duplicated enum members
19 	static foreach(v; __traits(allMembers, E))
20 		mixin("if (e == E." ~ v ~ ") return `" ~ v ~ "`;");
21 	return "Unrepresentable by " ~ Unqual!E.stringof ~ " value";
22 }
23 
24 void indent(ref NanoContext ctx)
25 {
26 	ctx.position.x += 20;
27 	ctx.size.x -= 20;
28 }
29 
30 void unindent(ref NanoContext ctx)
31 {
32 	ctx.position.x -= 20;
33 	ctx.size.x += 20;
34 }
35 
36 bool isPointInRect(Vector2f topleft, Vector2f size, Vector2f point)
37 {
38 	return point.x >= topleft.x && point.x < topleft.x+size.x &&
39 		point.y >= topleft.y && point.y < topleft.y+size.y;
40 }
41 
42 bool isPointInRect(Vector2i topleft, Vector2i size, Vector2i point)
43 {
44 	return point.x >= topleft.x && point.x < topleft.x+size.x &&
45 		point.y >= topleft.y && point.y < topleft.y+size.y;
46 }
47 
48 auto drawItem(T)(ref NanoContext ctx, float height, T item)
49 {
50 	import std.traits : isSomeString;
51 
52 	static if (isSomeString!T)
53 		return drawString(ctx, height, item);
54 	else
55 		return drawPodType(ctx, height, item);
56 }
57 
58 private auto drawString(Char)(ref NanoContext ctx, float height, const(Char)[] str)
59 {
60 	import nanogui.common : textAlign, text, roundedRect, fill, beginPath, linearGradient, fillPaint, fillColor;
61 
62 	// it is possible if the item is placed out of visible area
63 	if (ctx.size[ctx.orientation] <= 0)
64 		return false;
65 
66 	bool inside;
67 	if (isPointInRect(ctx.position, ctx.size, ctx.mouse))
68 	{
69 		const gradTop = ctx.theme.mButtonGradientTopFocused;
70 		const gradBot = ctx.theme.mButtonGradientBotFocused;
71 
72 		ctx.beginPath;
73 		ctx.roundedRect(
74 			ctx.position.x, ctx.position.y,
75 			ctx.size.x, ctx.size.y,
76 			ctx.theme.mButtonCornerRadius - 1
77 		);
78 
79 		const bg = ctx.linearGradient(
80 			ctx.position.x, ctx.position.y, 
81 			ctx.position.x, ctx.position.y + height,
82 			gradTop, gradBot
83 		);
84 
85 		ctx.fillPaint(bg);
86 		ctx.fill;
87 		ctx.fillColor(ctx.theme.mTextColor);
88 		inside = true;
89 	}
90 	ctx.textAlign(ctx.algn);
91 	ctx.text(ctx.position.x, ctx.position.y, str);
92 
93 	return inside;
94 }
95 
96 private auto drawPodType(T)(ref NanoContext ctx, float height, T item)
97 {
98 	import std.format : sformat;
99 	import std.traits : isIntegral, isFloatingPoint, isBoolean, isSomeString,
100 		isPointer, isSomeChar;
101 
102 	enum textBufferSize = 512;
103 	char[textBufferSize] buffer;
104 	size_t l;
105 
106 	// format specifier depends on type
107 	static if (is(T == enum))
108 	{
109 		const s = item.enumToString;
110 		l += sformat(buffer[l..$], "%s", s).length;
111 	}
112 	else static if (isIntegral!T)
113 		l += sformat(buffer[l..$], "%d", item).length;
114 	else static if (isFloatingPoint!T)
115 		l += sformat(buffer[l..$], "%f", item).length;
116 	else static if (isBoolean!T)
117 		l += sformat(buffer[l..$], item ? "true" : "false").length;
118 	else static if (isSomeString!T || isPointer!T)
119 		l += sformat(buffer[l..$], "%s", item).length;
120 	else static if (isSomeChar!T)
121 		l += sformat(buffer[l..$], "%s", item).length;
122 	else
123 		static assert(0, T.stringof);
124 
125 	buffer[l < $ ? l : $-1] = '\0';
126 
127 	return ctx.drawString(height, buffer[0..l]);
128 }
129 
130 mixin template DependencyProperty(T, alias string name)
131 {
132 	import std.string : capitalize;
133 
134 	protected
135 	{
136 		mixin("T m" ~ name.capitalize ~ ";");
137 	}
138 	public 
139 	{
140 		import std.format : format;
141 		mixin (format(q{
142 			final T %1$s() const { return m%2$s; }
143 			final void %1$s(T value)
144 			{ 
145 				if (value == m%2$s) return;
146 				m%2$s = value;
147 				invalidate();
148 			}
149 		}, name, name.capitalize));
150 	}
151 }