1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2009 Google Inc. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #import "config.h" 28 #import "ThemeChromiumMac.h" 29 30 #import "BlockExceptions.h" 31 #import "GraphicsContext.h" 32 #import "LocalCurrentGraphicsContext.h" 33 #import "ScrollView.h" 34 #import "WebCoreSystemInterface.h" 35 #include <wtf/StdLibExtras.h> 36 #import <objc/runtime.h> 37 38 using namespace std; 39 40 // This file (and its associated .h file) is a clone of ThemeMac.mm. 41 // Because the original file is designed to run in-process inside a Cocoa view, 42 // we must maintain a fork. Please maintain this file by performing parallel 43 // changes to it. 44 // 45 // The only changes from ThemeMac should be: 46 // - The classname change from ThemeMac to ThemeChromiumMac. 47 // - The import of FlippedView() and its use as the parent view for cell 48 // rendering. 49 // - In updateStates() the code to update the cells' inactive state. 50 // - In paintButton() the code to save/restore the window's default button cell. 51 // - The Snow Leopard focus ring bug fix and its use around every call to 52 // -[NSButtonCell drawWithFrame:inView:]. 53 // 54 // For all other differences, if it was introduced in this file, then the 55 // maintainer forgot to include it in the list; otherwise it is an update that 56 // should have been applied to this file but was not. 57 58 // FIXME: Default buttons really should be more like push buttons and not like buttons. 59 60 // --- START fix for Snow Leopard focus ring bug --- 61 62 // There is a bug in the Cocoa focus ring drawing code. The code calls +[NSView 63 // focusView] (to get the currently focused view) and then calls an NSRect- 64 // returning method on that view to obtain a clipping rect. However, if there is 65 // no focused view (as there won't be if the destination is a context), the rect 66 // returned from the method invocation on nil is garbage. 67 // 68 // The garbage fortunately does not clip the focus ring on Leopard, but 69 // unfortunately does so on Snow Leopard. Therefore, if a runtime test shows 70 // that focus ring drawing fails, we swizzle NSView to ensure it returns a valid 71 // view with a valid clipping rectangle. 72 // 73 // FIXME: After the referenced bug is fixed on all supported platforms, remove 74 // this code. 75 // 76 // References: 77 // <http://crbug.com/27493> 78 // <rdar://problem/7604051> (<http://openradar.appspot.com/7604051>) 79 80 @interface TCMVisibleView : NSView 81 82 @end 83 84 @implementation TCMVisibleView 85 86 - (struct CGRect)_focusRingVisibleRect 87 { 88 return CGRectZero; 89 } 90 91 - (id)_focusRingClipAncestor 92 { 93 return self; 94 } 95 96 @end 97 98 @interface NSView (TCMInterposing) 99 + (NSView *)TCMInterposing_focusView; 100 @end 101 102 namespace FocusIndicationFix { 103 104 bool currentOSHasSetFocusRingStyleInBitmapBug() 105 { 106 UInt32 pixel = 0; 107 UInt32* pixelPlane = &pixel; 108 UInt32** pixelPlanes = &pixelPlane; 109 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(UInt8**)pixelPlanes 110 pixelsWide:1 111 pixelsHigh:1 112 bitsPerSample:8 113 samplesPerPixel:4 114 hasAlpha:YES 115 isPlanar:NO 116 colorSpaceName:NSCalibratedRGBColorSpace 117 bitmapFormat:NSAlphaFirstBitmapFormat 118 bytesPerRow:4 119 bitsPerPixel:32]; 120 [NSGraphicsContext saveGraphicsState]; 121 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]]; 122 NSSetFocusRingStyle(NSFocusRingOnly); 123 NSRectFill(NSMakeRect(0, 0, 1, 1)); 124 [NSGraphicsContext restoreGraphicsState]; 125 [bitmap release]; 126 127 return !pixel; 128 } 129 130 bool swizzleFocusView() 131 { 132 if (!currentOSHasSetFocusRingStyleInBitmapBug()) 133 return false; 134 135 Class nsview = [NSView class]; 136 Method m1 = class_getClassMethod(nsview, @selector(focusView)); 137 Method m2 = class_getClassMethod(nsview, @selector(TCMInterposing_focusView)); 138 if (m1 && m2) { 139 method_exchangeImplementations(m1, m2); 140 return true; 141 } 142 143 return false; 144 } 145 146 static bool interpose = false; 147 148 // A class to restrict the amount of time spent messing with interposing. It 149 // only stacks one-deep. 150 class ScopedFixer { 151 public: 152 ScopedFixer() 153 { 154 static bool swizzled = swizzleFocusView(); 155 interpose = swizzled; 156 } 157 158 ~ScopedFixer() 159 { 160 interpose = false; 161 } 162 }; 163 164 } // namespace FocusIndicationFix 165 166 @implementation NSView (TCMInterposing) 167 168 + (NSView *)TCMInterposing_focusView 169 { 170 NSView *view = [self TCMInterposing_focusView]; // call original (was swizzled) 171 if (!view && FocusIndicationFix::interpose) { 172 static TCMVisibleView* fixedView = [[TCMVisibleView alloc] init]; 173 view = fixedView; 174 } 175 176 return view; 177 } 178 179 @end 180 181 // --- END fix for Snow Leopard focus ring bug --- 182 183 namespace WebCore { 184 185 // Pick up utility function from RenderThemeChromiumMac. 186 extern NSView* FlippedView(); 187 188 enum { 189 topMargin, 190 rightMargin, 191 bottomMargin, 192 leftMargin 193 }; 194 195 Theme* platformTheme() 196 { 197 DEFINE_STATIC_LOCAL(ThemeChromiumMac, themeMac, ()); 198 return &themeMac; 199 } 200 201 // Helper functions used by a bunch of different control parts. 202 203 static NSControlSize controlSizeForFont(const Font& font) 204 { 205 int fontSize = font.pixelSize(); 206 if (fontSize >= 16) 207 return NSRegularControlSize; 208 if (fontSize >= 11) 209 return NSSmallControlSize; 210 return NSMiniControlSize; 211 } 212 213 static LengthSize sizeFromFont(const Font& font, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes) 214 { 215 IntSize controlSize = sizes[controlSizeForFont(font)]; 216 if (zoomFactor != 1.0f) 217 controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor); 218 LengthSize result = zoomedSize; 219 if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0) 220 result.setWidth(Length(controlSize.width(), Fixed)); 221 if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0) 222 result.setHeight(Length(controlSize.height(), Fixed)); 223 return result; 224 } 225 226 static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor) 227 { 228 NSControlSize size; 229 if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) && 230 minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor)) 231 size = NSRegularControlSize; 232 else if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) && 233 minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor)) 234 size = NSSmallControlSize; 235 else 236 size = NSMiniControlSize; 237 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. 238 [cell setControlSize:size]; 239 } 240 241 static void updateStates(NSCell* cell, ControlStates states) 242 { 243 // Hover state is not supported by Aqua. 244 245 // Pressed state 246 bool oldPressed = [cell isHighlighted]; 247 bool pressed = states & PressedState; 248 if (pressed != oldPressed) 249 [cell setHighlighted:pressed]; 250 251 // Enabled state 252 bool oldEnabled = [cell isEnabled]; 253 bool enabled = states & EnabledState; 254 if (enabled != oldEnabled) 255 [cell setEnabled:enabled]; 256 257 // Focused state 258 bool oldFocused = [cell showsFirstResponder]; 259 bool focused = states & FocusState; 260 if (focused != oldFocused) 261 [cell setShowsFirstResponder:focused]; 262 263 // Checked and Indeterminate 264 bool oldIndeterminate = [cell state] == NSMixedState; 265 bool indeterminate = (states & IndeterminateState); 266 bool checked = states & CheckedState; 267 bool oldChecked = [cell state] == NSOnState; 268 if (oldIndeterminate != indeterminate || checked != oldChecked) 269 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; 270 271 // Window Inactive state 272 NSControlTint oldTint = [cell controlTint]; 273 bool windowInactive = (states & WindowInactiveState); 274 NSControlTint tint = windowInactive ? NSClearControlTint : [NSColor currentControlTint]; 275 if (tint != oldTint) 276 [cell setControlTint:tint]; 277 } 278 279 static IntRect inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor) 280 { 281 // Only do the inflation if the available width/height are too small. Otherwise try to 282 // fit the glow/check space into the available box's width/height. 283 int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[leftMargin] * zoomFactor + margins[rightMargin] * zoomFactor); 284 int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[topMargin] * zoomFactor + margins[bottomMargin] * zoomFactor); 285 IntRect result(zoomedRect); 286 if (widthDelta < 0) { 287 result.setX(result.x() - margins[leftMargin] * zoomFactor); 288 result.setWidth(result.width() - widthDelta); 289 } 290 if (heightDelta < 0) { 291 result.setY(result.y() - margins[topMargin] * zoomFactor); 292 result.setHeight(result.height() - heightDelta); 293 } 294 return result; 295 } 296 297 // Checkboxes 298 299 static const IntSize* checkboxSizes() 300 { 301 static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; 302 return sizes; 303 } 304 305 static const int* checkboxMargins(NSControlSize controlSize) 306 { 307 static const int margins[3][4] = 308 { 309 { 3, 4, 4, 2 }, 310 { 4, 3, 3, 3 }, 311 { 4, 3, 3, 3 }, 312 }; 313 return margins[controlSize]; 314 } 315 316 static LengthSize checkboxSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor) 317 { 318 // If the width and height are both specified, then we have nothing to do. 319 if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto()) 320 return zoomedSize; 321 322 // Use the font size to determine the intrinsic width of the control. 323 return sizeFromFont(font, zoomedSize, zoomFactor, checkboxSizes()); 324 } 325 326 static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor) 327 { 328 static NSButtonCell *checkboxCell; 329 if (!checkboxCell) { 330 checkboxCell = [[NSButtonCell alloc] init]; 331 [checkboxCell setButtonType:NSSwitchButton]; 332 [checkboxCell setTitle:nil]; 333 [checkboxCell setAllowsMixedState:YES]; 334 [checkboxCell setFocusRingType:NSFocusRingTypeExterior]; 335 } 336 337 // Set the control size based off the rectangle we're painting into. 338 setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor); 339 340 // Update the various states we respond to. 341 updateStates(checkboxCell, states); 342 343 return checkboxCell; 344 } 345 346 // FIXME: Share more code with radio buttons. 347 static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) 348 { 349 BEGIN_BLOCK_OBJC_EXCEPTIONS 350 351 // Determine the width and height needed for the control and prepare the cell for painting. 352 NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor); 353 354 context->save(); 355 356 NSControlSize controlSize = [checkboxCell controlSize]; 357 IntSize zoomedSize = checkboxSizes()[controlSize]; 358 zoomedSize.setWidth(zoomedSize.width() * zoomFactor); 359 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 360 IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor); 361 362 if (zoomFactor != 1.0f) { 363 inflatedRect.setWidth(inflatedRect.width() / zoomFactor); 364 inflatedRect.setHeight(inflatedRect.height() / zoomFactor); 365 context->translate(inflatedRect.x(), inflatedRect.y()); 366 context->scale(FloatSize(zoomFactor, zoomFactor)); 367 context->translate(-inflatedRect.x(), -inflatedRect.y()); 368 } 369 370 { 371 FocusIndicationFix::ScopedFixer fix; 372 [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; 373 } 374 [checkboxCell setControlView:nil]; 375 376 context->restore(); 377 378 END_BLOCK_OBJC_EXCEPTIONS 379 } 380 381 // Radio Buttons 382 383 static const IntSize* radioSizes() 384 { 385 static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; 386 return sizes; 387 } 388 389 static const int* radioMargins(NSControlSize controlSize) 390 { 391 static const int margins[3][4] = 392 { 393 { 2, 2, 4, 2 }, 394 { 3, 2, 3, 2 }, 395 { 1, 0, 2, 0 }, 396 }; 397 return margins[controlSize]; 398 } 399 400 static LengthSize radioSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor) 401 { 402 // If the width and height are both specified, then we have nothing to do. 403 if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto()) 404 return zoomedSize; 405 406 // Use the font size to determine the intrinsic width of the control. 407 return sizeFromFont(font, zoomedSize, zoomFactor, radioSizes()); 408 } 409 410 static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor) 411 { 412 static NSButtonCell *radioCell; 413 if (!radioCell) { 414 radioCell = [[NSButtonCell alloc] init]; 415 [radioCell setButtonType:NSRadioButton]; 416 [radioCell setTitle:nil]; 417 [radioCell setFocusRingType:NSFocusRingTypeExterior]; 418 } 419 420 // Set the control size based off the rectangle we're painting into. 421 setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor); 422 423 // Update the various states we respond to. 424 updateStates(radioCell, states); 425 426 return radioCell; 427 } 428 429 static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) 430 { 431 // Determine the width and height needed for the control and prepare the cell for painting. 432 NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor); 433 434 context->save(); 435 436 NSControlSize controlSize = [radioCell controlSize]; 437 IntSize zoomedSize = radioSizes()[controlSize]; 438 zoomedSize.setWidth(zoomedSize.width() * zoomFactor); 439 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 440 IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor); 441 442 if (zoomFactor != 1.0f) { 443 inflatedRect.setWidth(inflatedRect.width() / zoomFactor); 444 inflatedRect.setHeight(inflatedRect.height() / zoomFactor); 445 context->translate(inflatedRect.x(), inflatedRect.y()); 446 context->scale(FloatSize(zoomFactor, zoomFactor)); 447 context->translate(-inflatedRect.x(), -inflatedRect.y()); 448 } 449 450 BEGIN_BLOCK_OBJC_EXCEPTIONS 451 { 452 FocusIndicationFix::ScopedFixer fix; 453 [radioCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; 454 } 455 [radioCell setControlView:nil]; 456 END_BLOCK_OBJC_EXCEPTIONS 457 458 context->restore(); 459 } 460 461 // Buttons 462 463 // Buttons really only constrain height. They respect width. 464 static const IntSize* buttonSizes() 465 { 466 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; 467 return sizes; 468 } 469 470 #if ENABLE(DATALIST) 471 static const IntSize* listButtonSizes() 472 { 473 static const IntSize sizes[3] = { IntSize(21, 21), IntSize(19, 18), IntSize(17, 16) }; 474 return sizes; 475 } 476 #endif 477 478 static const int* buttonMargins(NSControlSize controlSize) 479 { 480 static const int margins[3][4] = 481 { 482 { 4, 6, 7, 6 }, 483 { 4, 5, 6, 5 }, 484 { 0, 1, 1, 1 }, 485 }; 486 return margins[controlSize]; 487 } 488 489 static void setupButtonCell(NSButtonCell *&buttonCell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor) 490 { 491 if (!buttonCell) { 492 buttonCell = [[NSButtonCell alloc] init]; 493 [buttonCell setTitle:nil]; 494 [buttonCell setButtonType:NSMomentaryPushInButton]; 495 if (states & DefaultState) 496 [buttonCell setKeyEquivalent:@"\r"]; 497 } 498 499 // Set the control size based off the rectangle we're painting into. 500 const IntSize* sizes = buttonSizes(); 501 #if ENABLE(DATALIST) 502 if (part == ListButtonPart) { 503 [buttonCell setBezelStyle:NSRoundedDisclosureBezelStyle]; 504 sizes = listButtonSizes(); 505 } else 506 #endif 507 if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) { 508 // Use the square button 509 if ([buttonCell bezelStyle] != NSShadowlessSquareBezelStyle) 510 [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle]; 511 } else if ([buttonCell bezelStyle] != NSRoundedBezelStyle) 512 [buttonCell setBezelStyle:NSRoundedBezelStyle]; 513 514 setControlSize(buttonCell, sizes, zoomedRect.size(), zoomFactor); 515 516 // Update the various states we respond to. 517 updateStates(buttonCell, states); 518 } 519 520 static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor) 521 { 522 bool isDefault = states & DefaultState; 523 static NSButtonCell *cells[2]; 524 setupButtonCell(cells[isDefault], part, states, zoomedRect, zoomFactor); 525 return cells[isDefault]; 526 } 527 528 static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) 529 { 530 BEGIN_BLOCK_OBJC_EXCEPTIONS 531 532 // Determine the width and height needed for the control and prepare the cell for painting. 533 NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor); 534 LocalCurrentGraphicsContext localContext(context); 535 536 NSControlSize controlSize = [buttonCell controlSize]; 537 #if ENABLE(DATALIST) 538 IntSize zoomedSize = (part == ListButtonPart ? listButtonSizes() : buttonSizes())[controlSize]; 539 #else 540 IntSize zoomedSize = buttonSizes()[controlSize]; 541 #endif 542 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored. 543 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 544 IntRect inflatedRect = zoomedRect; 545 if ([buttonCell bezelStyle] == NSRoundedBezelStyle) { 546 // Center the button within the available space. 547 if (inflatedRect.height() > zoomedSize.height()) { 548 inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2); 549 inflatedRect.setHeight(zoomedSize.height()); 550 } 551 552 // Now inflate it to account for the shadow. 553 inflatedRect = inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor); 554 555 if (zoomFactor != 1.0f) { 556 inflatedRect.setWidth(inflatedRect.width() / zoomFactor); 557 inflatedRect.setHeight(inflatedRect.height() / zoomFactor); 558 context->translate(inflatedRect.x(), inflatedRect.y()); 559 context->scale(FloatSize(zoomFactor, zoomFactor)); 560 context->translate(-inflatedRect.x(), -inflatedRect.y()); 561 } 562 } 563 564 { 565 FocusIndicationFix::ScopedFixer fix; 566 [buttonCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()]; 567 } 568 [buttonCell setControlView:nil]; 569 570 END_BLOCK_OBJC_EXCEPTIONS 571 } 572 573 // Theme overrides 574 575 int ThemeChromiumMac::baselinePositionAdjustment(ControlPart part) const 576 { 577 if (part == CheckboxPart || part == RadioPart) 578 return -2; 579 return Theme::baselinePositionAdjustment(part); 580 } 581 582 FontDescription ThemeChromiumMac::controlFont(ControlPart part, const Font& font, float zoomFactor) const 583 { 584 switch (part) { 585 case PushButtonPart: { 586 FontDescription fontDescription; 587 fontDescription.setIsAbsoluteSize(true); 588 fontDescription.setGenericFamily(FontDescription::SerifFamily); 589 590 NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(font)]]; 591 fontDescription.firstFamily().setFamily([nsFont familyName]); 592 fontDescription.setComputedSize([nsFont pointSize] * zoomFactor); 593 fontDescription.setSpecifiedSize([nsFont pointSize] * zoomFactor); 594 return fontDescription; 595 } 596 default: 597 return Theme::controlFont(part, font, zoomFactor); 598 } 599 } 600 601 LengthSize ThemeChromiumMac::controlSize(ControlPart part, const Font& font, const LengthSize& zoomedSize, float zoomFactor) const 602 { 603 switch (part) { 604 case CheckboxPart: 605 return checkboxSize(font, zoomedSize, zoomFactor); 606 case RadioPart: 607 return radioSize(font, zoomedSize, zoomFactor); 608 case PushButtonPart: 609 // Height is reset to auto so that specified heights can be ignored. 610 return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes()); 611 #if ENABLE(DATALIST) 612 case ListButtonPart: 613 return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, listButtonSizes()); 614 #endif 615 default: 616 return zoomedSize; 617 } 618 } 619 620 LengthSize ThemeChromiumMac::minimumControlSize(ControlPart part, const Font& font, float zoomFactor) const 621 { 622 switch (part) { 623 case SquareButtonPart: 624 case DefaultButtonPart: 625 case ButtonPart: 626 case ListButtonPart: 627 return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed)); 628 default: 629 return Theme::minimumControlSize(part, font, zoomFactor); 630 } 631 } 632 633 LengthBox ThemeChromiumMac::controlBorder(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const 634 { 635 switch (part) { 636 case SquareButtonPart: 637 case DefaultButtonPart: 638 case ButtonPart: 639 case ListButtonPart: 640 return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value()); 641 default: 642 return Theme::controlBorder(part, font, zoomedBox, zoomFactor); 643 } 644 } 645 646 LengthBox ThemeChromiumMac::controlPadding(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const 647 { 648 switch (part) { 649 case PushButtonPart: { 650 // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large 651 // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is 652 // by definition constrained, since we select mini only for small cramped environments. 653 // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent 654 // padding. 655 const int padding = 8 * zoomFactor; 656 return LengthBox(0, padding, 0, padding); 657 } 658 default: 659 return Theme::controlPadding(part, font, zoomedBox, zoomFactor); 660 } 661 } 662 663 void ThemeChromiumMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const 664 { 665 BEGIN_BLOCK_OBJC_EXCEPTIONS 666 switch (part) { 667 case CheckboxPart: { 668 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox 669 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. 670 NSCell *cell = checkbox(states, zoomedRect, zoomFactor); 671 NSControlSize controlSize = [cell controlSize]; 672 IntSize zoomedSize = checkboxSizes()[controlSize]; 673 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 674 zoomedSize.setWidth(zoomedSize.width() * zoomFactor); 675 zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor); 676 break; 677 } 678 case RadioPart: { 679 // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button 680 // shadow". We don't consider this part of the bounds of the control in WebKit. 681 NSCell *cell = radio(states, zoomedRect, zoomFactor); 682 NSControlSize controlSize = [cell controlSize]; 683 IntSize zoomedSize = radioSizes()[controlSize]; 684 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 685 zoomedSize.setWidth(zoomedSize.width() * zoomFactor); 686 zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor); 687 break; 688 } 689 case PushButtonPart: 690 case DefaultButtonPart: 691 case ButtonPart: { 692 NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor); 693 NSControlSize controlSize = [cell controlSize]; 694 695 // We inflate the rect as needed to account for the Aqua button's shadow. 696 if ([cell bezelStyle] == NSRoundedBezelStyle) { 697 IntSize zoomedSize = buttonSizes()[controlSize]; 698 zoomedSize.setHeight(zoomedSize.height() * zoomFactor); 699 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored. 700 zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor); 701 } 702 break; 703 } 704 default: 705 break; 706 } 707 END_BLOCK_OBJC_EXCEPTIONS 708 } 709 710 void ThemeChromiumMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const 711 { 712 switch (part) { 713 case CheckboxPart: 714 paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView); 715 break; 716 case RadioPart: 717 paintRadio(states, context, zoomedRect, zoomFactor, scrollView); 718 break; 719 case PushButtonPart: 720 case DefaultButtonPart: 721 case ButtonPart: 722 case SquareButtonPart: 723 case ListButtonPart: 724 paintButton(part, states, context, zoomedRect, zoomFactor, scrollView); 725 break; 726 default: 727 break; 728 } 729 } 730 731 } 732