macOS: No custom HUD drawing on macOS 10.10+
[vlc.git] / modules / gui / macosx / VLCHUDTextFieldCell.m
1 //
2 //  VLCHUDTextFieldCell.m
3 //  BGHUDAppKit
4 //
5 //  Created by BinaryGod on 6/2/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 //  History
36 //
37 //              8/30/2010 - Fixed placeholder alignment not rendering while in design view,
38 //                                      provided by [tylerb](GitHub).
39
40 #import "VLCHUDTextFieldCell.h"
41 #import "CompatibilityFixes.h"
42
43 @interface NSTextFieldCell (Private)
44
45 - (void)_drawKeyboardFocusRingWithFrame:(NSRect)rect inView:(NSView*)view;
46
47 @end
48
49 @interface VLCHUDTextFieldCell () {
50     bool fillsBackground;
51 }
52
53 @end
54
55 @implementation VLCHUDTextFieldCell
56
57 + (void)load
58 {
59     /* On 10.10+ we do not want custom drawing, therefore we swap out the implementation
60      * of the selectors below with their original implementations.
61      */
62     if (OSX_YOSEMITE_AND_HIGHER) {
63         swapoutOverride([VLCHUDTextFieldCell class], @selector(initTextCell:));
64         swapoutOverride([VLCHUDTextFieldCell class], @selector(initWithCoder:));
65         swapoutOverride([VLCHUDTextFieldCell class], @selector(setUpFieldEditorAttributes:));
66         swapoutOverride([VLCHUDTextFieldCell class], @selector(drawWithFrame:inView:));
67         swapoutOverride([VLCHUDTextFieldCell class], @selector(_drawKeyboardFocusRingWithFrame:inView:));
68     }
69 }
70
71 #pragma mark Drawing Functions
72
73 - (instancetype)initTextCell:(NSString *)aString
74 {
75     self = [super initTextCell: aString];
76
77     if (self) {
78         [self commonInit];
79     }
80
81     return self;
82 }
83
84 - (instancetype)initWithCoder:(NSCoder *)decoder
85 {
86     self = [super initWithCoder:decoder];
87
88     if (self) {
89         [self commonInit];
90     }
91
92     return self;
93 }
94
95 - (void)commonInit
96 {
97     // Init colors
98     _focusRing = [[NSShadow alloc] init];
99     [_focusRing setShadowColor:NSColor.whiteColor];
100     [_focusRing setShadowBlurRadius:3];
101     [_focusRing setShadowOffset:NSMakeSize(0, 0)];
102
103     _strokeColor = [NSColor colorWithDeviceRed:0.749f green:0.761f blue:0.788f alpha:1.0f];
104     _disabledStrokeColor = [NSColor colorWithDeviceRed:1 green:1 blue:1 alpha:0.2f];
105
106     _selectionHighlightActiveColor = [NSColor darkGrayColor];
107     _selectionTextActiveColor = [NSColor whiteColor];
108     _selectionHighlightInActiveColor = [NSColor darkGrayColor];
109     _selectionTextInActiveColor = [NSColor whiteColor];
110
111     _placeholderTextColor = [NSColor grayColor];
112     _cellTextColor = [NSColor whiteColor];
113     _textFillColor = [NSColor colorWithDeviceRed:.224f green:.224f blue:.224f alpha:.95f];
114
115     // Init some properties
116     [self setTextColor:_cellTextColor];
117
118     if ([self drawsBackground]) {
119         fillsBackground = YES;
120     }
121
122     [self setDrawsBackground: NO];
123 }
124
125 - (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
126 {
127     NSText *newText = [super setUpFieldEditorAttributes:textObj];
128     NSColor *textColor = _cellTextColor;
129     [(NSTextView *)newText setInsertionPointColor:textColor];
130     return newText;
131 }
132
133 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
134 {
135     // Adjust Rect
136     cellFrame = NSInsetRect(cellFrame, 0.5f, 0.5f);
137
138     // Create Path
139     NSBezierPath *path = [NSBezierPath bezierPath];
140
141     if ([self bezelStyle] == NSTextFieldRoundedBezel) {
142
143         [path appendBezierPathWithArcWithCenter:NSMakePoint(cellFrame.origin.x + (cellFrame.size.height /2), cellFrame.origin.y + (cellFrame.size.height /2))
144                                          radius:cellFrame.size.height /2
145                                      startAngle:90
146                                        endAngle:270];
147
148         [path appendBezierPathWithArcWithCenter:NSMakePoint(cellFrame.origin.x + (cellFrame.size.width - (cellFrame.size.height /2)), cellFrame.origin.y + (cellFrame.size.height /2))
149                                          radius:cellFrame.size.height /2
150                                      startAngle:270
151                                        endAngle:90];
152
153         [path closePath];
154
155     } else {
156         [path appendBezierPathWithRoundedRect: cellFrame xRadius: 3.0f yRadius: 3.0f];
157     }
158
159     // Draw Background
160     if (fillsBackground) {
161         [_textFillColor set];
162         [path fill];
163     }
164
165     if ([self isBezeled] || [self isBordered]) {
166
167         [NSGraphicsContext saveGraphicsState];
168
169         if ([super showsFirstResponder] && [[[self controlView] window] isKeyWindow] &&
170            ([self focusRingType] == NSFocusRingTypeDefault ||
171             [self focusRingType] == NSFocusRingTypeExterior)) {
172                [_focusRing set];
173            }
174
175         // Check State
176         if ([self isEnabled]) {
177             [_strokeColor set];
178         } else {
179             [_disabledStrokeColor set];
180         }
181
182         [path setLineWidth:1.0f];
183         [path stroke];
184
185         [NSGraphicsContext restoreGraphicsState];
186     }
187
188     // Get TextView for this editor
189     NSTextView* view = (NSTextView*)[[controlView window] fieldEditor: NO forObject: controlView];
190
191     // If window/app is active draw the highlight/text in active colors
192     if (![self isHighlighted]) {
193
194         if ([view selectedRange].length > 0) {
195
196             // Get Attributes of the selected text
197             NSMutableDictionary *dict = [[view selectedTextAttributes] mutableCopy];
198
199             if ([[[self controlView] window] isKeyWindow]) {
200                 [dict setObject:_selectionHighlightActiveColor
201                          forKey:NSBackgroundColorAttributeName];
202
203                 [view setTextColor:_selectionTextActiveColor
204                              range:[view selectedRange]];
205             } else {
206                 [dict setObject:_selectionHighlightInActiveColor
207                          forKey:NSBackgroundColorAttributeName];
208
209                 [view setTextColor:_selectionTextInActiveColor
210                              range:[view selectedRange]];
211             }
212
213             [view setSelectedTextAttributes:dict];
214         } else {
215             // Only change color (marks view as dirty) if it had a selection at some point,
216             // thus changing the colors.
217             if ([view textColor] != _cellTextColor) {
218                 [self setTextColor:_cellTextColor];
219                 [view setTextColor:_cellTextColor];
220             }
221         }
222     } else {
223
224         if ([self isEnabled]) {
225             if ([self isHighlighted]) {
226                 if ([[[self controlView] window] isKeyWindow]){
227                     [self setTextColor:_selectionTextActiveColor];
228                 } else {
229                     [self setTextColor:_selectionTextInActiveColor];
230                 }
231             } else {
232                 [self setTextColor:_cellTextColor];
233             }
234         } else {
235             [self setTextColor:_disabledCellTextColor];
236         }
237     }
238
239     // Check to see if the attributed placeholder has been set or not
240     if (![self placeholderAttributedString] && [self placeholderString]) {
241
242         NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
243
244         // Set the paragraph style
245         [style setAlignment: [self alignment]];
246
247         // Attributed string doesn't exist lets create it
248         NSDictionary *attributes = @{
249                                      NSForegroundColorAttributeName : _placeholderTextColor,
250                                      NSParagraphStyleAttributeName  : style
251                                      };
252         NSAttributedString *attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholderString attributes:attributes];
253         [self setPlaceholderAttributedString:attributedPlaceholder];
254     } else if ([self placeholderAttributedString] && [[self placeholderAttributedString] length] > 0) {
255
256         // Check to see if the proper styles have been applied
257         if ([[[self placeholderAttributedString] attribute:NSParagraphStyleAttributeName atIndex:1 effectiveRange:nil] alignment] != [self alignment]) {
258
259             NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
260
261             // Set the paragraph style
262             [style setAlignment:[self alignment]];
263             
264             // Get current attr string
265             NSMutableAttributedString *adjPlaceholder = [[NSMutableAttributedString alloc] initWithAttributedString:[self placeholderAttributedString]];
266             
267             // Add style attr
268             [adjPlaceholder addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, [adjPlaceholder length])];
269             
270             // Reset Placeholder to correct placeholder
271             [self setPlaceholderAttributedString:adjPlaceholder];
272         }
273     }
274     
275     // Adjust Frame so Text Draws correctly
276     switch (self.controlSize) {
277         case NSRegularControlSize:
278             cellFrame.origin.y += (self.bezelStyle != NSTextFieldRoundedBezel) ? 1 : 0;
279             break;
280             
281         case NSSmallControlSize:
282             cellFrame.origin.y += (self.bezelStyle == NSTextFieldRoundedBezel) ? 1 : 0;
283             break;
284             
285         case NSMiniControlSize:
286             cellFrame.origin.x += (self.bezelStyle == NSTextFieldRoundedBezel) ? 1 : 0;
287             break;
288             
289         default:
290             break;
291     }
292
293     [self drawInteriorWithFrame: cellFrame inView: controlView];
294 }
295
296 - (void)_drawKeyboardFocusRingWithFrame:(NSRect)rect inView:(NSView*)view
297 {
298     // Do nothing
299 }
300
301 @end