Skip to content

Commit fa37c18

Browse files
authored
Merge pull request #1287 from ychin/support-strikethrough
Support strikethrough/underdouble/underdashed/underdotted styles
2 parents b3cd8a5 + 882944f commit fa37c18

File tree

4 files changed

+142
-18
lines changed

4 files changed

+142
-18
lines changed

src/MacVim/MMCoreTextView.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
// From NSTextView
2828
NSSize insetSize;
2929

30-
float fontDescent;
30+
CGFloat fontDescent;
31+
CGFloat fontAscent;
32+
CGFloat fontXHeight;
3133
BOOL antialias;
3234
BOOL ligatures;
3335
BOOL thinStrokes;

src/MacVim/MMCoreTextView.m

Lines changed: 119 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,18 @@
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
4953
typedef 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) {

src/gui.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2527,7 +2527,14 @@ gui_outstr_nowrap(
25272527
if (hl_mask_todo & HL_UNDERCURL)
25282528
draw_flags |= DRAW_UNDERC;
25292529

2530-
// TODO: HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED
2530+
// MacVim note: underdouble/underdotted/underdashed are not implemented in Vim yet.
2531+
// These are MacVim-only for now.
2532+
if (hl_mask_todo & HL_UNDERDOUBLE)
2533+
draw_flags |= DRAW_UNDERDOUBLE;
2534+
if (hl_mask_todo & HL_UNDERDOTTED)
2535+
draw_flags |= DRAW_UNDERDOTTED;
2536+
if (hl_mask_todo & HL_UNDERDASHED)
2537+
draw_flags |= DRAW_UNDERDASHED;
25312538

25322539
// Do we strikethrough the text?
25332540
if (hl_mask_todo & HL_STRIKETHROUGH)

src/gui.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,18 @@
128128
#endif
129129
#define DRAW_CURSOR 0x20 // drawing block cursor (win32)
130130
#define DRAW_STRIKE 0x40 // strikethrough
131-
#define DRAW_WIDE 0x80 // drawing wide char (MacVim)
132-
#define DRAW_COMP 0x100 // drawing composing char (MacVim)
131+
// MacVim note: underdouble/underdotted/underdashed are not implemented in Vim yet.
132+
// These are MacVim-only for now.
133+
// IMPORTANT: If resolving a merge conflict when merging from upstream, if Vim decided
134+
// to use different values for these constants, MMCoreTextView.m would need
135+
// to be updated to reflect them as well, or the renderer won't understand
136+
// these values.
137+
#define DRAW_UNDERDOUBLE 0x80 // draw double underline
138+
#define DRAW_UNDERDOTTED 0x100 // draw dotted underline
139+
#define DRAW_UNDERDASHED 0x200 // draw dashed underline
140+
141+
#define DRAW_WIDE 0x1000 // drawing wide char (MacVim)
142+
#define DRAW_COMP 0x2000 // drawing composing char (MacVim)
133143

134144
// For our own tearoff menu item
135145
#define TEAR_STRING "-->Detach"

0 commit comments

Comments
 (0)