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