1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphic.Text;
25 
26 private:
27 
28 import Dgame.Graphic.Drawable;
29 import Dgame.Graphic.Transformable;
30 import Dgame.Graphic.Surface;
31 import Dgame.Graphic.Texture;
32 import Dgame.Graphic.Color;
33 
34 import Dgame.Math.Vertex;
35 import Dgame.Math.Geometry;
36 
37 import Dgame.System.Font;
38 
39 public:
40 
41 /**
42  * Text defines a graphical 2D text, that can be drawn on screen.
43  *
44  * Author: Randy Schuett (rswhite4@googlemail.com)
45  */
46 class Text : Transformable, Drawable {
47 private:
48     Vertex[4] _vertices;
49 
50     Texture _texture;
51     Font* _font;
52 
53     string _text;
54 
55     bool _redraw = true;
56 
57 protected:
58     @nogc
59     override void draw(ref const Window wnd) nothrow {
60         this.update();
61 
62         if (_text.length != 0)
63             wnd.draw(Geometry.TriangleStrip, super.getMatrix(), &_texture, _vertices[]);
64     }
65 
66     @nogc
67     final void _init() pure nothrow {
68         // #2
69         _vertices[1].texCoord.x = 1;
70         // #3
71         _vertices[2].texCoord.y = 1;
72         // #4
73         _vertices[3].texCoord.x = 1;
74         _vertices[3].texCoord.y = 1;
75 
76         foreach (ref Vertex v; _vertices) {
77             v.color = Color4f.White;
78         }
79     }
80 
81 public:
82     /**
83      * The foreground color. Default is Color4b.Black.
84      */
85     Color4b foreground = Color4b.Black;
86     /**
87      * The background color. Default is Color4b.White.
88      *
89      * Note: The background color is ignored if your mode is not Font.Mode.Shaded
90      */
91     Color4b background = Color4b.White;
92     /**
93      * The Font mode which is default Font.Mode.Solid.
94      */
95     Font.Mode mode = Font.Mode.Solid;
96 
97 final:
98     /**
99      * CTor
100      */
101     @nogc
102     this(ref Font fnt, string str = null) pure nothrow {
103         _font = &fnt;
104         _text = str;
105 
106         _init();
107     }
108 
109     /**
110      * Update the Texture and redraw the text, if necessary.
111      * This method is called automatically if the Text is drawn onto the Window.
112      * But if you need specific informations (e.g. width/height) before the draw call,
113      * you can call the method by yourself.
114      */
115     @nogc
116     void update() nothrow {
117         if (_text.length != 0 && _redraw) {
118             assert(_font, "No font given");
119 
120             _redraw = false;
121 
122             Surface srfc = _font.render(_text, this.foreground, this.background, this.mode);
123             _texture.loadFrom(srfc);
124 
125             // Update Vertices
126             immutable uint tw = _texture.width;
127             immutable uint th = _texture.height;
128 
129             // #2
130             _vertices[1].position.x = tw;
131             // #3
132             _vertices[2].position.y = th;
133             // #4
134             _vertices[3].position.x = tw;
135             _vertices[3].position.y = th;
136         }
137     }
138 
139     /**
140      * Returns the width
141      */
142     @property
143     @nogc
144     uint width() const pure nothrow {
145         return _texture.width;
146     }
147     
148     /**
149      * Returns the height
150      */
151     @property
152     @nogc
153     uint height() const pure nothrow {
154         return _texture.height;
155     }
156 
157     /**
158      * Returns the used Texture which contains the last rendered Text
159      */
160     @nogc
161     inout(Texture*) getTexture() inout pure nothrow {
162         return &_texture;
163     }
164 
165     /**
166      * Format a given string by using std.string.format.
167      * Therefore the formating pattern is identical.
168      */
169     void format(Args...)(string text, Args args) pure {
170         import std..string : format;
171 
172         immutable string formated = text.length != 0 ? format(text, args) : null;
173         if (formated != _text) {
174             _text = formated;
175             _redraw = true;
176         }
177     }
178 
179     /**
180      * Set or reset the current text by using std.conv.to!(string) if the data is not a string
181      */
182     void setData(T)(T data) pure nothrow {
183         static if (is(T == string))
184             immutable string text = data;
185         else static if (is(T == typeof(null)))
186             immutable string text = null;
187         else {
188             import std.conv : to;
189 
190             immutable string text = to!string(data);
191         }
192 
193         if (text != _text) {
194             _text = text;
195             _redraw = true;
196         }
197     }
198 
199     /**
200      * Concatenate the current string with another.
201      *
202      * Examples:
203      * ---
204      * Font fnt = new Font("samples/font/arial.ttf", 12);
205      * Text t = new Text(font);
206      * t.setData("My new string");
207      * t ~= "is great!"; // t draws now 'My new string is great' on screen.
208      * ---
209      * The example above is the same as if you do:
210      * ---
211      * t += "is great!";
212      * ---
213      * Both operators (~ and +) are allowed.
214      */
215     Text opBinary(string op)(string text) pure nothrow
216         if (op == "~" || op == "+")
217     {
218         _text ~= text;
219         _redraw = true;
220         
221         return this;
222     }
223 
224     /**
225      * Returns the current text
226      */
227     @nogc
228     string getText() const pure nothrow {
229         return _text;
230     }
231 
232     /**
233      * Set or reset the current Font
234      */
235     @nogc
236     void setFont(ref Font fnt) pure nothrow {
237         _font = &fnt;
238         _redraw = true;
239     }
240 
241     /**
242      * Returns a pointer to the current Font
243      */
244     @nogc
245     inout(Font*) getFont() inout pure nothrow {
246         return _font;
247     }
248 }