1 module ws.gui.text;
2 
3 import
4 	std.utf,
5 	std.conv,
6 	ws.io,
7 	ws.list,
8 	ws.gl.gl,
9 	ws.gl.draw,
10 	ws.gl.font,
11 	ws.gl.shader,
12 	ws.gui.base;
13 
14 
15 class Text: Base {
16 
17 	Font font;
18 	String text;
19 	Shader shader;
20 
21 	this(){
22 		style.bg.normal = [0, 0, 0, 0.5];
23 		style.fg.normal = [1, 1, 1, 1];
24 		text = new String;
25 		shader = Shader.load("2d_texture", [gl.attributeVertex: "vVertex", gl.attributeTexture: "vTexture0"], null, TEXTURE_VERT, TEXTURE_FRAG);
26 		setFont("sans", 11);
27 	}
28 
29 
30 	void setFont(string f, int size){
31 		font = Font.load(f, size);
32 		text.set(text.toString());
33 	}
34 
35 
36 	override void resize(int[2] size){
37 		super.resize(size);
38 		text.set(text.toString());
39 	}
40 
41 
42 	override void onDraw(){
43 		if(text.queue.length){
44 			text ~= text.queue;
45 			text.queue = "";
46 		}
47 		glActiveTexture(GL_TEXTURE0);
48 		shader.use();
49 		shader["Screen"].set(Draw.screen);
50 		shader["Image"].set(0);
51 		shader["Color"].set(style.fg.normal);
52 		float[3] scale = [1,1,0];
53 		shader["Scale"].set(scale);
54 		float[3] fp;
55 		foreach(g; text){
56 			if(
57 				g.c == '\n' || g.pos.x > size.x ||
58 				g.pos.y < 0 - cast(int)(font.size*1.4) ||
59 				g.pos.y > size.y || g.pos.x < -g.glyph.advance
60 			)
61 				continue;
62 			glBindTexture(GL_TEXTURE_2D, g.glyph.tex);
63 			fp = [cast(int)g.pos.x + pos.x + font.size, cast(int)g.pos.y + pos.y, 0];
64 			shader["Offset"].set(fp);
65 			g.glyph.vao.draw();
66 		}
67 	}
68 
69 
70 	/++
71 		List of characters (dchar + screen position + OpenGL glyph)
72 	+/
73 	class String: List!Character {
74 
75 		void opOpAssign(string op)(string s) if(op=="~") {
76 			foreach(dchar c; s)
77 				this ~= c;
78 			//return s.toUTF32();
79 		}
80 
81 		void opOpAssign(string op)(dchar c) if(op=="~"){
82 			if(c == '\n')
83 				lines++;
84 			if(gl.active()){
85 				auto character = new Character(c);
86 				if(!length || !cursor.next){
87 					push(character);
88 					cursor.prev = end;
89 				}else{
90 					auto it = super.insert(cursor.next, character, true);
91 					cursor.prev = it;
92 					cursor.next = ++it;
93 				}
94 				update(cursor.prev);
95 			}else
96 				queue ~= c;
97 		}
98 
99 		void set(string s){
100 			clear();
101 			this ~= s;
102 		}
103 
104 		override void clear(){
105 			super.clear();
106 			cursor.prev = begin();
107 			cursor.next = end();
108 		}
109 
110 		override string toString(){
111 			string s;
112 			foreach(g; text)
113 				s ~= g.c;
114 			return s;
115 		}
116 
117 		Cursor cursor;
118 
119 		void update(Iterator it){
120 			if(!begin)
121 				return;
122 			it = begin;//(it ? it : begin());
123 			Point start = Point(font.size.to!int, (font.size*1.4*lines + font.size*0.4).to!int);
124 			if(it.prev){
125 				start = 
126 					it.prev.get().c == '\n'
127 					? Point(cast(int)(font.size), it.prev.get().pos.y - cast(int)(font.size*1.4))
128 					: it.prev.get().pos + Point(cast(int)it.prev.get().glyph.advance, 0);
129 			}
130 			while(it){
131 				it.get().pos = start;
132 				if(it.get().c=='\n'){
133 					start.x = cast(int)(font.size);
134 					start.y -= cast(int)(font.size*1.4);
135 				}else{
136 					start.x += it.get().glyph.advance;
137 				}
138 				it = it.next;
139 			}
140 		}
141 		
142 		struct Cursor {
143 			Iterator prev;
144 			Iterator next;
145 			void opUnary(string s)() if(s == "--") {
146 				if(prev){
147 					next = prev;
148 					prev = prev.prev;
149 				}
150 			}
151 			void opUnary(string s)() if(s == "++") {
152 				if(next){
153 					prev = next;
154 					next = next.next;
155 				}
156 			}
157 		}
158 
159 		string queue;
160 
161 		protected:
162 			long lines;
163 
164 	}
165 
166 	class Character {
167 		dchar c;
168 		Point pos;
169 		Font.Glyph glyph;
170 		this(dchar c){
171 			this.c = c;
172 			glyph = font[c];
173 		}
174 	}
175 
176 
177 }