36
36
// TODO: What does DRAW_TRANSP flag do? If the background isn't drawn when
37
37
// this flag is set, then sometimes the character after the cursor becomes
38
38
// 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
44
44
#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
47
51
48
52
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
49
53
typedef NSString * NSAttributedStringKey ;
@@ -431,6 +435,8 @@ - (void)setFont:(NSFont *)newFont
431
435
font = [newFont retain ];
432
436
}
433
437
fontDescent = ceil (CTFontGetDescent ((CTFontRef)font));
438
+ fontAscent = CTFontGetAscent ((CTFontRef)font);
439
+ fontXHeight = CTFontGetXHeight ((CTFontRef)font);
434
440
435
441
// NOTE! Even though NSFontFixedAdvanceAttribute is a float, it will
436
442
// only render at integer sizes. Hence, we restrict the cell width to
@@ -884,6 +890,9 @@ - (void)drawRect:(NSRect)rect
884
890
CGContextSetBlendMode (ctx, kCGBlendModeCopy );
885
891
[lineString deleteCharactersInRange: NSMakeRange (0 , lineString.length)];
886
892
};
893
+
894
+ BOOL hasStrikeThrough = NO ;
895
+
887
896
for (size_t c = 0 ; c < grid.cols ; c++) {
888
897
GridCell cell = *grid_cell (&grid, r, c);
889
898
CGRect cellRect = {{rowRect.origin .x + cellSize.width * c, rowRect.origin .y }, cellSize};
@@ -945,19 +954,90 @@ - (void)drawRect:(NSRect)rect
945
954
}
946
955
}
947
956
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;
955
962
CGContextMoveToPoint (ctx, x, y);
956
963
CGContextAddCurveToPoint (ctx, x+0.25 *w, y, x+0.25 *w, y+h, x+0.5 *w, y+h);
957
964
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);
958
981
CGContextSetRGBStrokeColor (ctx, RED (cell.sp ), GREEN (cell.sp ), BLUE (cell.sp ), ALPHA (cell.sp ));
982
+ CGContextSetLineDash (ctx, 0 , dashLengths, 2 );
959
983
CGContextStrokePath (ctx);
960
984
}
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
+ }
961
1041
962
1042
// Draw the actual text
963
1043
if (cell.string ) {
@@ -981,6 +1061,31 @@ - (void)drawRect:(NSRect)rect
981
1061
}
982
1062
flushLineString ();
983
1063
[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
+
984
1089
CGContextRestoreGState (ctx);
985
1090
}
986
1091
if (thinStrokes) {
0 commit comments