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