1 | //␊ |
2 | // RoundedBox.m␊ |
3 | // RoundedBox␊ |
4 | //␊ |
5 | // Created by Matt Gemmell on 01/11/2005.␊ |
6 | // Copyright 2006 Matt Gemmell. http://mattgemmell.com/␊ |
7 | //␊ |
8 | // Permission to use this code:␊ |
9 | //␊ |
10 | // Feel free to use this code in your software, either as-is or ␊ |
11 | // in a modified form. Either way, please include a credit in ␊ |
12 | // your software's "About" box or similar, mentioning at least ␊ |
13 | // my name (Matt Gemmell). A link to my site would be nice too.␊ |
14 | //␊ |
15 | // Permission to redistribute this code:␊ |
16 | //␊ |
17 | // You can redistribute this code, as long as you keep these ␊ |
18 | // comments. You can also redistribute modified versions of the ␊ |
19 | // code, as long as you add comments to say that you've made ␊ |
20 | // modifications (keeping these original comments too).␊ |
21 | //␊ |
22 | // If you do use or redistribute this code, an email would be ␊ |
23 | // appreciated, just to let me know that people are finding my ␊ |
24 | // code useful. You can reach me at matt.gemmell@gmail.com␊ |
25 | //␊ |
26 | ␊ |
27 | #import "RoundedBox.h"␊ |
28 | ␊ |
29 | ␊ |
30 | @implementation RoundedBox␊ |
31 | ␊ |
32 | ␊ |
33 | - (id)initWithFrame:(NSRect)frame {␊ |
34 | self = [super initWithFrame:frame];␊ |
35 | if (self) {␊ |
36 | [self setDefaults];␊ |
37 | }␊ |
38 | return self;␊ |
39 | }␊ |
40 | ␊ |
41 | ␊ |
42 | - (void)dealloc␊ |
43 | {␊ |
44 | [borderColor release];␊ |
45 | [titleColor release];␊ |
46 | [gradientStartColor release];␊ |
47 | [gradientEndColor release];␊ |
48 | [backgroundColor release];␊ |
49 | [titleAttrs release];␊ |
50 | ␊ |
51 | [super dealloc];␊ |
52 | }␊ |
53 | ␊ |
54 | ␊ |
55 | - (void)setDefaults␊ |
56 | {␊ |
57 | borderWidth = 2.0;␊ |
58 | [self setBorderColor:[NSColor grayColor]];␊ |
59 | [self setTitleColor:[NSColor whiteColor]];␊ |
60 | [self setGradientStartColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]];␊ |
61 | [self setGradientEndColor:[NSColor colorWithCalibratedWhite:0.82 alpha:1.0]];␊ |
62 | [self setBackgroundColor:[NSColor colorWithCalibratedWhite:0.90 alpha:1.0]];␊ |
63 | [self setTitleFont:[NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];␊ |
64 | ␊ |
65 | // Set up text attributes for drawing␊ |
66 | NSMutableParagraphStyle *paragraphStyle;␊ |
67 | paragraphStyle = [[NSMutableParagraphStyle alloc] init];␊ |
68 | [paragraphStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];␊ |
69 | [paragraphStyle setAlignment:NSLeftTextAlignment];␊ |
70 | [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];␊ |
71 | ␊ |
72 | titleAttrs = [[NSMutableDictionary dictionaryWithObjectsAndKeys:␊ |
73 | [self titleFont], NSFontAttributeName,␊ |
74 | [self titleColor], NSForegroundColorAttributeName,␊ |
75 | [paragraphStyle autorelease], NSParagraphStyleAttributeName,␊ |
76 | nil] retain];␊ |
77 | ␊ |
78 | [self setDrawsFullTitleBar:YES];␊ |
79 | [self setSelected:NO];␊ |
80 | [self setDrawsGradientBackground:YES];␊ |
81 | }␊ |
82 | ␊ |
83 | ␊ |
84 | - (void)awakeFromNib␊ |
85 | {␊ |
86 | // For when we've been created in a nib file␊ |
87 | [self setDefaults];␊ |
88 | ␉␊ |
89 | }␊ |
90 | ␊ |
91 | ␊ |
92 | - (BOOL)preservesContentDuringLiveResize␊ |
93 | {␊ |
94 | // NSBox returns YES for this, but doing so would screw up the gradients.␊ |
95 | return NO;␊ |
96 | }␊ |
97 | ␊ |
98 | ␊ |
99 | - (void)drawRect:(NSRect)rect {␊ |
100 | ␊ |
101 | // Construct rounded rect path␊ |
102 | NSRect boxRect = [self bounds];␊ |
103 | NSRect bgRect = boxRect;␊ |
104 | bgRect = NSInsetRect(boxRect, borderWidth / 2.0, borderWidth / 2.0);␊ |
105 | bgRect = NSIntegralRect(bgRect);␊ |
106 | bgRect.origin.x += 0.5;␊ |
107 | bgRect.origin.y += 0.5;␊ |
108 | int minX = NSMinX(bgRect);␊ |
109 | int midX = NSMidX(bgRect);␊ |
110 | int maxX = NSMaxX(bgRect);␊ |
111 | int minY = NSMinY(bgRect);␊ |
112 | int midY = NSMidY(bgRect);␊ |
113 | int maxY = NSMaxY(bgRect);␊ |
114 | float radius = 4.0;␊ |
115 | NSBezierPath *bgPath = [NSBezierPath bezierPath];␊ |
116 | ␊ |
117 | // Bottom edge and bottom-right curve␊ |
118 | [bgPath moveToPoint:NSMakePoint(midX, minY)];␊ |
119 | [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) ␊ |
120 | toPoint:NSMakePoint(maxX, midY) ␊ |
121 | radius:radius];␊ |
122 | ␊ |
123 | // Right edge and top-right curve␊ |
124 | [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY) ␊ |
125 | toPoint:NSMakePoint(midX, maxY) ␊ |
126 | radius:radius];␊ |
127 | ␊ |
128 | // Top edge and top-left curve␊ |
129 | [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) ␊ |
130 | toPoint:NSMakePoint(minX, midY) ␊ |
131 | radius:radius];␊ |
132 | ␊ |
133 | // Left edge and bottom-left curve␊ |
134 | [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, minY) ␊ |
135 | toPoint:NSMakePoint(midX, minY) ␊ |
136 | radius:radius];␊ |
137 | [bgPath closePath];␊ |
138 | ␊ |
139 | ␊ |
140 | // Draw background␊ |
141 | ␊ |
142 | if ([self drawsGradientBackground]) {␊ |
143 | // Draw gradient background␊ |
144 | NSGraphicsContext *nsContext = [NSGraphicsContext currentContext];␊ |
145 | [nsContext saveGraphicsState];␊ |
146 | [bgPath addClip];␊ |
147 | CTGradient *gradient = [CTGradient gradientWithBeginningColor:[self gradientStartColor] endingColor:[self gradientEndColor]];␊ |
148 | NSRect gradientRect = [bgPath bounds];␊ |
149 | [gradient fillRect:gradientRect angle:270.0];␊ |
150 | [nsContext restoreGraphicsState];␊ |
151 | } else {␊ |
152 | // Draw solid color background␊ |
153 | [backgroundColor set];␊ |
154 | [bgPath fill];␊ |
155 | }␊ |
156 | ␊ |
157 | ␊ |
158 | // Create drawing rectangle for title␊ |
159 | ␊ |
160 | float titleHInset = borderWidth + 4.0;␊ |
161 | float titleVInset = borderWidth;␊ |
162 | NSSize titleSize = [[self title] sizeWithAttributes:titleAttrs];␊ |
163 | NSRect titleRect = NSMakeRect(boxRect.origin.x + titleHInset, ␊ |
164 | boxRect.origin.y + boxRect.size.height - titleSize.height - (titleVInset * 2.0), ␊ |
165 | titleSize.width + borderWidth, ␊ |
166 | titleSize.height);␊ |
167 | titleRect.size.width = MIN(titleRect.size.width, boxRect.size.width - (2.0 * titleHInset));␊ |
168 | ␊ |
169 | if ([self selected]) {␊ |
170 | [[NSColor alternateSelectedControlColor] set];␊ |
171 | // We use the alternate (darker) selectedControlColor since the regular one is too light.␊ |
172 | // The alternate one is the highlight color for NSTableView, NSOutlineView, etc.␊ |
173 | // This mimics how Automator highlights the selected action in a workflow.␊ |
174 | } else {␊ |
175 | [borderColor set];␊ |
176 | }␊ |
177 | ␊ |
178 | ␊ |
179 | // Draw title background␊ |
180 | //NSRectFill(titleRect);␊ |
181 | [[self titlePathWithinRect:bgRect cornerRadius:radius titleRect:titleRect] fill];␊ |
182 | ␊ |
183 | ␊ |
184 | // Draw rounded rect around entire box␊ |
185 | if (borderWidth > 0.0) {␊ |
186 | [bgPath setLineWidth:borderWidth];␊ |
187 | [bgPath stroke];␊ |
188 | }␊ |
189 | ␊ |
190 | ␊ |
191 | // Draw title text␊ |
192 | [[self title] drawInRect:titleRect withAttributes:titleAttrs];␊ |
193 | //[[self titleCell] setTextColor:[self titleColor]];␊ |
194 | //[[self titleCell] drawWithFrame:titleRect inView:self];␊ |
195 | }␊ |
196 | ␊ |
197 | ␊ |
198 | - (NSBezierPath *)titlePathWithinRect:(NSRect)rect cornerRadius:(float)radius titleRect:(NSRect)titleRect␊ |
199 | {␊ |
200 | // Construct rounded rect path␊ |
201 | ␊ |
202 | NSRect bgRect = rect;␊ |
203 | int minX = NSMinX(bgRect);␊ |
204 | int maxX = minX + titleRect.size.width + ((titleRect.origin.x - rect.origin.x) * 2.0);␊ |
205 | int maxY = NSMaxY(bgRect);␊ |
206 | int minY = NSMinY(titleRect) - (maxY - (titleRect.origin.y + titleRect.size.height));␊ |
207 | float titleExpansionThreshold = 20.0;␊ |
208 | // i.e. if there's less than 20px space to the right of the short titlebar, just draw the full one.␊ |
209 | ␊ |
210 | NSBezierPath *path = [NSBezierPath bezierPath];␊ |
211 | ␊ |
212 | [path moveToPoint:NSMakePoint(minX, minY)];␊ |
213 | ␊ |
214 | if (bgRect.size.width - titleRect.size.width >= titleExpansionThreshold && ![self drawsFullTitleBar]) {␊ |
215 | // Draw a short titlebar␊ |
216 | [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) ␊ |
217 | toPoint:NSMakePoint(maxX, maxY) ␊ |
218 | radius:radius];␊ |
219 | [path lineToPoint:NSMakePoint(maxX, maxY)];␊ |
220 | } else {␊ |
221 | // Draw full titlebar, since we're either set to always do so, or we don't have room for a short one.␊ |
222 | [path lineToPoint:NSMakePoint(NSMaxX(bgRect), minY)];␊ |
223 | [path appendBezierPathWithArcFromPoint:NSMakePoint(NSMaxX(bgRect), maxY) ␊ |
224 | toPoint:NSMakePoint(NSMaxX(bgRect) - (bgRect.size.width / 2.0), maxY) ␊ |
225 | radius:radius];␊ |
226 | }␊ |
227 | ␊ |
228 | [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) ␊ |
229 | toPoint:NSMakePoint(minX, minY) ␊ |
230 | radius:radius];␊ |
231 | ␊ |
232 | [path closePath];␊ |
233 | ␊ |
234 | return path;␊ |
235 | }␊ |
236 | ␊ |
237 | ␊ |
238 | - (void)setTitle:(NSString *)newTitle␊ |
239 | {␊ |
240 | [super setTitle:newTitle];␊ |
241 | [self setNeedsDisplay:YES];␊ |
242 | }␊ |
243 | ␊ |
244 | ␊ |
245 | - (BOOL)drawsFullTitleBar␊ |
246 | {␊ |
247 | return drawsFullTitleBar;␊ |
248 | }␊ |
249 | ␊ |
250 | ␊ |
251 | - (void)setDrawsFullTitleBar:(BOOL)newDrawsFullTitleBar␊ |
252 | {␊ |
253 | drawsFullTitleBar = newDrawsFullTitleBar;␊ |
254 | [self setNeedsDisplay:YES];␊ |
255 | }␊ |
256 | ␊ |
257 | ␊ |
258 | - (BOOL)selected␊ |
259 | {␊ |
260 | return selected;␊ |
261 | }␊ |
262 | ␊ |
263 | ␊ |
264 | - (void)setSelected:(BOOL)newSelected␊ |
265 | {␊ |
266 | selected = newSelected;␊ |
267 | [self setNeedsDisplay:YES];␊ |
268 | }␊ |
269 | ␊ |
270 | ␊ |
271 | - (NSColor *)borderColor␊ |
272 | {␊ |
273 | return borderColor;␊ |
274 | }␊ |
275 | ␊ |
276 | ␊ |
277 | - (void)setBorderColor:(NSColor *)newBorderColor␊ |
278 | {␊ |
279 | [newBorderColor retain];␊ |
280 | [borderColor release];␊ |
281 | borderColor = newBorderColor;␊ |
282 | [self setNeedsDisplay:YES];␊ |
283 | }␊ |
284 | ␊ |
285 | - (void)setBorderWidth:(float)newBorderWidth␊ |
286 | {␊ |
287 | ␉borderWidth = newBorderWidth;␊ |
288 | [self setNeedsDisplay:YES];␊ |
289 | }␊ |
290 | ␊ |
291 | - (NSColor *)titleColor␊ |
292 | {␊ |
293 | return titleColor;␊ |
294 | }␊ |
295 | ␊ |
296 | ␊ |
297 | - (void)setTitleColor:(NSColor *)newTitleColor␊ |
298 | {␊ |
299 | [newTitleColor retain];␊ |
300 | [titleColor release];␊ |
301 | titleColor = newTitleColor;␊ |
302 | ␊ |
303 | [titleAttrs setObject:titleColor forKey:NSForegroundColorAttributeName];␊ |
304 | ␊ |
305 | [self setNeedsDisplay:YES];␊ |
306 | }␊ |
307 | ␊ |
308 | ␊ |
309 | - (NSColor *)gradientStartColor␊ |
310 | {␊ |
311 | return gradientStartColor;␊ |
312 | }␊ |
313 | ␊ |
314 | ␊ |
315 | - (void)setGradientStartColor:(NSColor *)newGradientStartColor␊ |
316 | {␊ |
317 | // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry.␊ |
318 | NSColor *newCalibratedGradientStartColor = [newGradientStartColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];␊ |
319 | [newCalibratedGradientStartColor retain];␊ |
320 | [gradientStartColor release];␊ |
321 | gradientStartColor = newCalibratedGradientStartColor;␊ |
322 | if ([self drawsGradientBackground]) {␊ |
323 | [self setNeedsDisplay:YES];␊ |
324 | }␊ |
325 | }␊ |
326 | ␊ |
327 | ␊ |
328 | - (NSColor *)gradientEndColor␊ |
329 | {␊ |
330 | return gradientEndColor;␊ |
331 | }␊ |
332 | ␊ |
333 | ␊ |
334 | - (void)setGradientEndColor:(NSColor *)newGradientEndColor␊ |
335 | {␊ |
336 | // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry.␊ |
337 | NSColor *newCalibratedGradientEndColor = [newGradientEndColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];␊ |
338 | [newCalibratedGradientEndColor retain];␊ |
339 | [gradientEndColor release];␊ |
340 | gradientEndColor = newCalibratedGradientEndColor;␊ |
341 | if ([self drawsGradientBackground]) {␊ |
342 | [self setNeedsDisplay:YES];␊ |
343 | }␊ |
344 | }␊ |
345 | ␊ |
346 | ␊ |
347 | - (NSColor *)backgroundColor␊ |
348 | {␊ |
349 | return backgroundColor;␊ |
350 | }␊ |
351 | ␊ |
352 | ␊ |
353 | - (void)setBackgroundColor:(NSColor *)newBackgroundColor␊ |
354 | {␊ |
355 | [newBackgroundColor retain];␊ |
356 | [backgroundColor release];␊ |
357 | backgroundColor = newBackgroundColor;␊ |
358 | if (![self drawsGradientBackground]) {␊ |
359 | [self setNeedsDisplay:YES];␊ |
360 | }␊ |
361 | }␊ |
362 | ␊ |
363 | ␊ |
364 | - (BOOL)drawsGradientBackground␊ |
365 | {␊ |
366 | return drawsGradientBackground;␊ |
367 | }␊ |
368 | ␊ |
369 | ␊ |
370 | - (void)setDrawsGradientBackground:(BOOL)newDrawsGradientBackground␊ |
371 | {␊ |
372 | drawsGradientBackground = newDrawsGradientBackground;␊ |
373 | [self setNeedsDisplay:YES];␊ |
374 | }␊ |
375 | ␊ |
376 | ␊ |
377 | @end␊ |
378 | |