3636// TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when
3737// this flag is set, then sometimes the character after the cursor becomes
3838// blank. Everything seems to work fine by just ignoring this flag.
39- #define DRAW_TRANSP 0x01 /* draw with transparent bg */
40- #define DRAW_BOLD 0x02 /* draw bold text */
41- #define DRAW_UNDERL 0x04 /* draw underline text */
42- #define DRAW_UNDERC 0x08 /* draw undercurl text */
43- #define DRAW_ITALIC 0x10 /* draw italic text */
39+ #define DRAW_TRANSP 0x01 // draw with transparent bg
40+ #define DRAW_BOLD 0x02 // draw bold text
41+ #define DRAW_UNDERL 0x04 // draw underline text
42+ #define DRAW_UNDERC 0x08 // draw undercurl text
43+ #define DRAW_ITALIC 0x10 // draw italic text
4444#define DRAW_CURSOR 0x20
45- #define DRAW_WIDE 0x80 /* draw wide text */
46- #define DRAW_COMP 0x100 /* drawing composing char */
45+ #define DRAW_STRIKE 0x40 // draw strikethrough text
46+ #define DRAW_UNDERDOUBLE 0x80 // draw double underline
47+ #define DRAW_UNDERDOTTED 0x100 // draw dotted underline
48+ #define DRAW_UNDERDASHED 0x200 // draw dashed underline
49+ #define DRAW_WIDE 0x1000 // (MacVim only) draw wide text
50+ #define DRAW_COMP 0x2000 // (MacVim only) drawing composing char
4751
4852#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
4953typedef NSString * NSAttributedStringKey ;
@@ -431,6 +435,8 @@ - (void)setFont:(NSFont *)newFont
431435 font = [newFont retain ];
432436 }
433437 fontDescent = ceil (CTFontGetDescent ((CTFontRef)font));
438+ fontAscent = CTFontGetAscent ((CTFontRef)font);
439+ fontXHeight = CTFontGetXHeight ((CTFontRef)font);
434440
435441 // NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
436442 // only render at integer sizes. Hence, we restrict the cell width to
@@ -884,6 +890,9 @@ - (void)drawRect:(NSRect)rect
884890 CGContextSetBlendMode (ctx, kCGBlendModeCopy );
885891 [lineString deleteCharactersInRange: NSMakeRange (0 , lineString.length)];
886892 };
893+
894+ BOOL hasStrikeThrough = NO ;
895+
887896 for (size_t c = 0 ; c < grid.cols ; c++) {
888897 GridCell cell = *grid_cell (&grid, r, c);
889898 CGRect cellRect = {{rowRect.origin .x + cellSize.width * c, rowRect.origin .y }, cellSize};
@@ -945,19 +954,90 @@ - (void)drawRect:(NSRect)rect
945954 }
946955 }
947956
948- // Text underline styles
949- if (cell.textFlags & DRAW_UNDERL) {
950- CGRect rect = CGRectMake (cellRect.origin .x , cellRect.origin .y +0.4 *fontDescent, cellRect.size .width , 1 );
951- CGContextSetFillColor (ctx, COMPONENTS (cell.sp ));
952- CGContextFillRect (ctx, rect);
953- } else if (cell.textFlags & DRAW_UNDERC) {
954- const float x = cellRect.origin .x , y = cellRect.origin .y +1 , w = cellSize.width , h = 0.5 *fontDescent;
957+ // Text underline styles. We only allow one of them to be active.
958+ // Note: We are not currently using underlineThickness or underlinePosition. Should fix to use them.
959+ const CGFloat underlineY = 0.4 *fontDescent; // Just a hard-coded value for now. Should fix to use underlinePosition.
960+ if (cell.textFlags & DRAW_UNDERC) {
961+ const CGFloat x = cellRect.origin .x , y = cellRect.origin .y +1 , w = cellSize.width , h = 0.5 *fontDescent;
955962 CGContextMoveToPoint (ctx, x, y);
956963 CGContextAddCurveToPoint (ctx, x+0.25 *w, y, x+0.25 *w, y+h, x+0.5 *w, y+h);
957964 CGContextAddCurveToPoint (ctx, x+0.75 *w, y+h, x+0.75 *w, y, x+w, y);
965+ if (cell.textFlags & DRAW_WIDE) {
966+ // Need to draw another set for double-width characters
967+ const CGFloat x2 = x + cellSize.width ;
968+ CGContextAddCurveToPoint (ctx, x2+0.25 *w, y, x2+0.25 *w, y+h, x2+0.5 *w, y+h);
969+ CGContextAddCurveToPoint (ctx, x2+0.75 *w, y+h, x2+0.75 *w, y, x2+w, y);
970+ }
971+ CGContextSetRGBStrokeColor (ctx, RED (cell.sp ), GREEN (cell.sp ), BLUE (cell.sp ), ALPHA (cell.sp ));
972+ CGContextStrokePath (ctx);
973+ }
974+ else if (cell.textFlags & DRAW_UNDERDASHED) {
975+ const CGFloat dashLengths[] = {cellSize.width / 4 , cellSize.width / 4 };
976+
977+ const CGFloat x = cellRect.origin .x ;
978+ const CGFloat y = cellRect.origin .y +underlineY;
979+ CGContextMoveToPoint (ctx, x, y);
980+ CGContextAddLineToPoint (ctx, x + cellRect.size .width , y);
958981 CGContextSetRGBStrokeColor (ctx, RED (cell.sp ), GREEN (cell.sp ), BLUE (cell.sp ), ALPHA (cell.sp ));
982+ CGContextSetLineDash (ctx, 0 , dashLengths, 2 );
959983 CGContextStrokePath (ctx);
960984 }
985+ else if (cell.textFlags & DRAW_UNDERDOTTED) {
986+ // Calculate dot size to use. Normally, just do 1-pixel dots/gaps, since the line is one pixel thick.
987+ CGFloat dotSize = 1 , gapSize = 1 ;
988+ if (fmod (cellSize.width , 2 ) != 0 ) {
989+ // Width is not even number, so spacing them would look weird. Find another way.
990+ if (fmod (cellSize.width , 3 ) == 0 ) {
991+ // Width is divisible by 3, so just make the gap twice as long so they can be spaced out.
992+ dotSize = 1 ;
993+ gapSize = 2 ;
994+ }
995+ else {
996+ // Not disible by 2 or 3. Just Re-calculate dot size so be slightly larger than 1 so we can exactly
997+ // equal number of dots and gaps. This does mean we have a non-integer size, so we are relying
998+ // on anti-aliasing here to help this not look too bad, but it will still look slightly blurry.
999+ dotSize = cellSize.width / (ceil (cellSize.width / 2 ) * 2 );
1000+ gapSize = dotSize;
1001+ }
1002+ }
1003+ const CGFloat dashLengths[] = {dotSize, gapSize};
1004+
1005+ const CGFloat x = cellRect.origin .x ;
1006+ const CGFloat y = cellRect.origin .y +underlineY;
1007+ CGContextMoveToPoint (ctx, x, y);
1008+ CGContextAddLineToPoint (ctx, x + cellRect.size .width , y);
1009+ CGContextSetRGBStrokeColor (ctx, RED (cell.sp ), GREEN (cell.sp ), BLUE (cell.sp ), ALPHA (cell.sp ));
1010+ CGContextSetLineDash (ctx, 0 , dashLengths, 2 );
1011+ CGContextStrokePath (ctx);
1012+ }
1013+ else if (cell.textFlags & DRAW_UNDERDOUBLE) {
1014+ CGRect rect = CGRectMake (cellRect.origin .x , cellRect.origin .y +underlineY, cellRect.size .width , 1 );
1015+ CGContextSetFillColor (ctx, COMPONENTS (cell.sp ));
1016+ CGContextFillRect (ctx, rect);
1017+
1018+ // Draw second underline
1019+ if (underlineY - 3 < 0 ) {
1020+ // Not enough fontDescent to draw another line below, just draw above. This is not the desired
1021+ // solution but works.
1022+ rect = CGRectMake (cellRect.origin .x , cellRect.origin .y +underlineY + 3 , cellRect.size .width , 1 );
1023+ } else {
1024+ // Nominal situation. Just a second one below first one.
1025+ rect = CGRectMake (cellRect.origin .x , cellRect.origin .y +underlineY - 3 , cellRect.size .width , 1 );
1026+ }
1027+ CGContextSetFillColor (ctx, COMPONENTS (cell.sp ));
1028+ CGContextFillRect (ctx, rect);
1029+ } else if (cell.textFlags & DRAW_UNDERL) {
1030+ CGRect rect = CGRectMake (cellRect.origin .x , cellRect.origin .y +underlineY, cellRect.size .width , 1 );
1031+ CGContextSetFillColor (ctx, COMPONENTS (cell.sp ));
1032+ CGContextFillRect (ctx, rect);
1033+ }
1034+
1035+ // Text strikethrough
1036+ // We delay the rendering of strikethrough and only do it as a second-pass since we want to draw them on top
1037+ // of text, and text rendering is currently delayed via flushLineString().
1038+ if (cell.textFlags & DRAW_STRIKE) {
1039+ hasStrikeThrough = YES ;
1040+ }
9611041
9621042 // Draw the actual text
9631043 if (cell.string ) {
@@ -981,6 +1061,31 @@ - (void)drawRect:(NSRect)rect
9811061 }
9821062 flushLineString ();
9831063 [lineString release ];
1064+
1065+ if (hasStrikeThrough) {
1066+ // Second pass to render strikethrough. Unfortunately have to duplicate a little bit of code here to loop
1067+ // through the cells.
1068+ for (size_t c = 0 ; c < grid.cols ; c++) {
1069+ GridCell cell = *grid_cell (&grid, r, c);
1070+ CGRect cellRect = {{rowRect.origin .x + cellSize.width * c, rowRect.origin .y }, cellSize};
1071+ if (cell.textFlags & DRAW_WIDE)
1072+ cellRect.size .width *= 2 ;
1073+ if (cell.inverted ) {
1074+ cell.bg ^= 0xFFFFFF ;
1075+ cell.fg ^= 0xFFFFFF ;
1076+ cell.sp ^= 0xFFFFFF ;
1077+ }
1078+
1079+ // Text strikethrough
1080+ if (cell.textFlags & DRAW_STRIKE) {
1081+ CGRect rect = CGRectMake (cellRect.origin .x , cellRect.origin .y + fontDescent + fontXHeight / 2 , cellRect.size .width , 1 );
1082+ CGContextSetFillColor (ctx, COMPONENTS (cell.sp ));
1083+ CGContextFillRect (ctx, rect);
1084+ }
1085+
1086+ }
1087+ }
1088+
9841089 CGContextRestoreGState (ctx);
9851090 }
9861091 if (thinStrokes) {
0 commit comments