diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 4fc684d7a..1674bf79c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -4,7 +4,6 @@ #import @implementation NSBezierPath (BezierPathQuartzUtilities) -// This method works only in OS X v10.2 and later. - (CGPathRef)quartzPath { if (@available(macOS 14.0, *)) { return self.CGPath; @@ -96,11 +95,52 @@ - (void)formatMarkDown { [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:1].length; }]; - if (offset != 0) { // no match. text remain unchanged. + if (offset != 0) { // repeat until no more nested markdown [self formatMarkDown]; } } +- (void)annotateRubyInVerticalLayout:(BOOL)vertical { + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"(\\uFFF9)\\s*(\\w+?)\\s*((\\uFFFA)(.+?)(\\uFFFB))" + options:0 error:nil]; + NSInteger __block offset = 0; + [regex enumerateMatchesInString:self.string options:0 range:NSMakeRange(0, self.length) + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + result = [result resultByAdjustingRangesWithOffset:offset]; + if (@available(macOS 12.0, *)) { + NSString *rubyString = [self.string substringWithRange:[result rangeAtIndex:5]]; + NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:[result rangeAtIndex:2].location effectiveRange:NULL]; + NSFont *rubyFont = [self attribute:NSFontAttributeName atIndex:[result rangeAtIndex:5].location effectiveRange:NULL]; + rubyFont = [[NSFontManager sharedFontManager] convertFont:rubyFont toSize:[rubyFont pointSize] / 2]; + CGFloat baselineOffset = vertical ? baseFont.verticalFont.ascender - rubyFont.verticalFont.descender : rubyFont.ascender + baseFont.descender; + NSColor *rubyColor = [self attribute:NSForegroundColorAttributeName atIndex:[result rangeAtIndex:5].location effectiveRange:NULL]; + NSDictionary *rubyAttrs = @{NSFontAttributeName: rubyFont, NSForegroundColorAttributeName: rubyColor, CFBridgingRelease(kCTBaselineOffsetAttributeName): @(baselineOffset)}; + NSArray *rubySubStrings = [rubyString componentsSeparatedByString:@" "]; + NSRange baseRange = [result rangeAtIndex:2]; + if (rubySubStrings.count > baseRange.length) { + rubySubStrings = @[rubyString]; + } + for (NSUInteger i = 0; i < rubySubStrings.count; ++i) { + CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, kCTRubyOverhangNone, kCTRubyPositionBefore, (__bridge CFStringRef)rubySubStrings[i], (__bridge CFDictionaryRef)rubyAttrs); + [self addAttribute:CFBridgingRelease(kCTRubyAnnotationAttributeName) value:CFBridgingRelease(rubyAnnotation) range:NSMakeRange(baseRange.location + i, 1)]; + } + [self deleteCharactersInRange:[result rangeAtIndex:3]]; + [self deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:3].length + [result rangeAtIndex:1].length; + } else { + NSFont *rubyFont = [self attribute:NSFontAttributeName atIndex:[result rangeAtIndex:5].location effectiveRange:NULL]; + rubyFont = [[NSFontManager sharedFontManager] convertFont:rubyFont toSize:[rubyFont pointSize] / 2]; + [self superscriptRange:[result rangeAtIndex:5]]; + [self addAttribute:NSFontAttributeName value:rubyFont range:[result rangeAtIndex:5]]; + [self addAttribute:NSBaselineOffsetAttributeName value:@(rubyFont.ascender) range:[result rangeAtIndex:5]]; + [self deleteCharactersInRange:[result rangeAtIndex:6]]; + [self deleteCharactersInRange:[result rangeAtIndex:4]]; + [self deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:4].length + [result rangeAtIndex:1].length; + } + }]; +} + @end static const CGFloat kOffsetHeight = 5; @@ -404,23 +444,23 @@ - (void) setAttrs:(NSDictionary *)attrs } NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; - symbolAttrsBackFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀"]; - _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; - NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; - symbolAttrsBackStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁"]; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; - NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; - symbolAttrsForwardFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶"]; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" attributes:symbolAttrsForwardFill]; - NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; - symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷"]; + if (@available(macOS 10.13, *)) { + symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE92 forFont:symbolFont baseString:@"◀"]; + symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE95 forFont:symbolFont baseString:@"◁"]; + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE93 forFont:symbolFont baseString:@"▶"]; + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE94 forFont:symbolFont baseString:@"▷"]; + } else { + symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀"]; + symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁"]; + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶"]; + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷"]; + } + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" attributes:symbolAttrsForwardFill]; _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" attributes:symbolAttrsForwardStroke]; } } @@ -2041,6 +2081,7 @@ - (void)showPreedit:(NSString *)preedit } [item formatMarkDown]; + [item annotateRubyInVerticalLayout:theme.vertical]; if (!theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.headIndent = labelWidth; @@ -2152,8 +2193,10 @@ - (void)showPreedit:(NSString *)preedit } if (numCandidates > 0) { NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); + NSFont *refFont = getHighestFont(@[theme.attrs[NSFontAttributeName], theme.labelAttrs[NSFontAttributeName], + theme.commentAttrs[NSFontAttributeName]], theme.vertical); [self setLayoutForRange:candidateBlockRange - withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) + withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; if (!theme.linear && pagingRange.length > 0) { [self setLayoutForRange:pagingRange @@ -2302,6 +2345,19 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return lineHeight; } +static NSFont * getHighestFont(NSArray*fonts, BOOL vertical) { + NSFont *highestFont; + CGFloat maxHeight = 0.0; + for (NSFont *font in fonts) { + CGFloat fontHeight = getLineHeight(font, vertical); + if (fontHeight > maxHeight) { + highestFont = font; + maxHeight = fontHeight; + } + } + return highestFont; +} + static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, SquirrelConfig *config, NSString *prefix) { NSString *candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) {