1 module prova.entities.text;
2 
3 import prova.assets;
4 import prova.attachables;
5 import prova.core;
6 import prova.entities;
7 import prova.graphics;
8 import prova.math;
9 
10 ///
11 enum TextAlign { LEFT, CENTER, RIGHT }
12 ///
13 enum TextBaseline { TOP, MIDDLE, BOTTOM, HANGING, ALPHABETIC }
14 
15 ///
16 final class Text : Entity
17 {
18   ///
19   Color color;
20   ///
21   TextAlign textAlign;
22   ///
23   TextBaseline textBaseline;
24   private Sprite[] sprites;
25   private Glyph[] glyphs;
26   private Font font;
27   private string _text;
28 
29   ///
30   this(string text, Font font)
31   {
32     this.font = font;
33     this.text = text;
34   }
35 
36   ///
37   @property string text()
38   {
39     return _text;
40   }
41 
42   ///
43   @property void text(string text)
44   {
45     sprites.length = text.length;
46     glyphs.length = text.length;
47 
48     foreach(i; 0 .. text.length)
49     {
50       const int c = text[i];
51 
52       if(!font.hasGlyph(c))
53         continue;
54 
55       const Glyph glyph = font.getGlyph(c);
56 
57       Sprite sprite = new Sprite();
58       sprite.texture = font.texture;
59       sprite.clip = glyph.clip;
60       sprite.origin = Vector2(-glyph.clip.width / 2, glyph.clip.height / 2);
61 
62       sprites[i] = sprite;
63       glyphs[i] = glyph;
64     }
65 
66     _text = text;
67   }
68 
69   ///
70   Vector2 getSize()
71   {
72     Vector2 size = font.measureString(_text);
73     Vector3 scale = getWorldScale();
74 
75     return Vector2(size.x * scale.x, size.y * scale.y);
76   }
77 
78   ///
79   override void draw(RenderTarget renderTarget, Matrix transform)
80   {
81     Matrix glyphTransform = Matrix.identity;
82     glyphTransform = glyphTransform.translate(getOffset());
83 
84     foreach(i; 0 .. _text.length)
85     {
86       Glyph glyph = glyphs[i];
87       Sprite sprite = sprites[i];
88 
89       sprite.tint = color;
90 
91       Vector2 offset = glyph.offset;
92 
93       if(i > 0)
94         offset += font.getKerning(_text[i - 1], _text[i]);
95 
96       renderTarget.drawSprite(
97         sprite,
98         transform * glyphTransform.translate(offset)
99       );
100 
101       glyphTransform = glyphTransform.translate(glyph.advance);
102     }
103   }
104 
105   private Vector2 getOffset()
106   {
107     const Vector2 size = font.measureString(_text);
108     Vector2 offset;
109 
110     if(textAlign == TextAlign.CENTER)
111       offset.x -= size.x / 2;
112     else if(textAlign == TextAlign.RIGHT)
113       offset.x -= size.x;
114 
115     final switch(textBaseline)
116     {
117       case TextBaseline.TOP:
118         break;
119       case TextBaseline.MIDDLE:
120         offset.y += (font.ascentLine - font.descentLine) * scale.y / 2;
121         break;
122       case TextBaseline.BOTTOM:
123         offset.y += (font.size - font.descentLine) * scale.y;
124         break;
125       case TextBaseline.HANGING:
126         offset.y += (font.ascentLine - font.size) * scale.y;
127         break;
128       case TextBaseline.ALPHABETIC:
129         offset.y += font.size * scale.y;
130         break;
131     }
132 
133     return offset;
134   }
135 }