Created
July 14, 2013 03:46
-
-
Save beccadax/5993128 to your computer and use it in GitHub Desktop.
Better sheet APIs for 10.8. Any resemblance to Mavericks is purely coincidental.
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
// | |
// NSWindow+BetterSheets.h | |
// Gistapo | |
// | |
// Created by Brent Royal-Gordon on 7/12/13. | |
// Copyright (c) 2013 Architechies. All rights reserved. | |
// | |
#import <Cocoa/Cocoa.h> | |
#ifndef AVAILABLE_MAC_OS_VERSION_10_9_AND_LATER | |
enum { | |
NSModalResponseStop = (-1000), // Also used as the default response for sheets | |
NSModalResponseAbort = (-1001), | |
NSModalResponseContinue = (-1002), | |
}; | |
typedef NSInteger NSModalResponse; | |
#endif | |
@interface NSWindow (BetterSheets) | |
- (void)beginSheet:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler; | |
- (void)beginCriticalSheet:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler __attribute__((unavailable ("-beginCriticalSheet:completionHandler: not implemented by NSWindow+BetterSheets"))); | |
- (void)endSheet:(NSWindow *)sheetWindow; | |
- (void)endSheet:(NSWindow *)sheetWindow returnCode:(NSModalResponse)returnCode; | |
- (NSArray *)sheets; // An ordered array of the sheets on the window. This consists of the presented sheets in top-to-bottom order, followed by queued sheets in the order they were queued. This does not include nested/sub-sheets. | |
- (NSWindow *)sheetParent; | |
@end |
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
// | |
// NSWindow+BetterSheets.m | |
// Gistapo | |
// | |
// Created by Brent Royal-Gordon on 7/12/13. | |
// Copyright (c) 2013 Architechies. All rights reserved. | |
// | |
#import "NSWindow+BetterSheets.h" | |
@interface ArchSheetManager : NSObject | |
@property (weak) NSWindow * window; | |
@property (strong) NSMutableArray * enqueuedSheets; | |
@property (strong) NSMutableArray * enqueuedCompletionHandlers; | |
- (void)enqueueSheet:(NSWindow*)sheet completionHandler:(void (^)(NSModalResponse))completion; | |
- (void)enqueueCriticalSheet:(NSWindow*)sheet completionHandler:(void (^)(NSModalResponse))completion; | |
- (void)dequeueSheet:(NSWindow*)sheet returnCode:(NSModalResponse)returnCode; | |
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; | |
@end | |
#import <objc/runtime.h> | |
@implementation NSWindow (BetterSheetsConditional) | |
- (ArchSheetManager*)arch_sheetManager { | |
ArchSheetManager * manager = objc_getAssociatedObject(self, _cmd); | |
if(!manager) { | |
manager = [ArchSheetManager new]; | |
manager.window = self; | |
self.arch_sheetManager = manager; | |
} | |
return manager; | |
} | |
- (void)setArch_sheetManager:(ArchSheetManager*)sheetManager { | |
objc_setAssociatedObject(self, @selector(arch_sheetManager), sheetManager, OBJC_ASSOCIATION_RETAIN); | |
} | |
- (void)arch_beginSheet:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse))handler { | |
[self.arch_sheetManager enqueueSheet:sheetWindow completionHandler:handler]; | |
} | |
- (void)arch_beginCriticalSheet:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse))handler { | |
[self.arch_sheetManager enqueueCriticalSheet:sheetWindow completionHandler:handler]; | |
} | |
- (void)arch_endSheet:(NSWindow *)sheetWindow { | |
[self endSheet:sheetWindow returnCode:NSModalResponseStop]; | |
} | |
- (void)arch_endSheet:(NSWindow *)sheetWindow returnCode:(NSModalResponse)returnCode { | |
[self.arch_sheetManager dequeueSheet:sheetWindow returnCode:returnCode]; | |
} | |
- (NSWindow *)arch_sheetParent { | |
if(self.arch_sheetManager.window == self) { | |
return nil; | |
} | |
return self.arch_sheetManager.window; | |
} | |
- (NSArray *)arch_sheets { | |
return self.arch_sheetManager.enqueuedSheets; | |
} | |
static void ArchInstallMethodUnlessExists(Class class, SEL final) { | |
if(class_respondsToSelector(class, final)) { | |
return; | |
} | |
SEL original = NSSelectorFromString([NSString stringWithFormat:@"arch_%@", NSStringFromSelector(final)]); | |
Method m = class_getInstanceMethod(class, original); | |
class_addMethod(class, final, method_getImplementation(m), method_getTypeEncoding(m)); | |
} | |
+ (void)load { | |
ArchInstallMethodUnlessExists(self, @selector(beginSheet:completionHandler:)); | |
ArchInstallMethodUnlessExists(self, @selector(beginCriticalSheet:completionHandler:)); | |
ArchInstallMethodUnlessExists(self, @selector(endSheet:)); | |
ArchInstallMethodUnlessExists(self, @selector(endSheet:returnCode:)); | |
ArchInstallMethodUnlessExists(self, @selector(sheetParent)); | |
ArchInstallMethodUnlessExists(self, @selector(sheets)); | |
} | |
@end | |
@implementation ArchSheetManager | |
- (id)init { | |
if((self = [super init])) { | |
self.enqueuedSheets = [NSMutableArray new]; | |
self.enqueuedCompletionHandlers = [NSMutableArray new]; | |
} | |
return self; | |
} | |
- (void)addSheetToQueue:(NSWindow *)sheet completionHandler:(void (^)(NSModalResponse))completion { | |
if(!completion) { | |
completion = ^(NSModalResponse response){}; | |
} | |
[self.enqueuedSheets addObject:sheet]; | |
[self.enqueuedCompletionHandlers addObject:[completion copy]]; | |
sheet.arch_sheetManager = self; | |
} | |
- (void)enqueueSheet:(NSWindow *)sheet completionHandler:(void (^)(NSModalResponse))completion { | |
[self addSheetToQueue:sheet completionHandler:completion]; | |
if(sheet == self.enqueuedSheets[0]) { | |
[NSApp beginSheet:sheet modalForWindow:self.window modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:NULL]; | |
} | |
} | |
- (void)enqueueCriticalSheet:(NSWindow *)sheet completionHandler:(void (^)(NSModalResponse))completion { | |
NSAssert(NO, @"Critical sheets NYI"); | |
} | |
- (void)dequeueSheet:(NSWindow *)sheet returnCode:(NSModalResponse)returnCode { | |
if(self.enqueuedSheets[0] == sheet) { | |
[NSApp endSheet:sheet returnCode:returnCode]; | |
} | |
else { | |
[self doDequeueSheet:sheet returnCode:returnCode]; | |
} | |
} | |
- (void)doDequeueSheet:(NSWindow *)sheet returnCode:(NSModalResponse)returnCode { | |
NSUInteger index = [self.enqueuedSheets indexOfObject:sheet]; | |
NSAssert(index != NSNotFound, @"Can't dismiss a sheet that hasn't been enqueued"); | |
void (^completion)(NSModalResponse) = self.enqueuedCompletionHandlers[index]; | |
completion(returnCode); | |
sheet.arch_sheetManager = nil; | |
[self.enqueuedCompletionHandlers removeObjectAtIndex:index]; | |
[self.enqueuedSheets removeObjectAtIndex:index]; | |
} | |
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { | |
NSParameterAssert(sheet == self.enqueuedSheets[0]); | |
[sheet orderOut:self.window]; | |
[self doDequeueSheet:sheet returnCode:returnCode]; | |
if(self.enqueuedSheets.count) { | |
[NSApp beginSheet:self.enqueuedSheets[0] modalForWindow:self.window modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:NULL]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, are you willing to place this code under a BSD or MIT license or similar?