macOS: Add VLCPopUpButtonCell
[vlc.git] / modules / gui / macosx / VLCPopUpButtonCell.m
1 //
2 //  VLCPopUpButtonCell.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 "VLCPopUpButtonCell.h"
36
37 @implementation VLCPopUpButtonCell
38
39 - (instancetype)initWithCoder:(NSCoder *)coder
40 {
41     self = [super initWithCoder:coder];
42     if (self) {
43         _dropShadow = [[NSShadow alloc] init];
44         [_dropShadow setShadowColor:[NSColor blackColor]];
45         [_dropShadow setShadowBlurRadius:2];
46         [_dropShadow setShadowOffset:NSMakeSize(0, -1)];
47
48         _strokeColor = [NSColor colorWithDeviceRed:0.749f green:0.761f blue:0.788f alpha:1.0f];
49         _darkStrokeColor = [NSColor colorWithDeviceRed:0.141f green:0.141f blue:0.141f alpha:0.5f];
50         _disabledStrokeColor = [NSColor colorWithDeviceRed:0.749f green:0.761f blue:0.788f alpha:0.2f];
51         _selectionTextActiveColor = [NSColor whiteColor];
52         _selectionTextInActiveColor = [NSColor whiteColor];
53         _cellTextColor = [NSColor whiteColor];
54         _disabledCellTextColor = [NSColor colorWithDeviceRed:1 green:1 blue:1 alpha:0.2f];
55
56         _normalGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251f green:0.251f blue:0.255f alpha:0.5f]
57                                                         endingColor:[NSColor colorWithDeviceRed:0.118f green:0.118f blue:0.118f alpha:0.5f]];
58         _disabledNormalGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251f green:0.251f blue:0.255f alpha:0.2f]
59                                                                 endingColor:[NSColor colorWithDeviceRed:0.118f green:0.118f blue:0.118f alpha:0.2f]];
60     }
61     return self;
62 }
63
64 #pragma mark Drawing Functions
65
66 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
67
68     NSRect frame = cellFrame;
69
70     // Adjust frame by .5 so lines draw true
71     frame.origin.x += .5f;
72     frame.origin.y += .5f;
73     frame.size.height = [self cellSize].height;
74
75     // Make Adjustments to Frame based on Cell Size
76     switch ([self controlSize]) {
77
78         case NSRegularControlSize:
79             frame.origin.x += 3;
80             frame.size.width -= 7;
81             frame.origin.y += 2;
82             frame.size.height -= 7;
83             break;
84
85         case NSSmallControlSize:
86             frame.origin.y += 1;
87             frame.size.height -= 6;
88             frame.origin.x += 3;
89             frame.size.width -= 7;
90             break;
91
92         case NSMiniControlSize:
93             frame.origin.x += 1;
94             frame.size.width -= 4;
95             frame.size.height -= 2;
96             break;
97     }
98
99     if ([self isBordered]) {
100         NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:frame xRadius: 4 yRadius: 4];
101
102         [NSGraphicsContext saveGraphicsState];
103         [_dropShadow set];
104         [_darkStrokeColor set];
105         [path stroke];
106         [NSGraphicsContext restoreGraphicsState];
107
108         if ([self isEnabled]) {
109             [_normalGradient drawInBezierPath: path angle: 90];
110             [_strokeColor set];
111         } else {
112             [_disabledNormalGradient drawInBezierPath: path angle: 90];
113             [_disabledStrokeColor set];
114         }
115
116         [path setLineWidth: 1.0f ];
117         [path stroke];
118     }
119
120     // Draw the arrows
121     [self drawArrowsInRect: frame];
122
123     // Adjust rect for title drawing
124     switch ([self controlSize]) {
125
126         case NSRegularControlSize:
127
128             frame.origin.x += 8;
129             frame.origin.y += 1;
130             frame.size.width -= 29;
131             break;
132
133         case NSSmallControlSize:
134
135             frame.origin.x += 8;
136             frame.origin.y += 2;
137             frame.size.width -= 29;
138             break;
139
140         case NSMiniControlSize:
141
142             frame.origin.x += 8;
143             frame.origin.y += .5f;
144             frame.size.width -= 26;
145             break;
146     }
147
148     NSMutableAttributedString *aTitle = [[self attributedTitle] mutableCopy];
149
150     // Make sure aTitle actually contains something
151     if (aTitle.length > 0) {
152         [aTitle beginEditing];
153         [aTitle removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, aTitle.length)];
154
155         if (self.isEnabled) {
156             if (self.isHighlighted) {
157
158                 if (self.controlView.window.isKeyWindow) {
159                     [aTitle addAttribute:NSForegroundColorAttributeName
160                                    value:_selectionTextActiveColor
161                                    range:NSMakeRange(0, aTitle.length)];
162                 } else {
163                     [aTitle addAttribute:NSForegroundColorAttributeName
164                                    value:_selectionTextInActiveColor
165                                    range:NSMakeRange(0, aTitle.length)];
166                 }
167             } else {
168                 [aTitle addAttribute:NSForegroundColorAttributeName
169                                value:_cellTextColor
170                                range:NSMakeRange(0, aTitle.length)];
171             }
172         } else {
173             [aTitle addAttribute:NSForegroundColorAttributeName
174                            value:_disabledCellTextColor
175                            range:NSMakeRange(0, aTitle.length)];
176         }
177         [aTitle endEditing];
178     }
179
180     int arrowAdjustment = 0;
181
182     cellFrame.size.height -= 2;
183     if (self.isBordered) {
184         cellFrame.origin.x += 5;
185     }
186
187     switch (self.controlSize) {
188         case NSRegularControlSize:
189             arrowAdjustment = 21;
190             break;
191
192         case NSSmallControlSize:
193             arrowAdjustment = 18;
194             break;
195
196         case NSMiniControlSize:
197             arrowAdjustment = 15;
198             break;
199     }
200
201     NSRect titleFrame = NSMakeRect(cellFrame.origin.x + 5, NSMidY(cellFrame) - ([aTitle size].height/2), cellFrame.size.width - arrowAdjustment, [aTitle size].height);
202     NSRect imageFrame = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width - arrowAdjustment, cellFrame.size.height);
203
204     if([self image]) {
205
206         switch ([self imagePosition]) {
207
208             case NSImageLeft:
209             case NSNoImage:
210
211                 titleFrame.origin.x += 6;
212                 titleFrame.origin.x += [[self image] size].width;
213                 break;
214
215             case NSImageOnly:
216                 titleFrame.size.width = 0;
217                 break;
218
219             default:
220                 break;
221         }
222     }
223
224     if([self imagePosition] != NSImageOnly) {
225         [super drawTitle:aTitle withFrame:titleFrame inView:controlView];
226     }
227
228
229     if([self imagePosition] != NSNoImage) {
230         [self drawImage:[self image] withFrame:imageFrame inView:controlView];
231     }
232 }
233
234 - (void)drawArrowsInRect:(NSRect) frame {
235
236     CGFloat arrowsWidth;
237     CGFloat arrowsHeight;
238     CGFloat arrowWidth;
239     CGFloat arrowHeight;
240
241     int arrowAdjustment = 0;
242
243     //Adjust based on Control size
244     switch ([self controlSize]) {
245         default: // Silence uninitialized variable warnings
246         case NSRegularControlSize:
247
248             if ([self isBordered]) {
249
250                 arrowAdjustment = 21;
251             } else {
252
253                 arrowAdjustment = 11;
254             }
255
256             arrowWidth = 3.5f;
257             arrowHeight = 2.5f;
258             arrowsHeight = 2;
259             arrowsWidth = 2.5f;
260             break;
261
262         case NSSmallControlSize:
263
264             if ([self isBordered]) {
265
266                 arrowAdjustment = 18;
267             } else {
268
269                 arrowAdjustment = 8;
270             }
271
272             arrowWidth = 3.5f;
273             arrowHeight = 2.5f;
274             arrowsHeight = 2;
275             arrowsWidth = 2.5f;
276
277             break;
278
279         case NSMiniControlSize:
280
281             if ([self isBordered]) {
282
283                 arrowAdjustment = 15;
284             } else {
285
286                 arrowAdjustment = 5;
287             }
288
289             arrowWidth = 2.5f;
290             arrowHeight = 1.5f;
291             arrowsHeight = 1.5f;
292             arrowsWidth = 2;
293             break;
294     }
295
296     frame.origin.x += (frame.size.width - arrowAdjustment);
297     frame.size.width = arrowAdjustment;
298
299     if ([self pullsDown]) {
300
301         NSBezierPath *arrow = [[NSBezierPath alloc] init];
302
303         NSPoint points[3];
304
305         points[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowWidth), frame.origin.y + ((frame.size.height /2) - arrowHeight));
306         points[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowWidth), frame.origin.y + ((frame.size.height /2) - arrowHeight));
307         points[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) + arrowHeight));
308
309         [arrow appendBezierPathWithPoints: points count: 3];
310         
311         if ([self isEnabled]) {
312             
313             if ([self isHighlighted]) {
314                 
315                 if ([[[self controlView] window] isKeyWindow]) {
316                     
317                     [_selectionTextActiveColor set];
318                 } else {
319                     
320                     [_selectionTextInActiveColor set];
321                 }
322             } else {
323                 
324                 [_cellTextColor set];
325             }
326         } else {
327             
328             [_disabledCellTextColor set];
329         }
330         
331         [arrow fill];
332     } else {
333         
334         NSBezierPath *topArrow = [[NSBezierPath alloc] init];
335         
336         NSPoint topPoints[3];
337         
338         topPoints[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowsWidth), frame.origin.y + ((frame.size.height /2) - arrowsHeight));
339         topPoints[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowsWidth), frame.origin.y + ((frame.size.height /2) - arrowsHeight));
340         topPoints[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) - ((arrowsHeight * 2) + 2)));
341         
342         [topArrow appendBezierPathWithPoints: topPoints count: 3];
343         
344         if([self isEnabled]) {
345             
346             if([self isHighlighted]) {
347                 
348                 if([[[self controlView] window] isKeyWindow])
349                 {
350                     
351                     [_selectionTextActiveColor set];
352                 } else {
353                     
354                     [_selectionTextInActiveColor set];
355                 }
356             } else {
357                 
358                 [_cellTextColor set];
359             }
360         } else {
361             
362             [_disabledCellTextColor set];
363         }
364         [topArrow fill];
365         
366         NSBezierPath *bottomArrow = [[NSBezierPath alloc] init];
367         
368         NSPoint bottomPoints[3];
369         
370         bottomPoints[0] = NSMakePoint(frame.origin.x + ((frame.size.width /2) - arrowsWidth), frame.origin.y + ((frame.size.height /2) + arrowsHeight));
371         bottomPoints[1] = NSMakePoint(frame.origin.x + ((frame.size.width /2) + arrowsWidth), frame.origin.y + ((frame.size.height /2) + arrowsHeight));
372         bottomPoints[2] = NSMakePoint(frame.origin.x + (frame.size.width /2), frame.origin.y + ((frame.size.height /2) + ((arrowsHeight * 2) + 2)));
373         
374         [bottomArrow appendBezierPathWithPoints: bottomPoints count: 3];
375         
376         if ([self isEnabled]) {
377             
378             if ([self isHighlighted]) {
379                 
380                 if ([[[self controlView] window] isKeyWindow]) {
381                     
382                     [_selectionTextActiveColor set];
383                 } else {
384                     
385                     [_selectionTextInActiveColor set];
386                 }
387             } else {
388                 
389                 [_cellTextColor set];
390             }
391         } else {
392             
393             [_disabledCellTextColor set];
394         }
395         [bottomArrow fill];
396     }
397 }
398
399 @end