macOS: No custom HUD drawing on macOS 10.10+
[vlc.git] / modules / gui / macosx / VLCHUDSliderCell.m
1 //
2 //  VLCHUDSliderCell.m
3 //  BGHUDAppKit
4 //
5 //  Created by BinaryGod on 5/30/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 "VLCHUDSliderCell.h"
36 #import "CompatibilityFixes.h"
37
38 @implementation VLCHUDSliderCell
39
40 - (instancetype)initWithCoder:(NSCoder *)coder
41 {
42     self = [super initWithCoder:coder];
43     if (self) {
44         // Custom colors for the slider
45         _sliderColor            = [NSColor colorWithCalibratedRed:0.318 green:0.318 blue:0.318 alpha:0.6];
46         _disabledSliderColor    = [NSColor colorWithCalibratedRed:0.318 green:0.318 blue:0.318 alpha:0.2];
47         _strokeColor            = [NSColor colorWithCalibratedRed:0.749 green:0.761 blue:0.788 alpha:1.0];
48         _disabledStrokeColor    = [NSColor colorWithCalibratedRed:0.749 green:0.761 blue:0.788 alpha:0.2];
49
50         // Custom knob gradients
51         _knobGradient           = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251 green:0.251 blue:0.255 alpha:1.0]
52                                                                 endingColor:[NSColor colorWithDeviceRed:0.118 green:0.118 blue:0.118 alpha:1.0]];
53         _disableKnobGradient    = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceRed:0.251 green:0.251 blue:0.255 alpha:1.0]
54                                                                 endingColor:[NSColor colorWithDeviceRed:0.118 green:0.118 blue:0.118 alpha:1.0]];
55     }
56     return self;
57 }
58
59 NSAffineTransform* RotationTransform(const CGFloat angle, const NSPoint point)
60 {
61     NSAffineTransform*  transform = [NSAffineTransform transform];
62     [transform translateXBy:point.x yBy:point.y];
63     [transform rotateByRadians:angle];
64     [transform translateXBy:-(point.x) yBy:-(point.y)];
65     return transform;
66 }
67
68 #pragma clang diagnostic push
69 #pragma clang diagnostic ignored "-Wpartial-availability"
70 - (void)drawKnob:(NSRect)smallRect
71 {
72     if (OSX_YOSEMITE_AND_HIGHER) {
73         return [super drawKnob:smallRect];
74     }
75     NSBezierPath *path = [NSBezierPath bezierPath];
76     // Inset rect to have enough room for the stroke
77     smallRect = NSInsetRect(smallRect, 1.0, 1.0);
78
79     // Get min/max/mid coords for shape calculations
80     CGFloat minX = NSMinX(smallRect);
81     CGFloat minY = NSMinY(smallRect);
82     CGFloat maxX = NSMaxX(smallRect);
83     CGFloat maxY = NSMaxY(smallRect);
84     CGFloat midX = NSMidX(smallRect);
85     CGFloat midY = NSMidY(smallRect);
86
87     // Draw the knobs shape
88     if (self.numberOfTickMarks > 0) {
89         // We have tickmarks, draw an arrow-like shape
90         if (self.isVertical) {
91             // For some reason the rect is not alligned correctly at
92             // tickmarks and clipped, so this ugly thing is necessary:
93             maxY = maxY - 2;
94             midY = midY - 1;
95
96             // Right pointing arrow
97             [path moveToPoint:NSMakePoint(minX + 3, minY)];
98             [path lineToPoint:NSMakePoint(midX + 2, minY)];
99             [path lineToPoint:NSMakePoint(maxX, midY)];
100             [path lineToPoint:NSMakePoint(midX + 2, maxY)];
101             [path lineToPoint:NSMakePoint(minX + 3, maxY)];
102             [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY)
103                                            toPoint:NSMakePoint(minX, maxY - 3)
104                                             radius:2.5f];
105             [path lineToPoint:NSMakePoint(minX, maxY - 3)];
106             [path lineToPoint:NSMakePoint(minX, minY + 3)];
107             [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, minY)
108                                            toPoint:NSMakePoint(minX + 3, minY)
109                                             radius:2.5f];
110             [path closePath];
111         } else {
112             // Down pointing arrow
113             [path moveToPoint:NSMakePoint(minX + 3, minY)];
114             [path lineToPoint:NSMakePoint(maxX - 3, minY)];
115             [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY)
116                                            toPoint:NSMakePoint(maxX, minY + 3)
117                                             radius:2.5f];
118             [path lineToPoint:NSMakePoint(maxX, minY + 3)];
119             [path lineToPoint:NSMakePoint(maxX, midY + 2)];
120             [path lineToPoint:NSMakePoint(midX, maxY)];
121             [path lineToPoint:NSMakePoint(minX, midY + 2)];
122             [path lineToPoint:NSMakePoint(minX, minY + 3)];
123             [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, minY)
124                                            toPoint:NSMakePoint(minX + 3, minY)
125                                             radius:2.5f];
126             [path closePath];
127         }
128
129         // Rotate our knob if needed to the correct position
130         if (self.tickMarkPosition == NSTickMarkAbove) {
131             NSAffineTransform *transform = nil;
132             transform = RotationTransform(M_PI, NSMakePoint(midX, midY));
133             [path transformUsingAffineTransform:transform];
134         }
135     } else {
136         // We have no tickmarks, draw a round knob
137         [path appendBezierPathWithOvalInRect:NSInsetRect(smallRect, 0.5, 0.5)];
138     }
139
140     // Draw the knobs background
141     if (self.isEnabled && self.isHighlighted) {
142         [_knobGradient drawInBezierPath:path angle:270.0f];
143     } else if (self.isEnabled) {
144         [_knobGradient drawInBezierPath:path angle:90.0f];
145     } else {
146         [_disableKnobGradient drawInBezierPath:path angle:90.0f];
147     }
148
149     // Draw white stroke around the knob
150     if (self.isEnabled) {
151         [_strokeColor setStroke];
152     } else {
153         [_disabledStrokeColor setStroke];
154     }
155
156     [path setLineWidth:1.0];
157     [path stroke];
158 }
159
160 - (void)drawBarInside:(NSRect)fullRect flipped:(BOOL)flipped
161 {
162     if (OSX_YOSEMITE_AND_HIGHER) {
163         return [super drawBarInside:fullRect flipped:flipped];
164     }
165     if (self.isVertical) {
166         return [self drawVerticalBarInFrame:fullRect];
167     } else {
168         return [self drawHorizontalBarInFrame:fullRect];
169     }
170 }
171 #pragma clang diagnostic pop
172
173 - (void)drawVerticalBarInFrame:(NSRect)frame
174 {
175     // Adjust frame based on ControlSize
176     switch ([self controlSize]) {
177
178         case NSRegularControlSize:
179
180             if ([self numberOfTickMarks] != 0) {
181                 if ([self tickMarkPosition] == NSTickMarkRight) {
182                     frame.origin.x += 4;
183                 } else {
184                     frame.origin.x += frame.size.width - 9;
185                 }
186             } else {
187                 frame.origin.x = frame.origin.x + (((frame.origin.x + frame.size.width) /2) - 2.5f);
188             }
189             frame.origin.x += 0.5f;
190             frame.origin.y += 2.5f;
191             frame.size.height -= 6;
192             frame.size.width = 5;
193             break;
194
195         case NSSmallControlSize:
196
197             if ([self numberOfTickMarks] != 0) {
198                 if ([self tickMarkPosition] == NSTickMarkRight) {
199                     frame.origin.x += 3;
200                 } else {
201                     frame.origin.x += frame.size.width - 8;
202                 }
203             } else {
204                 frame.origin.x = frame.origin.x + (((frame.origin.x + frame.size.width) /2) - 2.5f);
205             }
206             frame.origin.y += 0.5f;
207             frame.size.height -= 1;
208             frame.origin.x += 0.5f;
209             frame.size.width = 5;
210             break;
211
212         case NSMiniControlSize:
213
214             if ([self numberOfTickMarks] != 0) {
215                 if ([self tickMarkPosition] == NSTickMarkRight) {
216                     frame.origin.x += 2.5f;
217                 } else {
218                     frame.origin.x += frame.size.width - 6.5f;
219                 }
220             } else {
221                 frame.origin.x = frame.origin.x + (((frame.origin.x + frame.size.width) /2) - 2);
222             }
223             frame.origin.x += 1;
224             frame.origin.y += 0.5f;
225             frame.size.height -= 1;
226             frame.size.width = 3;
227             break;
228     }
229     
230     // Draw Bar
231     NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect: frame xRadius: 2 yRadius: 2];
232     
233     if ([self isEnabled]) {
234         [_sliderColor set];
235         [path fill];
236
237         [_strokeColor set];
238         [path stroke];
239     } else {
240         [_disabledSliderColor set];
241         [path fill];
242
243         [_disabledStrokeColor set];
244         [path stroke];
245     }
246 }
247
248 - (void)drawHorizontalBarInFrame:(NSRect)frame
249 {
250     // Adjust frame based on ControlSize
251     switch ([self controlSize]) {
252
253         case NSRegularControlSize:
254
255             if ([self numberOfTickMarks] != 0) {
256                 if ([self tickMarkPosition] == NSTickMarkBelow) {
257                     frame.origin.y += 4;
258                 } else {
259                     frame.origin.y += frame.size.height - 10;
260                 }
261             } else {
262                 frame.origin.y = frame.origin.y + (((frame.origin.y + frame.size.height) /2) - 2.5f);
263             }
264             frame.origin.x += 2.5f;
265             frame.origin.y += 0.5f;
266             frame.size.width -= 5;
267             frame.size.height = 5;
268             break;
269
270         case NSSmallControlSize:
271
272             if ([self numberOfTickMarks] != 0) {
273                 if ([self tickMarkPosition] == NSTickMarkBelow) {
274                     frame.origin.y += 2;
275                 } else {
276                     frame.origin.y += frame.size.height - 8;
277                 }
278             } else {
279                 frame.origin.y = frame.origin.y + (((frame.origin.y + frame.size.height) /2) - 2.5f);
280             }
281             frame.origin.x += 0.5f;
282             frame.origin.y += 0.5f;
283             frame.size.width -= 1;
284             frame.size.height = 5;
285             break;
286
287         case NSMiniControlSize:
288
289             if ([self numberOfTickMarks] != 0) {
290                 if ([self tickMarkPosition] == NSTickMarkBelow) {
291                     frame.origin.y += 2;
292                 } else {
293                     frame.origin.y += frame.size.height - 6;
294                 }
295             } else {
296                 frame.origin.y = frame.origin.y + (((frame.origin.y + frame.size.height) /2) - 2);
297             }
298             frame.origin.x += 0.5f;
299             frame.origin.y += 0.5f;
300             frame.size.width -= 1;
301             frame.size.height = 3;
302             break;
303     }
304     
305     // Draw Bar
306     NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:frame xRadius:2 yRadius:2];
307     
308     if ([self isEnabled]) {
309         [_sliderColor set];
310         [path fill];
311         
312         [_strokeColor set];
313         [path stroke];
314     } else {
315         [_disabledSliderColor set];
316         [path fill];
317         
318         [_disabledStrokeColor set];
319         [path stroke];
320     }
321 }
322
323 - (void)drawTickMarks
324 {
325     if (OSX_YOSEMITE_AND_HIGHER) {
326         return [super drawTickMarks];
327     }
328     for (int i = 0; i < self.numberOfTickMarks; i++) {
329         NSRect tickMarkRect = [self rectOfTickMarkAtIndex:i];
330         if (self.isEnabled) {
331             [_strokeColor setFill];
332         } else {
333             [_disabledStrokeColor setFill];
334         }
335         NSRectFill(tickMarkRect);
336     }
337 }
338
339 @end