Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / mac / ThemeMac.mm
1 /*
2  * Copyright (C) 2008, 2010, 2011, 2012 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "platform/mac/ThemeMac.h"
28
29 #import <Carbon/Carbon.h>
30 #import "platform/graphics/GraphicsContextStateSaver.h"
31 #import "platform/mac/BlockExceptions.h"
32 #import "platform/mac/LocalCurrentGraphicsContext.h"
33 #import "platform/mac/WebCoreNSCellExtras.h"
34 #import "platform/scroll/ScrollView.h"
35 #include "wtf/StdLibExtras.h"
36
37 using namespace std;
38
39 NSRect focusRingClipRect;
40
41 // This is a view whose sole purpose is to tell AppKit that it's flipped.
42 @interface WebCoreFlippedView : NSControl
43 @end
44
45 @implementation WebCoreFlippedView
46
47 - (BOOL)isFlipped
48 {
49     return YES;
50 }
51
52 - (NSText *)currentEditor
53 {
54     return nil;
55 }
56
57 - (BOOL)_automaticFocusRingDisabled
58 {
59     return YES;
60 }
61
62 - (NSRect)_focusRingVisibleRect
63 {
64     if (NSIsEmptyRect(focusRingClipRect))
65         return [self visibleRect];
66
67     NSRect rect = focusRingClipRect;
68     rect.origin.y = [self bounds].size.height - NSMaxY(rect);
69
70     return rect;
71 }
72
73 - (NSView *)_focusRingClipAncestor
74 {
75     return self;
76 }
77
78 @end
79
80 @implementation NSFont (WebCoreTheme)
81
82 - (NSString*)webCoreFamilyName
83 {
84     if ([[self familyName] hasPrefix:@"."])
85         return [self fontName];
86
87     return [self familyName];
88 }
89
90 @end
91
92 namespace WebCore {
93
94 enum {
95     topMargin,
96     rightMargin,
97     bottomMargin,
98     leftMargin
99 };
100
101 Theme* platformTheme()
102 {
103     DEFINE_STATIC_LOCAL(ThemeMac, themeMac, ());
104     return &themeMac;
105 }
106
107 // Helper functions used by a bunch of different control parts.
108
109 static NSControlSize controlSizeForFont(const FontDescription& fontDescription)
110 {
111     int fontSize = fontDescription.computedPixelSize();
112     if (fontSize >= 16)
113         return NSRegularControlSize;
114     if (fontSize >= 11)
115         return NSSmallControlSize;
116     return NSMiniControlSize;
117 }
118
119 static LengthSize sizeFromNSControlSize(NSControlSize nsControlSize, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
120 {
121     IntSize controlSize = sizes[nsControlSize];
122     if (zoomFactor != 1.0f)
123         controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor);
124     LengthSize result = zoomedSize;
125     if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0)
126         result.setWidth(Length(controlSize.width(), Fixed));
127     if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0)
128         result.setHeight(Length(controlSize.height(), Fixed));
129     return result;
130 }
131
132 static LengthSize sizeFromFont(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
133 {
134     return sizeFromNSControlSize(controlSizeForFont(fontDescription), zoomedSize, zoomFactor, sizes);
135 }
136
137 static ControlSize controlSizeFromPixelSize(const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
138 {
139     if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) &&
140         minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor))
141         return NSRegularControlSize;
142     if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) &&
143         minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor))
144         return NSSmallControlSize;
145     return NSMiniControlSize;
146 }
147
148 static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
149 {
150     ControlSize size = controlSizeFromPixelSize(sizes, minZoomedSize, zoomFactor);
151     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
152         [cell setControlSize:(NSControlSize)size];
153 }
154
155 static void updateStates(NSCell* cell, ControlStates states)
156 {
157     // Hover state is not supported by Aqua.
158     
159     // Pressed state
160     bool oldPressed = [cell isHighlighted];
161     bool pressed = states & PressedState;
162     if (pressed != oldPressed)
163         [cell setHighlighted:pressed];
164     
165     // Enabled state
166     bool oldEnabled = [cell isEnabled];
167     bool enabled = states & EnabledState;
168     if (enabled != oldEnabled)
169         [cell setEnabled:enabled];
170     
171 #if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
172     // Focused state
173     bool oldFocused = [cell showsFirstResponder];
174     bool focused = states & FocusState;
175     if (focused != oldFocused)
176         [cell setShowsFirstResponder:focused];
177 #endif
178
179     // Checked and Indeterminate
180     bool oldIndeterminate = [cell state] == NSMixedState;
181     bool indeterminate = (states & IndeterminateState);
182     bool checked = states & CheckedState;
183     bool oldChecked = [cell state] == NSOnState;
184     if (oldIndeterminate != indeterminate || checked != oldChecked)
185         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
186         
187     // Window inactive state does not need to be checked explicitly, since we paint parented to 
188     // a view in a window whose key state can be detected.
189 }
190
191 static ThemeDrawState convertControlStatesToThemeDrawState(ThemeButtonKind kind, ControlStates states)
192 {
193     if (states & ReadOnlyState)
194         return kThemeStateUnavailableInactive;
195     if (!(states & EnabledState))
196         return kThemeStateUnavailableInactive;
197
198     // Do not process PressedState if !EnabledState or ReadOnlyState.
199     if (states & PressedState) {
200         if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall || kind == kThemeIncDecButtonMini)
201             return states & SpinUpState ? kThemeStatePressedUp : kThemeStatePressedDown;
202         return kThemeStatePressed;
203     }
204     return kThemeStateActive;
205 }
206
207 static IntRect inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor)
208 {
209     // Only do the inflation if the available width/height are too small.  Otherwise try to
210     // fit the glow/check space into the available box's width/height.
211     int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[leftMargin] * zoomFactor + margins[rightMargin] * zoomFactor);
212     int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[topMargin] * zoomFactor + margins[bottomMargin] * zoomFactor);
213     IntRect result(zoomedRect);
214     if (widthDelta < 0) {
215         result.setX(result.x() - margins[leftMargin] * zoomFactor);
216         result.setWidth(result.width() - widthDelta);
217     }
218     if (heightDelta < 0) {
219         result.setY(result.y() - margins[topMargin] * zoomFactor);
220         result.setHeight(result.height() - heightDelta);
221     }
222     return result;
223 }
224
225 // Checkboxes
226
227 static const IntSize* checkboxSizes()
228 {
229     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
230     return sizes;
231 }
232
233 static const int* checkboxMargins(NSControlSize controlSize)
234 {
235     static const int margins[3][4] =
236     {
237         { 3, 4, 4, 2 },
238         { 4, 3, 3, 3 },
239         { 4, 3, 3, 3 },
240     };
241     return margins[controlSize];
242 }
243
244 static LengthSize checkboxSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
245 {
246     // If the width and height are both specified, then we have nothing to do.
247     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
248         return zoomedSize;
249
250     // Use the font size to determine the intrinsic width of the control.
251     return sizeFromFont(fontDescription, zoomedSize, zoomFactor, checkboxSizes());
252 }
253
254 static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
255 {
256     static NSButtonCell *checkboxCell;
257     if (!checkboxCell) {
258         checkboxCell = [[NSButtonCell alloc] init];
259         [checkboxCell setButtonType:NSSwitchButton];
260         [checkboxCell setTitle:nil];
261         [checkboxCell setAllowsMixedState:YES];
262         [checkboxCell setFocusRingType:NSFocusRingTypeExterior];
263     }
264     
265     // Set the control size based off the rectangle we're painting into.
266     setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor);
267
268     // Update the various states we respond to.
269     updateStates(checkboxCell, states);
270     
271     return checkboxCell;
272 }
273
274 // FIXME: Share more code with radio buttons.
275 static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
276 {
277     BEGIN_BLOCK_OBJC_EXCEPTIONS
278
279     // Determine the width and height needed for the control and prepare the cell for painting.
280     NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor);
281     GraphicsContextStateSaver stateSaver(*context);
282
283     NSControlSize controlSize = [checkboxCell controlSize];
284     IntSize zoomedSize = checkboxSizes()[controlSize];
285     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
286     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
287     IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
288     
289     if (zoomFactor != 1.0f) {
290         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
291         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
292         context->translate(inflatedRect.x(), inflatedRect.y());
293         context->scale(FloatSize(zoomFactor, zoomFactor));
294         context->translate(-inflatedRect.x(), -inflatedRect.y());
295     }
296
297     LocalCurrentGraphicsContext localContext(context);
298     NSView *view = ThemeMac::ensuredView(scrollView);
299     [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:view];
300 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
301     if (states & FocusState)
302         [checkboxCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
303 #endif
304     [checkboxCell setControlView:nil];
305     
306     END_BLOCK_OBJC_EXCEPTIONS
307 }
308
309 // Radio Buttons
310
311 static const IntSize* radioSizes()
312 {
313     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
314     return sizes;
315 }
316
317 static const int* radioMargins(NSControlSize controlSize)
318 {
319     static const int margins[3][4] =
320     {
321         { 2, 2, 4, 2 },
322         { 3, 2, 3, 2 },
323         { 1, 0, 2, 0 },
324     };
325     return margins[controlSize];
326 }
327
328 static LengthSize radioSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
329 {
330     // If the width and height are both specified, then we have nothing to do.
331     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
332         return zoomedSize;
333
334     // Use the font size to determine the intrinsic width of the control.
335     return sizeFromFont(fontDescription, zoomedSize, zoomFactor, radioSizes());
336 }
337
338 static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
339 {
340     static NSButtonCell *radioCell;
341     if (!radioCell) {
342         radioCell = [[NSButtonCell alloc] init];
343         [radioCell setButtonType:NSRadioButton];
344         [radioCell setTitle:nil];
345         [radioCell setFocusRingType:NSFocusRingTypeExterior];
346     }
347     
348     // Set the control size based off the rectangle we're painting into.
349     setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor);
350
351     // Update the various states we respond to.
352     updateStates(radioCell, states);
353     
354     return radioCell;
355 }
356
357 static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
358 {
359     // Determine the width and height needed for the control and prepare the cell for painting.
360     NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor);
361     GraphicsContextStateSaver stateSaver(*context);
362
363     NSControlSize controlSize = [radioCell controlSize];
364     IntSize zoomedSize = radioSizes()[controlSize];
365     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
366     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
367     IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
368     
369     if (zoomFactor != 1.0f) {
370         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
371         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
372         context->translate(inflatedRect.x(), inflatedRect.y());
373         context->scale(FloatSize(zoomFactor, zoomFactor));
374         context->translate(-inflatedRect.x(), -inflatedRect.y());
375     }
376
377     LocalCurrentGraphicsContext localContext(context);
378     BEGIN_BLOCK_OBJC_EXCEPTIONS
379     NSView *view = ThemeMac::ensuredView(scrollView);
380     [radioCell drawWithFrame:NSRect(inflatedRect) inView:view];
381 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
382     if (states & FocusState)
383         [radioCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
384 #endif
385     [radioCell setControlView:nil];
386     END_BLOCK_OBJC_EXCEPTIONS
387 }
388
389 // Buttons
390
391 // Buttons really only constrain height. They respect width.
392 static const IntSize* buttonSizes()
393 {
394     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
395     return sizes;
396 }
397
398 static const int* buttonMargins(NSControlSize controlSize)
399 {
400     static const int margins[3][4] =
401     {
402         { 4, 6, 7, 6 },
403         { 4, 5, 6, 5 },
404         { 0, 1, 1, 1 },
405     };
406     return margins[controlSize];
407 }
408
409 static void setUpButtonCell(NSButtonCell *cell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
410 {
411     // Set the control size based off the rectangle we're painting into.
412     const IntSize* sizes = buttonSizes();
413     if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) {
414         // Use the square button
415         if ([cell bezelStyle] != NSShadowlessSquareBezelStyle)
416             [cell setBezelStyle:NSShadowlessSquareBezelStyle];
417     } else if ([cell bezelStyle] != NSRoundedBezelStyle)
418         [cell setBezelStyle:NSRoundedBezelStyle];
419
420     setControlSize(cell, sizes, zoomedRect.size(), zoomFactor);
421
422     // Update the various states we respond to.
423     updateStates(cell, states);
424 }
425
426 static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
427 {
428     static NSButtonCell *cell = nil;
429     if (!cell) {
430         cell = [[NSButtonCell alloc] init];
431         [cell setTitle:nil];
432         [cell setButtonType:NSMomentaryPushInButton];
433     }
434     setUpButtonCell(cell, part, states, zoomedRect, zoomFactor);
435     return cell;
436 }
437
438 static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
439 {
440     BEGIN_BLOCK_OBJC_EXCEPTIONS
441     
442     // Determine the width and height needed for the control and prepare the cell for painting.
443     NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor);
444     GraphicsContextStateSaver stateSaver(*context);
445
446     NSControlSize controlSize = [buttonCell controlSize];
447     IntSize zoomedSize = buttonSizes()[controlSize];
448     zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
449     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
450     IntRect inflatedRect = zoomedRect;
451     if ([buttonCell bezelStyle] == NSRoundedBezelStyle) {
452         // Center the button within the available space.
453         if (inflatedRect.height() > zoomedSize.height()) {
454             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2);
455             inflatedRect.setHeight(zoomedSize.height());
456         }
457
458         // Now inflate it to account for the shadow.
459         inflatedRect = inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
460
461         if (zoomFactor != 1.0f) {
462             inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
463             inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
464             context->translate(inflatedRect.x(), inflatedRect.y());
465             context->scale(FloatSize(zoomFactor, zoomFactor));
466             context->translate(-inflatedRect.x(), -inflatedRect.y());
467         }
468     } 
469
470     LocalCurrentGraphicsContext localContext(context);
471     NSView *view = ThemeMac::ensuredView(scrollView);
472
473     [buttonCell drawWithFrame:NSRect(inflatedRect) inView:view];
474 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
475     if (states & FocusState)
476         [buttonCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
477 #endif
478     [buttonCell setControlView:nil];
479
480     END_BLOCK_OBJC_EXCEPTIONS
481 }
482
483 // Stepper
484
485 static const IntSize* stepperSizes()
486 {
487     static const IntSize sizes[3] = { IntSize(19, 27), IntSize(15, 22), IntSize(13, 15) };
488     return sizes;
489 }
490
491 // We don't use controlSizeForFont() for steppers because the stepper height
492 // should be equal to or less than the corresponding text field height,
493 static NSControlSize stepperControlSizeForFont(const FontDescription& fontDescription)
494 {
495     int fontSize = fontDescription.computedPixelSize();
496     if (fontSize >= 27)
497         return NSRegularControlSize;
498     if (fontSize >= 22)
499         return NSSmallControlSize;
500     return NSMiniControlSize;
501 }
502
503 static void paintStepper(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView*)
504 {
505     // We don't use NSStepperCell because there are no ways to draw an
506     // NSStepperCell with the up button highlighted.
507
508     HIThemeButtonDrawInfo drawInfo;
509     drawInfo.version = 0;
510     drawInfo.state = convertControlStatesToThemeDrawState(kThemeIncDecButton, states);
511     drawInfo.adornment = kThemeAdornmentDefault;
512     ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
513     if (controlSize == NSSmallControlSize)
514         drawInfo.kind = kThemeIncDecButtonSmall;
515     else if (controlSize == NSMiniControlSize)
516         drawInfo.kind = kThemeIncDecButtonMini;
517     else
518         drawInfo.kind = kThemeIncDecButton;
519
520     IntRect rect(zoomedRect);
521     GraphicsContextStateSaver stateSaver(*context);
522     if (zoomFactor != 1.0f) {
523         rect.setWidth(rect.width() / zoomFactor);
524         rect.setHeight(rect.height() / zoomFactor);
525         context->translate(rect.x(), rect.y());
526         context->scale(FloatSize(zoomFactor, zoomFactor));
527         context->translate(-rect.x(), -rect.y());
528     }
529     CGRect bounds(rect);
530     CGRect backgroundBounds;
531     HIThemeGetButtonBackgroundBounds(&bounds, &drawInfo, &backgroundBounds);
532     // Center the stepper rectangle in the specified area.
533     backgroundBounds.origin.x = bounds.origin.x + (bounds.size.width - backgroundBounds.size.width) / 2;
534     if (backgroundBounds.size.height < bounds.size.height) {
535         int heightDiff = clampToInteger(bounds.size.height - backgroundBounds.size.height);
536         backgroundBounds.origin.y = bounds.origin.y + (heightDiff / 2) + 1;
537     }
538
539     LocalCurrentGraphicsContext localContext(context);
540     HIThemeDrawButton(&backgroundBounds, &drawInfo, localContext.cgContext(), kHIThemeOrientationNormal, 0);
541 }
542
543 // This will ensure that we always return a valid NSView, even if ScrollView doesn't have an associated document NSView.
544 // If the ScrollView doesn't have an NSView, we will return a fake NSView whose sole purpose is to tell AppKit that it's flipped.
545 NSView *ThemeMac::ensuredView(ScrollView* scrollView)
546 {
547
548     // Use a fake flipped view.
549     static NSView *flippedView = [[WebCoreFlippedView alloc] init];
550     [flippedView setFrameSize:NSSizeFromCGSize(scrollView->contentsSize())];
551
552     return flippedView;
553 }
554
555 void ThemeMac::setFocusRingClipRect(const FloatRect& rect)
556 {
557     focusRingClipRect = rect;
558 }
559
560 // Theme overrides
561
562 int ThemeMac::baselinePositionAdjustment(ControlPart part) const
563 {
564     if (part == CheckboxPart || part == RadioPart)
565         return -2;
566     return Theme::baselinePositionAdjustment(part);
567 }
568
569 FontDescription ThemeMac::controlFont(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
570 {
571     switch (part) {
572         case PushButtonPart: {
573             FontDescription result;
574             result.setIsAbsoluteSize(true);
575             result.setGenericFamily(FontDescription::SerifFamily);
576
577             NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(fontDescription)]];
578             result.firstFamily().setFamily([nsFont webCoreFamilyName]);
579             result.setComputedSize([nsFont pointSize] * zoomFactor);
580             result.setSpecifiedSize([nsFont pointSize] * zoomFactor);
581             return result;
582         }
583         default:
584             return Theme::controlFont(part, fontDescription, zoomFactor);
585     }
586 }
587
588 LengthSize ThemeMac::controlSize(ControlPart part, const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor) const
589 {
590     switch (part) {
591         case CheckboxPart:
592             return checkboxSize(fontDescription, zoomedSize, zoomFactor);
593         case RadioPart:
594             return radioSize(fontDescription, zoomedSize, zoomFactor);
595         case PushButtonPart:
596             // Height is reset to auto so that specified heights can be ignored.
597             return sizeFromFont(fontDescription, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes());
598         case InnerSpinButtonPart:
599             if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
600                 return zoomedSize;
601             return sizeFromNSControlSize(stepperControlSizeForFont(fontDescription), zoomedSize, zoomFactor, stepperSizes());
602         default:
603             return zoomedSize;
604     }
605 }
606
607 LengthSize ThemeMac::minimumControlSize(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
608 {
609     switch (part) {
610         case SquareButtonPart:
611         case ButtonPart:
612             return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed));
613         case InnerSpinButtonPart:{
614             IntSize base = stepperSizes()[NSMiniControlSize];
615             return LengthSize(Length(static_cast<int>(base.width() * zoomFactor), Fixed),
616                               Length(static_cast<int>(base.height() * zoomFactor), Fixed));
617         }
618         default:
619             return Theme::minimumControlSize(part, fontDescription, zoomFactor);
620     }
621 }
622
623 LengthBox ThemeMac::controlBorder(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
624 {
625     switch (part) {
626         case SquareButtonPart:
627         case ButtonPart:
628             return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value());
629         default:
630             return Theme::controlBorder(part, fontDescription, zoomedBox, zoomFactor);
631     }
632 }
633
634 LengthBox ThemeMac::controlPadding(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
635 {
636     switch (part) {
637         case PushButtonPart: {
638             // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
639             // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
640             // by definition constrained, since we select mini only for small cramped environments.
641             // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent
642             // padding.
643             const int padding = 8 * zoomFactor;
644             return LengthBox(0, padding, 0, padding);
645         }
646         default:
647             return Theme::controlPadding(part, fontDescription, zoomedBox, zoomFactor);
648     }
649 }
650
651 void ThemeMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const
652 {
653     BEGIN_BLOCK_OBJC_EXCEPTIONS
654     switch (part) {
655         case CheckboxPart: {
656             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
657             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
658             NSCell *cell = checkbox(states, zoomedRect, zoomFactor);
659             NSControlSize controlSize = [cell controlSize];
660             IntSize zoomedSize = checkboxSizes()[controlSize];
661             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
662             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
663             zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
664             break;
665         }
666         case RadioPart: {
667             // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button
668             // shadow".  We don't consider this part of the bounds of the control in WebKit.
669             NSCell *cell = radio(states, zoomedRect, zoomFactor);
670             NSControlSize controlSize = [cell controlSize];
671             IntSize zoomedSize = radioSizes()[controlSize];
672             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
673             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
674             zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
675             break;
676         }
677         case PushButtonPart:
678         case ButtonPart: {
679             NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor);
680             NSControlSize controlSize = [cell controlSize];
681
682             // We inflate the rect as needed to account for the Aqua button's shadow.
683             if ([cell bezelStyle] == NSRoundedBezelStyle) {
684                 IntSize zoomedSize = buttonSizes()[controlSize];
685                 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
686                 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
687                 zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
688             }
689             break;
690         }
691         case InnerSpinButtonPart: {
692             static const int stepperMargin[4] = { 0, 0, 0, 0 };
693             ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
694             IntSize zoomedSize = stepperSizes()[controlSize];
695             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
696             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
697             zoomedRect = inflateRect(zoomedRect, zoomedSize, stepperMargin, zoomFactor);
698             break;
699         }
700         default:
701             break;
702     }
703     END_BLOCK_OBJC_EXCEPTIONS
704 }
705
706 void ThemeMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const
707 {
708     switch (part) {
709         case CheckboxPart:
710             paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView);
711             break;
712         case RadioPart:
713             paintRadio(states, context, zoomedRect, zoomFactor, scrollView);
714             break;
715         case PushButtonPart:
716         case ButtonPart:
717         case SquareButtonPart:
718             paintButton(part, states, context, zoomedRect, zoomFactor, scrollView);
719             break;
720         case InnerSpinButtonPart:
721             paintStepper(states, context, zoomedRect, zoomFactor, scrollView);
722             break;
723         default:
724             break;
725     }
726 }
727
728 }