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