diff options
Diffstat (limited to 'cocoa/ScintillaCocoa.mm')
-rw-r--r-- | cocoa/ScintillaCocoa.mm | 259 |
1 files changed, 258 insertions, 1 deletions
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index 68590798b..7dc168012 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -15,6 +15,9 @@ */ #import <Cocoa/Cocoa.h> +#import <QuartzCore/CAGradientLayer.h> +#import <QuartzCore/CAAnimation.h> +#import <QuartzCore/CATransaction.h> #import <Carbon/Carbon.h> // Temporary @@ -134,6 +137,164 @@ static const KeyToCommand macMapDefault[] = //-------------------------------------------------------------------------------------------------- +/** + * Class to display the animated gold roundrect used on OS X for matches. + */ +@interface FindHighlightLayer : CAGradientLayer +{ +@private + NSString *sFind; + int positionFind; + BOOL retaining; + CGFloat widthText; + CGFloat heightLine; + NSString *sFont; + CGFloat fontSize; +} + +@property (copy) NSString *sFind; +@property (assign) int positionFind; +@property (assign) BOOL retaining; +@property (assign) CGFloat widthText; +@property (assign) CGFloat heightLine; +@property (copy) NSString *sFont; +@property (assign) CGFloat fontSize; + +- (void) animateMatch: (CGPoint)ptText bounce:(BOOL)bounce; +- (void) hideMatch; + +@end + +//-------------------------------------------------------------------------------------------------- + +@implementation FindHighlightLayer + +@synthesize sFind, positionFind, retaining, widthText, heightLine, sFont, fontSize; + +-(id) init { + if (self = [super init]) { + [self setNeedsDisplayOnBoundsChange: YES]; + // A gold to slightly redder gradient to match other applications + CGColorRef colGold = CGColorCreateGenericRGB(1.0, 1.0, 0, 1.0); + CGColorRef colGoldRed = CGColorCreateGenericRGB(1.0, 0.8, 0, 1.0); + self.colors = [NSArray arrayWithObjects:(id)colGoldRed, (id)colGold, nil]; + CGColorRelease(colGoldRed); + CGColorRelease(colGold); + + CGColorRef colGreyBorder = CGColorCreateGenericGray(0.756f, 0.5f); + self.borderColor = colGreyBorder; + CGColorRelease(colGreyBorder); + + self.borderWidth = 1.0; + self.cornerRadius = 5.0f; + self.shadowRadius = 1.0f; + self.shadowOpacity = 0.9f; + self.shadowOffset = CGSizeMake(0.0f, -2.0f); + self.anchorPoint = CGPointMake(0.5, 0.5); + } + return self; + +} + +const CGFloat paddingHighlightX = 4; +const CGFloat paddingHighlightY = 2; + +-(void) drawInContext:(CGContextRef)context { + if (!sFind || !sFont) + return; + + CFStringRef str = CFStringRef(sFind); + + CFMutableDictionaryRef styleDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CGColorRef color = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0); + CFDictionarySetValue(styleDict, kCTForegroundColorAttributeName, color); + CTFontRef fontRef = ::CTFontCreateWithName((CFStringRef)sFont, fontSize, NULL); + CFDictionaryAddValue(styleDict, kCTFontAttributeName, fontRef); + + CFAttributedStringRef attrString = ::CFAttributedStringCreate(NULL, str, styleDict); + CTLineRef textLine = ::CTLineCreateWithAttributedString(attrString); + // Indent from corner of bounds + CGContextSetTextPosition(context, paddingHighlightX, 3 + paddingHighlightY); + CTLineDraw(textLine, context); + + CFRelease(textLine); + CFRelease(attrString); + CFRelease(fontRef); + CGColorRelease(color); + CFRelease(styleDict); +} + +- (void) animateMatch: (CGPoint)ptText bounce:(BOOL)bounce { + if (!self.sFind || ![self.sFind length]) + return; + + CGFloat width = self.widthText + paddingHighlightX * 2; + CGFloat height = self.heightLine + paddingHighlightY * 2; + + // Adjust for padding + ptText.x -= paddingHighlightX; + ptText.y += paddingHighlightY; + + // Shift point to centre as expanding about centre + ptText.x += width / 2.0; + ptText.y -= height / 2.0; + + [CATransaction begin]; + [CATransaction setValue:[NSNumber numberWithFloat:0.0] forKey:kCATransactionAnimationDuration]; + self.bounds = CGRectMake(0,0, width, height); + self.position = ptText; + if (bounce) { + // Do not reset visibility when just moving + self.hidden = NO; + self.opacity = 1.0; + } + [self setNeedsDisplay]; + [CATransaction commit]; + + if (bounce) { + CABasicAnimation *animBounce = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + animBounce.duration = 0.15; + animBounce.autoreverses = YES; + animBounce.removedOnCompletion = NO; + animBounce.fromValue = [NSNumber numberWithFloat: 1.0]; + animBounce.toValue = [NSNumber numberWithFloat: 1.25]; + + if (self.retaining) { + + [self addAnimation: animBounce forKey:@"animateFound"]; + + } else { + + CABasicAnimation *animFade = [CABasicAnimation animationWithKeyPath:@"opacity"]; + animFade.duration = 0.1; + animFade.beginTime = 0.4; + animFade.removedOnCompletion = NO; + animFade.fromValue = [NSNumber numberWithFloat: 1.0]; + animFade.toValue = [NSNumber numberWithFloat: 0.0]; + + CAAnimationGroup *group = [CAAnimationGroup animation]; + [group setDuration:0.5]; + group.removedOnCompletion = NO; + group.fillMode = kCAFillModeForwards; + [group setAnimations:[NSArray arrayWithObjects:animBounce, animFade, nil]]; + + [self addAnimation:group forKey:@"animateFound"]; + } + } +} + +- (void) hideMatch { + self.sFind = @""; + self.positionFind = INVALID_POSITION; + self.hidden = YES; +} + +@end + +//-------------------------------------------------------------------------------------------------- + @implementation TimerTarget - (id) init: (void*) target @@ -210,6 +371,7 @@ ScintillaCocoa::ScintillaCocoa(NSView* view) { wMain= [view retain]; timerTarget = [[[TimerTarget alloc] init: this] retain]; + layerFindIndicator = NULL; Initialise(); } @@ -432,6 +594,16 @@ std::string ScintillaCocoa::CaseMapString(const std::string &s, int caseMapping) //-------------------------------------------------------------------------------------------------- /** + * Cancel all modes, both for base class and any find indicator. + */ +void ScintillaCocoa::CancelModes() { + ScintillaBase::CancelModes(); + HideFindIndicator(); +} + +//-------------------------------------------------------------------------------------------------- + +/** * Helper function to get the outer container which represents the Scintilla editor on application side. */ ScintillaView* ScintillaCocoa::TopContainer() @@ -538,6 +710,18 @@ sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPar bufferedDraw = false; break; + case SCI_FINDINDICATORSHOW: + ShowFindIndicatorForRange(NSMakeRange(wParam, lParam-wParam), YES); + return 0; + + case SCI_FINDINDICATORFLASH: + ShowFindIndicatorForRange(NSMakeRange(wParam, lParam-wParam), NO); + return 0; + + case SCI_FINDINDICATORHIDE: + HideFindIndicator(); + return 0; + case WM_UNICHAR: // Special case not used normally. Characters passed in this way will be inserted // regardless of their value or modifier states. That means no command interpretation is @@ -550,7 +734,7 @@ sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPar return 1; } return 0; - + default: sptr_t r = ScintillaBase::WndProc(iMessage, wParam, lParam); @@ -1280,6 +1464,11 @@ void ScintillaCocoa::ScrollText(int linesToMove) { // Move those pixels NSView *content = ContentView(); + if ([content layer]) { + [content setNeedsDisplay: YES]; + MoveFindIndicatorWithBounce(NO); + return; + } [content lockFocus]; int diff = vs.lineHeight * linesToMove; @@ -1341,6 +1530,7 @@ void ScintillaCocoa::SetHorizontalScrollPos() // does *not* belong to the scroll range. float relativePosition = (float) xOffset / (scrollWidth - textRect.Width()); [topContainer setHorizontalScrollPosition: relativePosition]; + MoveFindIndicatorWithBounce(NO); } //-------------------------------------------------------------------------------------------------- @@ -1375,6 +1565,8 @@ bool ScintillaCocoa::ModifyScrollBars(int nMax, int nPage) else pageSize = scrollRange; bool horizontalChange = [topContainer setHorizontalScrollRange: scrollRange page: pageSize]; + + MoveFindIndicatorWithBounce(NO); return verticalChange || horizontalChange; } @@ -1842,3 +2034,68 @@ void ScintillaCocoa::ActiveStateChanged(bool isActive) //-------------------------------------------------------------------------------------------------- +void ScintillaCocoa::ShowFindIndicatorForRange(NSRange charRange, BOOL retaining) +{ + NSView *content = ContentView(); + if (!layerFindIndicator) + { + layerFindIndicator = [[FindHighlightLayer alloc] init]; + [content setWantsLayer: YES]; + [[content layer] addSublayer:layerFindIndicator]; + } + [layerFindIndicator removeAnimationForKey:@"animateFound"]; + + if (charRange.length) + { + CFStringEncoding encoding = EncodingFromCharacterSet(IsUnicodeMode(), + vs.styles[STYLE_DEFAULT].characterSet); + std::vector<char> buffer(charRange.length); + pdoc->GetCharRange(&buffer[0], charRange.location, charRange.length); + + CFStringRef cfsFind = CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast<const UInt8 *>(&buffer[0]), + charRange.length, encoding, false); + layerFindIndicator.sFind = (NSString *)cfsFind; + CFRelease(cfsFind); + layerFindIndicator.retaining = retaining; + layerFindIndicator.positionFind = charRange.location; + int style = WndProc(SCI_GETSTYLEAT, charRange.location, 0); + std::vector<char> bufferFontName(WndProc(SCI_STYLEGETFONT, style, 0) + 1); + WndProc(SCI_STYLEGETFONT, style, (sptr_t)&bufferFontName[0]); + layerFindIndicator.sFont = [NSString stringWithUTF8String: &bufferFontName[0]]; + + layerFindIndicator.fontSize = WndProc(SCI_STYLEGETSIZEFRACTIONAL, style, 0) / + (float)SC_FONT_SIZE_MULTIPLIER; + layerFindIndicator.widthText = WndProc(SCI_POINTXFROMPOSITION, 0, charRange.location + charRange.length) - + WndProc(SCI_POINTXFROMPOSITION, 0, charRange.location); + layerFindIndicator.heightLine = WndProc(SCI_TEXTHEIGHT, 0, 0); + MoveFindIndicatorWithBounce(YES); + } + else + { + [layerFindIndicator hideMatch]; + } +} + +void ScintillaCocoa::MoveFindIndicatorWithBounce(BOOL bounce) +{ + if (layerFindIndicator) + { + NSView *content = ContentView(); + NSRect rcBounds = [content bounds]; + CGPoint ptText; + ptText.x = WndProc(SCI_POINTXFROMPOSITION, 0, layerFindIndicator.positionFind); + ptText.y = rcBounds.size.height - WndProc(SCI_POINTYFROMPOSITION, 0, layerFindIndicator.positionFind); + [layerFindIndicator animateMatch:ptText bounce:bounce]; + } +} + +void ScintillaCocoa::HideFindIndicator() +{ + if (layerFindIndicator) + { + [layerFindIndicator hideMatch]; + } +} + + |