1 module dlangui.platforms.console.consolefont;
2 
3 
4 public import dlangui.core.config;
5 static if (BACKEND_CONSOLE):
6 
7 import dlangui.graphics.fonts;
8 import dlangui.widgets.styles;
9 
10 class ConsoleFont : Font {
11     private Glyph _glyph;
12     this() {
13         _spaceWidth = 1;
14         _glyph.blackBoxX = 1;
15         _glyph.blackBoxY = 1;
16         _glyph.width = 0;
17         _glyph.originX = 0;
18         _glyph.originY = 0;
19         _glyph.subpixelMode = SubpixelRenderingMode.None;
20         _glyph.glyph = [0];
21     }
22 
23     /// returns font size (as requested from font engine)
24     override @property int size() { return 1; }
25     /// returns actual font height including interline space
26     override @property int height() { return 1; }
27     /// returns font weight
28     override @property int weight() { return 400; }
29     /// returns baseline offset
30     override @property int baseline() { return 0; }
31     /// returns true if font is italic
32     override @property bool italic() { return false; }
33     /// returns font face name
34     override @property string face() { return "console"; }
35     /// returns font family
36     override @property FontFamily family() { return FontFamily.MonoSpace; }
37     /// returns true if font object is not yet initialized / loaded
38     override @property bool isNull() { return false; }
39 
40     /// return true if antialiasing is enabled, false if not enabled
41     override @property bool antialiased() {
42         return false;
43     }
44 
45     /// returns true if font has fixed pitch (all characters have equal width)
46     override @property bool isFixed() {
47         return true;
48     }
49 
50     /// returns true if font is fixed
51     override @property int spaceWidth() {
52         return 1;
53     }
54 
55     /// returns character width
56     override int charWidth(dchar ch) {
57         return 1;
58     }
59 
60     /*******************************************************************************************
61     * Measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars.
62     *
63     * Supports Tab character processing and processing of menu item labels like '&File'.
64     *
65     * Params:
66     *          text = text string to measure
67     *          widths = output buffer to put measured widths (widths[i] will be set to cumulative widths text[0..i])
68     *          maxWidth = maximum width to measure - measure is stopping if max width is reached (pass MAX_WIDTH_UNSPECIFIED to measure all characters)
69     *          tabSize = tabulation size, in number of spaces
70     *          tabOffset = when string is drawn not from left position, use to move tab stops left/right
71     *          textFlags = TextFlag bit set - to control underline, hotkey label processing, etc...
72     * Returns:
73     *          number of characters measured (may be less than text.length if maxWidth is reached)
74     ******************************************************************************************/
75     override int measureText(const dchar[] text, ref int[] widths, int maxWidth = MAX_WIDTH_UNSPECIFIED, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
76         if (text.length == 0)
77             return 0;
78         const dchar * pstr = text.ptr;
79         uint len = cast(uint)text.length;
80         if (widths.length < len)
81             widths.length = len + 1;
82         int x = 0;
83         int charsMeasured = 0;
84         int * pwidths = widths.ptr;
85         int tabWidth = spaceWidth * tabSize; // width of full tab in pixels
86         tabOffset = tabOffset % tabWidth;
87         if (tabOffset < 0)
88             tabOffset += tabWidth;
89         foreach(int i; 0 .. len) {
90             //auto measureStart = std.datetime.Clock.currAppTick;
91             dchar ch = pstr[i];
92             if (ch == '\t') {
93                 // measure tab
94                 int tabPosition = (x + tabWidth - tabOffset) / tabWidth * tabWidth + tabOffset;
95                 while (tabPosition < x + spaceWidth)
96                     tabPosition += tabWidth;
97                 pwidths[i] = tabPosition;
98                 charsMeasured = i + 1;
99                 x = tabPosition;
100                 continue;
101             } else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
102                 pwidths[i] = x;
103                 continue; // skip '&' in hot key when measuring
104             }
105             int w = x + 1; // using advance
106             pwidths[i] = w;
107             x += 1;
108             charsMeasured = i + 1;
109             if (x > maxWidth)
110                 break;
111         }
112         return charsMeasured;
113     }
114 
115     /*****************************************************************************************
116     * Draw text string to buffer.
117     *
118     * Params:
119     *      buf =   graphics buffer to draw text to
120     *      x =     x coordinate to draw first character at
121     *      y =     y coordinate to draw first character at
122     *      text =  text string to draw
123     *      color =  color for drawing of glyphs
124     *      tabSize = tabulation size, in number of spaces
125     *      tabOffset = when string is drawn not from left position, use to move tab stops left/right
126     *      textFlags = set of TextFlag bit fields
127     ****************************************************************************************/
128     override void drawText(DrawBuf drawBuf, int x, int y, const dchar[] text, uint color, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
129         if (text.length == 0)
130             return; // nothing to draw - empty text
131         import dlangui.platforms.console.consoleapp;
132         import dlangui.platforms.console.dconsole;
133         ConsoleDrawBuf buf = cast(ConsoleDrawBuf)drawBuf;
134         if (!buf)
135             return;
136         if (_textSizeBuffer.length < text.length)
137             _textSizeBuffer.length = text.length;
138         int charsMeasured = measureText(text, _textSizeBuffer, MAX_WIDTH_UNSPECIFIED, tabSize, tabOffset, textFlags);
139         Rect clip = buf.clipRect; //clipOrFullRect;
140         if (clip.empty)
141             return; // not visible - clipped out
142         if (y + height < clip.top || y >= clip.bottom)
143             return; // not visible - fully above or below clipping rectangle
144         int _baseline = baseline;
145         bool underline = (textFlags & TextFlag.Underline) != 0;
146         int underlineHeight = 1;
147         int underlineY = y + _baseline + underlineHeight * 2;
148         buf.console.textColor = ConsoleDrawBuf.toConsoleColor(color);
149         buf.console.backgroundColor = CONSOLE_TRANSPARENT_BACKGROUND;
150         //Log.d("drawText: (", x, ',', y, ") '", text, "', color=", buf.console.textColor);
151         foreach(int i; 0 .. charsMeasured) {
152             dchar ch = text[i];
153             if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
154                 if (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))
155                     underline = true; // turn ON underline for hot key
156                 continue; // skip '&' in hot key when measuring
157             }
158             int xx = (i > 0) ? _textSizeBuffer[i - 1] : 0;
159             if (x + xx >= clip.right)
160                 break;
161             if (x + xx < clip.left)
162                 continue; // far at left of clipping region
163 
164             if (underline) {
165                 // draw underline
166                 buf.console.underline = true;
167                 // turn off underline after hot key
168                 if (!(textFlags & TextFlag.Underline)) {
169                     underline = false;
170                     buf.console.underline = false;
171                 }
172             }
173 
174             if (ch == ' ' || ch == '\t')
175                 continue;
176             int gx = x + xx;
177             if (gx < clip.left)
178                 continue;
179             buf.console.setCursor(gx, y);
180             buf.console.writeText(cast(dstring)(text[i .. i + 1]));
181         }
182         buf.console.underline = false;
183     }
184 
185     /*****************************************************************************************
186     * Draw text string to buffer.
187     *
188     * Params:
189     *      buf =   graphics buffer to draw text to
190     *      x =     x coordinate to draw first character at
191     *      y =     y coordinate to draw first character at
192     *      text =  text string to draw
193     *      charProps =  array of character properties, charProps[i] are properties for character text[i]
194     *      tabSize = tabulation size, in number of spaces
195     *      tabOffset = when string is drawn not from left position, use to move tab stops left/right
196     *      textFlags = set of TextFlag bit fields
197     ****************************************************************************************/
198     override void drawColoredText(DrawBuf drawBuf, int x, int y, const dchar[] text, const CustomCharProps[] charProps, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
199         if (text.length == 0)
200             return; // nothing to draw - empty text
201 
202         import dlangui.platforms.console.consoleapp;
203         import dlangui.platforms.console.dconsole;
204         ConsoleDrawBuf buf = cast(ConsoleDrawBuf)drawBuf;
205 
206         if (_textSizeBuffer.length < text.length)
207             _textSizeBuffer.length = text.length;
208         int charsMeasured = measureText(text, _textSizeBuffer, MAX_WIDTH_UNSPECIFIED, tabSize, tabOffset, textFlags);
209         Rect clip = buf.clipRect; //clipOrFullRect;
210         if (clip.empty)
211             return; // not visible - clipped out
212         if (y + height < clip.top || y >= clip.bottom)
213             return; // not visible - fully above or below clipping rectangle
214         int _baseline = baseline;
215         uint customizedTextFlags = (charProps.length ? charProps[0].textFlags : 0) | textFlags;
216         bool underline = (customizedTextFlags & TextFlag.Underline) != 0;
217         int underlineHeight = 1;
218         int underlineY = y + _baseline + underlineHeight * 2;
219         buf.console.backgroundColor = CONSOLE_TRANSPARENT_BACKGROUND;
220         foreach(int i; 0 .. charsMeasured) {
221             dchar ch = text[i];
222             uint color = i < charProps.length ? charProps[i].color : charProps[$ - 1].color;
223             buf.console.textColor = ConsoleDrawBuf.toConsoleColor(color);
224             customizedTextFlags = (i < charProps.length ? charProps[i].textFlags : charProps[$ - 1].textFlags) | textFlags;
225             underline = (customizedTextFlags & TextFlag.Underline) != 0;
226             // turn off underline after hot key
227             if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
228                 // draw underline
229                 buf.console.underline = true;
230                 // turn off underline after hot key
231                 if (!(textFlags & TextFlag.Underline)) {
232                     underline = false;
233                     buf.console.underline = false;
234                 }
235                 continue; // skip '&' in hot key when measuring
236             }
237             int xx = (i > 0) ? _textSizeBuffer[i - 1] : 0;
238             if (x + xx >= clip.right)
239                 break;
240             if (x + xx < clip.left)
241                 continue; // far at left of clipping region
242 
243             if (underline) {
244                 // draw underline
245                 buf.console.underline = true;
246                 // turn off underline after hot key
247                 if (!(customizedTextFlags & TextFlag.Underline)) {
248                     underline = false;
249                     buf.console.underline = false;
250                 }
251             }
252 
253             if (ch == ' ' || ch == '\t')
254                 continue;
255 
256             int gx = x + xx;
257             if (gx < clip.left)
258                 continue;
259             buf.console.setCursor(gx, y);
260             buf.console.writeText(cast(dstring)(text[i .. i + 1]));
261         }
262     }
263 
264     /// measure multiline text with line splitting, returns width and height in pixels
265     override Point measureMultilineText(const dchar[] text, int maxLines = 0, int maxWidth = 0, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
266         SimpleTextFormatter fmt;
267         FontRef fnt = FontRef(this);
268         return fmt.format(text, fnt, maxLines, maxWidth, tabSize, tabOffset, textFlags);
269     }
270 
271     /// draws multiline text with line splitting
272     override void drawMultilineText(DrawBuf buf, int x, int y, const dchar[] text, uint color, int maxLines = 0, int maxWidth = 0, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
273         SimpleTextFormatter fmt;
274         FontRef fnt = FontRef(this);
275         fmt.format(text, fnt, maxLines, maxWidth, tabSize, tabOffset, textFlags);
276         fmt.draw(buf, x, y, fnt, color);
277     }
278 
279 
280     /// get character glyph information
281     override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
282         return &_glyph;
283     }
284 
285     /// clear usage flags for all entries
286     override void checkpoint() {
287         // ignore
288     }
289     /// removes entries not used after last call of checkpoint() or cleanup()
290     override void cleanup() {
291         // ignore
292     }
293     /// clears glyph cache
294     override void clearGlyphCache() {
295         // ignore
296     }
297 
298     override void clear() {
299     }
300 
301     ~this() {
302         clear();
303     }
304 }
305 
306 class ConsoleFontManager : FontManager {
307     this() {
308         _font = new ConsoleFont();
309     }
310 
311     private FontRef _font;
312 
313     /// get font instance best matched specified parameters
314     override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) {
315         return _font;
316     }
317 
318     /// clear usage flags for all entries -- for cleanup of unused fonts
319     override void checkpoint() {
320         // ignore
321     }
322 
323     /// removes entries not used after last call of checkpoint() or cleanup()
324     override void cleanup() {
325         // ignore
326     }
327 
328 }
329 
330