diff options
| -rw-r--r-- | cocoa/ScintillaCocoa.h | 10 | ||||
| -rw-r--r-- | cocoa/ScintillaCocoa.mm | 259 | ||||
| -rw-r--r-- | cocoa/ScintillaFramework/ScintillaFramework.xcodeproj/project.pbxproj | 4 | ||||
| -rw-r--r-- | cocoa/ScintillaTest/AppController.mm | 5 | ||||
| -rw-r--r-- | doc/ScintillaDoc.html | 25 | ||||
| -rw-r--r-- | include/Scintilla.h | 3 | ||||
| -rw-r--r-- | include/Scintilla.iface | 9 | 
7 files changed, 313 insertions, 2 deletions
| diff --git a/cocoa/ScintillaCocoa.h b/cocoa/ScintillaCocoa.h index 5ee1f149f..e4179b1bb 100644 --- a/cocoa/ScintillaCocoa.h +++ b/cocoa/ScintillaCocoa.h @@ -57,6 +57,8 @@ extern "C" NSString* ScintillaRecPboardType;  @class ScintillaView; +@class FindHighlightLayer; +  /**   * Helper class to be used as timer target (NSTimer).   */ @@ -119,6 +121,8 @@ private:    NSTimer* tickTimer;    NSTimer* idleTimer; +  FindHighlightLayer *layerFindIndicator; +  protected:    PRectangle GetClientRectangle();    Point ConvertPoint(NSPoint point); @@ -127,6 +131,7 @@ protected:    virtual void Finalise();    virtual CaseFolder *CaseFolderForEncoding();    virtual std::string CaseMapString(const std::string &s, int caseMapping); +  virtual void CancelModes();  public:    NSView* ContentView(); @@ -209,6 +214,11 @@ public:    void HandleCommand(NSInteger command);    virtual void ActiveStateChanged(bool isActive); + +  // Find indicator +  void ShowFindIndicatorForRange(NSRange charRange, BOOL retaining); +  void MoveFindIndicatorWithBounce(BOOL bounce); +  void HideFindIndicator();  }; 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]; +  } +} + + diff --git a/cocoa/ScintillaFramework/ScintillaFramework.xcodeproj/project.pbxproj b/cocoa/ScintillaFramework/ScintillaFramework.xcodeproj/project.pbxproj index a5204a91c..5c772563d 100644 --- a/cocoa/ScintillaFramework/ScintillaFramework.xcodeproj/project.pbxproj +++ b/cocoa/ScintillaFramework/ScintillaFramework.xcodeproj/project.pbxproj @@ -158,6 +158,7 @@  		114B6FEB11FA7645004FB6AB /* PropSetSimple.h in Headers */ = {isa = PBXBuildFile; fileRef = 114B6FE011FA7645004FB6AB /* PropSetSimple.h */; };  		114B6FEC11FA7645004FB6AB /* StyleContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 114B6FE111FA7645004FB6AB /* StyleContext.h */; };  		114B6FED11FA7645004FB6AB /* WordList.h in Headers */ = {isa = PBXBuildFile; fileRef = 114B6FE211FA7645004FB6AB /* WordList.h */; }; +		1152A77315313E58000D4E1A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1152A77215313E58000D4E1A /* QuartzCore.framework */; };  		117ACE9114A29A1E002876F9 /* LexTCMD.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 117ACE9014A29A1E002876F9 /* LexTCMD.cxx */; };  		119FF1BF13C9D1820007CE42 /* QuartzTextStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 119FF1BE13C9D1820007CE42 /* QuartzTextStyle.h */; };  		11A0A8A1148602DF0018D143 /* LexCoffeeScript.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 11A0A8A0148602DF0018D143 /* LexCoffeeScript.cxx */; }; @@ -341,6 +342,7 @@  		114B6FE011FA7645004FB6AB /* PropSetSimple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PropSetSimple.h; path = ../../lexlib/PropSetSimple.h; sourceTree = SOURCE_ROOT; };  		114B6FE111FA7645004FB6AB /* StyleContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleContext.h; path = ../../lexlib/StyleContext.h; sourceTree = SOURCE_ROOT; };  		114B6FE211FA7645004FB6AB /* WordList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WordList.h; path = ../../lexlib/WordList.h; sourceTree = SOURCE_ROOT; }; +		1152A77215313E58000D4E1A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = ../../../../../../../../System/Library/Frameworks/QuartzCore.framework; sourceTree = "<group>"; };  		117ACE9014A29A1E002876F9 /* LexTCMD.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexTCMD.cxx; path = ../../lexers/LexTCMD.cxx; sourceTree = "<group>"; };  		119FF1BE13C9D1820007CE42 /* QuartzTextStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QuartzTextStyle.h; path = ../QuartzTextStyle.h; sourceTree = "<group>"; };  		11A0A8A0148602DF0018D143 /* LexCoffeeScript.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LexCoffeeScript.cxx; path = ../../lexers/LexCoffeeScript.cxx; sourceTree = "<group>"; }; @@ -376,6 +378,7 @@  			buildActionMask = 2147483647;  			files = (  				8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, +				1152A77315313E58000D4E1A /* QuartzCore.framework in Frameworks */,  			);  			runOnlyForDeploymentPostprocessing = 0;  		}; @@ -443,6 +446,7 @@  		1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {  			isa = PBXGroup;  			children = ( +				1152A77215313E58000D4E1A /* QuartzCore.framework */,  				0867D6A5FE840307C02AAC07 /* AppKit.framework */,  				D2F7E79907B2D74100F64583 /* CoreData.framework */,  				0867D69BFE84028FC02AAC07 /* Foundation.framework */, diff --git a/cocoa/ScintillaTest/AppController.mm b/cocoa/ScintillaTest/AppController.mm index fc8c331c4..c28974109 100644 --- a/cocoa/ScintillaTest/AppController.mm +++ b/cocoa/ScintillaTest/AppController.mm @@ -262,6 +262,11 @@ static const char * box_xpm[] = {                        wholeWord: NO                         scrollTo: YES                             wrap: YES]; + +  long matchStart = [mEditor getGeneralProperty: SCI_GETSELECTIONSTART parameter: 0]; +  long matchEnd = [mEditor getGeneralProperty: SCI_GETSELECTIONEND parameter: 0]; +  [mEditor setGeneralProperty: SCI_FINDINDICATORFLASH parameter: matchStart value:matchEnd]; +    if ([[searchField stringValue] isEqualToString: @"XX"])      [self showAutocompletion];  } diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index ebd64760d..6fec8becb 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -79,7 +79,7 @@      <h1>Scintilla Documentation</h1> -    <p>Last edited 26/March/2012 NH</p> +    <p>Last edited 16/April/2012 NH</p>      <p>There is <a class="jump" href="Design.html">an overview of the internal design of      Scintilla</a>.<br /> @@ -3856,6 +3856,29 @@ struct Sci_TextToFind {      Can be used to iterate through the document to discover all the indicator positions.      </p> +    <h3 id="FindIndicators">OS X Find Indicator</h3> + +    <p>On OS X search matches are highlighted with an animated gold rounded rectangle. +    The indicator shows, then briefly grows 25% and shrinks to the original size to draw the user's attention. +    While this feature is currently only implemented on OS X, it may be implemented on other platforms +    in the future.</p> + +    <p><b id="SCI_FINDINDICATORSHOW">SCI_FINDINDICATORSHOW(int start, int end)</b><br /> +     <b id="SCI_FINDINDICATORFLASH">SCI_FINDINDICATORFLASH(int start, int end)</b><br /> +     These two messages show and animate the find indicator. The indicator remains visible with +     <code>SCI_FINDINDICATORSHOW</code> and fades out after showing for half a second with  +     <code>SCI_FINDINDICATORFLASH</code>. +     <code>SCI_FINDINDICATORSHOW</code> behaves similarly to the OS X TextEdit and Safari applications +     and is best suited to editing documentation where the search target is often a word. +     <code>SCI_FINDINDICATORFLASH</code> is similar to Xcode and is suited to editing source code +     where the match will often be located next to operators which would otherwise be hidden under the indicator's +     padding. +     </p> + +    <p><b id="SCI_FINDINDICATORHIDE">SCI_FINDINDICATORHIDE</b><br /> +     This message hides the find indicator. +     </p> +      <h3 id="StyleByteIndicators">Style Byte Indicators (deprecated)</h3>      <p>By default, Scintilla organizes the style byte associated with each text byte as 5 bits of      style information (for 32 styles) and 3 bits of indicator information for 3 independent diff --git a/include/Scintilla.h b/include/Scintilla.h index 2459c0317..0df0900de 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -837,6 +837,9 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam,  #define SCI_SETTECHNOLOGY 2630  #define SCI_GETTECHNOLOGY 2631  #define SCI_CREATELOADER 2632 +#define SCI_FINDINDICATORSHOW 2640 +#define SCI_FINDINDICATORFLASH 2641 +#define SCI_FINDINDICATORHIDE 2642  #define SCI_STARTRECORD 3001  #define SCI_STOPRECORD 3002  #define SCI_SETLEXER 4001 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 6dda631e4..b7f9eede8 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -2217,6 +2217,15 @@ get int GetTechnology=2631(,)  # Create an ILoader*.  fun int CreateLoader=2632(int bytes,) +# On OS X, show a find indicator. +fun void FindIndicatorShow=2640(position start, position end) + +# On OS X, flash a find indicator, then fade out. +fun void FindIndicatorFlash=2641(position start, position end) + +# On OS X, hide the find indicator. +fun void FindIndicatorHide=2642(,) +  # Start notifying the container of all key presses and commands.  fun void StartRecord=3001(,) | 
