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