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