-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathRoundedView.m
executable file
·308 lines (254 loc) · 8.53 KB
/
RoundedView.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//
// RoundedView.m
// RoundedFloatingPanel
//
#import "RoundedView.h"
// text size
#define TEXTSIZE 32
#define INFOTEXTSIZE1 16
#define INFOTEXTSIZE2 12
#define V_TITLEOFFSET 5
// horizontal and vertical offset of the lower info text
#define H_INFOTEXTOFFSET1 20
#define V_INFOTEXTOFFSET1 50
#define H_INFOTEXTOFFSET2 50
#define V_INFOTEXTOFFSET2 105
#define BOXCORNERRADIUS 15
#define BOXTRANSPARENCY 0.6
// drop shadow of text
#define SHADOWOFFSET 3
#define SHADOWBLURRADIUS 2
#define SHADOWTRANSPARENCY 0.5
#define MINHEIGHT 65
#define MEDIUMHEIGHT 120
@implementation RoundedView
- (void)awakeFromNib
{
draggingWindow = NO;
fullHeight = [self frame].size.height;
enlargingWindow = NO;
}
- (void) setDelegate:(id)d
{
delegate = d;
}
- (id) delegate
{
return delegate;
}
- (void)keyDown:(NSEvent *)theEvent
{
if (delegate != nil)
[delegate keyDown:theEvent];
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
- (BOOL)canBecomeKeyView
{
return YES;
}
-(void)drawText:(NSRect)rect
{
// make rectangle slightly smaller to make sure text does not touch border of view
int padding = 10;
rect.origin.x += padding;
rect.size.width -= 2 * padding;
NSMutableDictionary * attrs = [NSMutableDictionary dictionary];
// drop shadow for text
NSShadow *shadow = [[NSShadow alloc] autorelease];
[shadow setShadowOffset:NSMakeSize(SHADOWOFFSET, -SHADOWOFFSET)];
[shadow setShadowBlurRadius:SHADOWBLURRADIUS];
[shadow setShadowColor:[NSColor colorWithDeviceWhite:0.0 alpha:SHADOWTRANSPARENCY]];
[attrs setObject: shadow forKey: NSShadowAttributeName];
// main text font
NSFont *font = [NSFont systemFontOfSize:TEXTSIZE];
[attrs setObject: font forKey: NSFontAttributeName];
// text color
[attrs setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
// text alignment
NSMutableParagraphStyle *pStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[pStyle autorelease];
[pStyle setAlignment:NSCenterTextAlignment];
[attrs setValue: pStyle forKey: NSParagraphStyleAttributeName];
bool drawInfoText1 = rect.size.height >= MEDIUMHEIGHT;
bool drawInfoText2 = rect.size.height >= fullHeight - 5;
// draw main text
rect.size.height -= V_TITLEOFFSET;
[title drawInRect:rect withAttributes: attrs];
// info text 1
font = [NSFont systemFontOfSize:INFOTEXTSIZE1];
[attrs setObject: font forKey: NSFontAttributeName];
[attrs removeObjectForKey:NSShadowAttributeName];
// draw info text 1
if (drawInfoText1) {
rect.size.height -= V_INFOTEXTOFFSET1;
[info1 drawInRect:rect withAttributes: attrs];
rect.size.height += V_INFOTEXTOFFSET1; // reset rectangle height
}
// draw info text 2
if (drawInfoText2) {
font = [NSFont systemFontOfSize:INFOTEXTSIZE2];
[attrs setObject: font forKey: NSFontAttributeName];
rect.size.height -= V_INFOTEXTOFFSET2;
[info2 drawInRect:rect withAttributes: attrs];
}
}
- (void)drawRect:(NSRect)rect
{
[[NSColor clearColor] set];
NSRectFill([self frame]);
int minX = NSMinX(rect);
int midX = NSMidX(rect);
int maxX = NSMaxX(rect);
int minY = NSMinY(rect);
int midY = NSMidY(rect);
int maxY = NSMaxY(rect);
float radius = BOXCORNERRADIUS; // 25 is correct value to duplicate Panther's App Switcher
NSBezierPath *bgPath = [NSBezierPath bezierPath];
// Bottom edge and bottom-right curve
[bgPath moveToPoint:NSMakePoint(midX, minY)];
[bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY)
toPoint:NSMakePoint(maxX, midY)
radius:radius];
// Right edge and top-right curve
[bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY)
toPoint:NSMakePoint(midX, maxY)
radius:radius];
// Top edge and top-left curve
[bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY)
toPoint:NSMakePoint(minX, midY)
radius:radius];
// Left edge and bottom-left curve
[bgPath appendBezierPathWithArcFromPoint:[self frame].origin
toPoint:NSMakePoint(midX, minY)
radius:radius];
[bgPath closePath];
NSColor *bgColor = [NSColor colorWithCalibratedWhite:0.0 alpha:BOXTRANSPARENCY];
[bgColor setFill];
[bgPath fill];
[self drawText: rect];
// We need to invalidate window shadow for a window with transparent parts, after drawing
// The shadow is computed from the opaque (or mostly-opaque) parts and therefore depends on what gets drawn.
// If shadow is not recomputed, ghost labels will be visible.
[[self window] invalidateShadow];
}
- (void) setTitle:(NSString*)t
{
[t retain];
[title release];
title = t;
[self setNeedsDisplay:YES];
}
- (void) setInfo1:(NSString*)i
{
[i retain];
[info1 release];
info1 = i;
[self setNeedsDisplay:YES];
}
- (void) setInfo2:(NSString*)i
{
[i retain];
[info2 release];
info2 = i;
[self setNeedsDisplay:YES];
}
- (void)scrollWheel:(NSEvent *)theEvent
{
[delegate scrollWheel:theEvent];
}
- (void)rightMouseDown:(NSEvent *)theEvent
{
[delegate rightMouseDown:theEvent];
}
- (BOOL) isValidHeight:(NSInteger) height {
return height == fullHeight
|| height == MEDIUMHEIGHT
|| height == MINHEIGHT;
}
// resizes the info dialog
// returns a button state for the resize button
- (NSControlStateValue) resizeInfo
{
NSRect windowFrame = [[self window] frame];
if (windowFrame.size.height == fullHeight) {
enlargingWindow = NO;
windowFrame.size.height = MEDIUMHEIGHT;
} else if (windowFrame.size.height > MEDIUMHEIGHT) {
if (enlargingWindow == YES)
windowFrame.size.height = fullHeight;
else
windowFrame.size.height = MEDIUMHEIGHT;
} else if (windowFrame.size.height > MINHEIGHT) {
if (enlargingWindow == YES) {
windowFrame.size.height = fullHeight;
} else {
windowFrame.size.height = MINHEIGHT;
}
} else if (windowFrame.size.height == MINHEIGHT) {
enlargingWindow = YES;
windowFrame.size.height = MEDIUMHEIGHT;
}
// Before macOS 10.9, windows without a title bar could cover the menu bar.
// Color Oracle before v1.1.5 had some extra code here to fix this, however,
// this code did not work properly with some versions of macOS.
// Color Oracle 1.1.5 now requires macOS 10.9 to avoid this problem.
// See "NSWindows constrained to not intersect the menu bar" of 10.9 AppKit Release Notes
[[self window] setFrame:windowFrame display:YES animate:YES];
// button state for resize button: the button status should indicate the direction in which the window will grow or shrink
NSControlStateValue state;
if (windowFrame.size.height == fullHeight)
state = NSOnState;
else if (windowFrame.size.height == MINHEIGHT) {
state = NSOffState;
} else {
// medium size
state = enlargingWindow ? NSOffState : NSOnState;
}
return state;
}
- (void)mouseUp:(NSEvent *)theEvent
{
if (draggingWindow == NO) {
if (delegate != nil)
[delegate mouseUp:theEvent];
}
draggingWindow = NO;
}
- (void)mouseDragged:(NSEvent *)event
{
// Before macOS 10.9, windows without a title bar could cover the menu bar.
// Color Oracle before v1.1.5 had some extra code here to fix this, however,
// this code did not work properly with some versions of macOS.
// Color Oracle 1.1.5 now requires macOS 10.9 to avoid this problem.
// See "NSWindows constrained to not intersect the menu bar" of 10.9 AppKit Release Notes
// start new dragging
if (draggingWindow == NO)
{
draggingWindow = YES;
initialWindowFrame = [[self window] frame];
// mouse location in global coordinates
dragInitialLocation = [NSEvent mouseLocation];
initialVOffset = dragInitialLocation.y - initialWindowFrame.origin.y;
dragInitialLocation.x -= initialWindowFrame.origin.x;
dragInitialLocation.y -= initialWindowFrame.origin.y;
return;
}
// continue dragging
if (draggingWindow == YES) {
NSPoint currentLocation;
NSPoint newOrigin;
// mouse location in global coordinates
currentLocation = [NSEvent mouseLocation];
newOrigin.x = currentLocation.x - dragInitialLocation.x;
newOrigin.y = currentLocation.y - dragInitialLocation.y;
[[self window] setFrameOrigin:newOrigin];
}
}
@end