2 * Copyright (C) 2008, 2010, 2011, 2012 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "platform/mac/ThemeMac.h"
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"
37 NSRect focusRingClipRect;
39 // This is a view whose sole purpose is to tell AppKit that it's flipped.
40 @interface WebCoreFlippedView : NSControl
43 @implementation WebCoreFlippedView
50 - (NSText *)currentEditor
55 - (BOOL)_automaticFocusRingDisabled
60 - (NSRect)_focusRingVisibleRect
62 if (NSIsEmptyRect(focusRingClipRect))
63 return [self visibleRect];
65 NSRect rect = focusRingClipRect;
66 rect.origin.y = [self bounds].size.height - NSMaxY(rect);
71 - (NSView *)_focusRingClipAncestor
78 @implementation NSFont (WebCoreTheme)
80 - (NSString*)webCoreFamilyName
82 if ([[self familyName] hasPrefix:@"."])
83 return [self fontName];
85 return [self familyName];
92 Theme* platformTheme()
94 DEFINE_STATIC_LOCAL(ThemeMac, themeMac, ());
98 // Helper functions used by a bunch of different control parts.
100 static NSControlSize controlSizeForFont(const FontDescription& fontDescription)
102 int fontSize = fontDescription.computedPixelSize();
104 return NSRegularControlSize;
106 return NSSmallControlSize;
107 return NSMiniControlSize;
110 static LengthSize sizeFromNSControlSize(NSControlSize nsControlSize, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
112 IntSize controlSize = sizes[nsControlSize];
113 if (zoomFactor != 1.0f)
114 controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor);
115 LengthSize result = zoomedSize;
116 if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0)
117 result.setWidth(Length(controlSize.width(), Fixed));
118 if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0)
119 result.setHeight(Length(controlSize.height(), Fixed));
123 static LengthSize sizeFromFont(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
125 return sizeFromNSControlSize(controlSizeForFont(fontDescription), zoomedSize, zoomFactor, sizes);
128 static ControlSize controlSizeFromPixelSize(const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
130 if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) &&
131 minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor))
132 return NSRegularControlSize;
133 if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) &&
134 minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor))
135 return NSSmallControlSize;
136 return NSMiniControlSize;
139 static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
141 ControlSize size = controlSizeFromPixelSize(sizes, minZoomedSize, zoomFactor);
142 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
143 [cell setControlSize:(NSControlSize)size];
146 static void updateStates(NSCell* cell, ControlStates states)
148 // Hover state is not supported by Aqua.
151 bool oldPressed = [cell isHighlighted];
152 bool pressed = states & PressedControlState;
153 if (pressed != oldPressed)
154 [cell setHighlighted:pressed];
157 bool oldEnabled = [cell isEnabled];
158 bool enabled = states & EnabledControlState;
159 if (enabled != oldEnabled)
160 [cell setEnabled:enabled];
162 #if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
164 bool oldFocused = [cell showsFirstResponder];
165 bool focused = states & FocusControlState;
166 if (focused != oldFocused)
167 [cell setShowsFirstResponder:focused];
170 // Checked and Indeterminate
171 bool oldIndeterminate = [cell state] == NSMixedState;
172 bool indeterminate = (states & IndeterminateControlState);
173 bool checked = states & CheckedControlState;
174 bool oldChecked = [cell state] == NSOnState;
175 if (oldIndeterminate != indeterminate || checked != oldChecked)
176 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
178 // Window inactive state does not need to be checked explicitly, since we paint parented to
179 // a view in a window whose key state can be detected.
182 static ThemeDrawState convertControlStatesToThemeDrawState(ThemeButtonKind kind, ControlStates states)
184 if (states & ReadOnlyControlState)
185 return kThemeStateUnavailableInactive;
186 if (!(states & EnabledControlState))
187 return kThemeStateUnavailableInactive;
189 // Do not process PressedState if !EnabledControlState or ReadOnlyControlState.
190 if (states & PressedControlState) {
191 if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall || kind == kThemeIncDecButtonMini)
192 return states & SpinUpControlState ? kThemeStatePressedUp : kThemeStatePressedDown;
193 return kThemeStatePressed;
195 return kThemeStateActive;
199 IntRect ThemeMac::inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor)
201 // Only do the inflation if the available width/height are too small. Otherwise try to
202 // fit the glow/check space into the available box's width/height.
203 int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[LeftMargin] * zoomFactor + margins[RightMargin] * zoomFactor);
204 int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[TopMargin] * zoomFactor + margins[BottomMargin] * zoomFactor);
205 IntRect result(zoomedRect);
206 if (widthDelta < 0) {
207 result.setX(result.x() - margins[LeftMargin] * zoomFactor);
208 result.setWidth(result.width() - widthDelta);
210 if (heightDelta < 0) {
211 result.setY(result.y() - margins[TopMargin] * zoomFactor);
212 result.setHeight(result.height() - heightDelta);
218 IntRect ThemeMac::inflateRectForAA(const IntRect& rect) {
219 const int margin = 2;
220 return IntRect(rect.x() - margin, rect.y() - margin, rect.width() + 2 * margin, rect.height() + 2 * margin);
224 IntRect ThemeMac::inflateRectForFocusRing(const IntRect& rect) {
225 #if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
226 // Just put a margin of 16 units around the rect. The UI elements that use this don't appropriately
227 // scale their focus rings appropriately (e.g, paint pickers), or switch to non-native widgets when
228 // scaled (e.g, check boxes and radio buttons).
229 const int margin = 16;
231 result.setX(rect.x() - margin);
232 result.setY(rect.y() - margin);
233 result.setWidth(rect.width() + 2 * margin);
234 result.setHeight(rect.height() + 2 * margin);
243 static const IntSize* checkboxSizes()
245 static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
249 static const int* checkboxMargins(NSControlSize controlSize)
251 static const int margins[3][4] =
257 return margins[controlSize];
260 static LengthSize checkboxSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
262 // If the width and height are both specified, then we have nothing to do.
263 if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
266 // Use the font size to determine the intrinsic width of the control.
267 return sizeFromFont(fontDescription, zoomedSize, zoomFactor, checkboxSizes());
270 static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
272 static NSButtonCell *checkboxCell;
274 checkboxCell = [[NSButtonCell alloc] init];
275 [checkboxCell setButtonType:NSSwitchButton];
276 [checkboxCell setTitle:nil];
277 [checkboxCell setAllowsMixedState:YES];
278 [checkboxCell setFocusRingType:NSFocusRingTypeExterior];
281 // Set the control size based off the rectangle we're painting into.
282 setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor);
284 // Update the various states we respond to.
285 updateStates(checkboxCell, states);
290 // FIXME: Share more code with radio buttons.
291 static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
293 BEGIN_BLOCK_OBJC_EXCEPTIONS
295 // Determine the width and height needed for the control and prepare the cell for painting.
296 NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor);
297 GraphicsContextStateSaver stateSaver(*context);
299 NSControlSize controlSize = [checkboxCell controlSize];
300 IntSize zoomedSize = checkboxSizes()[controlSize];
301 zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
302 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
303 IntRect inflatedRect = ThemeMac::inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
305 if (zoomFactor != 1.0f) {
306 inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
307 inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
308 context->translate(inflatedRect.x(), inflatedRect.y());
309 context->scale(zoomFactor, zoomFactor);
310 context->translate(-inflatedRect.x(), -inflatedRect.y());
313 LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
314 NSView *view = ThemeMac::ensuredView(scrollView);
315 [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:view];
316 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
317 if (states & FocusControlState)
318 [checkboxCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
320 [checkboxCell setControlView:nil];
322 END_BLOCK_OBJC_EXCEPTIONS
327 static const IntSize* radioSizes()
329 static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
333 static const int* radioMargins(NSControlSize controlSize)
335 static const int margins[3][4] =
341 return margins[controlSize];
344 static LengthSize radioSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
346 // If the width and height are both specified, then we have nothing to do.
347 if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
350 // Use the font size to determine the intrinsic width of the control.
351 return sizeFromFont(fontDescription, zoomedSize, zoomFactor, radioSizes());
354 static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
356 static NSButtonCell *radioCell;
358 radioCell = [[NSButtonCell alloc] init];
359 [radioCell setButtonType:NSRadioButton];
360 [radioCell setTitle:nil];
361 [radioCell setFocusRingType:NSFocusRingTypeExterior];
364 // Set the control size based off the rectangle we're painting into.
365 setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor);
367 // Update the various states we respond to.
368 // Cocoa draws NSMixedState NSRadioButton as NSOnState so we don't want that.
369 states &= ~IndeterminateControlState;
370 updateStates(radioCell, states);
375 static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
377 // Determine the width and height needed for the control and prepare the cell for painting.
378 NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor);
379 GraphicsContextStateSaver stateSaver(*context);
381 NSControlSize controlSize = [radioCell controlSize];
382 IntSize zoomedSize = radioSizes()[controlSize];
383 zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
384 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
385 IntRect inflatedRect = ThemeMac::inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
387 if (zoomFactor != 1.0f) {
388 inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
389 inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
390 context->translate(inflatedRect.x(), inflatedRect.y());
391 context->scale(zoomFactor, zoomFactor);
392 context->translate(-inflatedRect.x(), -inflatedRect.y());
395 LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
396 BEGIN_BLOCK_OBJC_EXCEPTIONS
397 NSView *view = ThemeMac::ensuredView(scrollView);
398 [radioCell drawWithFrame:NSRect(inflatedRect) inView:view];
399 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
400 if (states & FocusControlState)
401 [radioCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
403 [radioCell setControlView:nil];
404 END_BLOCK_OBJC_EXCEPTIONS
409 // Buttons really only constrain height. They respect width.
410 static const IntSize* buttonSizes()
412 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
416 static const int* buttonMargins(NSControlSize controlSize)
418 static const int margins[3][4] =
424 return margins[controlSize];
427 static void setUpButtonCell(NSButtonCell *cell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
429 // Set the control size based off the rectangle we're painting into.
430 const IntSize* sizes = buttonSizes();
431 if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) {
432 // Use the square button
433 if ([cell bezelStyle] != NSShadowlessSquareBezelStyle)
434 [cell setBezelStyle:NSShadowlessSquareBezelStyle];
435 } else if ([cell bezelStyle] != NSRoundedBezelStyle)
436 [cell setBezelStyle:NSRoundedBezelStyle];
438 setControlSize(cell, sizes, zoomedRect.size(), zoomFactor);
440 // Update the various states we respond to.
441 updateStates(cell, states);
444 static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
446 static NSButtonCell *cell = nil;
448 cell = [[NSButtonCell alloc] init];
450 [cell setButtonType:NSMomentaryPushInButton];
452 setUpButtonCell(cell, part, states, zoomedRect, zoomFactor);
456 static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
458 BEGIN_BLOCK_OBJC_EXCEPTIONS
460 // Determine the width and height needed for the control and prepare the cell for painting.
461 NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor);
462 GraphicsContextStateSaver stateSaver(*context);
464 NSControlSize controlSize = [buttonCell controlSize];
465 IntSize zoomedSize = buttonSizes()[controlSize];
466 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
467 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
468 IntRect inflatedRect = zoomedRect;
469 if ([buttonCell bezelStyle] == NSRoundedBezelStyle) {
470 // Center the button within the available space.
471 if (inflatedRect.height() > zoomedSize.height()) {
472 inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2);
473 inflatedRect.setHeight(zoomedSize.height());
476 // Now inflate it to account for the shadow.
477 inflatedRect = ThemeMac::inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
479 if (zoomFactor != 1.0f) {
480 inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
481 inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
482 context->translate(inflatedRect.x(), inflatedRect.y());
483 context->scale(zoomFactor, zoomFactor);
484 context->translate(-inflatedRect.x(), -inflatedRect.y());
488 LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
489 NSView *view = ThemeMac::ensuredView(scrollView);
491 [buttonCell drawWithFrame:NSRect(inflatedRect) inView:view];
492 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
493 if (states & FocusControlState)
494 [buttonCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
496 [buttonCell setControlView:nil];
498 END_BLOCK_OBJC_EXCEPTIONS
503 static const IntSize* stepperSizes()
505 static const IntSize sizes[3] = { IntSize(19, 27), IntSize(15, 22), IntSize(13, 15) };
509 // We don't use controlSizeForFont() for steppers because the stepper height
510 // should be equal to or less than the corresponding text field height,
511 static NSControlSize stepperControlSizeForFont(const FontDescription& fontDescription)
513 int fontSize = fontDescription.computedPixelSize();
515 return NSRegularControlSize;
517 return NSSmallControlSize;
518 return NSMiniControlSize;
521 static void paintStepper(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView*)
523 // We don't use NSStepperCell because there are no ways to draw an
524 // NSStepperCell with the up button highlighted.
526 HIThemeButtonDrawInfo drawInfo;
527 drawInfo.version = 0;
528 drawInfo.state = convertControlStatesToThemeDrawState(kThemeIncDecButton, states);
529 drawInfo.adornment = kThemeAdornmentDefault;
530 ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
531 if (controlSize == NSSmallControlSize)
532 drawInfo.kind = kThemeIncDecButtonSmall;
533 else if (controlSize == NSMiniControlSize)
534 drawInfo.kind = kThemeIncDecButtonMini;
536 drawInfo.kind = kThemeIncDecButton;
538 IntRect rect(zoomedRect);
539 GraphicsContextStateSaver stateSaver(*context);
540 if (zoomFactor != 1.0f) {
541 rect.setWidth(rect.width() / zoomFactor);
542 rect.setHeight(rect.height() / zoomFactor);
543 context->translate(rect.x(), rect.y());
544 context->scale(zoomFactor, zoomFactor);
545 context->translate(-rect.x(), -rect.y());
548 CGRect backgroundBounds;
549 HIThemeGetButtonBackgroundBounds(&bounds, &drawInfo, &backgroundBounds);
550 // Center the stepper rectangle in the specified area.
551 backgroundBounds.origin.x = bounds.origin.x + (bounds.size.width - backgroundBounds.size.width) / 2;
552 if (backgroundBounds.size.height < bounds.size.height) {
553 int heightDiff = clampToInteger(bounds.size.height - backgroundBounds.size.height);
554 backgroundBounds.origin.y = bounds.origin.y + (heightDiff / 2) + 1;
557 LocalCurrentGraphicsContext localContext(context, rect);
558 HIThemeDrawButton(&backgroundBounds, &drawInfo, localContext.cgContext(), kHIThemeOrientationNormal, 0);
561 // This will ensure that we always return a valid NSView, even if ScrollView doesn't have an associated document NSView.
562 // 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.
563 NSView *ThemeMac::ensuredView(ScrollView* scrollView)
566 // Use a fake flipped view.
567 static NSView *flippedView = [[WebCoreFlippedView alloc] init];
568 [flippedView setFrameSize:NSSizeFromCGSize(scrollView->contentsSize())];
573 void ThemeMac::setFocusRingClipRect(const FloatRect& rect)
575 focusRingClipRect = rect;
580 int ThemeMac::baselinePositionAdjustment(ControlPart part) const
582 if (part == CheckboxPart || part == RadioPart)
584 return Theme::baselinePositionAdjustment(part);
587 FontDescription ThemeMac::controlFont(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
590 case PushButtonPart: {
591 FontDescription result;
592 result.setIsAbsoluteSize(true);
593 result.setGenericFamily(FontDescription::SerifFamily);
595 NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(fontDescription)]];
596 result.firstFamily().setFamily([nsFont webCoreFamilyName]);
597 result.setComputedSize([nsFont pointSize] * zoomFactor);
598 result.setSpecifiedSize([nsFont pointSize] * zoomFactor);
602 return Theme::controlFont(part, fontDescription, zoomFactor);
606 LengthSize ThemeMac::controlSize(ControlPart part, const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor) const
610 return checkboxSize(fontDescription, zoomedSize, zoomFactor);
612 return radioSize(fontDescription, zoomedSize, zoomFactor);
614 // Height is reset to auto so that specified heights can be ignored.
615 return sizeFromFont(fontDescription, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes());
616 case InnerSpinButtonPart:
617 if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
619 return sizeFromNSControlSize(stepperControlSizeForFont(fontDescription), zoomedSize, zoomFactor, stepperSizes());
625 LengthSize ThemeMac::minimumControlSize(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
628 case SquareButtonPart:
630 return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed));
631 case InnerSpinButtonPart:{
632 IntSize base = stepperSizes()[NSMiniControlSize];
633 return LengthSize(Length(static_cast<int>(base.width() * zoomFactor), Fixed),
634 Length(static_cast<int>(base.height() * zoomFactor), Fixed));
637 return Theme::minimumControlSize(part, fontDescription, zoomFactor);
641 LengthBox ThemeMac::controlBorder(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
644 case SquareButtonPart:
646 return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value());
648 return Theme::controlBorder(part, fontDescription, zoomedBox, zoomFactor);
652 LengthBox ThemeMac::controlPadding(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
655 case PushButtonPart: {
656 // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large
657 // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
658 // by definition constrained, since we select mini only for small cramped environments.
659 // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent
661 const int padding = 8 * zoomFactor;
662 return LengthBox(0, padding, 0, padding);
665 return Theme::controlPadding(part, fontDescription, zoomedBox, zoomFactor);
669 void ThemeMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const
671 BEGIN_BLOCK_OBJC_EXCEPTIONS
674 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
675 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
676 NSCell *cell = checkbox(states, zoomedRect, zoomFactor);
677 NSControlSize controlSize = [cell controlSize];
678 IntSize zoomedSize = checkboxSizes()[controlSize];
679 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
680 zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
681 zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
685 // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button
686 // shadow". We don't consider this part of the bounds of the control in WebKit.
687 NSCell *cell = radio(states, zoomedRect, zoomFactor);
688 NSControlSize controlSize = [cell controlSize];
689 IntSize zoomedSize = radioSizes()[controlSize];
690 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
691 zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
692 zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
697 NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor);
698 NSControlSize controlSize = [cell controlSize];
700 // We inflate the rect as needed to account for the Aqua button's shadow.
701 if ([cell bezelStyle] == NSRoundedBezelStyle) {
702 IntSize zoomedSize = buttonSizes()[controlSize];
703 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
704 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
705 zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
709 case InnerSpinButtonPart: {
710 static const int stepperMargin[4] = { 0, 0, 0, 0 };
711 ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
712 IntSize zoomedSize = stepperSizes()[controlSize];
713 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
714 zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
715 zoomedRect = inflateRect(zoomedRect, zoomedSize, stepperMargin, zoomFactor);
721 END_BLOCK_OBJC_EXCEPTIONS
724 void ThemeMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const
728 paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView);
731 paintRadio(states, context, zoomedRect, zoomFactor, scrollView);
735 case SquareButtonPart:
736 paintButton(part, states, context, zoomedRect, zoomFactor, scrollView);
738 case InnerSpinButtonPart:
739 paintStepper(states, context, zoomedRect, zoomFactor, scrollView);