Last active
August 27, 2016 09:44
-
-
Save jakepetroules/7743bb5cad213719ad7d to your computer and use it in GitHub Desktop.
Drawing animated focus rings in Cocoa
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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