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 colorWithCalibratedRed:0.38 green:0.48 blue:0.68 alpha: 1.0]];␊ |
59 | [self setTitleColor:[NSColor whiteColor]];␊ |
60 | [self setGradientStartColor:[NSColor colorWithCalibratedWhite:0.93 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 | ␉␉NSGradient *gradient = [[[NSGradient alloc]␊ |
148 | ␉␉␉␉␉␉␉␉ initWithStartingColor:[self gradientStartColor]␊ |
149 | ␉␉␉␉␉␉␉␉ endingColor:[self gradientEndColor]] autorelease];␊ |
150 | NSRect gradientRect = [bgPath bounds];␊ |
151 | [gradient drawInRect:gradientRect angle:270.0];␊ |
152 | [nsContext restoreGraphicsState];␊ |
153 | } else {␊ |
154 | // Draw solid color background␊ |
155 | [backgroundColor set];␊ |
156 | [bgPath fill];␊ |
157 | }␊ |
158 | ␊ |
159 | ␊ |
160 | // Create drawing rectangle for title␊ |
161 | ␊ |
162 | float titleHInset = borderWidth + 4.0;␊ |
163 | float titleVInset = borderWidth;␊ |
164 | NSSize titleSize = [[self title] sizeWithAttributes:titleAttrs];␊ |
165 | NSRect titleRect = NSMakeRect(boxRect.origin.x + titleHInset, ␊ |
166 | boxRect.origin.y + boxRect.size.height - titleSize.height - (titleVInset * 2.0), ␊ |
167 | titleSize.width + borderWidth, ␊ |
168 | titleSize.height);␊ |
169 | titleRect.size.width = MIN(titleRect.size.width, boxRect.size.width - (2.0 * titleHInset));␊ |
170 | ␊ |
171 | if ([self selected]) {␊ |
172 | [[NSColor alternateSelectedControlColor] set];␊ |
173 | // We use the alternate (darker) selectedControlColor since the regular one is too light.␊ |
174 | // The alternate one is the highlight color for NSTableView, NSOutlineView, etc.␊ |
175 | // This mimics how Automator highlights the selected action in a workflow.␊ |
176 | } else {␊ |
177 | [borderColor set];␊ |
178 | }␊ |
179 | ␊ |
180 | ␊ |
181 | // Draw title background␊ |
182 | //NSRectFill(titleRect);␊ |
183 | [[self titlePathWithinRect:bgRect cornerRadius:radius titleRect:titleRect] fill];␊ |
184 | ␊ |
185 | ␊ |
186 | // Draw rounded rect around entire box␊ |
187 | if (borderWidth > 0.0) {␊ |
188 | [bgPath setLineWidth:borderWidth];␊ |
189 | [bgPath stroke];␊ |
190 | }␊ |
191 | ␊ |
192 | ␊ |
193 | // Draw title text␊ |
194 | [[self title] drawInRect:titleRect withAttributes:titleAttrs];␊ |
195 | //[[self titleCell] setTextColor:[self titleColor]];␊ |
196 | //[[self titleCell] drawWithFrame:titleRect inView:self];␊ |
197 | }␊ |
198 | ␊ |
199 | ␊ |
200 | - (NSBezierPath *)titlePathWithinRect:(NSRect)rect cornerRadius:(float)radius titleRect:(NSRect)titleRect␊ |
201 | {␊ |
202 | // Construct rounded rect path␊ |
203 | ␊ |
204 | NSRect bgRect = rect;␊ |
205 | int minX = NSMinX(bgRect);␊ |
206 | int maxX = minX + titleRect.size.width + ((titleRect.origin.x - rect.origin.x) * 2.0);␊ |
207 | int maxY = NSMaxY(bgRect);␊ |
208 | int minY = NSMinY(titleRect) - (maxY - (titleRect.origin.y + titleRect.size.height));␊ |
209 | float titleExpansionThreshold = 20.0;␊ |
210 | // i.e. if there's less than 20px space to the right of the short titlebar, just draw the full one.␊ |
211 | ␊ |
212 | NSBezierPath *path = [NSBezierPath bezierPath];␊ |
213 | ␊ |
214 | [path moveToPoint:NSMakePoint(minX, minY)];␊ |
215 | ␊ |
216 | if (bgRect.size.width - titleRect.size.width >= titleExpansionThreshold && ![self drawsFullTitleBar]) {␊ |
217 | // Draw a short titlebar␊ |
218 | [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) ␊ |
219 | toPoint:NSMakePoint(maxX, maxY) ␊ |
220 | radius:radius];␊ |
221 | [path lineToPoint:NSMakePoint(maxX, maxY)];␊ |
222 | } else {␊ |
223 | // Draw full titlebar, since we're either set to always do so, or we don't have room for a short one.␊ |
224 | [path lineToPoint:NSMakePoint(NSMaxX(bgRect), minY)];␊ |
225 | [path appendBezierPathWithArcFromPoint:NSMakePoint(NSMaxX(bgRect), maxY) ␊ |
226 | toPoint:NSMakePoint(NSMaxX(bgRect) - (bgRect.size.width / 2.0), maxY) ␊ |
227 | radius:radius];␊ |
228 | }␊ |
229 | ␊ |
230 | [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) ␊ |
231 | toPoint:NSMakePoint(minX, minY) ␊ |
232 | radius:radius];␊ |
233 | ␊ |
234 | [path closePath];␊ |
235 | ␊ |
236 | return path;␊ |
237 | }␊ |
238 | ␊ |
239 | ␊ |
240 | - (void)setTitle:(NSString *)newTitle␊ |
241 | {␊ |
242 | [super setTitle:newTitle];␊ |
243 | [self setNeedsDisplay:YES];␊ |
244 | }␊ |
245 | ␊ |
246 | ␊ |
247 | - (BOOL)drawsFullTitleBar␊ |
248 | {␊ |
249 | return drawsFullTitleBar;␊ |
250 | }␊ |
251 | ␊ |
252 | ␊ |
253 | - (void)setDrawsFullTitleBar:(BOOL)newDrawsFullTitleBar␊ |
254 | {␊ |
255 | drawsFullTitleBar = newDrawsFullTitleBar;␊ |
256 | [self setNeedsDisplay:YES];␊ |
257 | }␊ |
258 | ␊ |
259 | ␊ |
260 | - (BOOL)selected␊ |
261 | {␊ |
262 | return selected;␊ |
263 | }␊ |
264 | ␊ |
265 | ␊ |
266 | - (void)setSelected:(BOOL)newSelected␊ |
267 | {␊ |
268 | selected = newSelected;␊ |
269 | [self setNeedsDisplay:YES];␊ |
270 | }␊ |
271 | ␊ |
272 | ␊ |
273 | - (NSColor *)borderColor␊ |
274 | {␊ |
275 | return borderColor;␊ |
276 | }␊ |
277 | ␊ |
278 | ␊ |
279 | - (void)setBorderColor:(NSColor *)newBorderColor␊ |
280 | {␊ |
281 | [newBorderColor retain];␊ |
282 | [borderColor release];␊ |
283 | borderColor = newBorderColor;␊ |
284 | [self setNeedsDisplay:YES];␊ |
285 | }␊ |
286 | ␊ |
287 | - (void)setBorderWidth:(CGFloat)newBorderWidth␊ |
288 | {␊ |
289 | ␉borderWidth = newBorderWidth;␊ |
290 | [self setNeedsDisplay:YES];␊ |
291 | }␊ |
292 | ␊ |
293 | - (NSColor *)titleColor␊ |
294 | {␊ |
295 | return titleColor;␊ |
296 | }␊ |
297 | ␊ |
298 | ␊ |
299 | - (void)setTitleColor:(NSColor *)newTitleColor␊ |
300 | {␊ |
301 | [newTitleColor retain];␊ |
302 | [titleColor release];␊ |
303 | titleColor = newTitleColor;␊ |
304 | ␊ |
305 | [titleAttrs setObject:titleColor forKey:NSForegroundColorAttributeName];␊ |
306 | ␊ |
307 | [self setNeedsDisplay:YES];␊ |
308 | }␊ |
309 | ␊ |
310 | ␊ |
311 | - (NSColor *)gradientStartColor␊ |
312 | {␊ |
313 | return gradientStartColor;␊ |
314 | }␊ |
315 | ␊ |
316 | ␊ |
317 | - (void)setGradientStartColor:(NSColor *)newGradientStartColor␊ |
318 | {␊ |
319 | // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry.␊ |
320 | NSColor *newCalibratedGradientStartColor = [newGradientStartColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];␊ |
321 | [newCalibratedGradientStartColor retain];␊ |
322 | [gradientStartColor release];␊ |
323 | gradientStartColor = newCalibratedGradientStartColor;␊ |
324 | if ([self drawsGradientBackground]) {␊ |
325 | [self setNeedsDisplay:YES];␊ |
326 | }␊ |
327 | }␊ |
328 | ␊ |
329 | ␊ |
330 | - (NSColor *)gradientEndColor␊ |
331 | {␊ |
332 | return gradientEndColor;␊ |
333 | }␊ |
334 | ␊ |
335 | ␊ |
336 | - (void)setGradientEndColor:(NSColor *)newGradientEndColor␊ |
337 | {␊ |
338 | // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry.␊ |
339 | NSColor *newCalibratedGradientEndColor = [newGradientEndColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];␊ |
340 | [newCalibratedGradientEndColor retain];␊ |
341 | [gradientEndColor release];␊ |
342 | gradientEndColor = newCalibratedGradientEndColor;␊ |
343 | if ([self drawsGradientBackground]) {␊ |
344 | [self setNeedsDisplay:YES];␊ |
345 | }␊ |
346 | }␊ |
347 | ␊ |
348 | ␊ |
349 | - (NSColor *)backgroundColor␊ |
350 | {␊ |
351 | return backgroundColor;␊ |
352 | }␊ |
353 | ␊ |
354 | ␊ |
355 | - (void)setBackgroundColor:(NSColor *)newBackgroundColor␊ |
356 | {␊ |
357 | [newBackgroundColor retain];␊ |
358 | [backgroundColor release];␊ |
359 | backgroundColor = newBackgroundColor;␊ |
360 | if (![self drawsGradientBackground]) {␊ |
361 | [self setNeedsDisplay:YES];␊ |
362 | }␊ |
363 | }␊ |
364 | ␊ |
365 | ␊ |
366 | - (BOOL)drawsGradientBackground␊ |
367 | {␊ |
368 | return drawsGradientBackground;␊ |
369 | }␊ |
370 | ␊ |
371 | ␊ |
372 | - (void)setDrawsGradientBackground:(BOOL)newDrawsGradientBackground␊ |
373 | {␊ |
374 | drawsGradientBackground = newDrawsGradientBackground;␊ |
375 | [self setNeedsDisplay:YES];␊ |
376 | }␊ |
377 | ␊ |
378 | ␊ |
379 | @end␊ |
380 | |