1// 2// VLCHUDPopUpButtonCell.m 3// BGHUDAppKit 4// 5// Created by BinaryGod on 5/31/08. 6// 7// Copyright (c) 2008, Tim Davis (BinaryMethod.com, binary.god@gmail.com) 8// All rights reserved. 9// 10// Redistribution and use in source and binary forms, with or without modification, 11// are permitted provided that the following conditions are met: 12// 13// Redistributions of source code must retain the above copyright notice, this 14// list of conditions and the following disclaimer. 15// 16// Redistributions in binary form must reproduce the above copyright notice, 17// this list of conditions and the following disclaimer in the documentation and/or 18// other materials provided with the distribution. 19// 20// Neither the name of the BinaryMethod.com nor the names of its contributors 21// may be used to endorse or promote products derived from this software without 22// specific prior written permission. 23// 24// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND 25// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33// POSSIBILITY OF SUCH DAMAGE. 34 35#import "VLCHUDPopUpButtonCell.h" 36#import "CompatibilityFixes.h" 37#import "NSGradient+VLCAdditions.h" 38 39@implementation VLCHUDPopUpButtonCell 40 41+ (void)load 42{ 43 /* On 10.10+ we do not want custom drawing, therefore we swap out the implementation 44 * of the selectors below with their original implementations. 45 * Just calling super in the overridden methods is not enough, to get the same drawin 46 * a non-subclassed cell would use. 47 */ 48 if (OSX_YOSEMITE_AND_HIGHER) { 49 swapoutOverride([VLCHUDPopUpButtonCell class], @selector(initWithCoder:)); 50 swapoutOverride([VLCHUDPopUpButtonCell class], @selector(drawWithFrame:inView:)); 51 } 52} 53 54- (instancetype)initWithCoder:(NSCoder *)coder 55{ 56 self = [super initWithCoder:coder]; 57 if (self) { 58 _dropShadow = [[NSShadow alloc] init]; 59 [_dropShadow setShadowColor:[NSColor blackColor]]; 60 [_dropShadow setShadowBlurRadius:2]; 61 [_dropShadow setShadowOffset:NSMakeSize(0, -1)]; 62 63 _strokeColor = [NSColor colorWithDeviceRed:0.749f green:0.761f blue:0.788f alpha:1.0f]; 64 _darkStrokeColor = [NSColor colorWithDeviceRed:0.141f green:0.141f blue:0.141f alpha:0.5f]; 65 _disabledStrokeColor = [NSColor colorWithDeviceRed:0.749f green:0.761f blue:0.788f alpha:0.2f]; 66 _selectionTextActiveColor = [NSColor whiteColor]; 67 _selectionTextInActiveColor = [NSColor whiteColor]; 68 _cellTextColor = [NSColor whiteColor]; 69 _disabledCellTextColor = [NSColor colorWithDeviceRed:1 green:1 blue:1 alpha:0.2f]; 70 71 _normalGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251f green:0.251f blue:0.255f alpha:0.5f] 72 endingColor:[NSColor colorWithDeviceRed:0.118f green:0.118f blue:0.118f alpha:0.5f]]; 73 _disabledNormalGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251f green:0.251f blue:0.255f alpha:0.2f] 74 endingColor:[NSColor colorWithDeviceRed:0.118f green:0.118f blue:0.118f alpha:0.2f]]; 75 } 76 return self; 77} 78 79#pragma mark Drawing Functions 80 81- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 82 83 NSRect frame = cellFrame; 84 85 // Adjust frame by .5 so lines draw true 86 frame.origin.x += .5f; 87 frame.origin.y += .5f; 88 frame.size.height = [self cellSize].height; 89 90 // Make Adjustments to Frame based on Cell Size 91 switch ([self controlSize]) { 92 93 case NSRegularControlSize: 94 frame.origin.x += 3; 95 frame.size.width -= 7; 96 frame.origin.y += 2; 97 frame.size.height -= 7; 98 break; 99 100 case NSSmallControlSize: 101 frame.origin.y += 1; 102 frame.size.height -= 6; 103 frame.origin.x += 3; 104 frame.size.width -= 7; 105 break; 106 107 case NSMiniControlSize: 108 frame.origin.x += 1; 109 frame.size.width -= 4; 110 frame.size.height -= 2; 111 break; 112 } 113 114 if ([self isBordered]) { 115 NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:frame xRadius: 4 yRadius: 4]; 116 117 [NSGraphicsContext saveGraphicsState]; 118 [_dropShadow set]; 119 [_darkStrokeColor set]; 120 [path stroke]; 121 [NSGraphicsContext restoreGraphicsState]; 122 123 if ([self isEnabled]) { 124 [_normalGradient vlc_safeDrawInBezierPath: path angle: 90]; 125 [_strokeColor set]; 126 } else { 127 [_disabledNormalGradient vlc_safeDrawInBezierPath: path angle: 90]; 128 [_disabledStrokeColor set]; 129 } 130 131 [path setLineWidth: 1.0f ]; 132 [path stroke]; 133 } 134 135 // Draw the arrows 136 [self drawArrowsInRect: frame]; 137 138 // Adjust rect for title drawing 139 switch ([self controlSize]) { 140 141 case NSRegularControlSize: 142 143 frame.origin.x += 8; 144 frame.origin.y += 1; 145 frame.size.width -= 29; 146 break; 147 148 case NSSmallControlSize: 149 150 frame.origin.x += 8; 151 frame.origin.y += 2; 152 frame.size.width -= 29; 153 break; 154 155 case NSMiniControlSize: 156 157 frame.origin.x += 8; 158 frame.origin.y += .5f; 159 frame.size.width -= 26; 160 break; 161 } 162 163 NSMutableAttributedString *aTitle = [[self attributedTitle] mutableCopy]; 164 165 // Make sure aTitle actually contains something 166 if (aTitle.length > 0) { 167 [aTitle beginEditing]; 168 [aTitle removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, aTitle.length)]; 169 170 if (self.isEnabled) { 171 if (self.isHighlighted) { 172 173 if (self.controlView.window.isKeyWindow) { 174 [aTitle addAttribute:NSForegroundColorAttributeName 175 value:_selectionTextActiveColor 176 range:NSMakeRange(0, aTitle.length)]; 177 } else { 178 [aTitle addAttribute:NSForegroundColorAttributeName 179 value:_selectionTextInActiveColor 180 range:NSMakeRange(0, aTitle.length)]; 181 } 182 } else { 183 [aTitle addAttribute:NSForegroundColorAttributeName 184 value:_cellTextColor 185 range:NSMakeRange(0, aTitle.length)]; 186 } 187 } else { 188 [aTitle addAttribute:NSForegroundColorAttributeName 189 value:_disabledCellTextColor 190 range:NSMakeRange(0, aTitle.length)]; 191 } 192 [aTitle endEditing]; 193 } 194 195 int arrowAdjustment = 0; 196 197 cellFrame.size.height -= 2; 198 if (self.isBordered) { 199 cellFrame.origin.x += 5; 200 } 201 202 switch (self.controlSize) { 203 case NSRegularControlSize: 204 arrowAdjustment = 21; 205 break; 206 207 case NSSmallControlSize: 208 arrowAdjustment = 18; 209 break; 210 211 case NSMiniControlSize: 212 arrowAdjustment = 15; 213 break; 214 } 215 216 NSRect titleFrame = NSMakeRect(cellFrame.origin.x + 5, NSMidY(cellFrame) - ([aTitle size].height/2), cellFrame.size.width - arrowAdjustment, [aTitle size].height); 217 NSRect imageFrame = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width - arrowAdjustment, cellFrame.size.height); 218 219 if([self image]) { 220 221 switch ([self imagePosition]) { 222 223 case NSImageLeft: 224 case NSNoImage: 225 226 titleFrame.origin.x += 6; 227 titleFrame.origin.x += [[self image] size].width; 228 break; 229 230 case NSImageOnly: 231 titleFrame.size.width = 0; 232 break; 233 234 default: 235 break; 236 } 237 } 238 239 if([self imagePosition] != NSImageOnly) { 240 [super drawTitle:aTitle withFrame:titleFrame inView:controlView]; 241 } 242 243 244 if([self imagePosition] != NSNoImage) { 245 [self drawImage:[self image] withFrame:imageFrame inView:controlView]; 246 } 247} 248 249- (void)drawArrowsInRect:(NSRect) frame { 250 251 CGFloat arrowsWidth; 252 CGFloat arrowsHeight; 253 CGFloat arrowWidth; 254 CGFloat arrowHeight; 255 256 int arrowAdjustment = 0; 257 258 //Adjust based on Control size 259 switch ([self controlSize]) { 260 default: // Silence uninitialized variable warnings 261 case NSRegularControlSize: 262 263 if ([self isBordered]) { 264 265 arrowAdjustment = 21; 266 } else { 267 268 arrowAdjustment = 11; 269 } 270 271 arrowWidth = 3.5f; 272 arrowHeight = 2.5f; 273 arrowsHeight = 2; 274 arrowsWidth = 2.5f; 275 break; 276 277 case NSSmallControlSize: 278 279 if ([self isBordered]) { 280 281 arrowAdjustment = 18; 282 } else { 283 284 arrowAdjustment = 8; 285 } 286 287 arrowWidth = 3.5f; 288 arrowHeight = 2.5f; 289 arrowsHeight = 2; 290 arrowsWidth = 2.5f; 291 292 break; 293 294 case NSMiniControlSize: 295 296 if ([self isBordered]) { 297 298 arrowAdjustment = 15; 299 } else { 300 301 arrowAdjustment = 5; 302 } 303 304 arrowWidth = 2.5f; 305 arrowHeight = 1.5f; 306 arrowsHeight = 1.5f; 307 arrowsWidth = 2; 308 break; 309 } 310 311 frame.origin.x += (frame.size.width - arrowAdjustment); 312 frame.size.width = arrowAdjustment; 313 314 if ([self pullsDown]) { 315 316 NSBezierPath *arrow = [[NSBezierPath alloc] init]; 317 318 NSPoint points[3]; 319 320 points[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowWidth), frame.origin.y + ((frame.size.height /2) - arrowHeight)); 321 points[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowWidth), frame.origin.y + ((frame.size.height /2) - arrowHeight)); 322 points[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) + arrowHeight)); 323 324 [arrow appendBezierPathWithPoints: points count: 3]; 325 326 if ([self isEnabled]) { 327 328 if ([self isHighlighted]) { 329 330 if ([[[self controlView] window] isKeyWindow]) { 331 332 [_selectionTextActiveColor set]; 333 } else { 334 335 [_selectionTextInActiveColor set]; 336 } 337 } else { 338 339 [_cellTextColor set]; 340 } 341 } else { 342 343 [_disabledCellTextColor set]; 344 } 345 346 [arrow fill]; 347 } else { 348 349 NSBezierPath *topArrow = [[NSBezierPath alloc] init]; 350 351 NSPoint topPoints[3]; 352 353 topPoints[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowsWidth), frame.origin.y + ((frame.size.height /2) - arrowsHeight)); 354 topPoints[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowsWidth), frame.origin.y + ((frame.size.height /2) - arrowsHeight)); 355 topPoints[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) - ((arrowsHeight * 2) + 2))); 356 357 [topArrow appendBezierPathWithPoints: topPoints count: 3]; 358 359 if([self isEnabled]) { 360 361 if([self isHighlighted]) { 362 363 if([[[self controlView] window] isKeyWindow]) 364 { 365 366 [_selectionTextActiveColor set]; 367 } else { 368 369 [_selectionTextInActiveColor set]; 370 } 371 } else { 372 373 [_cellTextColor set]; 374 } 375 } else { 376 377 [_disabledCellTextColor set]; 378 } 379 [topArrow fill]; 380 381 NSBezierPath *bottomArrow = [[NSBezierPath alloc] init]; 382 383 NSPoint bottomPoints[3]; 384 385 bottomPoints[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowsWidth), frame.origin.y + ((frame.size.height /2) + arrowsHeight)); 386 bottomPoints[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowsWidth), frame.origin.y + ((frame.size.height /2) + arrowsHeight)); 387 bottomPoints[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) + ((arrowsHeight * 2) + 2))); 388 389 [bottomArrow appendBezierPathWithPoints: bottomPoints count: 3]; 390 391 if ([self isEnabled]) { 392 393 if ([self isHighlighted]) { 394 395 if ([[[self controlView] window] isKeyWindow]) { 396 397 [_selectionTextActiveColor set]; 398 } else { 399 400 [_selectionTextInActiveColor set]; 401 } 402 } else { 403 404 [_cellTextColor set]; 405 } 406 } else { 407 408 [_disabledCellTextColor set]; 409 } 410 [bottomArrow fill]; 411 } 412} 413 414@end 415