Skip to content

Instantly share code, notes, and snippets.

@jakepetroules
Last active August 27, 2016 09:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jakepetroules/7743bb5cad213719ad7d to your computer and use it in GitHub Desktop.
Save jakepetroules/7743bb5cad213719ad7d to your computer and use it in GitHub Desktop.
Drawing animated focus rings in Cocoa
#import <dlfcn.h>
static off_t lookupPrivateSymbol(const char *path, const char *symbol)
{
// TODO: Parse the Mach-O file
NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/nm";
task.arguments = @[@"-a", @(path)];
NSPipe *outputPipe = [NSPipe pipe];
task.standardOutput = outputPipe;
[task launch];
NSData *data = [[outputPipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray<NSString *> *lines = [string componentsSeparatedByString:@"\n"];
for (NSString *line in lines) {
if ([line containsString:@(symbol)]) {
NSScanner *scanner = [NSScanner scannerWithString:[[line componentsSeparatedByString:@" "] firstObject]];
unsigned long long offset = 0;
[scanner scanHexLongLong:&offset];
return (off_t)offset;
}
}
return 0;
}
static void qt_drawFocusRingOnPath(CGContextRef cg, NSBezierPath *focusRingPath, NSTimeInterval time)
{
CGContextSaveGState(cg);
CGContextBeginTransparencyLayer(cg, NULL);
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext
graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
NSNumber *useAnimatedFocusRing = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUseAnimatedFocusRing"];
if (![useAnimatedFocusRing isKindOfClass:[NSNumber class]])
useAnimatedFocusRing = nil;
BOOL hasYosemite = YES; // TODO: >= 10.10
// rdar://23700589
typedef void (*_NSSetFocusRingStyleForTimeFunction)(NSFocusRingPlacement placement, NSTimeInterval time);
static _NSSetFocusRingStyleForTimeFunction _NSSetFocusRingStyleForTime = 0;
if (!_NSSetFocusRingStyleForTime && hasYosemite) {
Dl_info info;
dladdr(&NSAppKitVersionNumber, &info);
off_t addr = lookupPrivateSymbol(info.dli_fname, "_NSSetFocusRingStyleForTime");
if (addr)
_NSSetFocusRingStyleForTime = (_NSSetFocusRingStyleForTimeFunction)(info.dli_fbase + addr);
}
if (_NSSetFocusRingStyleForTime && (!useAnimatedFocusRing || [useAnimatedFocusRing boolValue]))
_NSSetFocusRingStyleForTime(NSFocusRingOnly, time); // 0 <= time <= 0.25
else
NSSetFocusRingStyle(NSFocusRingOnly);
[focusRingPath setClip]; // Clear clip path to avoid artifacts when rendering the cursor at zero pos
[focusRingPath fill];
[NSGraphicsContext restoreGraphicsState];
CGContextEndTransparencyLayer(cg);
CGContextRestoreGState(cg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment