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