diff --git a/common/attrlist.c b/common/attrlist.c index 1ca52537..746b1e76 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -302,6 +302,12 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; + case uiAttributeUnderlineColor: + if (attr->spec.Value != spec->Value) + return 0; + if (attr->spec.Value != uiDrawUnderlineColorCustom) + return 1; + // otherwise fall through case uiAttributeColor: case uiAttributeBackground: // TODO use a closest match? diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 051bec16..0a5dd8ac 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,59 @@ // 12 february 2017 #import "uipriv_darwin.h" +// this is what AppKit does internally +// WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm +static NSColor *spellingColor = nil; +static NSColor *grammarColor = nil; +static NSColor *auxiliaryColor = nil; + +static NSColor *tryColorNamed(NSString *name) +{ + NSImage *img; + + img = [NSImage imageNamed:name]; + if (img == nil) + return nil; + return [NSColor colorWithPatternImage:img]; +} + +void initUnderlineColors(void) +{ + spellingColor = tryColorNamed(@"NSSpellingDot"); + if (spellingColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + spellingColor = tryColorNamed(@"SpellingDot"); + if (spellingColor == nil) + spellingColor = [NSColor redColor]; + } + [spellingColor retain]; // override autoreleasing + + grammarColor = tryColorNamed(@"NSGrammarDot"); + if (grammarColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + grammarColor = tryColorNamed(@"GrammarDot"); + if (grammarColor == nil) + grammarColor = [NSColor greenColor]; + } + [grammarColor retain]; // override autoreleasing + + auxiliaryColor = tryColorNamed(@"NSCorrectionDot"); + if (auxiliaryColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + auxiliaryColor = tryColorNamed(@"CorrectionDot"); + if (auxiliaryColor == nil) + auxiliaryColor = [NSColor blueColor]; + } + [auxiliaryColor retain]; // override autoreleasing +} + +void uninitUnderlineColors(void) +{ + [auxiliaryColor release]; + [grammarColor release]; + [spellingColor release]; +} + // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute // TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? @@ -64,15 +117,34 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou }); } +static CGColorRef mkcolor(uiAttributeSpec *spec) +{ + CGColorSpaceRef colorspace; + CGColorRef color; + CGFloat components[4]; + + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + components[0] = spec->R; + components[1] = spec->G; + components[2] = spec->B; + components[3] = spec->A; + color = CGColorCreate(colorspace, components); + CFRelease(colorspace); + return color; +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; - CGColorSpaceRef colorspace; CGColorRef color; - CGFloat components[4]; size_t ostart, oend; backgroundBlock block; + int32_t us; + CFNumberRef num; ostart = start; oend = end; @@ -112,16 +184,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t }); break; case uiAttributeColor: - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - if (colorspace == NULL) { - // TODO - } - components[0] = spec->R; - components[1] = spec->G; - components[2] = spec->B; - components[3] = spec->A; - color = CGColorCreate(colorspace, components); - CFRelease(colorspace); + color = mkcolor(spec); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; @@ -137,6 +200,45 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t else CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + us = kCTUnderlineStyleNone; + break; + case uiDrawUnderlineStyleSingle: + us = kCTUnderlineStyleSingle; + break; + case uiDrawUnderlineStyleDouble: + us = kCTUnderlineStyleDouble; + break; + case uiDrawUnderlineStyleSuggestion: + // TODO incorrect if a solid color + us = kCTUnderlineStyleThick; + break; + } + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &us); + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineStyleAttributeName, num); + CFRelease(num); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + color = mkcolor(spec); + break; + case uiDrawUnderlineColorSpelling: + color = [spellingColor CGColor]; + break; + case uiDrawUnderlineColorGrammar: + color = [grammarColor CGColor]; + break; + case uiDrawUnderlineColorAuxiliary: + color = [auxiliaryColor CGColor]; + break; + } + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineColorAttributeName, color); + if (spec->Value == uiDrawUnderlineColorCustom) + CFRelease(color); + break; // TODO } return 0; diff --git a/darwin/main.m b/darwin/main.m index 59a8683b..440343bc 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -125,6 +125,8 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; setupFontPanel(); + + initUnderlineColors(); } globalPool = [[NSAutoreleasePool alloc] init]; @@ -140,6 +142,7 @@ void uiUninit(void) [globalPool release]; @autoreleasepool { + uninitUnderlineColors(); [delegate release]; [realNSApp() setDelegate:nil]; [app release]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bf009522..32ae8688 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,5 +145,7 @@ extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m +extern void initUnderlineColors(void); +extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 1aa74a72..6e54dfcb 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -98,8 +98,45 @@ static void setupAttributedString(void) spec.Type = uiAttributeVerticalForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSingle; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " "); + next = "underlines"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleDouble; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorCustom; + spec.R = 0.5; + spec.G = 0.0; + spec.B = 1.0; + spec.A = 1.0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " ("); + next = "including underlines for spelling correction and the like"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSuggestion; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorSpelling; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple TODO"; diff --git a/ui_attrstr.h b/ui_attrstr.h index 8152c8d5..c3738a42 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -29,6 +29,7 @@ _UI_ENUM(uiAttribute) { // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? uiAttributeUnderline, // enum uiDrawUnderlineStyle + // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below