-
Notifications
You must be signed in to change notification settings - Fork 17
/
NSAlert+Popover.m
133 lines (108 loc) · 4.11 KB
/
NSAlert+Popover.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
// NSAlert+MenuItemPopUp.m
// Glow Foundation
//
// Created by Raffael Hannemann on 30.04.13.
// Copyright (c) 2013 Raffael Hannemann. All rights reserved.
//
#import "NSAlert+Popover.h"
#import <objc/runtime.h>
@implementation NSAlert (Popover)
static char COMPLETIONBLOCK_KEY;
static char PARENTPOPOVER_KEY;
static char TARGETVIEW_KEY;
static NSMutableArray *previouslyOpenedAlerts;
static NSPopover *currentlyOpenedPopover;
static NSAlert *currentlyOpenedAlert;
- (void) runAsPopoverForView:(NSView *) aView withCompletionBlock:(NSAlertCompletionBlock) aBlock {
[self runAsPopoverForView:aView preferredEdge:NSMaxYEdge withCompletionBlock:aBlock];
}
- (void) runAsPopoverForView:(NSView *) view preferredEdge:(NSRectEdge)preferredEdge withCompletionBlock:(NSAlertCompletionBlock)block {
// Set this alert as the target of all its buttons
for (NSButton *button in [self buttons]) {
[button setTarget:self];
[button setAction:@selector(stopSynchronousPopoverAlert:)];
}
// Store block and target view references for later usage
self.completionBlock = block;
self.targetView = view;
// Force a layout update, which hides unused buttons
[self layout];
if (!previouslyOpenedAlerts) {
previouslyOpenedAlerts = [NSMutableArray array];
}
// Instantiate a new NSPopover with a view controller that manages this alert's view
NSViewController *controller = [[NSViewController alloc] init];
NSPopover *popover = [[NSPopover alloc] init];
[controller setView:[self.window contentView]];
[popover setContentViewController:controller];
// Store the reference to this alert's parent popover
[self setParentPopover:popover];
// Enqueue the potentially currently opened alert
if (currentlyOpenedPopover) {
[previouslyOpenedAlerts addObject:currentlyOpenedAlert];
[currentlyOpenedPopover close];
}
// Open the alert within the popover and mark it as the currently shown one.
[popover showRelativeToRect:view.bounds ofView:view preferredEdge:NSMaxYEdge];
currentlyOpenedPopover = popover;
currentlyOpenedAlert = self;
}
- (void) stopSynchronousPopoverAlert: (NSButton *) clickedButton {
// Determine clicked button index
NSUInteger clickedIx = [[self buttons] indexOfObject:clickedButton];
// And determine the return code of this button
NSInteger returnCode = 0;
switch (clickedIx) {
case NSAlertFirstButtonReturn:
case NSAlertSecondButtonReturn:
case NSAlertThirdButtonReturn:
returnCode = clickedIx;
break;
default:
returnCode = NSAlertThirdButtonReturn +clickedIx -2;
break;
}
// Execute the calback with the return code
if (self.completionBlock) self.completionBlock(returnCode);
// Close the popover and remove if from the queue
NSPopover *parent = self.parentPopover;
if (parent) [parent close];
currentlyOpenedAlert = nil;
currentlyOpenedPopover = nil;
// Check for previously shown Popover Alerts
[self checkForPreviouslyShownAlerts];
}
- (void) checkForPreviouslyShownAlerts {
// If previously opened alerts are referenced, open the last one.
if (previouslyOpenedAlerts && previouslyOpenedAlerts.count>0) {
NSAlert *alert = [previouslyOpenedAlerts lastObject];
[previouslyOpenedAlerts removeObject:alert];
[alert runAsPopoverForView:alert.targetView withCompletionBlock:alert.completionBlock];
}
}
/**
* The following methods are required to add properties to the category.
*/
// Property Completion Block
- (void) setCompletionBlock: (NSAlertCompletionBlock) block{
objc_setAssociatedObject(self, &COMPLETIONBLOCK_KEY, block, OBJC_ASSOCIATION_COPY);
}
- (NSAlertCompletionBlock) completionBlock {
return objc_getAssociatedObject(self, &COMPLETIONBLOCK_KEY);
}
// Property Parent Popover
- (void) setParentPopover:(NSPopover *)parentPopover {
objc_setAssociatedObject(self, &PARENTPOPOVER_KEY, parentPopover, OBJC_ASSOCIATION_RETAIN);
}
- (NSPopover *) parentPopover {
return objc_getAssociatedObject(self, &PARENTPOPOVER_KEY);
}
// Property Target View
- (void) setTargetView:(NSPopover *)targetView {
objc_setAssociatedObject(self, &TARGETVIEW_KEY, targetView, OBJC_ASSOCIATION_RETAIN);
}
- (NSView *) targetView {
return objc_getAssociatedObject(self, &TARGETVIEW_KEY);
}
@end