Skip to content

Instantly share code, notes, and snippets.

@chadseld
Created July 16, 2015 18:32
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadseld/0892e54abdd1aef908d9 to your computer and use it in GitHub Desktop.
Save chadseld/0892e54abdd1aef908d9 to your computer and use it in GitHub Desktop.
OPTestKeyLoopView.m
//
// OPTestKeyLoopView.m
//
// Copyright (c) 2013 AgileBits Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "OPTestKeyLoopView.h"
@interface OPTestKeyView : NSObject
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@property (nonatomic, strong) NSColor *color;
@property (nonatomic, strong) NSFont *font;
@property (nonatomic, assign) NSRect frame;
@end
@implementation OPTestKeyView
@end
@interface OPTestKeyLoopView ()
@property (nonatomic, strong) NSView *rootView;
@property (nonatomic, strong) NSMutableArray *keyViews;
@property (nonatomic) NSRect rootFrame;
@end
@implementation OPTestKeyLoopView
+ (OPTestKeyLoopView *)sharedTestKeyLoopView {
static OPTestKeyLoopView *sharedView = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedView = [[self alloc] init];
sharedView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
});
return sharedView;
}
+ (void)enableForView:(NSView *)startView {
[self sharedTestKeyLoopView].rootView = startView;
[[NSNotificationCenter defaultCenter] addObserver:[self sharedTestKeyLoopView] selector:@selector(mainWindowFlagsChanged:) name:OPMainWindowFlagsChangedNotification object:nil];
}
+ (void)disable {
[self sharedTestKeyLoopView].rootView = nil;
[[self sharedTestKeyLoopView] clear];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)mainWindowFlagsChanged:(NSNotification *)aNotification {
NSUInteger modifierFlags = ([NSApp currentEvent].modifierFlags & NSDeviceIndependentModifierFlagsMask);
if (modifierFlags & NSAlternateKeyMask) {
NSView *hitView = self.rootView;
while (YES) {
NSPoint mouse = [hitView convertPoint:[[self.rootView window] mouseLocationOutsideOfEventStream] fromView:nil];
BOOL hitSubView = NO;
for (NSView *subview in hitView.subviews) {
if (subview == self) {
continue;
}
if (NSMouseInRect(mouse, subview.frame, hitView.isFlipped)) {
hitView = subview;
hitSubView = YES;
break;
}
}
if (!hitSubView) {
break;
}
}
if (modifierFlags & NSShiftKeyMask) {
[self showPreviousKeyViewLoopForView:hitView];
}
else {
[self showNextKeyViewLoopForView:hitView];
}
}
else {
[self clear];
}
}
- (void)insertIntoViewHirarchy {
if (self.superview) {
[self removeFromSuperview];
}
self.frame = [[[self.rootView window] contentView] frame];
[[[self.rootView window] contentView] addSubview:self positioned:NSWindowAbove relativeTo:nil];
}
- (void)showPreviousKeyViewLoopForView:(NSView *)startView {
[self insertIntoViewHirarchy];
if (!self.keyViews) self.keyViews = [NSMutableArray new];
[self.keyViews removeAllObjects];
NSLog(@"+++PreviousKeyViewLoop");
NSView *view = startView;
NSMutableSet *visitedViews = [NSMutableSet new];
NSInteger num = 0;
do {
NSRect frameInWindow = [view convertRect:view.bounds toView:nil];
OPTestKeyView *keyView = [[OPTestKeyView alloc] init];
keyView.frame = [self convertRect:frameInWindow fromView:nil];
keyView.title = [NSString stringWithFormat:@"%ld", num];
keyView.color = [NSColor redColor];
keyView.font = [NSFont fontWithName:@"Menlo" size:12];
NSLog(@"%ld %@", num, [view className]);
[self.keyViews addObject:keyView];
[visitedViews addObject:view];
view = view.previousKeyView;
num--;
} while (view && ![visitedViews containsObject:view]);
NSLog(@"---");
self.rootFrame = [self convertRect:[startView convertRect:startView.bounds toView:nil] fromView:nil];
[self setNeedsDisplay:YES];
}
- (void)showNextKeyViewLoopForView:(NSView *)startView {
[self insertIntoViewHirarchy];
if (!self.keyViews) self.keyViews = [NSMutableArray new];
[self.keyViews removeAllObjects];
NSLog(@"+++NextKeyViewLoop");
NSView *view = startView;
NSMutableSet *visitedViews = [NSMutableSet new];
NSInteger num = 0;
do {
NSRect frameInWindow = [view convertRect:view.bounds toView:nil];
OPTestKeyView *keyView = [[OPTestKeyView alloc] init];
keyView.frame = [self convertRect:frameInWindow fromView:nil];
keyView.title = [NSString stringWithFormat:@"%ld", num];
keyView.color = [NSColor blueColor];
keyView.font = [NSFont fontWithName:@"Menlo" size:12];
NSLog(@"%ld %@", num, [view className]);
[self.keyViews addObject:keyView];
[visitedViews addObject:view];
view = view.nextKeyView;
num++;
} while (view && ![visitedViews containsObject:view]);
NSLog(@"---");
self.rootFrame = [self convertRect:[startView convertRect:startView.bounds toView:nil] fromView:nil];
[self setNeedsDisplay:YES];
}
- (void)clear {
[self.keyViews removeAllObjects];
[self removeFromSuperview];
}
- (NSDictionary *)attributesWithFont:(NSFont *)font color:(NSColor *)color {
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setAlignment:NSCenterTextAlignment];
[paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
[paragraphStyle setLineSpacing:0];
NSDictionary *attributes = [[NSDictionary alloc] initWithObjectsAndKeys:
font, NSFontAttributeName,
color, NSForegroundColorAttributeName,
paragraphStyle, NSParagraphStyleAttributeName, nil];
return attributes;
}
- (void)drawRect:(NSRect)dirtyRect {
if (self.keyViews.count > 0) {
[[NSColor colorWithDeviceRed:1 green:0 blue:0 alpha:0.03] set];
NSRectFillUsingOperation(self.rootFrame, NSCompositeSourceOver);
}
for (OPTestKeyView *keyView in self.keyViews) {
[keyView.color set];
[NSBezierPath setDefaultLineWidth:0.5];
[NSBezierPath strokeRect:NSOffsetRect(NSIntegralRect(keyView.frame), 0.25, 0.25)];
NSDictionary *attributes = [self attributesWithFont:keyView.font color:keyView.color];
NSSize size = [keyView.title sizeWithAttributes:attributes];
NSRect textRect = NSIntegralRectWithOptions(NSInsetRect(keyView.frame, (keyView.frame.size.width - size.width)/2, (keyView.frame.size.height - size.height)/2), NSAlignMinXOutward|NSAlignMaxXInward|NSAlignMinYOutward|NSAlignMaxYInward);
[[NSColor whiteColor] set];
NSRectFill(textRect);
[keyView.title drawInRect:textRect withAttributes:attributes];
}
}
- (BOOL)isOpaque {
return NO;
}
- (NSView *)hitTest:(NSPoint)aPoint {
return nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment