forked from erkyrath/Inform7-IDE-Mac
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIFViewTrackingWindowController.m
178 lines (133 loc) · 5.15 KB
/
IFViewTrackingWindowController.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//
// IFViewTrackingWindowController.m
// Inform-xc2
//
// Created by Andrew Hunter on 01/02/2009.
// Copyright 2009 Andrew Hunter. All rights reserved.
//
#import "IFViewTrackingWindowController.h"
@interface IFViewTrackingWindowController(PrivateMethods)
- (void) repositionWindow; // Forces this object to reposition its window
@end
@implementation IFViewTrackingWindowController
// = Initialisation =
- (id) initWithView: (NSView*) newTargetView
inWindow: (NSWindow*) newTargetWindow {
// Create the window
NSWindow* window = [[[NSWindow alloc] initWithContentRect: NSMakeRect(0,0,100,100)
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: YES] autorelease];
// Initialise this object
self = [super initWithWindow: window];
if (self) {
targetView = [newTargetView retain];
targetWindow = [newTargetWindow retain];
// The target view must exist
if (!targetView) {
[self autorelease];
return nil;
}
// The target window should be configured to be transparent
[window setBackgroundColor: [NSColor clearColor]];
[window setOpaque: NO];
[window setHasShadow: NO];
// The initial content view is whatever the window is currently using
contentView = [[window contentView] retain];
// Register for notifications on the view, in particular frame changed events
[targetView setPostsFrameChangedNotifications: YES];
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(targetViewFrameChanged:) name: NSViewFrameDidChangeNotification object: targetView];
// Ideally we'd also track the view when it moves between windows, but OS X provides no easy way to do so, so we only monitor the window that it is in now
if (!targetWindow) {
[self autorelease];
return nil;
}
// We need to know when the window is being closed. We'd like to deal with open events as well, but we can't, so the owner of this object will need to display the window manually
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(targetWindowDidClose:) name: NSWindowWillCloseNotification object: targetWindow];
// Position the window so that it's ready to be opened
[self repositionWindow];
}
return self;
}
- (void) dealloc {
// Our window is no longer a child window of the window belonging to the target view
[targetWindow removeChildWindow: [self window]];
// Done with notifications
[[NSNotificationCenter defaultCenter] removeObserver: self];
// Finished with the views
[contentView release];
[targetView release];
[targetWindow release];
[super dealloc];
}
// = Showing/hiding the window =
- (void) queueShowHide {
// If we're not waiting...
if (!waitingToShowOrHide) {
// Queue up a show/hide event to run once all the current events have finished executing (prevents flicker)
[[NSRunLoop currentRunLoop] performSelector: @selector(showOrHide) target: self argument: nil order: 32 modes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
waitingToShowOrHide = YES;
}
}
- (void) showOrHide {
if (hideCount > showCount) {
// This window can't be a child window when we order it out or both will get hidden
if ([targetWindow childWindows] && [[targetWindow childWindows] indexOfObjectIdenticalTo: [self window]] != NSNotFound) {
[targetWindow removeChildWindow: [self window]];
}
// Close the window
[[self window] orderOut: self];
} else if (showCount > hideCount) {
// If this window is not already a child window of the target window then make it so
if ([[targetWindow childWindows] indexOfObjectIdenticalTo: [self window]] == NSNotFound || ![targetWindow childWindows]) {
[targetWindow addChildWindow: [self window]
ordered: NSWindowAbove];
[self repositionWindow];
}
// Finish showing the window
[super showWindow: self];
}
// No longer waiting for this event
hideCount = showCount = 0;
waitingToShowOrHide = NO;
}
- (IBAction) hideWindow: (id) sender {
hideCount++;
[self queueShowHide];
}
- (IBAction) showWindow: (id) sender {
showCount++;
[self queueShowHide];
}
// = View notifications =
- (void) targetViewFrameChanged: (NSNotification*) not {
// Reposition the window appropriately
[self repositionWindow];
}
// = Window notifications =
- (void) targetWindowDidClose: (NSNotification*) not {
// Our window closes along with its parent
[[self window] orderOut: self];
}
// = Updating the contents =
- (NSView*) contentView {
return contentView;
}
- (void) setContentView: (NSView*) newContentView {
[contentView autorelease];
contentView = [newContentView retain];
[[self window] setContentView: newContentView];
}
// = Positioning the window =
- (void) repositionWindow {
// Get the frame of the view, and translate it so that it's relative to its window
NSRect viewFrame = [targetView convertRect: [targetView bounds] toView: nil];
// Convert this so that it's in screen coordinates
NSRect windowFrame = [targetWindow contentRectForFrameRect: [targetWindow frame]];
viewFrame.origin.x += NSMinX(windowFrame);
viewFrame.origin.y += NSMinY(windowFrame);
// Position our window
[[self window] setFrame: viewFrame
display: YES];
}
@end