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