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