1 module evael.graphics.gui.controls.TextArea;
2 
3 import std.string;
4 import std.conv;
5 import std.typecons;
6 
7 import evael.graphics.gui.controls.Container;
8 import evael.graphics.gui.controls.TextBlock;
9 import evael.graphics.gui.controls.ScrollBar;
10 
11 import evael.utils.Math;
12 
13 import evael.utils.Size;
14 import evael.utils.Color;
15 
16 class TextArea : Container, IScrollable
17 {
18 	alias Line = Tuple!(wstring, "text", Color, "color");
19 
20 	private Line[] m_lines;
21 	
22 	/// Position of the text in the textarea
23 	private vec2 m_globalTextPosition;
24 
25 	/// Max lines that can be displayed
26 	private int m_maxLines;
27 
28 	public this(in float x, in float y, in int width, in int height)
29 	{
30 		this(vec2(x, y,), Size!int(width, height));
31 	}
32 
33 	public this()(in auto ref vec2 position, in auto ref Size!int size)
34 	{
35 		super(position, size);
36 
37 		this.m_name = "textArea";
38 
39 		this.m_globalTextPosition = vec2(0.0f);
40 		this.m_maxLines = 255;
41 
42 //		this.addChild(new ScrollBar(0, 0, 20, size.height));
43 	}
44 
45 
46 	public void onScroll(ScrollBar.ScrollDirection direction, in float scrollBarPosition)
47 	{
48 		
49 	}
50 
51 	public override void draw(in float deltaTime)
52 	{
53 		if(!this.m_isVisible)
54 		{
55 			return;
56 		}
57 		
58 		super.draw(deltaTime);
59 
60 		auto vg = this.m_nvg;
61 
62 		nvgSave(vg);
63 	
64 		nvgFontSize(vg, this.m_theme.fontSize);
65 		nvgFontFaceId(vg, this.m_theme.font.id);
66 
67 		float lineh;		
68 		nvgTextMetrics(vg, null, null, &lineh);
69 
70 		auto x = this.m_realPosition.x;
71 		auto y = this.m_realPosition.y + this.m_globalTextPosition.y;
72 
73 		NVGtextRow[3] rows;
74 
75 		foreach(ref line; this.m_lines)
76 		{
77 			const char* text = line.text.to!string.toStringz();
78 			const char* start = text;
79 			const char* end = text + line.text.length;
80 
81 			int nrows = nvgTextBreakLines(vg, start, end, this.m_size.width, rows.ptr, 3);
82 
83 			while(nrows) 
84 			{
85 				for(int i = 0; i < nrows; i++) 
86 				{
87 					NVGtextRow* row = &rows[i];
88 
89 					// We check if we really need to draw this line
90 					if(y >= this.m_realPosition.y)
91 					{	
92 						// TODO : use font class maybe ?
93 						if(this.m_theme.drawTextShadow)
94 						{
95 							nvgFontBlur(vg, 2);
96 							nvgFillColor(vg, Color.Black.asNvg);
97 							nvgTextAlign(vg, NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP);
98 							nvgText(vg, x + 1.6f, y + 1.6f, row.start, row.end);
99 							nvgFontBlur(vg, 0.8);
100 						}
101 
102 						nvgFillColor(vg, line.color.asNvg);
103 						nvgTextAlign(vg, NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP);
104 						nvgText(vg, x, y, row.start, row.end);
105 					}
106 
107 					auto nextLineY = y + lineh;
108 
109 					// We check if next line is gonna be displayed outside of the textarea
110 					if(nextLineY <= this.m_realPosition.y + this.m_size.height)
111 					{
112 						// No
113 						y = nextLineY;
114 					}
115 					else
116 					{
117 						// Yes, we need to update global text position instead of next line position
118 						this.m_globalTextPosition.y = this.m_globalTextPosition.y - lineh;	
119 					}
120 				}
121 
122 				nrows = nvgTextBreakLines(vg, rows[nrows - 1].next, end, this.m_size.width, rows.ptr, 3);
123 			}
124 		}
125 
126 		nvgRestore(vg);
127 	}
128 
129 	public void appendLine(in wstring text, in ref Color color = Color.Black)  
130 	{
131 		// We check if we reached lines limit
132 		if(this.m_lines.length == this.m_maxLines)
133 		{
134 			// We need to remove the first line
135 			this.m_lines = this.m_lines[1..$];
136 
137 			this.m_globalTextPosition.y = 0.0f;
138 		}
139 
140 		this.m_lines ~= Line(text, color);
141 	}
142 
143 	public void appendLine(in string text, in ref Color color = Color.Black)  
144 	{
145 		this.appendLine(to!wstring(text), color);
146 	}
147 
148 	/**
149 	 * Clear all lines
150 	 */
151 	public void clear() nothrow @nogc
152 	{
153 		this.m_controls = [];
154 		this.m_globalTextPosition = vec2(0.0f);
155 	}
156 
157 	@property
158 	{
159 		public void text(in wstring value)  
160 		{
161 			this.clear();
162 
163 			this.appendLine(value);
164 		}
165 
166 		public void maxLines(in int value) nothrow @nogc
167 		{
168 			this.m_maxLines = value;
169 		}
170 	}
171 }