diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/chromium/ThemeChromiumMac.mm | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/chromium/ThemeChromiumMac.mm')
-rw-r--r-- | Source/WebCore/platform/chromium/ThemeChromiumMac.mm | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/Source/WebCore/platform/chromium/ThemeChromiumMac.mm b/Source/WebCore/platform/chromium/ThemeChromiumMac.mm new file mode 100644 index 0000000..5e457f3 --- /dev/null +++ b/Source/WebCore/platform/chromium/ThemeChromiumMac.mm @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "ThemeChromiumMac.h" + +#import "BlockExceptions.h" +#import "GraphicsContext.h" +#import "LocalCurrentGraphicsContext.h" +#import "ScrollView.h" +#import "WebCoreSystemInterface.h" +#import <Carbon/Carbon.h> +#include <wtf/StdLibExtras.h> +#import <objc/runtime.h> + +using namespace std; + +// This file (and its associated .h file) is a clone of ThemeMac.mm. +// Because the original file is designed to run in-process inside a Cocoa view, +// we must maintain a fork. Please maintain this file by performing parallel +// changes to it. +// +// The only changes from ThemeMac should be: +// - The classname change from ThemeMac to ThemeChromiumMac. +// - The import of FlippedView() and its use as the parent view for cell +// rendering. +// - In updateStates() the code to update the cells' inactive state. +// - In paintButton() the code to save/restore the window's default button cell. +// - The Snow Leopard focus ring bug fix and its use around every call to +// -[NSButtonCell drawWithFrame:inView:]. +// +// For all other differences, if it was introduced in this file, then the +// maintainer forgot to include it in the list; otherwise it is an update that +// should have been applied to this file but was not. + +// FIXME: Default buttons really should be more like push buttons and not like buttons. + +// --- START fix for Snow Leopard focus ring bug --- + +// There is a bug in the Cocoa focus ring drawing code. The code calls +[NSView +// focusView] (to get the currently focused view) and then calls an NSRect- +// returning method on that view to obtain a clipping rect. However, if there is +// no focused view (as there won't be if the destination is a context), the rect +// returned from the method invocation on nil is garbage. +// +// The garbage fortunately does not clip the focus ring on Leopard, but +// unfortunately does so on Snow Leopard. Therefore, if a runtime test shows +// that focus ring drawing fails, we swizzle NSView to ensure it returns a valid +// view with a valid clipping rectangle. +// +// FIXME: After the referenced bug is fixed on all supported platforms, remove +// this code. +// +// References: +// <http://crbug.com/27493> +// <rdar://problem/7604051> (<http://openradar.appspot.com/7604051>) + +@interface TCMVisibleView : NSView + +@end + +@implementation TCMVisibleView + +- (struct CGRect)_focusRingVisibleRect +{ + return CGRectZero; +} + +- (id)_focusRingClipAncestor +{ + return self; +} + +@end + +@interface NSView (TCMInterposing) ++ (NSView *)TCMInterposing_focusView; +@end + +namespace FocusIndicationFix { + +bool currentOSHasSetFocusRingStyleInBitmapBug() +{ + UInt32 pixel = 0; + UInt32* pixelPlane = &pixel; + UInt32** pixelPlanes = &pixelPlane; + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(UInt8**)pixelPlanes + pixelsWide:1 + pixelsHigh:1 + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:4 + bitsPerPixel:32]; + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]]; + NSSetFocusRingStyle(NSFocusRingOnly); + NSRectFill(NSMakeRect(0, 0, 1, 1)); + [NSGraphicsContext restoreGraphicsState]; + [bitmap release]; + + return !pixel; +} + +bool swizzleFocusView() +{ + if (!currentOSHasSetFocusRingStyleInBitmapBug()) + return false; + + Class nsview = [NSView class]; + Method m1 = class_getClassMethod(nsview, @selector(focusView)); + Method m2 = class_getClassMethod(nsview, @selector(TCMInterposing_focusView)); + if (m1 && m2) { + method_exchangeImplementations(m1, m2); + return true; + } + + return false; +} + +static bool interpose = false; + +// A class to restrict the amount of time spent messing with interposing. It +// only stacks one-deep. +class ScopedFixer { +public: + ScopedFixer() + { + static bool swizzled = swizzleFocusView(); + interpose = swizzled; + } + + ~ScopedFixer() + { + interpose = false; + } +}; + +} // namespace FocusIndicationFix + +@implementation NSView (TCMInterposing) + ++ (NSView *)TCMInterposing_focusView +{ + NSView *view = [self TCMInterposing_focusView]; // call original (was swizzled) + if (!view && FocusIndicationFix::interpose) { + static TCMVisibleView* fixedView = [[TCMVisibleView alloc] init]; + view = fixedView; + } + + return view; +} + +@end + +// --- END fix for Snow Leopard focus ring bug --- + +namespace WebCore { + +// Pick up utility function from RenderThemeChromiumMac. +extern NSView* FlippedView(); + +enum { + topMargin, + rightMargin, + bottomMargin, + leftMargin +}; + +Theme* platformTheme() +{ + DEFINE_STATIC_LOCAL(ThemeChromiumMac, themeMac, ()); + return &themeMac; +} + +// Helper functions used by a bunch of different control parts. + +static NSControlSize controlSizeForFont(const Font& font) +{ + int fontSize = font.pixelSize(); + if (fontSize >= 16) + return NSRegularControlSize; + if (fontSize >= 11) + return NSSmallControlSize; + return NSMiniControlSize; +} + +static LengthSize sizeFromNSControlSize(NSControlSize nsControlSize, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes) +{ + IntSize controlSize = sizes[nsControlSize]; + if (zoomFactor != 1.0f) + controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor); + LengthSize result = zoomedSize; + if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0) + result.setWidth(Length(controlSize.width(), Fixed)); + if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0) + result.setHeight(Length(controlSize.height(), Fixed)); + return result; +} + +static LengthSize sizeFromFont(const Font& font, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes) +{ + return sizeFromNSControlSize(controlSizeForFont(font), zoomedSize, zoomFactor, sizes); +} + +static ControlSize controlSizeFromPixelSize(const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor) +{ + if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) && + minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor)) + return NSRegularControlSize; + if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) && + minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor)) + return NSSmallControlSize; + return NSMiniControlSize; +} + +static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor) +{ + ControlSize size = controlSizeFromPixelSize(sizes, minZoomedSize, zoomFactor); + if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. + [cell setControlSize:(NSControlSize)size]; +} + +static void updateStates(NSCell* cell, ControlStates states) +{ + // Hover state is not supported by Aqua. + + // Pressed state + bool oldPressed = [cell isHighlighted]; + bool pressed = states & PressedState; + if (pressed != oldPressed) + [cell setHighlighted:pressed]; + + // Enabled state + bool oldEnabled = [cell isEnabled]; + bool enabled = states & EnabledState; + if (enabled != oldEnabled) + [cell setEnabled:enabled]; + + // Focused state + bool oldFocused = [cell showsFirstResponder]; + bool focused = states & FocusState; + if (focused != oldFocused) + [cell setShowsFirstResponder:focused]; + + // Checked and Indeterminate + bool oldIndeterminate = [cell state] == NSMixedState; + bool indeterminate = (states & IndeterminateState); + bool checked = states & CheckedState; + bool oldChecked = [cell state] == NSOnState; + if (oldIndeterminate != indeterminate || checked != oldChecked) + [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; + + // Window Inactive state + NSControlTint oldTint = [cell controlTint]; + bool windowInactive = (states & WindowInactiveState); + NSControlTint tint = windowInactive ? static_cast<NSControlTint>(NSClearControlTint) + : [NSColor currentControlTint]; + if (tint != oldTint) + [cell setControlTint:tint]; +} + +static ThemeDrawState convertControlStatesToThemeDrawState(ThemeButtonKind kind, ControlStates states) +{ + if (states & ReadOnlyState) + return kThemeStateUnavailableInactive; + if (!(states & EnabledState)) + return kThemeStateUnavailableInactive; + + // Do not process PressedState if !EnabledState or ReadOnlyState. + if (states & PressedState) { + if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall || kind == kThemeIncDecButtonMini) + return states & SpinUpState ? kThemeStatePressedUp : kThemeStatePressedDown; + return kThemeStatePressed; + } + return kThemeStateActive; +} + +static IntRect inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor) +{ + // Only do the inflation if the available width/height are too small. Otherwise try to + // fit the glow/check space into the available box's width/height. + int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[leftMargin] * zoomFactor + margins[rightMargin] * zoomFactor); + int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[topMargin] * zoomFactor + margins[bottomMargin] * zoomFactor); + IntRect result(zoomedRect); + if (widthDelta < 0) { + result.setX(result.x() - margins[leftMargin] * zoomFactor); + result.setWidth(result.width() - widthDelta); + } + if (heightDelta < 0) { + result.setY(result.y() - margins[topMargin] * zoomFactor); + result.setHeight(result.height() - heightDelta); + } + return result; +} + +// Checkboxes + +static const IntSize* checkboxSizes() +{ + static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; + return sizes; +} + +static const int* checkboxMargins(NSControlSize controlSize) +{ + static const int margins[3][4] = + { + { 3, 4, 4, 2 }, + { 4, 3, 3, 3 }, + { 4, 3, 3, 3 }, + }; + return margins[controlSize]; +} + +static LengthSize checkboxSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor) +{ + // If the width and height are both specified, then we have nothing to do. + if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto()) + return zoomedSize; + + // Use the font size to determine the intrinsic width of the control. + return sizeFromFont(font, zoomedSize, zoomFactor, checkboxSizes()); +} + +static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor) +{ + static NSButtonCell *checkboxCell; + if (!checkboxCell) { + checkboxCell = [[NSButtonCell alloc] init]; + [checkboxCell setButtonType:NSSwitchButton]; + [checkboxCell setTitle:nil]; + [checkboxCell setAllowsMixedState:YES]; + [checkboxCell setFocusRingType:NSFocusRingTypeExterior]; + } + + // Set the control size based off the rectangle we're painting into. + setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor); + + // Update the various states we respond to. + updateStates(checkboxCell, states); + + return checkboxCell; +} + +// FIXME: Share more code with radio buttons. +static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Determine the width and height needed for the control and prepare the cell for painting. + NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor); + LocalCurrentGraphicsContext localContext(context); + + context->save(); + + NSControlSize controlSize = [checkboxCell controlSize]; + IntSize zoomedSize = checkboxSizes()[controlSize]; + zoomedSize.setWidth(zoomedSize.width() * zoomFactor); + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor); + + if (zoomFactor != 1.0f) { + inflatedRect.setWidth(inflatedRect.width() / zoomFactor); + inflatedRect.setHeight(inflatedRect.height() / zoomFactor); + context->translate(inflatedRect.x(), inflatedRect.y()); + context->scale(FloatSize(zoomFactor, zoomFactor)); + context->translate(-inflatedRect.x(), -inflatedRect.y()); + } + + { + FocusIndicationFix::ScopedFixer fix; + [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; + } + [checkboxCell setControlView:nil]; + + context->restore(); + + END_BLOCK_OBJC_EXCEPTIONS +} + +// Radio Buttons + +static const IntSize* radioSizes() +{ + static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; + return sizes; +} + +static const int* radioMargins(NSControlSize controlSize) +{ + static const int margins[3][4] = + { + { 2, 2, 4, 2 }, + { 3, 2, 3, 2 }, + { 1, 0, 2, 0 }, + }; + return margins[controlSize]; +} + +static LengthSize radioSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor) +{ + // If the width and height are both specified, then we have nothing to do. + if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto()) + return zoomedSize; + + // Use the font size to determine the intrinsic width of the control. + return sizeFromFont(font, zoomedSize, zoomFactor, radioSizes()); +} + +static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor) +{ + static NSButtonCell *radioCell; + if (!radioCell) { + radioCell = [[NSButtonCell alloc] init]; + [radioCell setButtonType:NSRadioButton]; + [radioCell setTitle:nil]; + [radioCell setFocusRingType:NSFocusRingTypeExterior]; + } + + // Set the control size based off the rectangle we're painting into. + setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor); + + // Update the various states we respond to. + updateStates(radioCell, states); + + return radioCell; +} + +static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) +{ + // Determine the width and height needed for the control and prepare the cell for painting. + NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor); + LocalCurrentGraphicsContext localContext(context); + + context->save(); + + NSControlSize controlSize = [radioCell controlSize]; + IntSize zoomedSize = radioSizes()[controlSize]; + zoomedSize.setWidth(zoomedSize.width() * zoomFactor); + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor); + + if (zoomFactor != 1.0f) { + inflatedRect.setWidth(inflatedRect.width() / zoomFactor); + inflatedRect.setHeight(inflatedRect.height() / zoomFactor); + context->translate(inflatedRect.x(), inflatedRect.y()); + context->scale(FloatSize(zoomFactor, zoomFactor)); + context->translate(-inflatedRect.x(), -inflatedRect.y()); + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS + { + FocusIndicationFix::ScopedFixer fix; + [radioCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; + } + [radioCell setControlView:nil]; + END_BLOCK_OBJC_EXCEPTIONS + + context->restore(); +} + +// Buttons + +// Buttons really only constrain height. They respect width. +static const IntSize* buttonSizes() +{ + static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; + return sizes; +} + +#if ENABLE(DATALIST) +static const IntSize* listButtonSizes() +{ + static const IntSize sizes[3] = { IntSize(21, 21), IntSize(19, 18), IntSize(17, 16) }; + return sizes; +} +#endif + +static const int* buttonMargins(NSControlSize controlSize) +{ + static const int margins[3][4] = + { + { 4, 6, 7, 6 }, + { 4, 5, 6, 5 }, + { 0, 1, 1, 1 }, + }; + return margins[controlSize]; +} + +static void setupButtonCell(NSButtonCell *&buttonCell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor) +{ + if (!buttonCell) { + buttonCell = [[NSButtonCell alloc] init]; + [buttonCell setTitle:nil]; + [buttonCell setButtonType:NSMomentaryPushInButton]; + if (states & DefaultState) + [buttonCell setKeyEquivalent:@"\r"]; + } + + // Set the control size based off the rectangle we're painting into. + const IntSize* sizes = buttonSizes(); +#if ENABLE(DATALIST) + if (part == ListButtonPart) { + [buttonCell setBezelStyle:NSRoundedDisclosureBezelStyle]; + sizes = listButtonSizes(); + } else +#endif + if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) { + // Use the square button + if ([buttonCell bezelStyle] != NSShadowlessSquareBezelStyle) + [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle]; + } else if ([buttonCell bezelStyle] != NSRoundedBezelStyle) + [buttonCell setBezelStyle:NSRoundedBezelStyle]; + + setControlSize(buttonCell, sizes, zoomedRect.size(), zoomFactor); + + // Update the various states we respond to. + updateStates(buttonCell, states); +} + +static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor) +{ + bool isDefault = states & DefaultState; + static NSButtonCell *cells[2]; + setupButtonCell(cells[isDefault], part, states, zoomedRect, zoomFactor); + return cells[isDefault]; +} + +static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Determine the width and height needed for the control and prepare the cell for painting. + NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor); + LocalCurrentGraphicsContext localContext(context); + + NSControlSize controlSize = [buttonCell controlSize]; +#if ENABLE(DATALIST) + IntSize zoomedSize = (part == ListButtonPart ? listButtonSizes() : buttonSizes())[controlSize]; +#else + IntSize zoomedSize = buttonSizes()[controlSize]; +#endif + zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored. + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + IntRect inflatedRect = zoomedRect; + if ([buttonCell bezelStyle] == NSRoundedBezelStyle) { + // Center the button within the available space. + if (inflatedRect.height() > zoomedSize.height()) { + inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2); + inflatedRect.setHeight(zoomedSize.height()); + } + + // Now inflate it to account for the shadow. + inflatedRect = inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor); + + if (zoomFactor != 1.0f) { + inflatedRect.setWidth(inflatedRect.width() / zoomFactor); + inflatedRect.setHeight(inflatedRect.height() / zoomFactor); + context->translate(inflatedRect.x(), inflatedRect.y()); + context->scale(FloatSize(zoomFactor, zoomFactor)); + context->translate(-inflatedRect.x(), -inflatedRect.y()); + } + } + + { + FocusIndicationFix::ScopedFixer fix; + [buttonCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; + } + [buttonCell setControlView:nil]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +// Stepper + +static const IntSize* stepperSizes() +{ + static const IntSize sizes[3] = { IntSize(19, 27), IntSize(15, 22), IntSize(13, 15) }; + return sizes; +} + +// We don't use controlSizeForFont() for steppers because the stepper height +// should be equal to or less than the corresponding text field height, +static NSControlSize stepperControlSizeForFont(const Font& font) +{ + int fontSize = font.pixelSize(); + if (fontSize >= 18) + return NSRegularControlSize; + if (fontSize >= 13) + return NSSmallControlSize; + return NSMiniControlSize; +} + +static void paintStepper(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView*) +{ + // We don't use NSStepperCell because there are no ways to draw an + // NSStepperCell with the up button highlighted. + + HIThemeButtonDrawInfo drawInfo; + drawInfo.version = 0; + drawInfo.state = convertControlStatesToThemeDrawState(kThemeIncDecButton, states); + drawInfo.adornment = kThemeAdornmentDefault; + ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor); + if (controlSize == NSSmallControlSize) + drawInfo.kind = kThemeIncDecButtonSmall; + else if (controlSize == NSMiniControlSize) + drawInfo.kind = kThemeIncDecButtonMini; + else + drawInfo.kind = kThemeIncDecButton; + + IntRect rect(zoomedRect); + context->save(); + if (zoomFactor != 1.0f) { + rect.setWidth(rect.width() / zoomFactor); + rect.setHeight(rect.height() / zoomFactor); + context->translate(rect.x(), rect.y()); + context->scale(FloatSize(zoomFactor, zoomFactor)); + context->translate(-rect.x(), -rect.y()); + } + CGRect bounds(rect); + // Adjust 'bounds' so that HIThemeDrawButton(bounds,...) draws exactly on 'rect'. + CGRect backgroundBounds; + HIThemeGetButtonBackgroundBounds(&bounds, &drawInfo, &backgroundBounds); + if (bounds.origin.x != backgroundBounds.origin.x) + bounds.origin.x += bounds.origin.x - backgroundBounds.origin.x; + if (bounds.origin.y != backgroundBounds.origin.y) + bounds.origin.y += bounds.origin.y - backgroundBounds.origin.y; + HIThemeDrawButton(&bounds, &drawInfo, context->platformContext(), kHIThemeOrientationNormal, 0); + context->restore(); +} + +// Theme overrides + +int ThemeChromiumMac::baselinePositionAdjustment(ControlPart part) const +{ + if (part == CheckboxPart || part == RadioPart) + return -2; + return Theme::baselinePositionAdjustment(part); +} + +FontDescription ThemeChromiumMac::controlFont(ControlPart part, const Font& font, float zoomFactor) const +{ + switch (part) { + case PushButtonPart: { + FontDescription fontDescription; + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::SerifFamily); + + NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(font)]]; + fontDescription.firstFamily().setFamily([nsFont familyName]); + fontDescription.setComputedSize([nsFont pointSize] * zoomFactor); + fontDescription.setSpecifiedSize([nsFont pointSize] * zoomFactor); + return fontDescription; + } + default: + return Theme::controlFont(part, font, zoomFactor); + } +} + +LengthSize ThemeChromiumMac::controlSize(ControlPart part, const Font& font, const LengthSize& zoomedSize, float zoomFactor) const +{ + switch (part) { + case CheckboxPart: + return checkboxSize(font, zoomedSize, zoomFactor); + case RadioPart: + return radioSize(font, zoomedSize, zoomFactor); + case PushButtonPart: + // Height is reset to auto so that specified heights can be ignored. + return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes()); +#if ENABLE(DATALIST) + case ListButtonPart: + return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, listButtonSizes()); +#endif + case InnerSpinButtonPart: + // We don't use inner spin buttons on Mac. + return LengthSize(Length(Fixed), Length(Fixed)); + case OuterSpinButtonPart: + if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto()) + return zoomedSize; + return sizeFromNSControlSize(stepperControlSizeForFont(font), zoomedSize, zoomFactor, stepperSizes()); + default: + return zoomedSize; + } +} + +LengthSize ThemeChromiumMac::minimumControlSize(ControlPart part, const Font& font, float zoomFactor) const +{ + switch (part) { + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case ListButtonPart: + return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed)); + case InnerSpinButtonPart: + // We don't use inner spin buttons on Mac. + return LengthSize(Length(Fixed), Length(Fixed)); + case OuterSpinButtonPart: { + IntSize base = stepperSizes()[NSMiniControlSize]; + return LengthSize(Length(static_cast<int>(base.width() * zoomFactor), Fixed), + Length(static_cast<int>(base.height() * zoomFactor), Fixed)); + } + default: + return Theme::minimumControlSize(part, font, zoomFactor); + } +} + +LengthBox ThemeChromiumMac::controlBorder(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const +{ + switch (part) { + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case ListButtonPart: + return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value()); + default: + return Theme::controlBorder(part, font, zoomedBox, zoomFactor); + } +} + +LengthBox ThemeChromiumMac::controlPadding(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const +{ + switch (part) { + case PushButtonPart: { + // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large + // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is + // by definition constrained, since we select mini only for small cramped environments. + // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent + // padding. + const int padding = 8 * zoomFactor; + return LengthBox(0, padding, 0, padding); + } + default: + return Theme::controlPadding(part, font, zoomedBox, zoomFactor); + } +} + +void ThemeChromiumMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + switch (part) { + case CheckboxPart: { + // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox + // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. + NSCell *cell = checkbox(states, zoomedRect, zoomFactor); + NSControlSize controlSize = [cell controlSize]; + IntSize zoomedSize = checkboxSizes()[controlSize]; + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + zoomedSize.setWidth(zoomedSize.width() * zoomFactor); + zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor); + break; + } + case RadioPart: { + // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button + // shadow". We don't consider this part of the bounds of the control in WebKit. + NSCell *cell = radio(states, zoomedRect, zoomFactor); + NSControlSize controlSize = [cell controlSize]; + IntSize zoomedSize = radioSizes()[controlSize]; + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + zoomedSize.setWidth(zoomedSize.width() * zoomFactor); + zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor); + break; + } + case PushButtonPart: + case DefaultButtonPart: + case ButtonPart: { + NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor); + NSControlSize controlSize = [cell controlSize]; + + // We inflate the rect as needed to account for the Aqua button's shadow. + if ([cell bezelStyle] == NSRoundedBezelStyle) { + IntSize zoomedSize = buttonSizes()[controlSize]; + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored. + zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor); + } + break; + } + case OuterSpinButtonPart: { + static const int stepperMargin[4] = { 0, 0, 0, 0 }; + ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor); + IntSize zoomedSize = stepperSizes()[controlSize]; + zoomedSize.setHeight(zoomedSize.height() * zoomFactor); + zoomedSize.setWidth(zoomedSize.width() * zoomFactor); + zoomedRect = inflateRect(zoomedRect, zoomedSize, stepperMargin, zoomFactor); + break; + } + default: + break; + } + END_BLOCK_OBJC_EXCEPTIONS +} + +void ThemeChromiumMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const +{ + switch (part) { + case CheckboxPart: + paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView); + break; + case RadioPart: + paintRadio(states, context, zoomedRect, zoomFactor, scrollView); + break; + case PushButtonPart: + case DefaultButtonPart: + case ButtonPart: + case SquareButtonPart: + case ListButtonPart: + paintButton(part, states, context, zoomedRect, zoomFactor, scrollView); + break; + case OuterSpinButtonPart: + paintStepper(states, context, zoomedRect, zoomFactor, scrollView); + break; + default: + break; + } +} + +} |